Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Install dependencies and run:
uv run python main.py
```

The app auto-creates `gear_data.json` with seeded example data on first run, so you can immediately see the full workflow. Use `uv` (recommended) for automatic environment management; fall back to `pip install -r requirements.txt` if needed.
On first run the app asks for a storage folder, remembers it in the platform's standard application-config location, and creates `gear_data.json` there with seeded example data. Use `uv` (recommended) for automatic environment management; fall back to `pip install -r requirements.txt` if needed.

---

Expand All @@ -22,6 +22,11 @@ The app auto-creates `gear_data.json` with seeded example data on first run, so
- Constants: `CATEGORIES`, `CATEGORY_EMOJI`, `BIG_THREE`, `WEIGHT_TYPES`, thresholds
- Helper functions: `find_gear()`, `find_trip()`, `compute_trip_summary()`, `total_weight_oz()`, etc.

- **`packrat_preferences.py`** — Cross-platform preference layer. Handles:
- OS-standard config and suggested data directories via `platformdirs`
- Atomic `preferences.json` reads/writes
- Startup precedence between `--data`, remembered storage, and onboarding

- **`gear_tui.py`** — Textual UI layer. Handles:
- Screens (gear inventory, trips tab, reports tab) and modal dialogs (forms, pickers, confirmations)
- DataTable widgets, search filtering, keyboard + mouse bindings
Expand Down Expand Up @@ -87,6 +92,8 @@ The app auto-creates `gear_data.json` with seeded example data on first run, so
### Data Persistence
- `load_data(path)` — reads JSON; auto-seeds with `example_data()` if file missing
- `save_data(path, data)` — atomic writes via temp file (`path + ".tmp"`, then `os.replace()`)
- The selected folder is remembered outside the repository; its library is always named `gear_data.json`
- `--data` is an exact-file override for one launch and does not update preferences
- **No locking** — avoid editing from multiple machines simultaneously or sync conflicts will create backup files

### Markdown Export
Expand Down Expand Up @@ -162,7 +169,7 @@ uv run python main.py
## Common Gotchas

- **Modal dialogs cut off:** Terminal too small (need ≥130×42). Zoom or expand window.
- **Data file not created:** Check write permissions in the working directory.
- **Data file not created:** Check write permissions in the selected storage folder.
- **Markdown export missing:** Files go to `exports/` subdirectory auto-created next to `gear_data.json`.
- **Weight calculations wrong:** Verify `qty` is set (defaults to 1) and `weight_type` matches the calculation logic (base/worn/consumable are summed separately).
- **Stale UI after edit:** Modal dismisses and returns updated dict; catch with `@on(SomeScreen.ScreenType.Submitted)` or similar pattern.
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,19 @@ permissions:

jobs:
test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
python-version: ["3.9", "3.12"]
include:
- os: ubuntu-latest
python-version: "3.9"
- os: ubuntu-latest
python-version: "3.12"
- os: windows-latest
python-version: "3.12"
- os: macos-latest
python-version: "3.12"

steps:
- uses: actions/checkout@v4
Expand Down
39 changes: 31 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

A mouse-and-keyboard terminal app for tracking backpacking gear and
building per-trip pack lists, built with [Textual](https://textual.textualize.io).
Data lives in one JSON file next to the code, so the whole thing is
portable — drop the folder in iCloud Drive / Dropbox / OneDrive and run it
from any Mac or Windows machine.
Data lives in one JSON file in a folder you choose on first launch. Packrat
remembers that folder in your operating system's standard application-config
location, so the code and your personal data remain separate. Choose an
iCloud Drive, Dropbox, or OneDrive folder if you want the library synced
between Windows, macOS, and Linux computers.

## Requirements

Expand All @@ -19,14 +21,19 @@ With `uv` (installs the right Python packages automatically, no manual venv):
uv run python main.py
```

The first launch opens a short setup screen. Accept the suggested
platform-specific folder or enter another folder; Packrat creates
`gear_data.json` there with clearly labeled example items.

Without `uv`:

```bash
pip install -r requirements.txt # or: pip install textual
pip install -r requirements.txt # or: pip install textual platformdirs
python3 main.py
```

To point at a data file somewhere else (e.g. a specific iCloud folder):
To use an exact data file for one launch without changing the remembered
preference:

```bash
uv run python main.py --data "/Users/you/Library/Mobile Documents/com~apple~CloudDocs/Gear/gear_data.json"
Expand All @@ -38,9 +45,10 @@ uv run python main.py --data "/Users/you/Library/Mobile Documents/com~apple~Clou
- `gear_tui.py` — the Textual UI: screens, forms, tables, styling.
- `gear_core.py` — data model, JSON persistence, and Markdown rendering.
Pure functions, no UI code — this is what generates the pack-list exports.
- `gear_data.json` — your data. Created automatically on first run if it
doesn't exist, seeded with a few example items/trip (clearly labeled) so
the format is obvious.
- `packrat_preferences.py` — cross-platform preference paths and persistence.
- `gear_data.json` — created in your selected storage folder and seeded with
a few example items/trip (clearly labeled) so the format is obvious. The
repository copy is not used as the runtime default.
- `pyproject.toml` / `uv.lock` — project + locked dependencies for `uv`.
- `requirements.txt` — plain-pip fallback if you're not using `uv`.

Expand All @@ -58,6 +66,8 @@ are:
edits an item's quantity/note and **P** opens the pack audit.
- **Ctrl+S** saves forms and picker dialogs; **Enter** confirms confirmations.
- **Ctrl+B** writes a manual `.bak` snapshot beside your data file.
- **Ctrl+P** opens Storage Preferences to open another library or copy the
current library to a new folder and switch to it.

- **Click a table row** to select it; **click it again** (or press Enter)
to open/edit it. This two-step click mirrors how most file browsers work
Expand Down Expand Up @@ -99,6 +109,19 @@ It is still a flat JSON file rather than a mergeable database. If Packrat
reports an external-change conflict, restart it to load the newer file before
editing again.

## Storage preferences

The setup screen and Storage Preferences select a folder; the library inside
that folder is always named `gear_data.json`. **Open / Create** opens an
existing valid library or creates a new example library. **Copy Current &
Switch** copies the active library to an unused destination and leaves the
original file intact. It will not overwrite an existing destination library.

If the saved preference is damaged or its library cannot be opened, Packrat
returns to setup with the error instead of silently using another data file.
The `--data` option remains useful for scripts and one-off libraries and never
changes the remembered folder by itself.

## Backing it up

Press **Ctrl+B** for an on-demand snapshot, copy the JSON file whenever you
Expand Down
5 changes: 0 additions & 5 deletions gear_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@
AUDIT_STATUSES = ("covered", "omitted", "unresolved")
DATA_VERSION = 2

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
DEFAULT_DATA_PATH = os.path.join(SCRIPT_DIR, "gear_data.json")
DEFAULT_EXPORT_DIR = os.path.join(SCRIPT_DIR, "exports")


class DataValidationError(ValueError):
"""Raised when a data file doesn't match Packrat's expected schema."""

Expand Down
Loading
Loading