mirror of
https://github.com/PR0M3TH3AN/VoxVera.git
synced 2025-09-08 15:08:42 +00:00
Merge branch 'main' into codex/populate-gui-text-fields-for-editing
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,6 +3,9 @@
|
|||||||
tmp*/
|
tmp*/
|
||||||
venv/
|
venv/
|
||||||
.venv
|
.venv
|
||||||
|
voxvera/
|
||||||
|
voxvera/src/
|
||||||
|
|
||||||
# OS and editor files
|
# OS and editor files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
19
README.md
19
README.md
@@ -6,18 +6,19 @@ Generate printable flyers with QR codes linking to Tor (.onion) or HTTPS sites,
|
|||||||
|
|
||||||
## 🚀 Key Features
|
## 🚀 Key Features
|
||||||
|
|
||||||
* **Interactive setup**: `voxvera init` prompts for metadata or extracts from a PDF form. When editing body text, a small Tkinter window opens with the current content pre-filled. If no GUI is available it falls back to `$EDITOR`.
|
// 🔧 merged conflicting changes from codex/populate-gui-text-fields-for-editing vs main
|
||||||
* **Template support**: `voxvera init --template <name>` copies built‑in templates (`blank`, `voxvera`).
|
* **Interactive setup**: `voxvera init` prompts for metadata or extracts from a PDF form. When editing body text, a small Tkinter GUI window opens with existing content pre-filled, falling back to the user's `$EDITOR` if the GUI isn't available.
|
||||||
|
* **Template support**: `voxvera init --template <name>` copies built-in templates (`blank`, `voxvera`).
|
||||||
* **Build assets**: `voxvera build [--pdf <path>] [--download <file.zip>]` generates HTML, obfuscated JS/CSS, QR codes, and bundles PDFs.
|
* **Build assets**: `voxvera build [--pdf <path>] [--download <file.zip>]` generates HTML, obfuscated JS/CSS, QR codes, and bundles PDFs.
|
||||||
* **Batch import**: `voxvera import` processes all JSON configs in `imports/`.
|
* **Batch import**: `voxvera import` processes all JSON configs in `imports/`.
|
||||||
* **Onion hosting**: `voxvera serve` publishes via Tor/OnionShare and updates flyer links.
|
* **Onion hosting**: `voxvera serve` publishes via Tor/OnionShare and updates flyer links.
|
||||||
* **All‑in‑one**: `voxvera quickstart` runs init, build, and serve in sequence.
|
* **All-in-one**: `voxvera quickstart` runs init, build, and serve in sequence.
|
||||||
* **Dependency check**: `voxvera check` verifies presence of required tools.
|
* **Dependency check**: `voxvera check` verifies presence of required tools.
|
||||||
* **GUI**: Minimal Electron wrapper (`gui/electron`) for non‑CLI users.
|
* **GUI**: Minimal Electron wrapper (`gui/electron`) for non-CLI users.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📥 Fool‑Proof Installation
|
## 📥 Fool-Proof Installation
|
||||||
|
|
||||||
### 1. Prebuilt Binary (Linux)
|
### 1. Prebuilt Binary (Linux)
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
|
|||||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
|
||||||
echo 'Add ~/.local/bin to your PATH and restart your shell.'
|
echo 'Add ~/.local/bin to your PATH and restart your shell.'
|
||||||
fi
|
fi
|
||||||
```
|
````
|
||||||
|
|
||||||
### 2. Homebrew (macOS)
|
### 2. Homebrew (macOS)
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ brew tap PR0M3TH3AN/voxvera
|
|||||||
brew install voxvera
|
brew install voxvera
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. pipx (cross‑platform)
|
### 3. pipx (cross-platform)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pipx install voxvera
|
pipx install voxvera
|
||||||
@@ -115,7 +116,7 @@ voxvera quickstart
|
|||||||
### Other Commands
|
### Other Commands
|
||||||
|
|
||||||
* `voxvera init --template <name>` — copy a template into `dist/`.
|
* `voxvera init --template <name>` — copy a template into `dist/`.
|
||||||
* `voxvera import` — batch‑import JSON configs from `imports/`.
|
* `voxvera import` — batch-import JSON configs from `imports/`.
|
||||||
* `voxvera check` — dependency health check.
|
* `voxvera check` — dependency health check.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -146,4 +147,4 @@ See the `docs/` folder for detailed guides:
|
|||||||
|
|
||||||
## 📜 License
|
## 📜 License
|
||||||
|
|
||||||
MIT © 2025 thePR0M3TH3AN
|
MIT © 2025 thePR0M3TH3AN
|
@@ -100,42 +100,36 @@ def _open_editor_terminal(initial: str) -> str:
|
|||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
|
|
||||||
|
|
||||||
|
# 🔧 merged conflicting changes from codex/populate-gui-text-fields-for-editing vs main
|
||||||
def open_editor(initial: str) -> str:
|
def open_editor(initial: str) -> str:
|
||||||
"""Edit text in a small GUI window if possible.
|
"""Open a simple GUI text editor with pre-populated content if possible.
|
||||||
|
|
||||||
Existing text is pre-filled in the editor. When ``tkinter`` or a display
|
Existing text is pre-filled in the editor. If tkinter or a display
|
||||||
server is unavailable the function falls back to ``$EDITOR`` in the
|
server is unavailable, falls back to the user's $EDITOR in the terminal.
|
||||||
terminal.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import scrolledtext
|
from tkinter import scrolledtext
|
||||||
|
root = tk.Tk()
|
||||||
|
root.title("Edit text")
|
||||||
except Exception:
|
except Exception:
|
||||||
return _open_editor_terminal(initial)
|
return _open_editor_terminal(initial)
|
||||||
|
|
||||||
try:
|
|
||||||
root = tk.Tk()
|
|
||||||
root.title("Edit text")
|
|
||||||
except tk.TclError:
|
|
||||||
return _open_editor_terminal(initial)
|
|
||||||
|
|
||||||
result = {"text": initial or ""}
|
result = {"text": initial or ""}
|
||||||
|
|
||||||
text = scrolledtext.ScrolledText(root, width=80, height=20)
|
text = scrolledtext.ScrolledText(root, width=80, height=20)
|
||||||
text.pack(expand=True, fill="both")
|
text.pack(expand=True, fill="both")
|
||||||
if initial:
|
if initial:
|
||||||
text.insert("1.0", initial)
|
text.insert("1.0", initial)
|
||||||
text.focus_set()
|
text.focus_set()
|
||||||
|
|
||||||
def finalize():
|
def save_and_close():
|
||||||
result["text"] = text.get("1.0", "end-1c")
|
result["text"] = text.get("1.0", "end-1c")
|
||||||
root.quit()
|
root.destroy()
|
||||||
|
|
||||||
tk.Button(root, text="Save", command=finalize).pack()
|
save_btn = tk.Button(root, text="Save", command=save_and_close)
|
||||||
root.protocol("WM_DELETE_WINDOW", finalize)
|
save_btn.pack()
|
||||||
|
root.protocol("WM_DELETE_WINDOW", save_and_close)
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
root.destroy()
|
|
||||||
return result["text"]
|
return result["text"]
|
||||||
|
|
||||||
|
|
||||||
@@ -345,7 +339,11 @@ def serve(config_path: str):
|
|||||||
print(f"Directory {dir_path} not found", file=sys.stderr)
|
print(f"Directory {dir_path} not found", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
logfile = dir_path / 'onionshare.log'
|
logfile = dir_path / 'onionshare.log'
|
||||||
proc = subprocess.Popen(['onionshare-cli', '--website', '--public', '--persistent', f'{dir_path}/.onionshare-session', str(dir_path)], stdout=open(logfile, 'w'), stderr=subprocess.STDOUT)
|
proc = subprocess.Popen(
|
||||||
|
['onionshare-cli', '--website', '--public', '--persistent',
|
||||||
|
f'{dir_path}/.onionshare-session', str(dir_path)],
|
||||||
|
stdout=open(logfile, 'w'), stderr=subprocess.STDOUT
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
import time
|
import time
|
||||||
import re as _re
|
import re as _re
|
||||||
@@ -393,7 +391,8 @@ def import_configs():
|
|||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
parser = argparse.ArgumentParser(prog='voxvera')
|
parser = argparse.ArgumentParser(prog='voxvera')
|
||||||
parser.add_argument('--config', default=str(ROOT / 'src' / 'config.json'), help='Path to config.json')
|
parser.add_argument('--config', default=str(ROOT / 'src' / 'config.json'),
|
||||||
|
help='Path to config.json')
|
||||||
sub = parser.add_subparsers(dest='command')
|
sub = parser.add_subparsers(dest='command')
|
||||||
|
|
||||||
p_init = sub.add_parser('init', help='Update configuration interactively or from PDF')
|
p_init = sub.add_parser('init', help='Update configuration interactively or from PDF')
|
||||||
|
Reference in New Issue
Block a user