Compare commits
2 Commits
5b497cf414
...
85fdfebd74
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85fdfebd74 | ||
|
|
57895a04d3 |
23
CLAUDE.md
23
CLAUDE.md
@@ -20,6 +20,9 @@ python run.py --skip-fetch --skip-link
|
|||||||
# Diagnose mod folder name / steam_id issues
|
# Diagnose mod folder name / steam_id issues
|
||||||
python check_names.py
|
python check_names.py
|
||||||
python check_names.py --fix --fix-ids
|
python check_names.py --fix --fix-ids
|
||||||
|
|
||||||
|
# Launch the GUI
|
||||||
|
python gui.py
|
||||||
```
|
```
|
||||||
|
|
||||||
There is no build step, linter config, or package install beyond `pip install -r requirements.txt`.
|
There is no build step, linter config, or package install beyond `pip install -r requirements.txt`.
|
||||||
@@ -80,6 +83,26 @@ Pass 2 builds `ok_disk_names` — the set of disk names that already match the s
|
|||||||
|
|
||||||
`--fix-ids` corrects `meta.cpp` using steam IDs from `comparison.json` (sourced from Steam Workshop URLs in the HTML presets) as the authoritative source.
|
`--fix-ids` corrects `meta.cpp` using steam IDs from `comparison.json` (sourced from Steam Workshop URLs in the HTML presets) as the authoritative source.
|
||||||
|
|
||||||
|
### GUI package
|
||||||
|
|
||||||
|
`gui/` is a CustomTkinter desktop application wrapping the CLI toolchain. Entry point is `gui.py` at the project root, which calls `gui.run_app()`.
|
||||||
|
|
||||||
|
**Key files:**
|
||||||
|
- `gui/__init__.py` — sets dark theme + blue color scheme; exports `run_app()`
|
||||||
|
- `gui/app.py` — `ArmaModManagerApp` main window; manages view routing, config loading, thread-safe log queue, and background pipeline execution
|
||||||
|
- `gui/wizard.py` — `SetupWizard` dialog shown on first launch when no `config.json` exists
|
||||||
|
- `gui/_constants.py` — window dimensions, status color constants, file paths
|
||||||
|
- `gui/_io.py` — `_QueueWriter` redirects stdout/stderr to a thread-safe queue so pipeline output streams into the Logs view
|
||||||
|
|
||||||
|
**Views** (`gui/views/`): each inherits `BaseView`; `build()` runs once on creation, `refresh()` runs on each navigation:
|
||||||
|
- `dashboard.py` — overview, status, quick stats
|
||||||
|
- `mods.py` — browse and manage downloaded mods by group
|
||||||
|
- `tools.py` — link/unlink, rename folders, sync missing mods, check server
|
||||||
|
- `logs.py` — real-time log viewer fed from the stdout/stderr queue
|
||||||
|
- `settings.py` — in-app editor for `config.json` (server URL, paths, credentials)
|
||||||
|
|
||||||
|
**`selection.json`** — GUI selection state file, tracked in git. Persists which mods/groups are selected between GUI sessions. Written by the GUI; safe to delete (GUI recreates it on next save).
|
||||||
|
|
||||||
## Python Version Compatibility
|
## Python Version Compatibility
|
||||||
|
|
||||||
Minimum is Python **3.9**. All files that use `X | Y` union type annotations **must** have `from __future__ import annotations` as the first import. Without it, the `|` syntax raises `TypeError` at runtime on Python < 3.10. Every module in `arma_modlist_tools/` already has it; any new CLI script you add must include it too.
|
Minimum is Python **3.9**. All files that use `X | Y` union type annotations **must** have `from __future__ import annotations` as the first import. Without it, the `|` syntax raises `TypeError` at runtime on Python < 3.10. Every module in `arma_modlist_tools/` already has it; any new CLI script you add must include it too.
|
||||||
|
|||||||
41
README.md
41
README.md
@@ -21,6 +21,7 @@ Python toolchain for managing Arma 3 mod presets: parse launcher exports, compar
|
|||||||
- [update_mods.py](#update_modspy)
|
- [update_mods.py](#update_modspy)
|
||||||
- [check_names.py](#check_namespy)
|
- [check_names.py](#check_namespy)
|
||||||
- [run.py](#runpy)
|
- [run.py](#runpy)
|
||||||
|
- [gui.py](#guipy)
|
||||||
6. [Migrating Existing Mods](#migrating-existing-mods)
|
6. [Migrating Existing Mods](#migrating-existing-mods)
|
||||||
7. [Folder Structure](#folder-structure)
|
7. [Folder Structure](#folder-structure)
|
||||||
8. [Moving to a New Device](#moving-to-a-new-device)
|
8. [Moving to a New Device](#moving-to-a-new-device)
|
||||||
@@ -35,6 +36,7 @@ Python toolchain for managing Arma 3 mod presets: parse launcher exports, compar
|
|||||||
| Python | >= 3.9 | `python --version` |
|
| Python | >= 3.9 | `python --version` |
|
||||||
| requests | any | `pip install requests` |
|
| requests | any | `pip install requests` |
|
||||||
| tqdm | any | `pip install tqdm` |
|
| tqdm | any | `pip install tqdm` |
|
||||||
|
| customtkinter | any | `pip install customtkinter` (GUI only) |
|
||||||
| Windows or Linux | — | Windows uses junctions, Linux uses symlinks |
|
| Windows or Linux | — | Windows uses junctions, Linux uses symlinks |
|
||||||
|
|
||||||
Run the dep checker to confirm everything is ready:
|
Run the dep checker to confirm everything is ready:
|
||||||
@@ -472,6 +474,30 @@ Orchestrator that chains all four pipeline steps. Described in [Quick Start](#qu
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### gui.py
|
||||||
|
|
||||||
|
Launch the graphical interface for the toolchain.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python gui.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Opens a CustomTkinter desktop window with a sidebar navigation and the following views:
|
||||||
|
|
||||||
|
| View | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| Dashboard | Overview: status, quick stats, recent activity |
|
||||||
|
| Mods | Browse and manage downloaded mods by group |
|
||||||
|
| Tools | Link/unlink, rename, sync missing, check server |
|
||||||
|
| Logs | Real-time log output from pipeline operations |
|
||||||
|
| Settings | Edit `config.json` (server URL, paths, credentials) |
|
||||||
|
|
||||||
|
On first launch (no `config.json`), a setup wizard walks you through creating one.
|
||||||
|
|
||||||
|
**Requires:** `customtkinter` (`pip install customtkinter`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Migrating Existing Mods
|
## Migrating Existing Mods
|
||||||
|
|
||||||
If the Arma 3 Server already has mods installed and you want to bring them under this toolchain without re-downloading:
|
If the Arma 3 Server already has mods installed and you want to bring them under this toolchain without re-downloading:
|
||||||
@@ -520,6 +546,19 @@ arma-modlist-tools/
|
|||||||
| |- config.py # config.json loader
|
| |- config.py # config.json loader
|
||||||
| |- compat.py # OS detection + encoding fix
|
| |- compat.py # OS detection + encoding fix
|
||||||
|
|
|
|
||||||
|
|- gui/ # GUI package (CustomTkinter desktop app)
|
||||||
|
| |- __init__.py # Theme setup + run_app() entry point
|
||||||
|
| |- app.py # Main window, view management, pipeline runner
|
||||||
|
| |- wizard.py # First-run config setup wizard
|
||||||
|
| |- _constants.py # Window size, color, path constants
|
||||||
|
| |- _io.py # stdout/stderr → thread-safe queue for live logs
|
||||||
|
| |- views/
|
||||||
|
| |- dashboard.py # Overview view
|
||||||
|
| |- mods.py # Mod browser view
|
||||||
|
| |- tools.py # Tool actions view
|
||||||
|
| |- logs.py # Real-time log view
|
||||||
|
| |- settings.py # Config editor view
|
||||||
|
|
|
||||||
|- modlist_html/ # INPUT: put your .html preset exports here
|
|- modlist_html/ # INPUT: put your .html preset exports here
|
||||||
| |- MyPreset_A.html
|
| |- MyPreset_A.html
|
||||||
| |- MyPreset_B.html
|
| |- MyPreset_B.html
|
||||||
@@ -540,7 +579,9 @@ arma-modlist-tools/
|
|||||||
|- config.json # YOUR config (gitignored — contains credentials)
|
|- config.json # YOUR config (gitignored — contains credentials)
|
||||||
|- config.template.json # Template to copy from
|
|- config.template.json # Template to copy from
|
||||||
|- requirements.txt
|
|- requirements.txt
|
||||||
|
|- selection.json # GUI selection state (persisted between sessions)
|
||||||
|
|
|
|
||||||
|
|- gui.py # GUI entry point
|
||||||
|- run.py # Orchestrator (parse + compare + fetch + link)
|
|- run.py # Orchestrator (parse + compare + fetch + link)
|
||||||
|- parse_modlist.py # Step 1 standalone
|
|- parse_modlist.py # Step 1 standalone
|
||||||
|- compare_modlists.py # Step 2 standalone
|
|- compare_modlists.py # Step 2 standalone
|
||||||
|
|||||||
@@ -33,12 +33,19 @@ def group(name: str) -> None:
|
|||||||
print(f"{'-'*60}")
|
print(f"{'-'*60}")
|
||||||
|
|
||||||
|
|
||||||
|
class _SkipTest(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def test(name: str, fn) -> None:
|
def test(name: str, fn) -> None:
|
||||||
global _passed, _failed
|
global _passed, _failed, _skipped
|
||||||
try:
|
try:
|
||||||
fn()
|
fn()
|
||||||
print(f" [PASS] {name}")
|
print(f" [PASS] {name}")
|
||||||
_passed += 1
|
_passed += 1
|
||||||
|
except _SkipTest as exc:
|
||||||
|
print(f" [SKIP] {name} ({exc})")
|
||||||
|
_skipped += 1
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print(f" [FAIL] {name}")
|
print(f" [FAIL] {name}")
|
||||||
for line in traceback.format_exc().splitlines():
|
for line in traceback.format_exc().splitlines():
|
||||||
@@ -1323,7 +1330,7 @@ def _test_comparison_json_consistent_with_html():
|
|||||||
html_dir = Path(__file__).parent / "modlist_html"
|
html_dir = Path(__file__).parent / "modlist_html"
|
||||||
json_file = Path(__file__).parent / "modlist_json" / "comparison.json"
|
json_file = Path(__file__).parent / "modlist_json" / "comparison.json"
|
||||||
if not json_file.exists():
|
if not json_file.exists():
|
||||||
raise AssertionError(f"comparison.json not found: {json_file}")
|
raise _SkipTest("comparison.json not found (run pipeline first)")
|
||||||
|
|
||||||
presets = parse_modlist_dir(html_dir)
|
presets = parse_modlist_dir(html_dir)
|
||||||
fresh = compare_presets(*presets)
|
fresh = compare_presets(*presets)
|
||||||
|
|||||||
Reference in New Issue
Block a user