feat(webapp): a persistent nav bar and one vocabulary (phase 2 of v4.3.0) #519
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/phase2-nav-and-vocabulary"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #378, closes #379. Phase 2 of the v4.3.0 build order (#514): the navigation spine, then one vocabulary.
#378 nav bar.
NavBaris now the whole bar, rendered once from a layout route (AppLayoutinApp.jsx) that also carries the singleAuthGuard, replacing 16 per-route copies. Left to right: the Quest Board wordmark (home), a campaign switcher (shown when the user is in more than one campaign; otherwise the campaign's name), and inside a campaign the links Sessions / Prep / Wiki / Highlights (Prep only for GMs, since/planningbounces everyone else). One wrapping flex row, so on a phone the campaign links drop to a second line. "Inside a campaign" is read from the/campaigns/:id/*route, and on session surfaces from a declaration the page makes once it knows its campaign (CampaignContext), tagged with the pathname so it cannot linger after navigation. The switcher's data is the campaign list the dashboard already fetches, requested once per app load and de-duplicated by the shared client. The wiki tab and "Open wiki" button no longer open a new tab; every remainingtarget="_blank"points at an external URL. Page roots droppedmin-h-screenbecause the in-flow bar was adding a permanent scrollbar; the two wiki layouts that hard-coded a viewport calc useflex-1.#379 vocabulary. One term per concept, from a new
vocabulary.js: Prep (was GM Workbench / Session Prep Sheet), Wiki (was Campaign Story, lore), Quests (was Thread Tracker / Plot Threads / loose ends / open loops), Highlights (was Quote Board), Suggested wiki updates (was AI / Entry / Bot / lore proposals; the Celery worker writes them, not the bot), and the recording link reads "Recording →" instead of "Dashboard →".GenerationHistoryrows and filter show human tool labels fromTOOL_LABELS, which the Prep palette also builds from;WikiArticlerenders the character timeline once. Route paths, API field names,tool_idvalues and code identifiers that mirror backend endpoints are unchanged, so bookmarks and Discord links keep working. The full old→new table and the deliberate survivors are in the completion comments.vitest 523 passed (48 files, +11), eslint clean apart from the pre-existing
CampaignDetailwarning,vite buildgreen. Frontend only; no backend change, no migration.🤖 Generated with Claude Code
Navigation was a strict tree walk. The only thing every page shared was a corner strip — name, Profile, Admin, theme, Sign out — so every cross-page move went through a page-specific "← Campaigns" / "← Campaign" / "← Session" breadcrumb. There was no global way to reach campaigns, and none at all to reach a campaign's wiki or prep from a session. NavBar becomes the whole bar rather than the right-hand cluster of one, and App renders it once in a layout route instead of each page pasting a copy into its own header. Left to right: the Quest Board wordmark (always home), a campaign switcher, and — when you are inside a campaign — Sessions, Prep, Wiki and Highlights for it. The right-hand cluster is unchanged. Three decisions worth recording: * "Inside a campaign" is read from the route on the /campaigns/:id/* family and, on the session surfaces whose URL carries no campaign id, from a declaration the page makes once it has loaded its session (CampaignContext). A declaration is tagged with the pathname it was made on, so it stops counting the moment you navigate away — no clearing on unmount, which would flicker the links off and on between two sessions of the same campaign. * The switcher's data is the campaign list the dashboard already fetches. The layout outlives every page, so that is one request per app load rather than one per navigation, and the shared client de-dupes it against the dashboard's own call. * Prep is shown to GMs only. /campaigns/:id/planning already bounces everyone else back to the campaign, and a link that exists to reject you is worse than no link. Labels use the phase-2 vocabulary from #379 from the start: Sessions, Prep, Wiki, Highlights — not "Quote Board". Two knock-on changes: * The wiki tab and the "Open wiki" button on the campaign page drop target="_blank". Both are internal routes, and the wiki tab in particular is styled identically to the no-op journal tab beside it, so opening a new window from it broke the back button for the whole wiki subtree. The shelf's "Draft created — Review" link goes the same way and becomes an in-app <Link> rather than a full page load. Every remaining _blank in the frontend points at an external URL (a character sheet, Google Calendar, a markdown link). * The shell now owns the viewport height, so pages drop the min-h-screen they each carried; without that the bar's height would be added to a full screen and put a permanent scrollbar on every page. The two wiki layouts that hard-coded min-h-[calc(100vh-61px)] use flex-1 instead, which needs no magic number at all. Per-page breadcrumbs stay for now; #374/#386 reduce them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>The same concept answered to a different name on almost every screen, which makes two screens about one thing look like two features. The prep workspace alone was "planning" (route), "GM Workbench" (heading and entry button), "Session Prep" (palette) and "Session Prep Sheet" (panel), and its output was split between "beat notes" and a "Session Shelf". One term per concept now, and the canonical labels live in a new `src/vocabulary.js` so the next rename is one file: Prep — was GM Workbench / Session Prep Sheet / planning-as-a-noun. "Beat Planner" survives as a tool name inside Prep, but what it writes is "prep notes" in copy (`beat_notes` stays the field). Wiki — was Campaign Story / storyline / lore. The landing page's <h1> now matches the breadcrumb that always said Wiki. Quests — was Thread Tracker / loose ends / open loops. The shelf's cold open lists "Open quests" (`open_loops` stays the API field). Highlights — was Quote Board. Suggested wiki updates — was AI Proposals / Entry Proposals / Bot Proposals / lore proposals. "Bot" was also just wrong: the Celery worker writes these, not the Discord bot, so the name sent GMs to the wrong component when they broke. Shared with #375, which will not redo it. Two things that were not renames: * `GenerationHistory` printed raw `tool_id` in its rows and its filter, so a GM read "session_prep" and "loot_parcel". Both go through `TOOL_LABELS`, which the Prep palette also builds its labels from — one map, so a tool cannot be called two things in two places. Unknown ids fall back to the id rather than vanishing. * `WikiArticle` rendered a character's timeline twice: a cramped infobox row and the full section below the article. The section stays; the infobox row and the now-unused `milestones` prop are gone. Deliberately unchanged, because they are contracts rather than words: route paths (`/campaigns/:id/planning`, `/wiki/proposals`), API fields (`beat_notes`, `open_loops`, `lore_*`), `tool_id` values, and the `workbench`/`Lore*` code identifiers that mirror backend endpoints. Component filenames stay too — renaming `CampaignPlanning.jsx` is churn with no user-visible gain. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>