mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 02:29:33 +01:00
fix(story-mode): the frontend rules describe how balloons are placed now
The Story Mode entry still said _storyPlaceBalloon places Claude's balloon over the head agent() last drew. Placement moved to _storyFitBalloon, from the scene's finished frame, with the panel growing for caption and balloon together; the rule now says so, and names the two things a change must keep: never place from the frame on screen, and compute the growth at the panel's usual height so a refit cannot oscillate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
6744bfcf86
commit
0bf6b77170
1 file changed
+1
-1
+1
-1
@@ -54,7 +54,7 @@ Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. L
|
||||
|
||||
**Settings surface** (`#appSettingsModal` + `#sessionOptionsModal` + `#createCaseModal`): one `set-*` language shared through a single `:is(...)` id scope in styles.css. App Settings' rail is a table of contents over ONE scrolling document (`switchSettingsTab` scrolls); Session Options and Add Case really switch (`switchOptionsTab` / `switchCaseModalTab`), and their larger per-modal size blocks are the design, not drift. ⚠️ **The load/save contract is `getElementById` by id**: renaming or dropping a control id silently stops it loading or saving. ⚠️ The Session Options "Session" entry still keys off `context` (label-only rename). ⚠️ Add Case keeps its legacy `.form-row` markup via an adapter; every `<details>` there needs `.set-adv-chev` plus both marker suppressions. ⚠️ Model cards and the effort segment are views over hidden `<select>`s, which stay the source of truth. ⚠️ `.modal-tabs*` classes are retired; `admin-ui.js` needs `.set-rail-items` + `.set-doc` to survive any restructure. Guard: `test/app-settings-structure.test.ts`. → [architecture-invariants#settings-surface-app-settings-session-options-add-case](../../docs/architecture-invariants.md#settings-surface-app-settings-session-options-add-case)
|
||||
|
||||
**Story Mode** (`story-mode.js` + `story-mode.css`, `showStoryButton`, per-device, default OFF, header button hidden on phones by mobile.css): the active Claude session's turns as comic pages from `GET /api/sessions/:id/story` (server core `src/story-mode.ts`, pure; reader `src/story-reader.ts`). ⚠️ Live refresh is the transcript tool events plus the stop and prompt-submitted hooks (extra `addListener`s in `connectSSE`, so the handler map keeps one method per event) and a 4 s poll while the turn runs, one request in flight, debounced; never the watcher's `transcript:complete`, which interactive transcripts never raise. ⚠️ Every string on the page is the agent's: `textContent` only, containers carry `data-i18n-skip`; a file opens through `openFilePreview()`, never a new route. ⚠️ Canvas click regions become real buttons (`_storySyncHits`), so they are focusable and labelled; a panel keeps its clock across refreshes (only a NEW panel animates in). ⚠️ The overlay owns Escape with a WINDOW capture listener (it runs before app.js's document one): one layer per press: a file preview opened from a panel (it has no Escape of its own outside the folder grid), then the drawer, then the overlay; a folder grid on top and a preview in edit mode keep their own keys. ⚠️ Its comic palette is its own (`--story-*` tokens, `.story-paper` on a light skin), so a skin never repaints it. ⚠️ Wide panels draw their scene in a centered stage (`STORY_STAGE_MAX`); a scene with `fullWidth` (the final splash) uses the whole panel. ⚠️ A panel GROWS to its words instead of covering or clipping them: the final panel's words and chips are IN FLOW (`.story-splash`), and any other panel whose caption reaches past the band above its art gets an inline `min-height` (`_storyFitCaption`, re-run on resize and on the Director's cut toggle); rows grow with them (`grid-auto-rows: minmax(270px, auto)`). Every scene keeps the panel's usual height (`--story-panel-min`) as its stage (`rt.stageH`) and stands at the bottom (`oy`, also passed to `_storyPlaceBalloon`), background filling the rest, or a taller panel would scale Claude up with it. ⚠️ **Save image** composites the page itself (each panel canvas, then every caption, balloon and chip redrawn from its computed style and box, `_storyPaintBox`); never rasterize the DOM through SVG (self-hosted fonts do not reach it). Session Options carries a Story button too (`#sessionOptionsStoryBtn`, same setting), the way in on phones. ⚠️ Claude's speech balloon is placed by `_storyPlaceBalloon()` in the band above the head `agent()` last drew (cyan body only), clamped (`--story-lines`) to stay inside the panel and off the head; never position it per kind in CSS (that is what covered the POW burst and the agent's head on narrow panels). Drawer prose (a prompt, a helper's answer) links rooted paths only. ⚠️ Every file a panel or drawer opens goes through `_storyOpenFile()`: keyboard focus moves to the preview's close button, Tab cycles inside the preview while it is open (`_storyTrapTab`), and Escape gives focus back to what opened it. ⚠️ Panels are kept by IDENTITY (kind + first event id, `rt.identity`), never by index: a fold into a montage near the start shifts every later index and must not rebuild (and re-animate) them. ⚠️ A balloon's text is `.story-balloon-text`, after the visually hidden speaker words (`.story-vh`, "Claude says: "): the clamp and Save image must find the text span by class, never `firstElementChild` (that is how long balloons once stopped clamping). ⚠️ The drawer keeps its nodes when a refresh changes nothing (`st.drawerSig`), and a real change rebuilds and gives focus back to the control that does the same thing; helper steps are filled AFTER that decision. It is bound to its turn (`st.drawer.pageId`): when the newest turn moves on, the page holds that turn (`st.drawerHolds`) until the drawer closes. It starts under the bar (`--story-bar-h`, measured by the ResizeObserver), and where it covers the whole page `.story-main` is `inert` (`_storyDrawerCover`). ⚠️ Text colours come from the `--story-*-text` tokens (4.5:1 in both skins); the accents themselves are for fills and borders. ⚠️ Whatever a scene draws on its canvas goes into **Save image**, a file people share: a command's output reaches the canvas only as a run panel's `printed` lines, filtered server-side by `printableLine()` (story-mode.ts: secret-looking lines dropped, addresses and token-like strings masked); never draw raw output or a drawer's content on a canvas. Tests: `test/story-mode.test.ts`, `test/routes/story-routes.test.ts`, `test/story-mode-ui.test.ts` (JSDOM, every scene drawn in every shape), `test/story-corpus.test.ts` (eleven sanitized real sessions, every page pinned), `test/story-mode.browser.test.ts` (browser suite).
|
||||
**Story Mode** (`story-mode.js` + `story-mode.css`, `showStoryButton`, per-device, default OFF, header button hidden on phones by mobile.css): the active Claude session's turns as comic pages from `GET /api/sessions/:id/story` (server core `src/story-mode.ts`, pure; reader `src/story-reader.ts`). ⚠️ Live refresh is the transcript tool events plus the stop and prompt-submitted hooks (extra `addListener`s in `connectSSE`, so the handler map keeps one method per event) and a 4 s poll while the turn runs, one request in flight, debounced; never the watcher's `transcript:complete`, which interactive transcripts never raise. ⚠️ Every string on the page is the agent's: `textContent` only, containers carry `data-i18n-skip`; a file opens through `openFilePreview()`, never a new route. ⚠️ Canvas click regions become real buttons (`_storySyncHits`), so they are focusable and labelled; a panel keeps its clock across refreshes (only a NEW panel animates in). ⚠️ The overlay owns Escape with a WINDOW capture listener (it runs before app.js's document one): one layer per press: a file preview opened from a panel (it has no Escape of its own outside the folder grid), then the drawer, then the overlay; a folder grid on top and a preview in edit mode keep their own keys. ⚠️ Its comic palette is its own (`--story-*` tokens, `.story-paper` on a light skin), so a skin never repaints it. ⚠️ Wide panels draw their scene in a centered stage (`STORY_STAGE_MAX`); a scene with `fullWidth` (the final splash) uses the whole panel. ⚠️ A panel GROWS to its words instead of covering or clipping them: the final panel's words and chips are IN FLOW (`.story-splash`), and any other panel whose caption reaches past the band above its art gets an inline `min-height` (`_storyFitCaption`, re-run on resize and on the Director's cut toggle); rows grow with them (`grid-auto-rows: minmax(270px, auto)`). Every scene keeps the panel's usual height (`--story-panel-min`) as its stage (`rt.stageH`) and stands at the bottom (`oy`), background filling the rest, or a taller panel would scale Claude up with it. ⚠️ **Save image** composites the page itself (each panel canvas, then every caption, balloon and chip redrawn from its computed style and box, `_storyPaintBox`); never rasterize the DOM through SVG (self-hosted fonts do not reach it). Session Options carries a Story button too (`#sessionOptionsStoryBtn`, same setting), the way in on phones. ⚠️ Every speech balloon is placed by `_storyFitBalloon()` (called from `_storyFitCaption`) from the scene's layout at its FINISHED frame (`StoryArt.layout`, a dry run on a scratch canvas: never the frame on screen, where an entrance still has objects in flight): Claude's in the band above his head (`agent()` without a `body`, so a cyan helper never holds it), the ask panel's and yours left of his face. A balloon never covers an object a scene registers with `hit()` nor Claude's face; a click region that holds his face is Claude himself and only the face rule applies to it. When there is no room for two lines (a long caption pushes it down), the panel GROWS for caption and balloon together; the growth is computed at the panel's usual height (`storyBalloonRoom`, pure), so a refit of the grown panel asks for the same height and never oscillates; the rest clamps (`--story-lines`, `_storyClampBalloon`). Never position a balloon per kind in CSS (that is what covered the POW burst and the agent's head on narrow panels). Drawer prose (a prompt, a helper's answer) links rooted paths only. ⚠️ Every file a panel or drawer opens goes through `_storyOpenFile()`: keyboard focus moves to the preview's close button, Tab cycles inside the preview while it is open (`_storyTrapTab`), and Escape gives focus back to what opened it. ⚠️ Panels are kept by IDENTITY (kind + first event id, `rt.identity`), never by index: a fold into a montage near the start shifts every later index and must not rebuild (and re-animate) them. ⚠️ A balloon's text is `.story-balloon-text`, after the visually hidden speaker words (`.story-vh`, "Claude says: "): the clamp and Save image must find the text span by class, never `firstElementChild` (that is how long balloons once stopped clamping). ⚠️ The drawer keeps its nodes when a refresh changes nothing (`st.drawerSig`), and a real change rebuilds and gives focus back to the control that does the same thing; helper steps are filled AFTER that decision. It is bound to its turn (`st.drawer.pageId`): when the newest turn moves on, the page holds that turn (`st.drawerHolds`) until the drawer closes. It starts under the bar (`--story-bar-h`, measured by the ResizeObserver), and where it covers the whole page `.story-main` is `inert` (`_storyDrawerCover`). ⚠️ Text colours come from the `--story-*-text` tokens (4.5:1 in both skins); the accents themselves are for fills and borders. ⚠️ Whatever a scene draws on its canvas goes into **Save image**, a file peopLine truncated
|
||||
|
||||
**Header button visibility**: most header controls are opt-in and hidden by a marker class (`btn-multimonitor--hidden`, `btn-response-viewer-header--hidden`, `btn-file-viewer--hidden`, `btn-cron--hidden`, and the desktop-gated `btn-split--hidden` / `btn-tile-grid--hidden`, which also need a `@media (max-width: 1179px)` backstop) that `applyHeaderVisibilitySettings()` (settings-ui.js) toggles after settings load; the multi-monitor button is instead stripped at render by `renderIndexHtml`. ⚠️ Hiding must go through the marker class: the base rules are `display:inline-flex !important`, so an inline style cannot override them. Current desktop default is WS/CPU/MEM + File Viewer + Tiles (1180px and wider) + gear, with the token chip and lifecycle-log button OFF. ⚠️ New header controls must not leak onto phones; `test/mobile-header-buttons-policy.test.ts` is the static guard. → [architecture-invariants#header-button-visibility-multi-monitor-response-viewer-file-viewer-cron](../../docs/architecture-invariants.md#header-button-visibility-multi-monitor-response-viewer-file-viewer-cron)
|
||||
|
||||
|
||||
Reference in new issue
Block a user