mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix(story-mode): Escape closes a file preview opened from a panel first
Codeman's file preview has no Escape of its own outside the folder grid, so with Story Mode on top it stayed open and blocked the page. While Story Mode is open, Escape now closes one layer per press: the preview, then the drawer, then the overlay. A folder grid and a preview in edit mode keep their own keys. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2653db10b5
commit
d20208bda6
2 files changed
+10
-6
No files matched your search
+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): drawer first, then the overlay, and nothing while the file preview or folder grid is open on top. ⚠️ Its comic palette is its own (`--story-*` tokens, `.story-paper` on a light skin), so a skin never repaints it. Tests: `test/story-mode.test.ts`, `test/routes/story-routes.test.ts`.
|
||||
**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. Tests: `test/story-mode.test.ts`, `test/routes/story-routes.test.ts`.
|
||||
|
||||
**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)
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
* existing `openFilePreview()` (workspace confinement, attachment routes for outside paths, remote
|
||||
* hosts), never a new file route; editing goes through the file viewer's own edit mode.
|
||||
* ⚠️ The overlay owns Escape while it is open (a window capture listener runs before app.js's
|
||||
* document listener): the drawer closes first, then the overlay, and nothing closes while the file
|
||||
* preview or folder grid is open on top of it.
|
||||
* document listener), one layer per press: a file preview opened from a panel, then the drawer,
|
||||
* then the overlay. A folder grid on top and a preview in edit mode keep their own keys.
|
||||
*
|
||||
* @mixin Extends CodemanApp.prototype via Object.assign
|
||||
* @dependency app.js (this.activeSessionId, this.sessions, this.loadAppSettingsFromStorage, this.getDefaultSettings)
|
||||
@@ -1683,12 +1683,16 @@ Object.assign(CodemanApp.prototype, {
|
||||
}, STORY_POLL_MS);
|
||||
st.keyHandler = (e) => {
|
||||
if (e.key !== 'Escape' || !st.open) return;
|
||||
// The file preview or folder grid opened from a panel is on top: its own Escape closes it.
|
||||
if (document.getElementById('filePreviewOverlay')?.classList.contains('visible')) return;
|
||||
// A folder grid on top steps back by itself (preview, then grid), and a preview in edit mode
|
||||
// keeps its keys. A plain file preview has no Escape of its own outside the grid, so Story
|
||||
// closes it: one Escape per layer, the preview first.
|
||||
if (document.getElementById('folderGridOverlay')?.classList.contains('visible')) return;
|
||||
const previewOpen = document.getElementById('filePreviewOverlay')?.classList.contains('visible');
|
||||
if (previewOpen && this.filePreviewEdit) return;
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (st.drawer) this._storyCloseDrawer();
|
||||
if (previewOpen) this.closeFilePreview?.();
|
||||
else if (st.drawer) this._storyCloseDrawer();
|
||||
else this.closeStoryMode();
|
||||
};
|
||||
window.addEventListener('keydown', st.keyHandler, true);
|
||||
|
||||
Reference in new issue
Block a user