The Shelves layer: where every box physically lives

Eric's spec, all nine points. Two committed local-only stores follow
the local_games.json pattern — furniture.json (units of openings with
interior dims; a dimensionless opening is a virtual spot like a travel
case) and locations.json (game key -> opening + note). Shelf layouts
are nobody's data but the owner's; nothing touches upload.

The Shelves page builds furniture without hand-editing JSON — the
acceptance bar (two double-wides above three rows of four cubes, two
bookcases, a travel case) is a TEST, driven entirely through the
endpoints the UI calls. Presets for Kallax/Billy/custom/virtual,
grid creation with A1-style labels, openings editable/deletable/
reorderable. Units render as grids: zone, count, fill bar (stacked
thinnest-axis vs interior height), ⚠ on overfull or any resident that
can't fit. Openings open as a modal — a bottom sheet at phone widths,
search-first with thumb-sized targets for the moving-day loop.
Unshelved games list alongside with one-tap suggestions (only openings
they verifiably fit, with room).

Containment composes: a game stored inside another box inherits its
container's location, rides along in the opening's resident list
(marked), and refuses direct assignment naming its container. The
detail page's where-it-lives card gains the picker (openings grouped
by unit, each labeled fits / doesn't fit / can't verify) plus virtual
notes ("lent to Sarah, June"); the Library list shows a location line,
filters by unit or unshelved, and search matches location text and
zones.

bggpipe dims drops its hardcoded Kallax for the user's actual
furniture: per-opening capacity, overfull and misfit warnings,
unshelved count. bggpipe shelve --import loads a name,opening CSV
(ids or labels), rejecting — never guessing — unknown names, ambiguous
copies, unknown/ambiguous openings, misfits, and contained games.

Ten new tests incl. the acceptance flow, inheritance, CSV rejects,
and a phone-sheet smoke; 372 total.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
Eric Wagoner
2026-08-09 13:45:36 -04:00
co-authored by Claude Fable 5
parent e85f72c546
commit 4f446f6f2a
17 changed files with 1601 additions and 62 deletions
+23
View File
@@ -109,6 +109,29 @@ BGG application approval can take a week or more. Until then: `extract` works im
- `https://boardgamegeek.com/xmlapi2/collection?username=YOU&own=1&version=1`
- `https://boardgamegeek.com/xmlapi2/collection?username=YOU&own=1&version=1&subtype=boardgameexpansion`
## Where everything lives
The **Shelves** page maps your physical storage: create units from presets
(IKEA Kallax cube 13.25″ × 13.25″ × 15.4″, Billy shelf, custom sizes) as
grids of rows × columns, add one-off openings (double-wides, a top shelf),
and no-size virtual spots ("travel case", "lent out"). Openings carry free-
text zones, are editable and reorderable, and everything lands in
`data/furniture.json` + `data/locations.json` — committed local stores;
your shelf layout is never sent anywhere.
Assign games by tapping an unshelved game's suggested openings (only
openings it actually fits, with room to spare), by searching inside an
opening, or from a game's detail page (openings grouped by unit, each
labeled fits / doesn't fit / can't verify). Games stored inside another
box inherit their container's spot. On a phone over `--lan`, the opening
view docks as a bottom sheet — the moving-day loop is search, tap, done.
Bulk-load a reviewed plan with `bggpipe shelve --import plan.csv` (columns
`name,opening`, accepting opening ids or labels) — rejects are reported
(unknown name, ambiguous copies, unknown opening, doesn't fit, lives
inside another box), never guessed. `bggpipe dims` reports per-opening
capacity, overfull warnings, and misfits against your real furniture.
## Shelf-space planning
Physical box dimensions live on BGG *versions*, not games, so enrich collects