Compare commits

...
3 Commits
Author SHA1 Message Date
thePR0M3TH3AN e6ff21c069 chore: release v0.3.6 2026-03-17 16:16:17 -04:00
thePR0M3TH3AN 1fcb86ac8b chore: release v0.3.5 2026-03-17 15:35:17 -04:00
thePR0M3TH3AN 2b29d71ac5 chore: release v0.3.4 2026-03-17 14:18:42 -04:00
82 changed files with 857 additions and 378 deletions

No files matched your search

+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
--add-data "voxvera/locales${SEP}voxvera/locales" \
--add-data "voxvera/src${SEP}voxvera/src" \
--add-data "voxvera/templates${SEP}voxvera/templates" \
--add-data "voxvera/resources${SEP}voxvera/resources" \
--add-data "voxvera/resources/tor${SEP}voxvera/resources/tor" \
--add-data "requirements.txt${SEP}." \
--name "${{ matrix.artifact_name }}" \
voxvera/cli.py
+7 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,14 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Recommended local check**: Run `bash scripts/check.sh` before opening a PR. It mirrors the main lint/test/build checks in one command.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+7
View File
@@ -30,6 +30,13 @@ pip install .
voxvera vendorize # Optional: pull dependencies locally for offline use
```
### 5. Local Validation
Run the full local validation sweep with:
```bash
bash scripts/check.sh
```
This runs linting, Python tests, Electron smoke tests, dependency checks, and the docs/site rebuild steps used during development.
---
## Key Features
+1
View File
@@ -53,6 +53,7 @@ Generates an architecture-specific binary (e.g., `voxvera-linux-x86_64`) in `vox
```bash
./scripts/build_binaries.sh
```
This directory is local build output only. Do not treat old files in `voxvera/resources/bin/` as canonical release history; the GitHub Release artifacts are the publish surface.
### AppImage (Linux)
Wraps the standard binary into a self-contained AppImage.
-1
View File
@@ -1 +0,0 @@
+13 -4
View File
@@ -15,9 +15,12 @@ echo "Starting E2E verification at $(date)"
if [ -n "${VOXVERA_BIN:-}" ] && [ -f "$VOXVERA_BIN" ]; then
voxvera_cmd="$VOXVERA_BIN"
echo "Using provided VoxVera binary: $voxvera_cmd" >&2
elif command -v voxvera >/dev/null 2>&1; then
voxvera_cmd="$(command -v voxvera)"
echo "Using existing VoxVera on PATH: $voxvera_cmd" >&2
else
echo "Installing VoxVera in editable mode..."
python3 -m pip install --break-system-packages -e ".[e2e]"
python3 -m pip install --break-system-packages --no-build-isolation -e ".[e2e]"
voxvera_cmd="$(command -v voxvera || echo "$HOME/.local/bin/voxvera")"
fi
@@ -30,16 +33,22 @@ DemoUser
voxvera
EOI
config_path="$HOME/host/voxvera/config.json"
if [ ! -f "$config_path" ]; then
echo "CRITICAL: Expected config file not found after init: $config_path" >&2
exit 1
fi
echo "Building demo flyer..."
"$voxvera_cmd" --lang en build
"$voxvera_cmd" --lang en --config "$config_path" build
# Verify build output exists in host/
folder_name="voxvera"
host_dir="voxvera/host/$folder_name"
host_dir="$HOME/host/$folder_name"
if [ ! -d "$host_dir" ]; then
echo "CRITICAL: Host directory $host_dir not found!" >&2
ls -R voxvera/host/ || true
ls -R "$HOME/host/" || true
exit 1
fi
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+6 -4
View File
@@ -16,9 +16,10 @@ Create a new JSON file in `voxvera/locales/{lang_code}.json`.
- **Formatting**: You can use Markdown-style strike-throughs (`~~redacted text~~`) in any of these strings; they will be automatically converted to the stylized "redacted" effect on the flyers.
### 2. The Landing Page
The main landing page (`site/index.html`) must be synchronized to include your new language.
- Open `site/index.html` and add your language to the `const locales` object in the `<script>` tag.
- Run `voxvera build-site` to refresh the public assets and ensure the pre-rendered HTML matches your new locale.
The main landing page is generated from the shared template and locale files.
- Add your language in `voxvera/locales/{lang_code}.json`.
- Do **not** edit `site/index.html` directly; it is generated output.
- Run `voxvera build-site` to refresh the public assets and inject your new locale into the generated HTML.
### 3. Documentation
VoxVera uses the **Doc-Sync Engine** to keep manuals in sync.
@@ -31,12 +32,13 @@ Run the following commands to verify your changes:
- `voxvera --lang {lang_code} check` (Verify CLI translation)
- `voxvera build-docs` (Generate localized manuals)
- `voxvera build-site` (Synchronize the public website and generate the latest portable bundle)
- Open `site/index.html` in a browser and test the language switcher.
- Open the generated `site/index.html` in a browser and test the language switcher.
## Code Contributions
- **Dependencies**: if you add a new Python library to `requirements.txt`, you MUST run `voxvera vendorize` to include it in the portable distribution.
- **Layout Integrity**: VoxVera uses visual width validation. If you add new UI elements, ensure they respect the physical boundaries of an 8.5"x11" flyer.
- **Tests**: Ensure all tests pass: `pytest tests/`. Run the integrity suite specifically when modifying locales: `pytest tests/test_integrity.py`.
- **Generated artifacts**: Treat `voxvera/resources/bin/` as local build output, not source files. Release binaries belong in GitHub Releases, not hand-edited repo state.
- **Style**: Follow the existing coding style (PEP 8 for Python).
- **Documentation**: If adding a new feature, update the master templates in `docs/templates/` and run `voxvera build-docs`.
+2 -2
View File
@@ -26,7 +26,7 @@
<div><label>URL Message <input type="text" id="url_message"></label></div>
<div><label>URL <input type="text" id="url"></label></div>
<div><label>Tear-off link <input type="text" id="tear_off_link"></label></div>
<div><label>Binary message <input type="text" id="binary_message"></label></div>
<div><label>Footer message <input type="text" id="footer_message"></label></div>
<button type="button" id="quickstart">Generate &amp; Serve</button>
</form>
<p id="onion-address"></p>
@@ -42,7 +42,7 @@
document.addEventListener('DOMContentLoaded', load);
document.getElementById('quickstart').addEventListener('click', async () => {
const ids = ['name','folder_name','title','subtitle','headline','content','url_message','url','tear_off_link','binary_message'];
const ids = ['name','folder_name','title','subtitle','headline','content','url_message','url','tear_off_link','footer_message'];
const cfg = {};
ids.forEach(id => {
const el = document.getElementById(id);
+16 -6
View File
@@ -4,6 +4,7 @@ const path = require('path');
const which = require('which');
const fs = require('fs');
const { launchTor } = require('./tor.js');
const { extractOnionUrl } = require('./runtime-utils');
let mainWindow;
let onionProc;
@@ -74,8 +75,12 @@ async function runServe (retry = false) {
onionProc.stdout.on('data', buf => {
const line = buf.toString();
const m = line.match(/OnionShare is hosting at (http.*\.onion)/);
if (m) { gotURL = true; clearTimeout(softTimeout); }
const onionUrl = extractOnionUrl(line);
if (onionUrl) {
gotURL = true;
clearTimeout(softTimeout);
mainWindow.webContents.send('onion-url', onionUrl);
}
mainWindow.webContents.send('log', { text: line });
});
@@ -105,7 +110,12 @@ function getConfigPath() {
ipcMain.handle('load-config', async () => {
const p = getConfigPath();
const raw = fs.readFileSync(p, 'utf8');
return JSON.parse(raw);
const config = JSON.parse(raw);
if (config.footer_message === undefined && config.binary_message !== undefined) {
config.footer_message = config.binary_message;
}
delete config.binary_message;
return config;
});
ipcMain.handle('run-quickstart', async (_, config) => {
@@ -134,9 +144,9 @@ ipcMain.handle('run-quickstart', async (_, config) => {
if (mainWindow) {
mainWindow.webContents.send('log', { text: line, isError: false });
}
const m = line.match(/Onion URL:\s*(https?:\/\/[a-z0-9.-]+\.onion)/i);
if (m && mainWindow) {
mainWindow.webContents.send('onion-url', m[1]);
const onionUrl = extractOnionUrl(line);
if (onionUrl && mainWindow) {
mainWindow.webContents.send('onion-url', onionUrl);
}
});
proc.stderr.on('data', data => {
+2 -1
View File
@@ -4,7 +4,8 @@
"main": "main.js",
"scripts": {
"start": "electron .",
"lint": "echo 'lint pass'"
"lint": "echo 'lint pass'",
"test": "node --test runtime-utils.test.js"
},
"devDependencies": {
"electron": "^29.0.0",
+16
View File
@@ -0,0 +1,16 @@
function extractOnionUrl(line) {
const match = line.match(/Onion URL:\s*(https?:\/\/[a-z0-9.-]+\.onion)/i)
|| line.match(/OnionShare is hosting at\s*(https?:\/\/[a-z0-9.-]+\.onion)/i);
return match ? match[1] : null;
}
function platformDirName(platform = process.platform) {
if (platform === 'win32') return 'win';
if (platform === 'darwin') return 'mac';
return 'linux';
}
module.exports = {
extractOnionUrl,
platformDirName,
};
+28
View File
@@ -0,0 +1,28 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const { extractOnionUrl, platformDirName } = require('./runtime-utils');
test('extractOnionUrl handles current CLI output', () => {
assert.equal(
extractOnionUrl('[voxvera] Onion URL: http://test123.onion'),
'http://test123.onion'
);
});
test('extractOnionUrl handles legacy OnionShare output', () => {
assert.equal(
extractOnionUrl('OnionShare is hosting at http://legacy456.onion'),
'http://legacy456.onion'
);
});
test('extractOnionUrl returns null when no onion URL is present', () => {
assert.equal(extractOnionUrl('still bootstrapping'), null);
});
test('platformDirName maps bundled resource folders correctly', () => {
assert.equal(platformDirName('linux'), 'linux');
assert.equal(platformDirName('darwin'), 'mac');
assert.equal(platformDirName('win32'), 'win');
});
+4 -2
View File
@@ -3,14 +3,16 @@ const path = require('path');
const fs = require('fs');
const which = require('which');
const getPort = (...args) => import('get-port').then(m => m.default(...args));
const { platformDirName } = require('./runtime-utils');
async function launchTor() {
const socks = await getPort();
const control = await getPort();
const torBase = path.join(__dirname, '..', '..', 'voxvera', 'resources', 'tor');
let exe = path.join(torBase, process.platform,
const platformDir = platformDirName(process.platform);
let exe = path.join(torBase, platformDir,
process.platform === 'win32' ? 'tor.exe' : 'tor');
let obfs4 = path.join(torBase, process.platform,
let obfs4 = path.join(torBase, platformDir,
process.platform === 'win32' ? 'obfs4proxy.exe' : 'obfs4proxy');
const missing = p => {
+2 -2
View File
@@ -7,6 +7,6 @@
"content": "The Android Open Source Project (AOSP) represents the essence of what Android was originally intended to be: an open, transparent, and adaptable platform. It strips away the layers of proprietary software and corporate oversight that have become synonymous with mainstream Android distributions. With AOSP, you gain access to a raw, unfiltered version of Android, allowing you to truly make your device your own.<br><br>For developers, AOSP offers a playground of possibilities, where the only limits are your skills and imagination. It serves as the foundation for countless custom ROMs, each tailored to specific needs, whether for enhanced performance, privacy, or unique features that mainstream versions lack. Privacy enthusiasts will appreciate the absence of pre-installed bloatware and tracking services that often come wit stock Android, providing a cleaner, more secure user experience.<br><br>AOSP isn't just for developers or tech enthusiasts; it's for anyone who values digital freedom. It allows you to reclaim control over your device, tailoring it to your needs without the restrictions of carriers or manufacturers. Discover how AOSP can transform your mobile experience. Start your journey towards a more open, customizable future today.",
"url_message": "Visit the link below to learn more.",
"url": "https://source.android.com",
"tear_off_link": "http://aosp.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+3 -3
View File
@@ -7,6 +7,6 @@
"content": "What is Bitcoin? It's more than just digital money; it's a revolutionary technology designed to fix some of the world's most pressing problems. Bitcoin operates on a decentralized network, free from the control of any government or central authority. This ensures that no one can manipulate its supply or value, making it a sound form of money that protects your wealth from inflation.<br><br>Our current financial system is built on flawed incentives—encouraging debt, dependency, and centralization of power. Bitcoin changes that by creating a system where honesty, transparency, and individual responsibility are rewarded. In the Bitcoin network, there is no room for corruption or manipulation because the rules are enforced by mathematics, not by people.<br><br>Bitcoin aligns incentives in a way that promotes long-term thinking, savings, and innovation. It empowers individuals to take control of their financial future, fostering a global community where value can be transferred freely and securely without intermediaries. This is why we need Bitcoin—because it creates a world where incentives are properly aligned, leading to a more fair and prosperous society for all.<br><br>Take the step to learn more about Bitcoin and how it can transform not just your financial life, but the world. Tear off a link to the left and explore this groundbreaking technology. Join the movement towards a better future.",
"url_message": "Follow this link to learn more.",
"url": "https://bitcoin.org/en",
"tear_off_link": "http://bitcoin.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+1 -1
View File
@@ -8,5 +8,5 @@
"url_message": "",
"url": "",
"tear_off_link": "",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+3 -3
View File
@@ -7,6 +7,6 @@
"content": "content",
"url_message": "url message",
"url": "url",
"tear_off_link": "http://example-folder_name.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+3 -3
View File
@@ -7,6 +7,6 @@
"content": "Who is Jesus? He is more than just a historical figure; He is the Son of God, the Savior of the world. Jesus Himself said, 'I am the way, the truth, and the life. No one comes to the Father except through Me' (John 14:6). He came to bridge the gap between God and humanity, offering us a path to forgiveness, peace, and eternal life.<br><br>We all face brokenness in our lives—whether it's in our relationships, our hearts, or our world. Jesus came to heal that brokenness. He lived a perfect life, died on the cross for our sins, and rose again, defeating death and offering us the hope of eternal life with God.<br><br>Without Jesus, we are lost in our sin, separated from God. But through Him, we can be forgiven and restored. He offers us not just a better life now, but the promise of an eternal life with Him. This is why we need Him—because only in Jesus can we find true peace, purpose, and the assurance of eternal life.<br><br>Take the step to learn more about Jesus and the life-changing impact He can have on your life. Tear off a link to the left and explore His teachings. Find the hope you've been searching for.",
"url_message": "Follow this link to learn more.",
"url": "http://media.faithforthefamily.com/ra/godsoloved/english.mp3",
"tear_off_link": "http://jesus.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+3 -3
View File
@@ -7,6 +7,6 @@
"content": "Why Meshtastic? In times of crisis, traditional communication networks can fail, leaving us isolated and vulnerable. Meshtastic provides a powerful solution—an alternative local communication network that operates independently of centralized systems. It’s designed for emergency situations, allowing you to stay connected when it matters most.<br><br>Meshtastic devices create a mesh network that can relay messages over long distances, even without internet or cellular coverage. This decentralized approach ensures that your communications are resilient and reliable, no matter the circumstances. But it’s not just about staying connected—Meshtastic also offers encryption, so your messages remain secure and private.<br><br>In a world where privacy is increasingly compromised, Meshtastic empowers you to take control of your communication. Whether you’re coordinating a local community response, staying in touch with loved ones during a disaster, or simply preparing for the unexpected, Meshtastic provides the tools you need to communicate securely and effectively.<br><br>Take the step to learn more about Meshtastic and how you can build your own emergency communication network. Tear off a link to the left and explore this innovative technology. Ensure you’re prepared for whatever comes your way.",
"url_message": "Follow this link to learn more.",
"url": "https://meshtastic.org",
"tear_off_link": "http://meshtastic.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+3 -3
View File
@@ -7,6 +7,6 @@
"content": "What is Nostr? Unlike traditional social media platforms, Nostr is a protocol—a decentralized system that empowers users to connect and share without the constraints of a central authority. Nostr is censorship-resistant, meaning your voice can’t be silenced by corporations or governments. It’s designed to give you control over your online presence, free from the influence of algorithms or ads that prioritize engagement over meaningful connections.<br><br>On Nostr, there are no hidden agendas to keep you doomscrolling. You engage with content on your terms, not based on what an algorithm thinks will keep you online the longest. Nostr allows you to build genuine connections and participate in conversations that matter, all while maintaining your privacy and autonomy.<br><br>In a world where online speech is increasingly controlled and manipulated, Nostr offers a refreshing alternative. It’s not just a platform; it’s a movement towards true digital freedom, where your interactions are driven by your interests and values, not by what generates the most ad revenue.<br><br>Take the step to learn more about Nostr and how it can transform the way you connect online. Tear off a link the left and explore this groundbreaking protocol. Join a community that values freedom and authentic interaction.",
"url_message": "Follow this link to learn more.",
"url": "https://nostr.how/en/what-is-nostr",
"tear_off_link": "http://nostr.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion/",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+4 -4
View File
@@ -5,8 +5,8 @@
"subtitle": "DO ~~NOT~~ DISTRIBUTE",
"headline": "OPERATION VOX VERA",
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Follow this link to learn more. Use Tor Browser.",
"url_message": "Follow this link to learn more.",
"url": "https://voxvera.org/",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
}
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
+11 -3
View File
@@ -6,13 +6,20 @@ cd "$(dirname "$0")/.."
ARCH=$(uname -m)
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
OUTPUT="voxvera/resources/bin/VoxVera-$ARCH.AppImage"
if [ ! -f "$BINARY" ]; then
echo "Binary not found: $BINARY. Run scripts/build_binaries.sh first." >&2
echo "Binary not found: $BINARY. Building it first..."
bash scripts/build_binaries.sh
fi
if [ ! -f "$BINARY" ]; then
echo "Binary still not found after build: $BINARY." >&2
exit 1
fi
APPDIR=build/AppDir
rm -rf "$APPDIR"
mkdir -p "$APPDIR/usr/bin"
cp "$BINARY" "$APPDIR/usr/bin/voxvera"
chmod +x "$APPDIR/usr/bin/voxvera"
@@ -46,7 +53,8 @@ chmod +x appimagetool
echo "Running appimagetool..."
export ARCH
mkdir -p voxvera/resources/bin
./appimagetool --appimage-extract-and-run "$APPDIR" voxvera/resources/bin/VoxVera-x86_64.AppImage
rm -f "$OUTPUT"
./appimagetool --appimage-extract-and-run "$APPDIR" "$OUTPUT"
rm appimagetool
echo "AppImage created at voxvera/resources/bin/VoxVera-x86_64.AppImage"
echo "AppImage created at $OUTPUT"
+10 -3
View File
@@ -16,13 +16,19 @@ fi
BINARY=${1:-"voxvera/resources/bin/voxvera-linux-$ARCH"}
if [ ! -f "$BINARY" ]; then
echo "Binary not found: $BINARY." >&2
echo "Binary not found: $BINARY. Building it first..."
bash scripts/build_binaries.sh
fi
if [ ! -f "$BINARY" ]; then
echo "Binary still not found after build: $BINARY." >&2
exit 1
fi
VERSION=$(grep "__version__" voxvera/__init__.py | cut -d '"' -f 2)
PKG_DIR="build/voxvera_${VERSION}_${DEB_ARCH}"
OUTPUT="voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
echo "Building .deb package for version $VERSION ($DEB_ARCH)..."
@@ -40,6 +46,7 @@ sed -i "s/^Architecture: .*/Architecture: $DEB_ARCH/" "$PKG_DIR/DEBIAN/control"
# Build the package
mkdir -p voxvera/resources/bin
dpkg-deb --build "$PKG_DIR" "voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
rm -f "$OUTPUT"
dpkg-deb --build "$PKG_DIR" "$OUTPUT"
echo "Debian package created at voxvera/resources/bin/voxvera_${VERSION}_${DEB_ARCH}.deb"
echo "Debian package created at $OUTPUT"
@@ -1,5 +1,5 @@
$toolsDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera.exe'
$url = 'https://github.com/PR0M3TH3AN/VoxVera/releases/latest/download/voxvera-windows.exe'
$packageArgs = @{
packageName = 'voxvera'
+1 -1
View File
@@ -14,7 +14,7 @@ voxvera = ["templates/**", "src/**", "resources/**", "locales/**", "resources/bi
[project]
name = "voxvera"
version = "0.3.3"
version = "0.3.6"
description = "VoxVera CLI utilities"
readme = "README.md"
authors = [{name = "VoxVera"}]
+16 -6
View File
@@ -13,9 +13,19 @@ if ! command -v pyinstaller &> /dev/null; then
pip install pyinstaller --break-system-packages
fi
ARTIFACT_DIR="voxvera/resources/bin"
BUILD_BINARY_NAME="voxvera-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
# Clean previous builds
rm -rf build/ dist_bin/
mkdir -p voxvera/resources/bin
python3 - <<'PY'
import shutil
from pathlib import Path
for path in (Path("build"), Path("dist_bin")):
if path.exists():
shutil.rmtree(path, ignore_errors=True)
PY
mkdir -p "$ARTIFACT_DIR"
echo "Building VoxVera binary for $(uname)..."
@@ -34,11 +44,11 @@ $PY_CMD --onefile \
--add-data "voxvera/locales:voxvera/locales" \
--add-data "voxvera/src:voxvera/src" \
--add-data "voxvera/templates:voxvera/templates" \
--add-data "voxvera/resources:voxvera/resources" \
--add-data "voxvera/resources/tor:voxvera/resources/tor" \
--add-data "requirements.txt:." \
--name "$BINARY_NAME" \
--distpath voxvera/resources/bin \
--distpath "$ARTIFACT_DIR" \
voxvera/cli.py
echo "Binary build complete. Located in voxvera/resources/bin/"
ls -lh voxvera/resources/bin/
echo "Binary build complete. Located in $ARTIFACT_DIR/"
ls -lh "$ARTIFACT_DIR"/
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
echo "==> flake8"
python3 -m flake8 voxvera tests \
--jobs=1 \
--exclude=voxvera/vendor \
--ignore=E501,W503,W293,E402,E306,E704,E261,F841,E302,F401,E401,F541
echo "==> pytest"
python3 -m pytest tests/ -v
echo "==> electron smoke tests"
(cd gui/electron && npm test)
echo "==> dependency check"
python3 -m voxvera.cli --lang en check
echo "==> build docs"
python3 -m voxvera.cli build-docs
echo "==> build site"
python3 -m voxvera.cli build-site
echo "All local checks passed."
+2 -2
View File
@@ -7,6 +7,6 @@
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Get VoxVera and start spreading your message.",
"url": "https://github.com/PR0M3TH3AN/VoxVera",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
"tear_off_link": "http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
Binary file not shown.
Binary file not shown.
+82 -37
View File
@@ -40,12 +40,13 @@
[dir="rtl"] .qr-code-body {
flex-direction: row-reverse;
justify-content: flex-start;
}
[dir="rtl"] .qr-code-url {
text-align: right;
margin-right: 0;
margin-left: 10px;
padding-right: 4px;
padding-left: 0;
}
[dir="rtl"] .message {
@@ -94,6 +95,7 @@
.print-button, .download-button, .lang-select {
padding: 10px 20px;
min-height: 42px;
font-size: 14px;
cursor: pointer;
background-color: #000;
@@ -101,6 +103,12 @@
border: none;
border-radius: 0px;
font-family: 'Courier New', Courier, monospace;
line-height: 1.2;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
white-space: nowrap;
}
.lang-select {
@@ -127,6 +135,7 @@
.tear-off {
display: flex;
height: 1in; /* Fixed height for consistency */
gap: 12px;
padding: 5px;
border: 1px dashed #000;
font-size: 10px;
@@ -139,9 +148,11 @@
.tear-off-text {
flex: 1;
min-width: 0;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
padding-top: 1px;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
}
.tear-off a {
@@ -157,7 +168,6 @@
width: 80px;
height: 80px;
border: 0px solid #000;
margin-left: 10px;
flex-shrink: 0;
}
@@ -240,8 +250,9 @@
width: 100%;
height: auto;
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
gap: 10px;
margin-top: 10px;
padding: 0;
box-sizing: border-box;
@@ -257,12 +268,14 @@
.qr-code-url {
font-size: 12px;
text-align: left;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
flex-grow: 1;
margin-right: 10px;
max-width: 60%;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
flex: 1 1 auto;
min-width: 0;
max-width: none;
padding-left: 4px;
}
hr {
@@ -298,6 +311,14 @@
margin-top: 5px;
}
.container.no-tear-offs .left-tear-offs {
display: none;
}
.container.no-tear-offs .content {
width: 100%;
}
@media print {
body, html {
width: 8.5in;
@@ -330,10 +351,20 @@
.button-bar {
display: flex;
align-items: stretch;
gap: 10px;
padding: 20px 0 30px;
}
#download-link {
display: inline-flex;
text-decoration: none;
}
#download-link .download-button {
height: 100%;
}
/* Dropdown styles */
.dropdown {
position: relative;
@@ -382,7 +413,7 @@
</style>
<script>
// VoxVera Multi-Language Engine
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f", "download_attachment": "Download Attachment"}, "landing": {"title": "\u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629", "subtitle": "~~\u0644\u0627~~ \u0644\u0644\u0646\u0634\u0631", "headline": "\u0639\u0645\u0644\u064a\u0629 VOX VERA", "content": "\u062a\u062d\u0631\u0631 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629 \u0645\u0639 VoxVera. \u0623\u062f\u0627\u0629 \u062a\u0633\u0648\u064a\u0642 \u0639\u0635\u0627\u0628\u0627\u062a \u0645\u062c\u0647\u0648\u0644\u0629 \u0648\u0646\u0634\u0631 \u0631\u0633\u0627\u0626\u0644. \u0633\u0648\u0627\u0621 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0623\u0648 \u0641\u064a \u0627\u0644\u0639\u0627\u0644\u0645 \u0627\u0644\u0645\u0627\u062f\u064a\u060c \u064a\u0645\u0643\u0646\u0647\u0627 \u062a\u0645\u0643\u064a\u0646\u0643 \u0645\u0646 \u0646\u0634\u0631 \u0623\u0641\u0643\u0627\u0631\u0643 \u0628\u062c\u0631\u0623\u0629\u060c \u0645\u062d\u0645\u064a\u0629 \u0628\u0625\u062e\u0641\u0627\u0621 \u0627\u0644\u0647\u0648\u064a\u0629 \u0627\u0644\u0643\u0627\u0645\u0644 (\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0633\u062a\u0636\u064a\u0641 \u0639\u0628\u0631 Tor). \u0642\u0645 \u0628\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0643\u0648\u062f\u060c \u0648\u0635\u0645\u0645 \u0645\u0648\u0642\u0639 \u0645\u0646\u0634\u0648\u0631\u060c \u0648\u0627\u0633\u062a\u0636\u064a\u0641\u0647 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0642\u0645 \u0628\u062a\u0636\u062e\u064a\u0645 \u0631\u0633\u0627\u0644\u062a\u0643.<br><br>\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0645\u064a\u0645\u064a\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0623\u0641\u0643\u0627\u0631\u0643 \u0641\u064a \u0645\u062f\u0631\u0633\u062a\u0643\u060c \u0623\u0648 \u0645\u0643\u0627\u0646 \u0639\u0645\u0644\u0643\u060c \u0623\u0648 \u0645\u062c\u062a\u0645\u0639\u0627\u062a\u0643 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a\u060c \u0623\u0648 \u062d\u062a\u0649 \u0639\u0644\u0649 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0639\u0627\u0644\u0645. \u062a\u0636\u0645\u0646 VoxVera \u062a\u0631\u062f\u062f \u0635\u062f\u0649 \u0631\u0633\u0627\u0644\u062a\u0643 \u0628\u0639\u064a\u062f\u0627\u064b \u0648\u0639\u0644\u0649 \u0646\u0637\u0627\u0642 \u0648\u0627\u0633\u0639\u060c \u0645\u0639 \u0623\u0642\u0633\u0627\u0645 \u0642\u0627\u0628\u0644\u0629 \u0644\u0644\u0646\u0632\u0639 \u062a\u062a\u0645\u064a\u0632 \u0628\u0631\u0648\u0627\u0628\u0637 \u0641\u0631\u064a\u062f\u0629 \u0648\u0623\u0643\u0648\u0627\u062f QR \u0644\u0633\u0647\u0648\u0644\u0629 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0637\u0628\u0627\u0639\u0629.<br><br>\u064a\u0645\u0643\u0646 \u0627\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629. \u064a\u0645\u0643\u0646 \u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u0645\u0646\u0634\u0648\u0631\u0627\u062a \u0639\u0628\u0631 \u0634\u0628\u0643\u0629 Tor\u060c \u0645\u0645\u0627 \u064a\u062d\u0645\u064a \u0627\u0644\u0645\u0636\u064a\u0641 \u0648\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645\u064a\u0646 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629.<br><br>\u0627\u0646\u0636\u0645 \u0625\u0644\u064a\u0646\u0627 \u0641\u064a \u062b\u0648\u0631\u0629 \u062a\u0642\u062f\u0631 \u0627\u0644\u062d\u0642\u064a\u0642\u0629 \u0648\u0627\u0644\u0634\u0641\u0627\u0641\u064a\u0629. \u0645\u0639\u0627\u064b\u060c \u064a\u0645\u0643\u0646\u0646\u0627 \u0628\u0646\u0627\u0621 \u0634\u0628\u0643\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0637\u0646\u064a\u0646 \u0627\u0644\u0645\u0633\u062a\u0646\u064a\u0631\u064a\u0646 \u0627\u0644\u0630\u064a\u0646 \u0644\u0627 \u064a\u062e\u0634\u0648\u0646 \u0627\u0644\u062a\u062d\u062f\u062b.", "url_message": "\u0627\u062d\u0635\u0644 \u0639\u0644\u0649 VoxVera \u0648\u0627\u0628\u062f\u0623 \u0628\u0646\u0634\u0631 \u0631\u0633\u0627\u0644\u062a\u0643.", "name": "\u0645\u0646\u0634\u0648\u0631\u0627\u062a Vox Vera \u0627\u0644Line truncated
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f", "download_attachment": "\u062a\u0646\u0632\u064a\u0644 \u0627\u0644\u0645\u0631\u0641\u0642", "tor_browser_download_url": "https://www.torproject.org/ar/download/"}, "landing": {"title": "\u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629", "subtitle": "~~\u0644\u0627~~ \u0644\u0644\u0646\u0634\u0631", "headline": "\u0639\u0645\u0644\u064a\u0629 VOX VERA", "content": "\u062a\u062d\u0631\u0631 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629 \u0645\u0639 VoxVera. \u0623\u062f\u0627\u0629 \u062a\u0633\u0648\u064a\u0642 \u0639\u0635\u0627\u0628\u0627\u062a \u0645\u062c\u0647\u0648\u0644\u0629 \u0648\u0646\u0634\u0631 \u0631\u0633\u0627\u0626\u0644. \u0633\u0648\u0627\u0621 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0623\u0648 \u0641\u064a \u0627\u0644\u0639\u0627\u0644\u0645 \u0627\u0644\u0645\u0627\u062f\u064a\u060c \u064a\u0645\u0643\u0646\u0647\u0627 \u062a\u0645\u0643\u064a\u0646\u0643 \u0645\u0646 \u0646\u0634\u0631 \u0623\u0641\u0643\u0627\u0631\u0643 \u0628\u062c\u0631\u0623\u0629\u060c \u0645\u062d\u0645\u064a\u0629 \u0628\u0625\u062e\u0641\u0627\u0621 \u0627\u0644\u0647\u0648\u064a\u0629 \u0627\u0644\u0643\u0627\u0645\u0644 (\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0633\u062a\u0636\u064a\u0641 \u0639\u0628\u0631 Tor). \u0642\u0645 \u0628\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0643\u0648\u062f\u060c \u0648\u0635\u0645\u0645 \u0645\u0648\u0642\u0639 \u0645\u0646\u0634\u0648\u0631\u060c \u0648\u0627\u0633\u062a\u0636\u064a\u0641\u0647 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0642\u0645 \u0628\u062a\u0636\u062e\u064a\u0645 \u0631\u0633\u0627\u0644\u062a\u0643.<br><br>\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0645\u064a\u0645\u064a\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0623\u0641\u0643\u0627\u0631\u0643 \u0641\u064a \u0645\u062f\u0631\u0633\u062a\u0643\u060c \u0623\u0648 \u0645\u0643\u0627\u0646 \u0639\u0645\u0644\u0643\u060c \u0623\u0648 \u0645\u062c\u062a\u0645\u0639\u0627\u062a\u0643 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a\u060c \u0623\u0648 \u062d\u062a\u0649 \u0639\u0644\u0649 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0639\u0627\u0644\u0645. \u062a\u0636\u0645\u0646 VoxVera \u062a\u0631\u062f\u062f \u0635\u062f\u0649 \u0631\u0633\u0627\u0644\u062a\u0643 \u0628\u0639\u064a\u062f\u0627\u064b \u0648\u0639\u0644\u0649 \u0646\u0637\u0627\u0642 \u0648\u0627\u0633\u0639\u060c \u0645\u0639 \u0623\u0642\u0633\u0627\u0645 \u0642\u0627\u0628\u0644\u0629 \u0644\u0644\u0646\u0632\u0639 \u062a\u062a\u0645\u064a\u0632 \u0628\u0631\u0648\u0627\u0628\u0637 \u0641\u0631\u064a\u062f\u0629 \u0648\u0623\u0643\u0648\u0627\u062f QR \u0644\u0633\u0647\u0648\u0644\u0629 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0637\u0628\u0627\u0639\u0629.<br><br>\u064a\u0645\u0643\u0646 \u0627\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629. \u064a\u0645\u0643\u0646 \u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u0645\u0646\u0634\u0648\u0631\u0627\u062a \u0639\u0628\u0631 \u0634\u0628\u0643\u0629 Tor\u060c \u0645\u0645\u0627 \u064a\u062d\u0645\u064a \u0627\u0644\u0645\u0636\u064a\u0641 \u0648\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645\u064a\u0646 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629.<br><br>\u0627\u0646\u0636\u0645 \u0625\u0644\u064a\u0646\u0627 \u0641\u064a \u062b\u0648\u0631\u0629 \u062a\u0642\u062f\u0631 \u0627\u0644\u062d\u0642\u064a\u0642\u0629 \u0648\u0627\u0644\u0634\u0641\u0627\u0641\u064a\u0629. \u0645\u0639\u0627\u064b\u060c \u064a\u0645\u0643\u0646\u0646\u0627 \u0628\u0646\u0627\u0621 \u0634\u0628\u0643\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0637\u0646\u064a\u0646 \u0627\u0644\u0645\u0633\u062a\u0646\u064a\u0631\u064a\u0646 \u0627\u0644\u0630\u064a\u0646 \u0644\u0627 \u064a\u062e\u0634\u0648\u0646 \u0627\u0644\u062a\u062d\u062f\u062b.", "url_message": "\u0627\u062d\u0635\u0644 \u0639\u0644\u0649 VoxVera \u0648\u0627\u0628\u062f\u0623 \u0628\u0646\u0634\Line truncated
function t(path, lang) {
const parts = path.split('.');
@@ -417,6 +448,19 @@
}
}
});
document.querySelectorAll('[data-i18n-href]').forEach(el => {
const path = el.getAttribute('data-i18n-href');
const href = t(path, lang);
if (href) {
el.href = href;
}
});
const torBrowserHref = t('web.tor_browser_download_url', lang);
if (torBrowserHref && torBrowserHref !== 'web.tor_browser_download_url') {
document.querySelectorAll('.tor-browser-link').forEach(el => {
el.href = torBrowserHref;
});
}
document.getElementById('lang-selector').value = lang;
document.documentElement.lang = lang;
}
@@ -441,13 +485,13 @@
</head>
<body>
<div class="container">
<div class="container ">
<div class="left-tear-offs">
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -457,9 +501,9 @@
<!-- Repeat for other tear-offs -->
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -468,9 +512,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -479,9 +523,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -490,9 +534,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -501,9 +545,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -512,9 +556,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -523,9 +567,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -534,9 +578,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -545,9 +589,9 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">Use Tor Browser</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="https://voxvera.org/" target="_blank">https://voxvera.org/</a><br>
<a class="tear-off-link" href="http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/" target="_blank">http://asvkk5xs5cvx6gi6zxgszhcuermrszkyp3pgxzmf76b65qcv22ruuxqd.onion/</a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
@@ -566,6 +610,7 @@
<div class="qr-code-url">
<span class="url-message" data-i18n="landing.url_message">Get VoxVera and start spreading your message.</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<a id="qr-link-2" href="https://github.com/PR0M3TH3AN/VoxVera" target="_blank">https://github.com/PR0M3TH3AN/VoxVera</a>
</div>
<img src="qrcode-content.png" alt="QR Code">
@@ -574,7 +619,7 @@
<hr>
<div class="footer">
<p class="credit"><span data-i18n="web.built_with">built with</span> <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
<p class="binary" id="binary-message">0110010 0101011 0110010 0111101 0110100</p>
<p class="binary" id="footer-message">0110010 0101011 0110010 0111101 0110100</p>
</div>
</div>
</div>
@@ -586,4 +631,4 @@
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
</div>
</body>
</html>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 568 B

+47
View File
@@ -126,6 +126,31 @@ def test_import_preserves_session(tmp_path, monkeypatch):
assert (host_dir / "index.html").exists()
def test_import_rejects_zip_path_traversal(tmp_path, monkeypatch, capsys):
_setup_tmp(monkeypatch, tmp_path)
malicious_zip = tmp_path / "malicious.zip"
with zipfile.ZipFile(malicious_zip, "w") as zf:
zf.writestr("../escape.txt", "owned")
zf.writestr("config.json", json.dumps({"folder_name": "evil"}))
cli.import_site(str(malicious_zip))
captured = capsys.readouterr()
assert "Refusing to extract path outside destination" in captured.out
assert not (tmp_path / "escape.txt").exists()
assert not ((tmp_path / "data" / "host" / "evil").exists())
def test_load_config_migrates_binary_message_to_footer_message(tmp_path):
config_path = tmp_path / "legacy-config.json"
config_path.write_text(json.dumps({"binary_message": "0101", "folder_name": "legacy"}), encoding="utf-8")
loaded = cli.load_config(str(config_path))
assert loaded["footer_message"] == "0101"
assert "binary_message" not in loaded
def test_check_all_present(capsys, monkeypatch):
monkeypatch.setattr(shutil, "which", lambda cmd: "/usr/bin/" + cmd)
cli.main(["check"])
@@ -336,11 +361,31 @@ def test_build_without_url_skips_qr(tmp_path, monkeypatch, capsys):
assert dest.is_dir()
assert (dest / "config.json").exists()
assert (dest / "index.html").exists()
html = (dest / "index.html").read_text(encoding="utf-8")
assert 'class="container no-tear-offs"' in html
# QR codes should not exist since no URLs were provided
assert not (dest / "qrcode-content.png").exists()
assert not (dest / "qrcode-tear-offs.png").exists()
def test_build_injects_localized_tor_browser_download_links(tmp_path, monkeypatch):
"""Test that the localized Tor Browser CTA gets the expected localized URL."""
_setup_tmp(monkeypatch, tmp_path)
res_root = tmp_path / "data" / "voxvera"
test_data_dir = tmp_path / "data"
config = json.load(open(res_root / "src" / "config.json"))
config["lang"] = "de"
with open(test_data_dir / "config.json", "w") as f:
json.dump(config, f)
cli.main(["--config", str(test_data_dir / "config.json"), "build"])
html = (test_data_dir / "host" / config["folder_name"] / "index.html").read_text(encoding="utf-8")
assert 'href="https://www.torproject.org/de/download/"' in html
assert "Tor-Browser verwenden" in html
def test_build_with_only_url(tmp_path, monkeypatch):
"""Test that QR codes work when only url (content link) is set."""
_setup_tmp(monkeypatch, tmp_path)
@@ -388,6 +433,8 @@ def test_build_with_only_tear_off_link(tmp_path, monkeypatch):
test_data_dir = tmp_path / "data"
dest = test_data_dir / "host" / config["folder_name"]
html = (dest / "index.html").read_text(encoding="utf-8")
assert 'class="container no-tear-offs"' not in html
# Only tear-off QR should exist (will be updated by serve)
assert not (dest / "qrcode-content.png").exists()
assert (dest / "qrcode-tear-offs.png").exists()
+3 -3
View File
@@ -131,7 +131,7 @@ class TestFullWorkflow:
"url": "https://example.com/external",
"tear_off_link": "", # Will be set by serve
"url_message": "Visit our site",
"binary_message": "0101010",
"footer_message": "0101010",
}
config_path = tmp_path / "config.json"
@@ -230,7 +230,7 @@ class TestFullWorkflow:
"url": "https://example.com",
"tear_off_link": "",
"url_message": "Test message",
"binary_message": "101010",
"footer_message": "101010",
}
config_path = tmp_path / "config.json"
@@ -365,7 +365,7 @@ class TestSiteFiles:
"url": "https://example.com",
"tear_off_link": "",
"url_message": "",
"binary_message": "",
"footer_message": "",
}
config_path = tmp_path / "config.json"
+18
View File
@@ -45,6 +45,24 @@ def test_locale_completeness():
assert not missing_info, "\n".join(missing_info)
def test_critical_locale_labels_are_not_left_in_english():
"""Verify key translated labels are not left as raw English in non-English locales."""
locales_dir = REPO_ROOT / "voxvera" / "locales"
en_data = json.loads((locales_dir / "en.json").read_text(encoding="utf-8"))
critical_paths = [
("cli", "attachment_path_label"),
("cli", "manage_action_edit"),
("web", "download_attachment"),
]
for lp in locales_dir.glob("*.json"):
if lp.name == "en.json":
continue
data = json.loads(lp.read_text(encoding="utf-8"))
for section, key in critical_paths:
assert data[section][key] != en_data[section][key], f"{lp.name} leaves {section}.{key} in English"
def test_export_import_roundtrip(tmp_path, monkeypatch):
"""Verify that export -> import preserves the Tor identity key perfectly."""
# Setup tmp environment
+1 -1
View File
@@ -1,7 +1,7 @@
import sys
from pathlib import Path
__version__ = "0.3.3"
__version__ = "0.3.6"
# Add the vendor directory to sys.path ONLY for PyInstaller (frozen) builds.
# For pip/pipx installs the system/venv packages are correct and the vendored
+92 -16
View File
@@ -16,11 +16,11 @@ from importlib.resources.abc import Traversable
# avoid pulling in vendored copies that may be incomplete.
if getattr(sys, 'frozen', False):
try:
import pkg_resources
import setuptools
import jaraco
import platformdirs
import packaging
import pkg_resources # noqa: F401
import setuptools # noqa: F401
import jaraco # noqa: F401
import platformdirs # noqa: F401
import packaging # noqa: F401
except Exception:
pass
@@ -28,6 +28,25 @@ from InquirerPy import prompt, inquirer
from rich.console import Console
from voxvera import __version__
GITHUB_RECOMMENDED_BINARY_LIMIT = 95 * 1024 * 1024
LEGACY_CONFIG_KEYS = {
"binary_message": "footer_message",
}
TOR_BROWSER_DOWNLOAD_URLS = {
"ar": "https://www.torproject.org/ar/download/",
"de": "https://www.torproject.org/de/download/",
"en": "https://www.torproject.org/download/",
"es": "https://www.torproject.org/es/download/",
"fa": "https://www.torproject.org/fa/download/",
"fr": "https://www.torproject.org/fr/download/",
"he": "https://www.torproject.org/he/download/",
"ja": "https://www.torproject.org/ja/download/",
"pt": "https://www.torproject.org/pt-BR/download/",
"ru": "https://www.torproject.org/ru/download/",
"tr": "https://www.torproject.org/tr/download/",
"zh": "https://www.torproject.org/zh-CN/download/",
}
# package root (contains bundled templates and src/)
if getattr(sys, 'frozen', False):
ROOT = Path(sys._MEIPASS).joinpath("voxvera")
@@ -44,6 +63,30 @@ DATA_DIR = Path(os.environ.get("VOXVERA_DIR", Path.home()))
CURRENT_LOCALE = {}
def tor_browser_download_url(lang_code: str) -> str:
"""Return the best Tor Browser download URL for a supported locale."""
return TOR_BROWSER_DOWNLOAD_URLS.get(lang_code, TOR_BROWSER_DOWNLOAD_URLS["en"])
def normalize_config(data: dict) -> dict:
"""Normalize config keys while preserving compatibility with older exports."""
normalized = dict(data)
for old_key, new_key in LEGACY_CONFIG_KEYS.items():
if new_key not in normalized and old_key in normalized:
normalized[new_key] = normalized[old_key]
for old_key in LEGACY_CONFIG_KEYS:
normalized.pop(old_key, None)
normalized.setdefault("footer_message", "")
return normalized
def build_render_context(data: dict) -> dict:
"""Add derived template values without mutating persisted config shape."""
render_data = dict(data)
render_data["tear_off_state_class"] = "" if render_data.get("tear_off_link", "").strip() else "no-tear-offs"
return render_data
def _newlines_to_br(text: str) -> str:
"""Convert all forms of newline encoding to <br> for static HTML output.
@@ -192,12 +235,12 @@ def check_deps():
def load_config(path: str) -> dict:
with open(path, "r") as fh:
return json.load(fh)
return normalize_config(json.load(fh))
def save_config(data: dict, path: str):
with open(path, "w") as fh:
json.dump(data, fh, indent=2)
json.dump(normalize_config(data), fh, indent=2)
def _open_editor_terminal(initial: str) -> str:
@@ -438,6 +481,14 @@ def interactive_update(config_path: str):
"transformer": _len_transform(120),
"validate": _len_validator(120),
},
{
"type": "input",
"message": t("cli.footer_message_label"),
"name": "footer_message",
"default": data.get("footer_message", ""),
"transformer": _len_transform(120),
"validate": _len_validator(120),
},
]
data.update(prompt(link_qs))
@@ -504,13 +555,14 @@ def build_assets(
config_path = Path(config_path)
data = load_config(config_path)
render_data = build_render_context(data)
with open(src_dir / "index-master.html", "r") as fh:
html = fh.read()
# Bundle locales for the web engine
all_locales = {}
locale_files = sorted(glob.glob(str(ROOT / "locales" / "*.json")))
# Load master defaults from src/config.json
src_config_path = ROOT / "src" / "config.json"
master_defaults = {}
@@ -527,6 +579,7 @@ def build_assets(
"web": l_data.get("web", {}),
"landing": l_data.get("landing", {})
}
all_locales[code]["web"]["tor_browser_download_url"] = tor_browser_download_url(code)
# Override landing data with user custom config data if it differs from master defaults
for field in ["title", "subtitle", "headline", "content", "url_message"]:
@@ -569,7 +622,7 @@ def build_assets(
html = html.replace(f"{{{{t_landing_{field}}}}}", val_str)
# 2. Statically replace config placeholders {{key}}
for key, value in data.items():
for key, value in render_data.items():
val_str = str(value)
# Support Markdown-style redaction ~~text~~ for user fields too
val_str = re.sub(r"~~(.*?)~~", r'<span class="redacted">\1</span>', val_str)
@@ -586,20 +639,20 @@ def build_assets(
data["url"] = f"download/{att_filename}"
save_config(data, config_path)
html = html.replace("{{url}}", data["url"])
folder_name = data["folder_name"]
dest = DATA_DIR / "host" / folder_name
os.makedirs(dest, exist_ok=True)
# 1. Refresh QR codes directly in destination
# generate_qr only creates them if URLs are present in config
# We generate to a temp dir first to ensure we ONLY get what's in current config
import tempfile
with tempfile.TemporaryDirectory() as qr_tmp:
qr_tmp_path = Path(qr_tmp)
generate_qr(config_path, qr_tmp_path)
# Clear old QR codes from destination and copy new ones
for qr_file in ["qrcode-content.png", "qrcode-tear-offs.png"]:
qr_dest = dest / qr_file
@@ -858,6 +911,19 @@ import zipfile
import tempfile
def _safe_extract_zip(zipf: zipfile.ZipFile, dest_dir: Path) -> None:
"""Extract a ZIP archive without allowing path traversal."""
dest_dir = dest_dir.resolve()
for member in zipf.infolist():
member_path = Path(member.filename)
if member_path.is_absolute():
raise ValueError(f"Refusing to extract absolute path from archive: {member.filename}")
target_path = (dest_dir / member_path).resolve()
if not str(target_path).startswith(str(dest_dir) + os.sep) and target_path != dest_dir:
raise ValueError(f"Refusing to extract path outside destination: {member.filename}")
zipf.extractall(dest_dir)
def get_export_dir() -> Path:
"""Get the default export directory (~/voxvera-exports)."""
export_dir = Path.home() / "voxvera-exports"
@@ -911,7 +977,11 @@ def import_site(zip_path: str):
with tempfile.TemporaryDirectory() as tmpdir:
with zipfile.ZipFile(zip_path, 'r') as zipf:
zipf.extractall(tmpdir)
try:
_safe_extract_zip(zipf, Path(tmpdir))
except ValueError as e:
print(f"Error: Invalid export file. {e}")
return
tmp_path = Path(tmpdir)
config_file = tmp_path / "config.json"
@@ -1096,6 +1166,7 @@ def build_site():
# 2. Build the HTML from master template
data = load_config(config_path)
render_data = build_render_context(data)
res = _src_res("index-master.html")
if getattr(sys, 'frozen', False):
template_path = res
@@ -1109,7 +1180,7 @@ def build_site():
# Bundle locales (Include landing tokens for the main site)
all_locales = {}
locale_files = sorted(glob.glob(str(ROOT / "locales" / "*.json")))
# Load master defaults from src/config.json
src_config_path = ROOT / "src" / "config.json"
master_defaults = {}
@@ -1126,6 +1197,7 @@ def build_site():
"web": l_data.get("web", {}),
"landing": l_data.get("landing", {})
}
all_locales[code]["web"]["tor_browser_download_url"] = tor_browser_download_url(code)
# Override landing data with user custom config data if it differs from master defaults
for field in ["title", "subtitle", "headline", "content", "url_message"]:
@@ -1170,7 +1242,7 @@ def build_site():
html = html.replace("download/download.zip", "download/voxvera-source.zip")
# 4. Inject site-specific config data (handles any remaining {{key}} placeholders)
for key, value in data.items():
for key, value in render_data.items():
val_str = _newlines_to_br(str(value))
html = html.replace(f"{{{{{key}}}}}", val_str)
@@ -1185,11 +1257,15 @@ def build_site():
# Bundle source version
bundle_source(download_dir / "voxvera-source.zip")
# Copy standalone binaries if they exist
# Copy standalone binaries if they exist, but avoid writing oversized
# artifacts into the tracked site tree where GitHub rejects >100 MB files.
bin_dir = ROOT / "resources" / "bin"
if bin_dir.exists():
for bin_file in bin_dir.iterdir():
if bin_file.is_file():
if bin_file.stat().st_size > GITHUB_RECOMMENDED_BINARY_LIMIT:
print(f"Skipping oversized site download artifact: {bin_file.name}")
continue
shutil.copy(bin_file, download_dir / bin_file.name)
print("site/ directory successfully synchronized.")
+2 -2
View File
@@ -7,6 +7,6 @@
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Get VoxVera and start spreading your message.",
"url": "https://github.com/PR0M3TH3AN/VoxVera",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100"
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100"
}
Binary file not shown.
+112 -67
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=850">
<title id="site-title">{{name}}</title>
<title id="site-title">Vox Vera Printable Flyers</title>
<style>
body {
font-family: 'Courier New', Courier, 'Nimbus Mono L', 'Liberation Mono', 'Noto Sans Arabic', 'Noto Sans Hebrew', 'Noto Sans Devanagari', 'Noto Sans JP', 'Noto Sans SC', 'Noto Sans Arabic UI', monospace;
@@ -40,12 +40,13 @@
[dir="rtl"] .qr-code-body {
flex-direction: row-reverse;
justify-content: flex-start;
}
[dir="rtl"] .qr-code-url {
text-align: right;
margin-right: 0;
margin-left: 10px;
padding-right: 4px;
padding-left: 0;
}
[dir="rtl"] .message {
@@ -94,6 +95,7 @@
.print-button, .download-button, .lang-select {
padding: 10px 20px;
min-height: 42px;
font-size: 14px;
cursor: pointer;
background-color: #000;
@@ -101,6 +103,12 @@
border: none;
border-radius: 0px;
font-family: 'Courier New', Courier, monospace;
line-height: 1.2;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
white-space: nowrap;
}
.lang-select {
@@ -127,6 +135,7 @@
.tear-off {
display: flex;
height: 1in; /* Fixed height for consistency */
gap: 12px;
padding: 5px;
border: 1px dashed #000;
font-size: 10px;
@@ -139,9 +148,11 @@
.tear-off-text {
flex: 1;
min-width: 0;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
padding-top: 1px;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
}
.tear-off a {
@@ -157,7 +168,6 @@
width: 80px;
height: 80px;
border: 0px solid #000;
margin-left: 10px;
flex-shrink: 0;
}
@@ -240,8 +250,9 @@
width: 100%;
height: auto;
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
gap: 10px;
margin-top: 10px;
padding: 0;
box-sizing: border-box;
@@ -257,12 +268,14 @@
.qr-code-url {
font-size: 12px;
text-align: left;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
flex-grow: 1;
margin-right: 10px;
max-width: 60%;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
flex: 1 1 auto;
min-width: 0;
max-width: none;
padding-left: 4px;
}
hr {
@@ -298,6 +311,14 @@
margin-top: 5px;
}
.container.no-tear-offs .left-tear-offs {
display: none;
}
.container.no-tear-offs .content {
width: 100%;
}
@media print {
body, html {
width: 8.5in;
@@ -330,10 +351,20 @@
.button-bar {
display: flex;
align-items: stretch;
gap: 10px;
padding: 20px 0 30px;
}
#download-link {
display: inline-flex;
text-decoration: none;
}
#download-link .download-button {
height: 100%;
}
/* Dropdown styles */
.dropdown {
position: relative;
@@ -382,7 +413,7 @@
</style>
<script>
// VoxVera Multi-Language Engine
const locales = {{locales}}; // Injected at build time
const locales = {"ar": {"meta": {"language_name": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629", "flag": "\ud83c\uddf8\ud83c\udde6", "direction": "rtl"}, "web": {"tear_off_tor_browser": "\u0627\u0633\u062a\u062e\u062f\u0645 \u0645\u062a\u0635\u0641\u062d Tor", "tear_off_desc": "\u0644\u0644\u0645\u0633\u0627\u0639\u062f\u0629 \u0641\u064a \u0646\u0634\u0631 \u0647\u0630\u0647 \u0627\u0644\u0631\u0633\u0627\u0644\u0629\u060c \u0627\u0646\u062a\u0642\u0644 \u0625\u0644\u0649", "tear_off_footer": "\u0627\u0646\u0642\u0631 \u0641\u0648\u0642 \u0627\u0644\u0632\u0631 \u0623\u062f\u0646\u0627\u0647 \u0644\u0644\u0637\u0628\u0627\u0639\u0629", "built_with": "\u0628\u0646\u064a \u0628\u0648\u0627\u0633\u0637\u0629", "print_button": "\u0627\u0637\u0628\u0639 \u0627\u0644\u0645\u0646\u0634\u0648\u0631", "download_button": "\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0623\u062f\u0627\u0629 \u0648\u0627\u0644\u0643\u0648\u062f", "download_attachment": "\u062a\u0646\u0632\u064a\u0644 \u0627\u0644\u0645\u0631\u0641\u0642", "tor_browser_download_url": "https://www.torproject.org/ar/download/"}, "landing": {"title": "\u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629", "subtitle": "~~\u0644\u0627~~ \u0644\u0644\u0646\u0634\u0631", "headline": "\u0639\u0645\u0644\u064a\u0629 VOX VERA", "content": "\u062a\u062d\u0631\u0631 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629 \u0645\u0639 VoxVera. \u0623\u062f\u0627\u0629 \u062a\u0633\u0648\u064a\u0642 \u0639\u0635\u0627\u0628\u0627\u062a \u0645\u062c\u0647\u0648\u0644\u0629 \u0648\u0646\u0634\u0631 \u0631\u0633\u0627\u0626\u0644. \u0633\u0648\u0627\u0621 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0623\u0648 \u0641\u064a \u0627\u0644\u0639\u0627\u0644\u0645 \u0627\u0644\u0645\u0627\u062f\u064a\u060c \u064a\u0645\u0643\u0646\u0647\u0627 \u062a\u0645\u0643\u064a\u0646\u0643 \u0645\u0646 \u0646\u0634\u0631 \u0623\u0641\u0643\u0627\u0631\u0643 \u0628\u062c\u0631\u0623\u0629\u060c \u0645\u062d\u0645\u064a\u0629 \u0628\u0625\u062e\u0641\u0627\u0621 \u0627\u0644\u0647\u0648\u064a\u0629 \u0627\u0644\u0643\u0627\u0645\u0644 (\u0625\u0630\u0627 \u0643\u0646\u062a \u062a\u0633\u062a\u0636\u064a\u0641 \u0639\u0628\u0631 Tor). \u0642\u0645 \u0628\u062a\u062d\u0645\u064a\u0644 \u0627\u0644\u0643\u0648\u062f\u060c \u0648\u0635\u0645\u0645 \u0645\u0648\u0642\u0639 \u0645\u0646\u0634\u0648\u0631\u060c \u0648\u0627\u0633\u062a\u0636\u064a\u0641\u0647 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a \u0648\u0642\u0645 \u0628\u062a\u0636\u062e\u064a\u0645 \u0631\u0633\u0627\u0644\u062a\u0643.<br><br>\u0627\u0633\u062a\u062e\u062f\u0645 \u0627\u0644\u0642\u0648\u0629 \u0627\u0644\u0645\u064a\u0645\u064a\u0629 \u0644\u0645\u0634\u0627\u0631\u0643\u0629 \u0623\u0641\u0643\u0627\u0631\u0643 \u0641\u064a \u0645\u062f\u0631\u0633\u062a\u0643\u060c \u0623\u0648 \u0645\u0643\u0627\u0646 \u0639\u0645\u0644\u0643\u060c \u0623\u0648 \u0645\u062c\u062a\u0645\u0639\u0627\u062a\u0643 \u0639\u0628\u0631 \u0627\u0644\u0625\u0646\u062a\u0631\u0646\u062a\u060c \u0623\u0648 \u062d\u062a\u0649 \u0639\u0644\u0649 \u0645\u0633\u062a\u0648\u0649 \u0627\u0644\u0639\u0627\u0644\u0645. \u062a\u0636\u0645\u0646 VoxVera \u062a\u0631\u062f\u062f \u0635\u062f\u0649 \u0631\u0633\u0627\u0644\u062a\u0643 \u0628\u0639\u064a\u062f\u0627\u064b \u0648\u0639\u0644\u0649 \u0646\u0637\u0627\u0642 \u0648\u0627\u0633\u0639\u060c \u0645\u0639 \u0623\u0642\u0633\u0627\u0645 \u0642\u0627\u0628\u0644\u0629 \u0644\u0644\u0646\u0632\u0639 \u062a\u062a\u0645\u064a\u0632 \u0628\u0631\u0648\u0627\u0628\u0637 \u0641\u0631\u064a\u062f\u0629 \u0648\u0623\u0643\u0648\u0627\u062f QR \u0644\u0633\u0647\u0648\u0644\u0629 \u0625\u0639\u0627\u062f\u0629 \u0627\u0644\u0637\u0628\u0627\u0639\u0629.<br><br>\u064a\u0645\u0643\u0646 \u0627\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0627\u0644\u062e\u0635\u0648\u0635\u064a\u0629. \u064a\u0645\u0643\u0646 \u0645\u0634\u0627\u0631\u0643\u0629 \u0627\u0644\u0645\u0646\u0634\u0648\u0631\u0627\u062a \u0639\u0628\u0631 \u0634\u0628\u0643\u0629 Tor\u060c \u0645\u0645\u0627 \u064a\u062d\u0645\u064a \u0627\u0644\u0645\u0636\u064a\u0641 \u0648\u0627\u0644\u0645\u0633\u062a\u062e\u062f\u0645\u064a\u0646 \u0645\u0646 \u0627\u0644\u0631\u0642\u0627\u0628\u0629.<br><br>\u0627\u0646\u0636\u0645 \u0625\u0644\u064a\u0646\u0627 \u0641\u064a \u062b\u0648\u0631\u0629 \u062a\u0642\u062f\u0631 \u0627\u0644\u062d\u0642\u064a\u0642\u0629 \u0648\u0627\u0644\u0634\u0641\u0627\u0641\u064a\u0629. \u0645\u0639\u0627\u064b\u060c \u064a\u0645\u0643\u0646\u0646\u0627 \u0628\u0646\u0627\u0621 \u0634\u0628\u0643\u0629 \u0645\u0646 \u0627\u0644\u0645\u0648\u0627\u0637\u0646\u064a\u0646 \u0627\u0644\u0645\u0633\u062a\u0646\u064a\u0631\u064a\u0646 \u0627\u0644\u0630\u064a\u0646 \u0644\u0627 \u064a\u062e\u0634\u0648\u0646 \u0627\u0644\u062a\u062d\u062f\u062b.", "url_message": "\u0627\u062d\u0635\u0644 \u0639\u0644\u0649 VoxVera \u0648\u0627\u0628\u062f\u0623 \u0628\u0646\u0634\Line truncated
function t(path, lang) {
const parts = path.split('.');
@@ -417,6 +448,19 @@
}
}
});
document.querySelectorAll('[data-i18n-href]').forEach(el => {
const path = el.getAttribute('data-i18n-href');
const href = t(path, lang);
if (href) {
el.href = href;
}
});
const torBrowserHref = t('web.tor_browser_download_url', lang);
if (torBrowserHref && torBrowserHref !== 'web.tor_browser_download_url') {
document.querySelectorAll('.tor-browser-link').forEach(el => {
el.href = torBrowserHref;
});
}
document.getElementById('lang-selector').value = lang;
document.documentElement.lang = lang;
}
@@ -441,14 +485,14 @@
</head>
<body>
<div class="container">
<div class="container no-tear-offs">
<div class="left-tear-offs">
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -457,10 +501,10 @@
<!-- Repeat for other tear-offs -->
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -468,10 +512,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -479,10 +523,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -490,10 +534,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -501,10 +545,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -512,10 +556,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -523,10 +567,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -534,10 +578,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -545,10 +589,10 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<span data-i18n="web.tear_off_desc">to help spread this message go to</span><br>
<a class="tear-off-link" href="" target="_blank"></a><br>
<span data-i18n="web.tear_off_footer">click the button below to print</span>
</div>
<div class="qr-code">
<img src="qrcode-tear-offs.png" alt="QR Code">
@@ -556,34 +600,35 @@
</div>
</div>
<div class="content">
<h1 id="top-secret-title" data-i18n="landing.title">{{t_landing_title}}</h1>
<div class="distribute" id="do-not-distribute" data-i18n="landing.subtitle">{{t_landing_subtitle}}</div>
<h1 id="operation-title-2" data-i18n="landing.headline">{{t_landing_headline}}</h1>
<h1 id="top-secret-title" data-i18n="landing.title">TOP SECRET</h1>
<div class="distribute" id="do-not-distribute" data-i18n="landing.subtitle">DO <span class="redacted">NOT</span> DISTRIBUTE</div>
<h1 id="operation-title-2" data-i18n="landing.headline">OPERATION VOX VERA</h1>
<hr>
<div class="message">
<div class="text-container" id="main-content" contenteditable="false" data-i18n="landing.content">{{t_landing_content}}</div>
<div class="text-container" id="main-content" contenteditable="false" data-i18n="landing.content">Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.</div>
<div class="qr-code-body">
<div class="qr-code-url">
<span class="url-message" data-i18n="landing.url_message">{{t_landing_url_message}}</span><br><br>
<a id="qr-link-2" href="{{url}}" target="_blank">{{url}}</a>
<span class="url-message" data-i18n="landing.url_message">Get VoxVera and start spreading your message.</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="https://www.torproject.org/download/" target="_blank" rel="noopener noreferrer">Use Tor Browser</a><br><br>
<a id="qr-link-2" href="https://github.com/PR0M3TH3AN/VoxVera" target="_blank">https://github.com/PR0M3TH3AN/VoxVera</a>
</div>
<img src="qrcode-content.png" alt="QR Code">
</div>
</div>
<hr>
<div class="footer">
<p class="credit"><span data-i18n="web.built_with">{{t_web_built_with}}</span> <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
<p class="binary" id="binary-message">{{binary_message}}</p>
<p class="credit"><span data-i18n="web.built_with">built with</span> <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
<p class="binary" id="footer-message">0110010 0101011 0110010 0111101 0110100</p>
</div>
</div>
</div>
<div class="button-bar">
<button class="print-button" onclick="window.print()" data-i18n="web.print_button">{{t_web_print_button}}</button>
<button class="print-button" onclick="window.print()" data-i18n="web.print_button">Print this page</button>
<a id="download-link" href="download/voxvera-source.zip" download>
<button class="download-button" data-i18n="web.download_button">{{t_web_download_button}}</button>
<button class="download-button" data-i18n="web.download_button">Download Source Code</button>
</a>
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
</div>
</body>
</html>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 B

+4 -4
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "يتجاوز الحد",
"url_label": "URL (رابط المحتوى - مورد خارجي)",
"url_message_label": "رسالة الـ URL",
"binary_message_label": "رسالة ثنائية",
"footer_message_label": "رسالة التذييل",
"template_not_found": "القالب {name} غير موجود",
"template_copied": "تم نسخ القالب إلى {dest}",
"flyer_created": "تم إنشاء ملفات المنشور تحت {dest}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] تمهيد Tor: {progress}% ...",
"no_urls_qr_skip": "لم يتم تكوين أي روابط بعد. سيتم إنشاء أكواد QR بعد 'voxvera serve'",
"input_file_not_found": "ملف الإدخال {path} غير موجود.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "مسار ملف المرفق (اختياري - سيتم تقديمه من موقع Tor)",
"manage_action_edit": "تحرير",
"manage_templates": "--- إنشاء من قالب ---",
"template_select": "اختر قالباً:",
"body_over_limit_warning": "تحذير: نص المحتوى يتجاوز 1000 حرف. قد يتم قطعه في المنشور.",
@@ -98,7 +98,7 @@
"built_with": "بني بواسطة",
"print_button": "اطبع المنشور",
"download_button": "تحميل الأداة والكود",
"download_attachment": "Download Attachment"
"download_attachment": "تنزيل المرفق"
},
"landing": {
"title": "سري للغاية",
+4 -4
View File
@@ -21,7 +21,7 @@
"body_limit_exceeded": "überschreitet das Limit",
"url_label": "URL (Inhaltslink - externe Ressource)",
"url_message_label": "URL-Nachricht",
"binary_message_label": "Binäre Nachricht",
"footer_message_label": "Fußzeilen-Nachricht",
"template_not_found": "Vorlage {name} nicht gefunden",
"template_copied": "Vorlage nach {dest} kopiert",
"flyer_created": "Flyer-Dateien erstellt unter {dest}",
@@ -72,8 +72,8 @@
"bootstrapping_progress": "[{name}] Tor-Bootstrapping: {progress}% ...",
"no_urls_qr_skip": "Noch keine URLs konfiguriert. QR-Codes werden nach 'voxvera serve' generiert",
"input_file_not_found": "Eingabedatei {path} existiert nicht.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Pfad zur Anhangsdatei (optional - wird über die Tor-Seite bereitgestellt)",
"manage_action_edit": "Bearbeiten",
"manage_templates": "--- Aus Vorlage erstellen ---",
"template_select": "Wählen Sie eine Vorlage:",
"body_over_limit_warning": "Warnung: Der Textkörper überschreitet 1000 Zeichen. Er könnte auf dem Flyer abgeschnitten werden.",
@@ -97,7 +97,7 @@
"built_with": "erstellt mit",
"print_button": "Flyer drucken",
"download_button": "Tool & Quellcode herunterladen",
"download_attachment": "Download Attachment"
"download_attachment": "Anhang herunterladen"
},
"landing": {
"title": "STRENG GEHEIM",
+2 -2
View File
@@ -21,7 +21,7 @@
"body_limit_exceeded": "exceeds limit",
"url_label": "URL (content link - external resource)",
"url_message_label": "URL message",
"binary_message_label": "Binary message",
"footer_message_label": "Footer message",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"template_not_found": "Template {name} not found",
"template_copied": "Template copied to {dest}",
@@ -154,7 +154,7 @@
"subtitle": "DO ~~NOT~~ DISTRIBUTE",
"headline": "OPERATION VOX VERA",
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Follow this link to learn more. Use Tor Browser."
"url_message": "Follow this link to learn more."
}
}
}
+4 -4
View File
@@ -21,7 +21,7 @@
"body_limit_exceeded": "excede el límite",
"url_label": "URL (enlace de contenido - recurso externo)",
"url_message_label": "Mensaje de la URL",
"binary_message_label": "Mensaje binario",
"footer_message_label": "Mensaje de pie de página",
"template_not_found": "Plantilla {name} no encontrada",
"template_copied": "Plantilla copiada a {dest}",
"flyer_created": "Archivos del volante creados en {dest}",
@@ -72,8 +72,8 @@
"bootstrapping_progress": "[{name}] Arranque de Tor: {progress}% ...",
"no_urls_qr_skip": "No hay URLs configuradas todavía. Los códigos QR se generarán después de 'voxvera serve'",
"input_file_not_found": "El archivo de entrada {path} no existe.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Ruta del archivo adjunto (opcional - se servirá desde el sitio Tor)",
"manage_action_edit": "Editar",
"manage_templates": "--- Crear Desde Plantilla ---",
"template_select": "Seleccione una plantilla:",
"body_over_limit_warning": "Advertencia: El texto del cuerpo supera los 1000 caracteres. Puede cortarse en el volante.",
@@ -97,7 +97,7 @@
"built_with": "construido con",
"print_button": "Imprimir volante",
"download_button": "Descargar Herramienta y Código",
"download_attachment": "Download Attachment"
"download_attachment": "Descargar archivo adjunto"
},
"landing": {
"title": "ALTO SECRETO",
+4 -4
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "بیش از حد مجاز",
"url_label": "URL (پیوند محتوا - منبع خارجی)",
"url_message_label": "پیام URL",
"binary_message_label": "پیام دودویی (Binary)",
"footer_message_label": "پیام پابرگ",
"template_not_found": "قالب {name} یافت نشد",
"template_copied": "قالب به {dest} کپی شد",
"flyer_created": "فایل‌های اعلامیه در {dest} ایجاد شدند",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] راه‌اندازی Tor: {progress}% ...",
"no_urls_qr_skip": "هنوز آدرسی پیکربندی نشده است. کدهای QR پس از 'voxvera serve' ایجاد می‌شوند",
"input_file_not_found": "فایل ورودی {path} وجود ندارد.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "مسیر فایل پیوست (اختیاری - از طریق سایت Tor ارائه می‌شود)",
"manage_action_edit": "ویرایش",
"manage_templates": "--- ایجاد از قالب ---",
"template_select": "یک قالب انتخاب کنید:",
"body_over_limit_warning": "هشدار: متن بدنه از 1000 کاراکتر بیشتر است. ممکن است در بروشور بریده شود.",
@@ -98,7 +98,7 @@
"built_with": "ساخته شده با",
"print_button": "چاپ اعلامیه",
"download_button": "دانلود ابزار و کد منبع",
"download_attachment": "Download Attachment"
"download_attachment": "دانلود پیوست"
},
"landing": {
"title": "به‌کلی محرمانه",
+4 -4
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "dépasse la limite",
"url_label": "URL (lien de contenu - ressource externe)",
"url_message_label": "Message de l'URL",
"binary_message_label": "Message binaire",
"footer_message_label": "Message de pied de page",
"template_not_found": "Modèle {name} non trouvé",
"template_copied": "Modèle copié vers {dest}",
"flyer_created": "Fichiers du flyer créés sous {dest}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Amorçage de Tor : {progress}% ...",
"no_urls_qr_skip": "Aucune URL configurée pour le moment. Les codes QR seront générés après 'voxvera serve'",
"input_file_not_found": "Le fichier d'entrée {path} n'existe pas.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Chemin du fichier joint (optionnel - sera servi depuis le site Tor)",
"manage_action_edit": "Modifier",
"manage_templates": "--- Créer à partir d'un modèle ---",
"template_select": "Sélectionnez un modèle :",
"body_over_limit_warning": "Avertissement : Le texte dépasse 1000 caractères. Il pourrait être coupé sur le tract.",
@@ -98,7 +98,7 @@
"built_with": "créé avec",
"print_button": "Imprimer le flyer",
"download_button": "Télécharger l'outil et le code",
"download_attachment": "Download Attachment"
"download_attachment": "Télécharger la pièce jointe"
},
"landing": {
"title": "TOP SECRET",
+4 -4
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "חורג מהמגבלה",
"url_label": "URL (קישור לתוכן - משאב חיצוני)",
"url_message_label": "הודעת URL",
"binary_message_label": "הודעה בינארית",
"footer_message_label": "הודעת כותרת תחתונה",
"template_not_found": "התבנית {name} לא נמצאה",
"template_copied": "התבנית הועתקה אל {dest}",
"flyer_created": "קובצי הפלייר נוצרו תחת {dest}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] איתחול Tor: {progress}% ...",
"no_urls_qr_skip": "עדיין לא הוגדרו כתובות URL. קודי QR ייווצרו לאחר 'voxvera serve'",
"input_file_not_found": "קובץ הקלט {path} אינו קיים.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "נתיב קובץ מצורף (אופציונלי - יוגש מאתר ה-Tor)",
"manage_action_edit": "עריכה",
"manage_templates": "--- יצירה מתבנית ---",
"template_select": "בחר תבנית:",
"body_over_limit_warning": "אזהרה: טקסט הגוף חורג מ-1000 תווים. הוא עלול להיחתך בעלון.",
@@ -98,7 +98,7 @@
"built_with": "נבנה עם",
"print_button": "הדפס פלייר",
"download_button": "הורד כלי וקוד מקור",
"download_attachment": "Download Attachment"
"download_attachment": "הורד קובץ מצורף"
},
"landing": {
"title": "סודי ביותר",
+5 -5
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "सीमा से अधिक है",
"url_label": "URL (सामग्री लिंक - बाहरी संसाधन)",
"url_message_label": "URL संदेश",
"binary_message_label": "बाइनरी संदेश",
"footer_message_label": "फ़ुटर संदेश",
"template_not_found": "टेम्पलेट {name} नहीं मिला",
"template_copied": "टेम्पलेट {dest} पर कॉपी किया गया",
"flyer_created": "फ्लायर फाइलें {dest} के अंतर्गत बनाई गई हैं",
@@ -30,7 +30,7 @@
"onionshare_error": "{folder_name} के लिए OnionShare अप्रत्याशित रूप से बंद हो गया।",
"onionshare_causes": "सामान्य कारण: नेटवर्क अवरुद्ध, गलत सिस्टम घड़ी, या OnionShare पहले से ही चल रहा है।",
"onionshare_logs_tip": "पूरे लॉग के लिए {logfile} देखें।",
"onion_url_msg": "[{folder_name}] Onion URL: {onion_url}",
"onion_url_msg": "[{folder_name}] ओनियन URL: {onion_url}",
"onionshare_running_pid": "[{folder_name}] OnionShare चल रहा है (PID {pid})।",
"export_not_found": "त्रुटि: {host_root} में साइट फ़ोल्डर '{folder_name}' नहीं मिला",
"export_success": "'{folder_name}' को {output_path} पर निर्यात किया गया",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Tor बूटस्ट्रैपिंग: {progress}% ...",
"no_urls_qr_skip": "अभी तक कोई URL कॉन्फ़िगर नहीं किया गया है। QR कोड 'voxvera serve' के बाद जेनरेट किए जाएंगे",
"input_file_not_found": "इनपुट फ़ाइल {path} मौजूद नहीं है।",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "अटैचमेंट फ़ाइल पथ (वैकल्पिक - Tor साइट से परोसा जाएगा)",
"manage_action_edit": "संपादित करें",
"manage_templates": "--- टेम्पलेट से बनाएं ---",
"template_select": "एक टेम्पलेट चुनें:",
"body_over_limit_warning": "चेतावनी: मुख्य पाठ 1000 अक्षरों से अधिक है। यह पर्चे पर कटा हुआ दिख सकता है।",
@@ -98,7 +98,7 @@
"built_with": "बनाया गया",
"print_button": "फ्लायर प्रिंट करें",
"download_button": "टूल और कोड डाउनलोड करें",
"download_attachment": "Download Attachment"
"download_attachment": "अटैचमेंट डाउनलोड करें"
},
"landing": {
"title": "अति गोपनीय",
+5 -5
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "制限を超えています",
"url_label": "URL (コンテンツリンク - 外部リソース)",
"url_message_label": "URLメッセージ",
"binary_message_label": "バイナリメッセージ",
"footer_message_label": "フッターメッセージ",
"template_not_found": "テンプレート {name} が見つかりません",
"template_copied": "テンプレートを {dest} にコピーしました",
"flyer_created": "フライヤーファイルが {dest} に作成されました",
@@ -30,7 +30,7 @@
"onionshare_error": "{folder_name} の OnionShare が予期せず終了しました。",
"onionshare_causes": "主な原因: ネットワークの遮断、システム時刻の不一致、または OnionShare が既に実行されている可能性があります。",
"onionshare_logs_tip": "詳細は {logfile} のログを確認してください。",
"onion_url_msg": "[{folder_name}] Onion URL: {onion_url}",
"onion_url_msg": "[{folder_name}] オニオンURL: {onion_url}",
"onionshare_running_pid": "[{folder_name}] OnionShare 実行中 (PID {pid})",
"export_not_found": "エラー: サイトフォルダ '{folder_name}' が {host_root} 内に見つかりません",
"export_success": "'{folder_name}' を {output_path} にエクスポートしました",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Tor 起動中: {progress}% ...",
"no_urls_qr_skip": "URLがまだ設定されていません。QRコードは 'voxvera serve' の後に生成されます",
"input_file_not_found": "入力ファイル {path} が存在しません。",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "添付ファイルのパス(任意 - Torサイトから配信されます)",
"manage_action_edit": "編集",
"manage_templates": "--- テンプレートから作成 ---",
"template_select": "テンプレートを選択してください:",
"body_over_limit_warning": "警告:本文が1000文字を超えています。チラシ上で途切れる可能性があります。",
@@ -98,7 +98,7 @@
"built_with": "built with",
"print_button": "フライヤーを印刷",
"download_button": "ツールとソースをダウンロード",
"download_attachment": "Download Attachment"
"download_attachment": "添付ファイルをダウンロード"
},
"landing": {
"title": "極秘事項",
+4 -4
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "excede o limite",
"url_label": "URL (link de conteúdo - recurso externo)",
"url_message_label": "Mensagem da URL",
"binary_message_label": "Mensagem binária",
"footer_message_label": "Mensagem de rodapé",
"template_not_found": "Modelo {name} não encontrado",
"template_copied": "Modelo copiado para {dest}",
"flyer_created": "Arquivos do panfleto criados em {dest}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Inicialização do Tor: {progress}% ...",
"no_urls_qr_skip": "Nenhuma URL configurada ainda. Os códigos QR serão gerados após 'voxvera serve'",
"input_file_not_found": "Arquivo de entrada {path} não existe.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Caminho do arquivo anexo (opcional - será servido pelo site Tor)",
"manage_action_edit": "Editar",
"manage_templates": "--- Criar a partir de Modelo ---",
"template_select": "Selecione um modelo:",
"body_over_limit_warning": "Aviso: O texto do corpo excede 1000 caracteres. Pode ser cortado no panfleto.",
@@ -98,7 +98,7 @@
"built_with": "construído com",
"print_button": "Imprimir Panfleto",
"download_button": "Baixar Ferramenta e Código",
"download_attachment": "Download Attachment"
"download_attachment": "Baixar anexo"
},
"landing": {
"title": "ALTO SECRETO",
+5 -5
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "превышает лимит",
"url_label": "URL (ссылка на контент - внешний ресурс)",
"url_message_label": "Сообщение URL",
"binary_message_label": "Бинарное сообщение",
"footer_message_label": "Сообщение внизу страницы",
"template_not_found": "Шаблон {name} не найден",
"template_copied": "Шаблон скопирован в {dest}",
"flyer_created": "Файлы листовки созданы в {dest}",
@@ -30,7 +30,7 @@
"onionshare_error": "OnionShare для {folder_name} неожиданно завершил работу.",
"onionshare_causes": "Общие причины: заблокирована сеть, неточное системное время или OnionShare уже запущен.",
"onionshare_logs_tip": "Полные логи см. в {logfile}.",
"onion_url_msg": "[{folder_name}] Onion URL: {onion_url}",
"onion_url_msg": "[{folder_name}] Onion-URL: {onion_url}",
"onionshare_running_pid": "[{folder_name}] OnionShare запущен (PID {pid}).",
"export_not_found": "Ошибка: папка сайта '{folder_name}' не найдена в {host_root}",
"export_success": "Экспортировано '{folder_name}' в {output_path}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Загрузка Tor: {progress}% ...",
"no_urls_qr_skip": "URL еще не настроены. QR-коды будут созданы после 'voxvera serve'",
"input_file_not_found": "Входной файл {path} не существует.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Путь к файлу вложения (необязательно - будет доступен с сайта Tor)",
"manage_action_edit": "Редактировать",
"manage_templates": "--- Создать из шаблона ---",
"template_select": "Выберите шаблон:",
"body_over_limit_warning": "Предупреждение: Текст превышает 1000 символов. Он может быть обрезан на листовке.",
@@ -98,7 +98,7 @@
"built_with": "создано с помощью",
"print_button": "Печать листовки",
"download_button": "Скачать инструмент и код",
"download_attachment": "Download Attachment"
"download_attachment": "Скачать вложение"
},
"landing": {
"title": "СОВЕРШЕННО СЕКРЕТНО",
+4 -4
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "imezidi kikomo",
"url_label": "URL (kiungo cha maudhui - rasilimali ya nje)",
"url_message_label": "Ujumbe wa URL",
"binary_message_label": "Ujumbe wa mfumo wa binary",
"footer_message_label": "Ujumbe wa chini ya ukurasa",
"template_not_found": "Kiolezo {name} hakijapatikana",
"template_copied": "Kiolezo kimeandaliwa kwenda {dest}",
"flyer_created": "Faili za kipeperushi zimeundwa chini ya {dest}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Tor Inapakia: {progress}% ...",
"no_urls_qr_skip": "Hakuna URL zilizowekwa bado. QR codes zitaundwa baada ya 'voxvera serve'",
"input_file_not_found": "Faili ya kuingiza {path} haipo.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Njia ya faili ya kiambatisho (si lazima - itatolewa kutoka tovuti ya Tor)",
"manage_action_edit": "Hariri",
"manage_templates": "--- Tengeneza Kutoka Kiolezo ---",
"template_select": "Chagua kiolezo:",
"body_over_limit_warning": "Onyo: Maandishi ya mwili yanazidi herufi 1000. Yanaweza kukatwa kwenye kipeperushi.",
@@ -98,7 +98,7 @@
"built_with": "imejengwa na",
"print_button": "Chapa Kipeperushi",
"download_button": "Pakua Zana na Chanzo",
"download_attachment": "Download Attachment"
"download_attachment": "Pakua kiambatisho"
},
"landing": {
"title": "SIRI KUBWA",
+5 -5
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "sınırı aşıyor",
"url_label": "URL (içerik bağlantısı - harici kaynak)",
"url_message_label": "URL mesajı",
"binary_message_label": "İkili (Binary) mesaj",
"footer_message_label": "Alt bilgi mesajı",
"template_not_found": "Şablon {name} bulunamadı",
"template_copied": "Şablon {dest} konumuna kopyalandı",
"flyer_created": "İlan dosyaları {dest} altında oluşturuldu",
@@ -30,7 +30,7 @@
"onionshare_error": "{folder_name} için OnionShare beklenmedik bir şekilde kapandı.",
"onionshare_causes": "Yaygın nedenler: Ağ engellenmiş, sistem saati yanlış veya OnionShare zaten çalışıyor olabilir.",
"onionshare_logs_tip": "Tam günlükler için {logfile} dosyasına bakın.",
"onion_url_msg": "[{folder_name}] Onion URL: {onion_url}",
"onion_url_msg": "[{folder_name}] Onion URL'si: {onion_url}",
"onionshare_running_pid": "[{folder_name}] OnionShare çalışıyor (PID {pid}).",
"export_not_found": "Hata: '{folder_name}' site klasörü {host_root} içinde bulunamadı",
"export_success": "'{folder_name}' şuraya dışa aktarıldı: {output_path}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Tor Başlatılıyor: %{progress} ...",
"no_urls_qr_skip": "Henüz URL yapılandırılmadı. QR kodları 'voxvera serve' komutundan sonra oluşturulacak",
"input_file_not_found": "{path} giriş dosyası mevcut değil.",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "Ek dosyası yolu (isteğe bağlı - Tor sitesinden sunulacaktır)",
"manage_action_edit": "Düzenle",
"manage_templates": "--- Şablondan Oluştur ---",
"template_select": "Bir şablon seçin:",
"body_over_limit_warning": "Uyarı: Gövde metni 1000 karakteri aşıyor. El ilanında kesilebilir.",
@@ -98,7 +98,7 @@
"built_with": "ile yapıldı",
"print_button": "İlanı Yazdır",
"download_button": "Aracı ve Kaynak Kodunu İndir",
"download_attachment": "Download Attachment"
"download_attachment": "Eki indir"
},
"landing": {
"title": "ÇOK GİZLİ",
+5 -5
View File
@@ -22,7 +22,7 @@
"body_limit_exceeded": "超过限制",
"url_label": "URL (内容链接 - 外部资源)",
"url_message_label": "URL 消息",
"binary_message_label": "二进制消息",
"footer_message_label": "页脚消息",
"template_not_found": "模板 {name} 未找到",
"template_copied": "模板已复制到 {dest}",
"flyer_created": "传单文件已创建在 {dest}",
@@ -30,7 +30,7 @@
"onionshare_error": "{folder_name} 的 OnionShare 意外退出。",
"onionshare_causes": "常见原因:网络受阻、系统时间不准,或 OnionShare 已在运行。",
"onionshare_logs_tip": "查看 {logfile} 获取完整日志。",
"onion_url_msg": "[{folder_name}] Onion URL: {onion_url}",
"onion_url_msg": "[{folder_name}] Onion 地址: {onion_url}",
"onionshare_running_pid": "[{folder_name}] OnionShare 正在运行 (PID {pid})。",
"export_not_found": "错误:在 {host_root} 中未找到站点文件夹 '{folder_name}'",
"export_success": "已将 '{folder_name}' 导出到 {output_path}",
@@ -73,8 +73,8 @@
"bootstrapping_progress": "[{name}] Tor 引导中: {progress}% ...",
"no_urls_qr_skip": "尚未配置 URL。QR 码将在 'voxvera serve' 后生成",
"input_file_not_found": "输入文件 {path} 不存在。",
"attachment_path_label": "Attachment file path (optional - will be served from Tor site)",
"manage_action_edit": "Edit",
"attachment_path_label": "附件文件路径(可选 - 将由 Tor 站点提供)",
"manage_action_edit": "编辑",
"manage_templates": "--- 从模板创建 ---",
"template_select": "选择一个模板:",
"body_over_limit_warning": "警告:正文超过1000个字符。传单上可能会被截断。",
@@ -98,7 +98,7 @@
"built_with": "构建自",
"print_button": "打印传单",
"download_button": "下载工具与源码",
"download_attachment": "Download Attachment"
"download_attachment": "下载附件"
},
"landing": {
"title": "绝密",
+1 -1
View File
@@ -13,7 +13,7 @@ The `voxvera build` command processes these source files:
1. **QR code generation** -- Reads URLs from `config.json` and generates PNG QR codes using the `qrcode` and `Pillow` Python libraries.
2. **HTML minification** -- Minifies embedded JavaScript with `jsmin` and the overall HTML with `htmlmin2`. Produces `index.html` and `nostr.html` from the master templates.
3. **Binary message injection** -- Writes the configured binary message into the built HTML.
3. **Footer message injection** -- Writes the configured footer message into the built HTML.
4. **Asset packaging** -- Copies all built files into `host/<folder_name>/`.
No external tools (Node.js, ImageMagick, qrencode, etc.) are required. The entire build pipeline uses pure Python packages.
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Get VoxVera and start spreading your message.",
"url": "https://github.com/PR0M3TH3AN/VoxVera",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+71 -26
View File
@@ -40,12 +40,13 @@
[dir="rtl"] .qr-code-body {
flex-direction: row-reverse;
justify-content: flex-start;
}
[dir="rtl"] .qr-code-url {
text-align: right;
margin-right: 0;
margin-left: 10px;
padding-right: 4px;
padding-left: 0;
}
[dir="rtl"] .message {
@@ -94,6 +95,7 @@
.print-button, .download-button, .lang-select {
padding: 10px 20px;
min-height: 42px;
font-size: 14px;
cursor: pointer;
background-color: #000;
@@ -101,6 +103,12 @@
border: none;
border-radius: 0px;
font-family: 'Courier New', Courier, monospace;
line-height: 1.2;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
white-space: nowrap;
}
.lang-select {
@@ -127,6 +135,7 @@
.tear-off {
display: flex;
height: 1in; /* Fixed height for consistency */
gap: 12px;
padding: 5px;
border: 1px dashed #000;
font-size: 10px;
@@ -139,9 +148,11 @@
.tear-off-text {
flex: 1;
min-width: 0;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
padding-top: 1px;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
}
.tear-off a {
@@ -157,7 +168,6 @@
width: 80px;
height: 80px;
border: 0px solid #000;
margin-left: 10px;
flex-shrink: 0;
}
@@ -240,8 +250,9 @@
width: 100%;
height: auto;
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
gap: 10px;
margin-top: 10px;
padding: 0;
box-sizing: border-box;
@@ -257,12 +268,14 @@
.qr-code-url {
font-size: 12px;
text-align: left;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
flex-grow: 1;
margin-right: 10px;
max-width: 60%;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
flex: 1 1 auto;
min-width: 0;
max-width: none;
padding-left: 4px;
}
hr {
@@ -298,6 +311,14 @@
margin-top: 5px;
}
.container.no-tear-offs .left-tear-offs {
display: none;
}
.container.no-tear-offs .content {
width: 100%;
}
@media print {
body, html {
width: 8.5in;
@@ -330,10 +351,20 @@
.button-bar {
display: flex;
align-items: stretch;
gap: 10px;
padding: 20px 0 30px;
}
#download-link {
display: inline-flex;
text-decoration: none;
}
#download-link .download-button {
height: 100%;
}
/* Dropdown styles */
.dropdown {
position: relative;
@@ -417,6 +448,19 @@
}
}
});
document.querySelectorAll('[data-i18n-href]').forEach(el => {
const path = el.getAttribute('data-i18n-href');
const href = t(path, lang);
if (href) {
el.href = href;
}
});
const torBrowserHref = t('web.tor_browser_download_url', lang);
if (torBrowserHref && torBrowserHref !== 'web.tor_browser_download_url') {
document.querySelectorAll('.tor-browser-link').forEach(el => {
el.href = torBrowserHref;
});
}
document.getElementById('lang-selector').value = lang;
document.documentElement.lang = lang;
}
@@ -441,11 +485,11 @@
</head>
<body>
<div class="container">
<div class="container {{tear_off_state_class}}">
<div class="left-tear-offs">
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -457,7 +501,7 @@
<!-- Repeat for other tear-offs -->
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -468,7 +512,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -479,7 +523,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -490,7 +534,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -501,7 +545,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -512,7 +556,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -523,7 +567,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -534,7 +578,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -545,7 +589,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -566,6 +610,7 @@
<div class="qr-code-url">
<span class="url-message" data-i18n="landing.url_message">{{t_landing_url_message}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<a id="qr-link-2" href="{{url}}" target="_blank">{{url}}</a>
</div>
<img src="qrcode-content.png" alt="QR Code">
@@ -574,7 +619,7 @@
<hr>
<div class="footer">
<p class="credit"><span data-i18n="web.built_with">{{t_web_built_with}}</span> <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
<p class="binary" id="binary-message">{{binary_message}}</p>
<p class="binary" id="footer-message">{{footer_message}}</p>
</div>
</div>
</div>
@@ -586,4 +631,4 @@
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
</div>
</body>
</html>
</html>
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "The Android Open Source Project (AOSP) represents the essence of what Android was originally intended to be: an open, transparent, and adaptable platform. It strips away the layers of proprietary software and corporate oversight that have become synonymous with mainstream Android distributions. With AOSP, you gain access to a raw, unfiltered version of Android, allowing you to truly make your device your own.<br><br>For developers, AOSP offers a playground of possibilities, where the only limits are your skills and imagination. It serves as the foundation for countless custom ROMs, each tailored to specific needs, whether for enhanced performance, privacy, or unique features that mainstream versions lack. Privacy enthusiasts will appreciate the absence of pre-installed bloatware and tracking services that often come wit stock Android, providing a cleaner, more secure user experience.<br><br>AOSP isn't just for developers or tech enthusiasts; it's for anyone who values digital freedom. It allows you to reclaim control over your device, tailoring it to your needs without the restrictions of carriers or manufacturers. Discover how AOSP can transform your mobile experience. Start your journey towards a more open, customizable future today.",
"url_message": "Visit the link below to learn more.",
"url": "https://source.android.com",
"tear_off_link": "http://aosp.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "What is Bitcoin? It's more than just digital money; it's a revolutionary technology designed to fix some of the world's most pressing problems. Bitcoin operates on a decentralized network, free from the control of any government or central authority. This ensures that no one can manipulate its supply or value, making it a sound form of money that protects your wealth from inflation.<br><br>Our current financial system is built on flawed incentives—encouraging debt, dependency, and centralization of power. Bitcoin changes that by creating a system where honesty, transparency, and individual responsibility are rewarded. In the Bitcoin network, there is no room for corruption or manipulation because the rules are enforced by mathematics, not by people.<br><br>Bitcoin aligns incentives in a way that promotes long-term thinking, savings, and innovation. It empowers individuals to take control of their financial future, fostering a global community where value can be transferred freely and securely without intermediaries. This is why we need Bitcoin—because it creates a world where incentives are properly aligned, leading to a more fair and prosperous society for all.<br><br>Take the step to learn more about Bitcoin and how it can transform not just your financial life, but the world. Tear off a link to the left and explore this groundbreaking technology. Join the movement towards a better future.",
"url_message": "Follow this link to learn more.",
"url": "https://bitcoin.org/en",
"tear_off_link": "http://bitcoin.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+1 -1
View File
@@ -8,7 +8,7 @@
"url_message": "",
"url": "",
"tear_off_link": "",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "content",
"url_message": "url message",
"url": "url",
"tear_off_link": "http://example-folder_name.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "Who is Jesus? He is more than just a historical figure; He is the Son of God, the Savior of the world. Jesus Himself said, 'I am the way, the truth, and the life. No one comes to the Father except through Me' (John 14:6). He came to bridge the gap between God and humanity, offering us a path to forgiveness, peace, and eternal life.<br><br>We all face brokenness in our lives—whether it's in our relationships, our hearts, or our world. Jesus came to heal that brokenness. He lived a perfect life, died on the cross for our sins, and rose again, defeating death and offering us the hope of eternal life with God.<br><br>Without Jesus, we are lost in our sin, separated from God. But through Him, we can be forgiven and restored. He offers us not just a better life now, but the promise of an eternal life with Him. This is why we need Him—because only in Jesus can we find true peace, purpose, and the assurance of eternal life.<br><br>Take the step to learn more about Jesus and the life-changing impact He can have on your life. Tear off a link to the left and explore His teachings. Find the hope you've been searching for.",
"url_message": "Follow this link to learn more.",
"url": "http://media.faithforthefamily.com/ra/godsoloved/english.mp3",
"tear_off_link": "http://jesus.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "Why Meshtastic? In times of crisis, traditional communication networks can fail, leaving us isolated and vulnerable. Meshtastic provides a powerful solution—an alternative local communication network that operates independently of centralized systems. It’s designed for emergency situations, allowing you to stay connected when it matters most.<br><br>Meshtastic devices create a mesh network that can relay messages over long distances, even without internet or cellular coverage. This decentralized approach ensures that your communications are resilient and reliable, no matter the circumstances. But it’s not just about staying connected—Meshtastic also offers encryption, so your messages remain secure and private.<br><br>In a world where privacy is increasingly compromised, Meshtastic empowers you to take control of your communication. Whether you’re coordinating a local community response, staying in touch with loved ones during a disaster, or simply preparing for the unexpected, Meshtastic provides the tools you need to communicate securely and effectively.<br><br>Take the step to learn more about Meshtastic and how you can build your own emergency communication network. Tear off a link to the left and explore this innovative technology. Ensure you’re prepared for whatever comes your way.",
"url_message": "Follow this link to learn more.",
"url": "https://meshtastic.org",
"tear_off_link": "http://meshtastic.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+2 -2
View File
@@ -7,8 +7,8 @@
"content": "What is Nostr? Unlike traditional social media platforms, Nostr is a protocol—a decentralized system that empowers users to connect and share without the constraints of a central authority. Nostr is censorship-resistant, meaning your voice can’t be silenced by corporations or governments. It’s designed to give you control over your online presence, free from the influence of algorithms or ads that prioritize engagement over meaningful connections.<br><br>On Nostr, there are no hidden agendas to keep you doomscrolling. You engage with content on your terms, not based on what an algorithm thinks will keep you online the longest. Nostr allows you to build genuine connections and participate in conversations that matter, all while maintaining your privacy and autonomy.<br><br>In a world where online speech is increasingly controlled and manipulated, Nostr offers a refreshing alternative. It’s not just a platform; it’s a movement towards true digital freedom, where your interactions are driven by your interests and values, not by what generates the most ad revenue.<br><br>Take the step to learn more about Nostr and how it can transform the way you connect online. Tear off a link the left and explore this groundbreaking protocol. Join a community that values freedom and authentic interaction.",
"url_message": "Follow this link to learn more.",
"url": "https://nostr.how/en/what-is-nostr",
"tear_off_link": "http://nostr.6dshf2gnj7yzxlfcaczlyi57up4mvbtd5orinuj5bjsfycnhz2w456yd.onion/",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+3 -3
View File
@@ -5,10 +5,10 @@
"subtitle": "DO ~~NOT~~ DISTRIBUTE",
"headline": "OPERATION VOX VERA",
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Follow this link to learn more. Use Tor Browser.",
"url_message": "Follow this link to learn more.",
"url": "https://voxvera.org/",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+3 -3
View File
@@ -5,10 +5,10 @@
"subtitle": "DO ~~NOT~~ DISTRIBUTE",
"headline": "OPERATION VOX VERA",
"content": "Break free from censorship with VoxVera. An anonymous guerrilla marketing and message-spreading tool. Whether online or in the physical world, it can empower you to spread your ideas boldly, shielded by complete anonymity (if you host over Tor). Download the code, design a flyer site, host it online and amplify your message.<br><br>Use memetic power to share your ideas in your school, workplace, online communities, or even globally. VoxVera ensures your message resonates far and wide, with tear-off sections featuring unique URLs and QR codes for easy reprinting.<br><br>Privacy can be maintained. Flyers can be shared via the Tor network, protecting hoster and users from censorship.<br><br>Join us in a revolution that values truth and transparency. Together, we can build a network of informed citizens who are unafraid to speak out.",
"url_message": "Follow this link to learn more. Use Tor Browser.",
"url_message": "Follow this link to learn more.",
"url": "https://voxvera.org/",
"tear_off_link": "https://voxvera.org/",
"binary_message": "0110010 0101011 0110010 0111101 0110100",
"tear_off_link": "",
"footer_message": "0110010 0101011 0110010 0111101 0110100",
"attachment_path": "",
"attachment_filename": ""
}
+71 -26
View File
@@ -40,12 +40,13 @@
[dir="rtl"] .qr-code-body {
flex-direction: row-reverse;
justify-content: flex-start;
}
[dir="rtl"] .qr-code-url {
text-align: right;
margin-right: 0;
margin-left: 10px;
padding-right: 4px;
padding-left: 0;
}
[dir="rtl"] .message {
@@ -94,6 +95,7 @@
.print-button, .download-button, .lang-select {
padding: 10px 20px;
min-height: 42px;
font-size: 14px;
cursor: pointer;
background-color: #000;
@@ -101,6 +103,12 @@
border: none;
border-radius: 0px;
font-family: 'Courier New', Courier, monospace;
line-height: 1.2;
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
white-space: nowrap;
}
.lang-select {
@@ -127,6 +135,7 @@
.tear-off {
display: flex;
height: 1in; /* Fixed height for consistency */
gap: 12px;
padding: 5px;
border: 1px dashed #000;
font-size: 10px;
@@ -139,9 +148,11 @@
.tear-off-text {
flex: 1;
min-width: 0;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
padding-top: 1px;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
}
.tear-off a {
@@ -157,7 +168,6 @@
width: 80px;
height: 80px;
border: 0px solid #000;
margin-left: 10px;
flex-shrink: 0;
}
@@ -240,8 +250,9 @@
width: 100%;
height: auto;
display: flex;
justify-content: space-between;
justify-content: flex-start;
align-items: center;
gap: 10px;
margin-top: 10px;
padding: 0;
box-sizing: border-box;
@@ -257,12 +268,14 @@
.qr-code-url {
font-size: 12px;
text-align: left;
word-wrap: break-word;
overflow-wrap: break-word;
hyphens: auto;
flex-grow: 1;
margin-right: 10px;
max-width: 60%;
word-wrap: normal;
overflow-wrap: anywhere;
word-break: normal;
hyphens: none;
flex: 1 1 auto;
min-width: 0;
max-width: none;
padding-left: 4px;
}
hr {
@@ -298,6 +311,14 @@
margin-top: 5px;
}
.container.no-tear-offs .left-tear-offs {
display: none;
}
.container.no-tear-offs .content {
width: 100%;
}
@media print {
body, html {
width: 8.5in;
@@ -330,10 +351,20 @@
.button-bar {
display: flex;
align-items: stretch;
gap: 10px;
padding: 20px 0 30px;
}
#download-link {
display: inline-flex;
text-decoration: none;
}
#download-link .download-button {
height: 100%;
}
/* Dropdown styles */
.dropdown {
position: relative;
@@ -417,6 +448,19 @@
}
}
});
document.querySelectorAll('[data-i18n-href]').forEach(el => {
const path = el.getAttribute('data-i18n-href');
const href = t(path, lang);
if (href) {
el.href = href;
}
});
const torBrowserHref = t('web.tor_browser_download_url', lang);
if (torBrowserHref && torBrowserHref !== 'web.tor_browser_download_url') {
document.querySelectorAll('.tor-browser-link').forEach(el => {
el.href = torBrowserHref;
});
}
document.getElementById('lang-selector').value = lang;
document.documentElement.lang = lang;
}
@@ -441,11 +485,11 @@
</head>
<body>
<div class="container">
<div class="container {{tear_off_state_class}}">
<div class="left-tear-offs">
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -457,7 +501,7 @@
<!-- Repeat for other tear-offs -->
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -468,7 +512,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -479,7 +523,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -490,7 +534,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -501,7 +545,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -512,7 +556,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -523,7 +567,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -534,7 +578,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -545,7 +589,7 @@
</div>
<div class="tear-off">
<div class="tear-off-text">
<span data-i18n="web.tear_off_tor_browser">{{t_web_tear_off_tor_browser}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<span data-i18n="web.tear_off_desc">{{t_web_tear_off_desc}}</span><br>
<a class="tear-off-link" href="{{tear_off_link}}" target="_blank">{{tear_off_link}}</a><br>
<span data-i18n="web.tear_off_footer">{{t_web_tear_off_footer}}</span>
@@ -566,6 +610,7 @@
<div class="qr-code-url">
<span class="url-message" data-i18n="landing.url_message">{{t_landing_url_message}}</span><br><br>
<a class="tor-browser-link" data-i18n="web.tear_off_tor_browser" data-i18n-href="web.tor_browser_download_url" href="{{t_web_tor_browser_download_url}}" target="_blank" rel="noopener noreferrer">{{t_web_tear_off_tor_browser}}</a><br><br>
<a id="qr-link-2" href="{{url}}" target="_blank">{{url}}</a>
</div>
<img src="qrcode-content.png" alt="QR Code">
@@ -574,7 +619,7 @@
<hr>
<div class="footer">
<p class="credit"><span data-i18n="web.built_with">{{t_web_built_with}}</span> <a href="https://github.com/PR0M3TH3AN/VoxVera">voxvera</a></p>
<p class="binary" id="binary-message">{{binary_message}}</p>
<p class="binary" id="footer-message">{{footer_message}}</p>
</div>
</div>
</div>
@@ -586,4 +631,4 @@
<select class="lang-select" id="lang-selector" onchange="setLanguage(this.value)"></select>
</div>
</body>
</html>
</html>