Shelf: scene packs — bundle cards into scenes, swap the board (#244) #264

Merged
claude-bot merged 2 commits from feat/v3.11-scene-packs into main 2026-07-27 18:23:42 +00:00
Contributor

Closes #244.

Bundle shelf cards into named scenes during prep, then swap the board's scene in one tap during play.

Approach

A "scene" is just the free-text scene tag that has been on ShelfCard since #239no new table and no migration. Creating a scene is tagging a card; a scene stops existing when its last card leaves it. The scene bar is derived from the cards themselves.

Filtering hides cards, it never moves them. A hidden card's stored x/y is untouched, so swapping back to a scene restores the exact layout the GM left — the spatial-memory guardrail from #235 ("never auto-move the GM's cards"). Cards also keep their index in the full list while filtered, so a never-placed card's cascade fallback position doesn't drift as scenes change.

Backend

One real gap: every field on ShelfCardUpdate uses None for "leave unchanged", so a card could join a scene and never leave it. An empty/blank string is now the clear sentinel, and names are stored stripped so " Docks" and "Docks" are one chip rather than two.

Frontend

  • Scene barAll / one chip per scene / Unfiled, each with a count. Hidden entirely until at least one card is tagged, so an untagged shelf looks exactly as it does today.
  • Per-card scene picker — a native select over the known scenes plus + New scene…, which swaps in a text input. Matches CopyFromSelect's idiom and stays keyboard-reachable on a card.

Threaded through the surfaces that index the board:

  • 19 and the stage's prev/next walk the visible scene, so "the Nth card" means the Nth one on screen.
  • A staged card stays staged across a scene swap — the stage is "now playing", not a view of the board. Its position counter reads blank when it isn't in the visible set.
  • A Ctrl-K hit from another scene swaps the board to that scene, otherwise the stage would have no board card behind it.

Testing

All run locally in containers matching the CI jobs:

  • Backend: 777 passed (full suite); new test_scene_is_stripped_and_cleared_by_empty_string covers strip, leave-unchanged, and the clear sentinel.
  • Frontend: 413 passed (full suite); new SessionShelf.test.jsx with 7 cases covering chip derivation, the no-scene-bar case, scene swapping, Unfiled, returning to All, tagging, and clearing.
  • ruff check / ruff format --check clean; eslint clean (the one warning is pre-existing in CampaignDetail.jsx).

🤖 Generated with Claude Code

Closes #244. Bundle shelf cards into named scenes during prep, then swap the board's scene in one tap during play. ## Approach A "scene" is just the free-text `scene` tag that has been on `ShelfCard` since #239 — **no new table and no migration**. Creating a scene is tagging a card; a scene stops existing when its last card leaves it. The scene bar is derived from the cards themselves. **Filtering hides cards, it never moves them.** A hidden card's stored `x`/`y` is untouched, so swapping back to a scene restores the exact layout the GM left — the spatial-memory guardrail from #235 ("never auto-move the GM's cards"). Cards also keep their index in the *full* list while filtered, so a never-placed card's cascade fallback position doesn't drift as scenes change. ## Backend One real gap: every field on `ShelfCardUpdate` uses `None` for "leave unchanged", so a card could join a scene and never leave it. An empty/blank string is now the clear sentinel, and names are stored stripped so `" Docks"` and `"Docks"` are one chip rather than two. ## Frontend - **Scene bar** — `All` / one chip per scene / `Unfiled`, each with a count. Hidden entirely until at least one card is tagged, so an untagged shelf looks exactly as it does today. - **Per-card scene picker** — a native select over the known scenes plus `+ New scene…`, which swaps in a text input. Matches `CopyFromSelect`'s idiom and stays keyboard-reachable on a card. Threaded through the surfaces that index the board: - `1`–`9` and the stage's prev/next walk the **visible** scene, so "the Nth card" means the Nth one on screen. - A staged card **stays staged** across a scene swap — the stage is "now playing", not a view of the board. Its position counter reads blank when it isn't in the visible set. - A Ctrl-K hit from another scene swaps the board to that scene, otherwise the stage would have no board card behind it. ## Testing All run locally in containers matching the CI jobs: - Backend: `777 passed` (full suite); new `test_scene_is_stripped_and_cleared_by_empty_string` covers strip, leave-unchanged, and the clear sentinel. - Frontend: `413 passed` (full suite); new `SessionShelf.test.jsx` with 7 cases covering chip derivation, the no-scene-bar case, scene swapping, `Unfiled`, returning to `All`, tagging, and clearing. - `ruff check` / `ruff format --check` clean; `eslint` clean (the one warning is pre-existing in `CampaignDetail.jsx`). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The `scene` column and its PATCH have existed since #239, but every field on
ShelfCardUpdate uses `None` for "leave unchanged", so there was no way to clear
a tag once set — a card could join a scene and never leave it.

Treat an empty/blank string as the clear sentinel, and store names stripped so
" Docks" and "Docks" are the same scene on the board rather than two chips.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
feat(frontend): shelf scene packs — tag cards, swap the board (#244)
All checks were successful
CI / Frontend tests, audit, and build (pull_request) Successful in 2m3s
CI / Backend lint (ruff) (pull_request) Successful in 30s
CI / Docker image build (pull_request) Successful in 44s
CI / Bot tests and audit (pull_request) Successful in 1m37s
CI / Backend migration, tests, and audit (pull_request) Successful in 4m52s
c6997215b9
Bundle cards into named scenes during prep and swap the board's scene in one tap
during play. A scene is just the free-text `scene` tag on a card, so there is no
scene table: creating one is tagging a card, and a scene stops existing when its
last card leaves it.

Filtering hides cards, it never moves them — a hidden card's stored x/y is
untouched, so swapping back restores the exact layout the GM left (#235's
spatial-memory guardrail). Cards keep their index in the *full* list when
filtered, so a never-placed card's cascade fallback doesn't drift between
scenes.

Also threaded through the surfaces that index the board:
- 1-9 and the stage's prev/next now walk the visible scene, so "the Nth card"
  means the Nth one on screen.
- A staged card stays staged across a scene swap; its position counter reads
  blank when it isn't in the visible set.
- A Ctrl-K hit from another scene swaps the board to that scene, otherwise the
  stage would have no board card behind it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/v3.11-scene-packs 2026-07-27 18:23:42 +00:00
Sign in to join this conversation.
No description provided.