One settings surface: App Settings, Session Options and Add Case - #278
Merged
Conversation
…yout preview The document side of the settings modal was wider than it needed to be: every row is text on the left and a switch pinned to the right, so a 960px shell plus a 62ch cap on the description left a dead gap of ~350px between the two. The shell is now 840px, the rail 196px, and descriptions run to 78ch, which closes the gap and makes the right side sit proportionally with the rail. Section order now leads with what you look at first: System (the version this install runs and whether an update is waiting, with Updates promoted above Paths/Automation/Remote access), then Terminal & Input, then Header & Panels. The modal opens scrolled to System instead of Terminal & Input. Header & Panels gains two things: - every chip carries the icon of the button it switches on, so the list reads as the header itself rather than as a column of names (File Viewer shows the folder button, Cron the clock, and so on); - a live preview above the chips: a scale model of the app with a header bar, right-docked panels, a toolbar and floating windows, rebuilt on every chip change so "what does this add" is answered in place, before saving. The preview owns no icons of its own - it CLONES `.set-chip-ico` out of the chip - so each icon has exactly one copy in index.html and a chip can never drift from the button it previews. A chip joins the preview by carrying `data-preview` (which slot) and `data-preview-order` (where in it); readouts that are not buttons (plan usage, CPU, font size) use `data-preview-text` instead. The frame is painted from skin tokens only, since hardcoded black alphas turned it into a grey slab on the four light skins, and it is marked `data-i18n-skip`: the mock tab names are decoration, and the labels inside are copies of chip text i18n has already translated. Cron moved into its own Scheduling group (it is a toolbar button, not a header one, and the preview places it accordingly). test/app-settings-structure.test.ts pins the new contract: the rail and the document agree on order, System leads with the version above the paths, and every previewed chip has both an icon to clone and a slot that exists.
Session Options was the last modal still wearing the old chrome: a strip of top tabs over `.form-row` stacks, sitting next to a settings modal that had just been rebuilt around a rail and grouped row cards. It now uses the same surface. The `set-*` rules move from `#appSettingsModal` to `:is(#appSettingsModal, #sessionOptionsModal)`. An `:is()` list takes the specificity of its most specific argument, and both arguments are ids, so every rule keeps exactly the weight it had - nothing downstream shifts in the cascade. What the two modals do NOT share is what the rail means: - App Settings stays a table of contents over one scrolling document. - Session Options switches: one `.set-section` visible, `.hidden` on the rest. Summary owns its own scroller and Respawn is long, so stacking them into a single document would bury both. `switchOptionsTab` now queries `.set-rail-item` (it read `.modal-tab-btn` before) and resets the document scroll, so a switched-to section starts at its own top. Phones get a horizontal, scrollable rail strip rather than App Settings' sticky jump pill, which Session Options has no equivalent of. That is close to the tab bar it replaces, so the phone gesture is unchanged. Content is regrouped into the row language - label, description, control pinned right - across all four sections: usage limits / respawn loop / cycle steps / loop control, identity / token management / this session, tracker / limits, and the summary timeline. The three cycle-step checkboxes became chips, which is why `_syncSettingsChips` now covers both modals and Session Options registers one delegated change listener per page for them. Every id and handler the JS reads is preserved, and the component classes it queries (`.duration-preset-btn`, `.duration-custom-input`, `.color-swatch`, `.respawn-status-text`, `.run-summary-filters .filter-btn`) are untouched. `data-claude-only` moved onto the rail entries, so external-CLI sessions still lose Respawn and Ralph and land on Context. `.modal-tabs`/`.modal-tab-btn`/`.modal-tab-content` now belong to #createCaseModal alone. test/session-options-structure.test.ts pins the rail to section pairing, the ids openSessionOptions reads, the one-visible-section invariant and the Claude-only entries.
The welcome column was 880px tall inside a 752px overlay on a 1470x842 window, so it ran off both ends (title above the top edge, "Or click Run to start" below the bottom one) with no way to scroll to either. .welcome-content is now a flex column bounded at the overlay height with every child fixed except the Resume list, which shrinks and scrolls internally. Short windows (<=900px tall) get a tighter rhythm as well, so the list keeps usable height instead of collapsing to two rows. The open-tabs rail drops its border-right (the gradient already reads as docked) and widens 19vw -> 25vw, which stays inside the gutter at the 1180px gate (295px of 310px). The status pill moves from beside the name down to the created/active stamps line, handing the full row width to the session name: names render whole instead of ellipsizing "w34-claudeman: mindreading" into "w34-claudeman: ...", and wrap to a second line only when they still do not fit. Verified against the live server with the edited files served into the page: content fits the overlay at 1180x800 through 2560x1440 and on phone widths, no clipped names or stamps, no page errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ab chrome Three things, all on the same surface. **Tighter.** The shell drops to 760x620 (was 840x700) and the density comes down with it: rail 176px, doc padding 15px, row padding 5px 10px, group gaps 3px, section head 0.88rem, row label 0.76rem, description 0.645rem. The model cards were the biggest block in the document and shrink the most (6px 8px padding, 0.72rem name). The toggle switches keep their size on purpose - only the space around them was the problem. **Checkboxes stay checkboxes.** The respawn cycle steps go back to real checkboxes in a row card (`.set-checks` / `.set-check`) rather than the chips they briefly became: they are numbered steps of one sequence, not a set of independent tags, and chips read as the latter. **Add Case joins the surface.** Same shell, rail and sections; its rail switches panels like Session Options'. The six panels keep their legacy `.form-row` markup - every id in them is read back by session-ui.js, so restructuring the forms would be a lot of risk for no visual gain. Instead an adapter block scoped to `#createCaseModal .set-doc` maps the old primitives onto the look: a form row paints as a row card, its label as a row label, its `.form-hint` as a row description, `<details class="advanced-options">` as a collapsed group head. `.form-row` everywhere else is untouched. With that, `.modal-tabs` / `.modal-tab-btn` / `.modal-tab-content` have no users left, so their CSS is deleted from both stylesheets and the guard in test/app-settings-structure.test.ts flips from "the settings modal must not steal these shared classes" to "nothing uses them any more" - a reappearance now means a modal drifted back off the shared surface.
…esume The shared surface is tuned for App Settings: a long, dense document you scan. Add Case and Session Options are the opposite - a handful of short panels you act on once - and at that density they read as a few small fields marooned in a large empty frame, with rail entries too small to aim at. Both now take the same size-up while App Settings stays tight: 900px wide, a 236px rail with 0.9rem entries and 19px icons, 0.88rem row labels, 0.82rem fields, and `height: auto` between a 560px floor and 88vh - so the shell is as tall as the panel showing instead of a fixed box the content rattles in (Summary opened two thirds empty before). Respawn is reordered around what people come to it for: - Auto-resume is a CALLOUT again, not the first row of a list. It is what turns a limit-halted overnight run back on, so it gets an accent card, an icon, and a hit target covering the whole card (the label wraps its own switch - no `for`, since nesting already associates them and the pair has historically double-fired). The armed "resumes at HH:MM" note renders inside it. - Loop control (status + Enable/Stop) moves ABOVE the loop configuration. A running loop is the thing you open this tab to see or stop, and Enable is the point of the tab either way; it was previously below three groups of config. - Enable/Stop and the status pill scale with the rows around them. The Context tab is renamed Session, since "context" only described one of its three groups, and those groups become Identity / Context window / Behavior.
`summary { display: flex }` in the Add Case adapter drops the browser's own
disclosure triangle, so Clone options, Container settings, Advanced SSH,
Discover existing sessions and Advanced container settings rendered as plain
uppercase headings with nothing to say they open. Reported as exactly that.
Each summary now carries an explicit chevron that rotates 180 degrees on
`[open]`, matching the Advanced group in App Settings, plus a hover state on
the row. The default marker is suppressed in both spellings (`list-style` and
`::-webkit-details-marker`) so a browser that would still paint one does not
end up with two.A docs pass landed in this worktree while the preview was up (a respawn loop on
the throwaway session it was serving), and it is the documentation this work
needed, so it is reviewed and kept rather than thrown away.
- docs/architecture-invariants.md gains a "Settings surface" section: the one
`:is()` scope and why the id-only list preserves specificity, the anatomy,
the two meanings of the rail, the deliberate two sizes, the phone strip, the
Add Case adapter, the flex-summary chevron trap, the Respawn ordering, the
retired tab chrome, and the live preview's clone-the-chip-icon rule.
- Settings paths are repointed everywhere they moved: Display -> Header &
Panels (header buttons, cron, multi-monitor, response viewer, file viewer),
Settings -> App Settings -> System -> Updates, Panels -> Header & Panels ->
Cross-session features (Read My Mind), Display -> Terminal & Input (gesture
control), Claude Model -> Models -> New Claude sessions.
- Stale counts refreshed (route modules, frontend modules, type files, config
files) and the typecheck script named.
- browser-testing-guide gains the three modal ids and the `set-*` selectors.
- The styles.css block comment covers all three modals.
Two claims it got wrong are corrected here: an external-CLI session opens
Session Options on the Session tab (`switchOptionsTab('context')`), not
Summary - measured in the browser - and the Cron toggle lives under Header &
Panels -> Scheduling, with no "Header Displays" step under it any more.App Settings opened on a System section that mixed the two things worth seeing
immediately (what this install runs, whether a newer release is waiting) with
three groups nobody sets twice (CLAUDE.md template path, default working
directory, image watcher, Cloudflare tunnel).
Split in two. **Updates** is now the first section and carries only the current
version and the update action, so the modal opens on it and the second thing in
reach is Terminal & Input, where Local Echo lives. **System** keeps Paths,
Automation and Remote access and tails the document, last in the rail.
Also fixes the admin-ui load-order test, which broke on this branch: it located
the modules with a bare `indexOf('session-ui.js')`, and the modal markup now
cites those modules in comments well above the script tags, so it was comparing
a comment against a `<script src>`. It matches the script tag itself now.Uh oh!
There was an error while loading. Please reload this page.
CreatureSurvive pushed a commit
to CreatureSurvive/Codeman
that referenced
this pull request
Aug 17, 2026
Brings in christianhaberl#4 (three commits, authorship preserved) and adapts it across the 211 commits master gained since the branch was cut: - App Settings control re-authored for the set-* surface (PR Ark0N#278): a set-row in Layout -> Tabs, replacing the old settings-item markup the branch targeted. i18n description synced. - Lineage arcs (PR Ark0N#291, post-branch) are SKIPPED in sidebar layout: computeLineagePath()'s U-bridge geometry hangs from the horizontal strip's bottom edge and has no meaning against a vertical list. The lineage strip-scroll listener now also redraws subagent/ultracode connectors while the sidebar scrolls vertically. - The desktop home tab rail (post-branch) defers to the sidebar: both dock the session list flush left, and the rail would render z-ordered under it. - Active-row reveal unified into _scrollActiveTabIntoView() (Ark0N#257 landed on master after the branch): sidebar mode branches to scrollIntoView block:'nearest', and _fullRenderSessionTabs() restores scrollTop alongside the Ark0N#257 scrollLeft restore so ambient rebuilds cannot yank a mid-scroll sidebar back to the top. - Mobile active-tab hoisting the branch guarded against no longer exists on master (removed by Ark0N#257); kept master's order-stable render. Verified: typecheck, lint, format:check, check:frontend-syntax, check:public-assets, PostCSS parse of both merged stylesheets, the 26 new jsdom tests, the structural guard suites, and the headless-Chromium harness (scripts/verify-session-sidebar.mts) green across all seven layout states at 1600/1000/393px against current master. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One visual language for the three modals people actually live in, and a
reordering of each around what you open it for.
App Settings
The document side was wider than it needed to be: every row is text on the left
with its control pinned right, so a 960px shell plus a 62ch cap on descriptions
left roughly 350px of nothing between the two. It is now 760x620 with a 176px
rail and descriptions that run most of the way to their switch. Toggle switches
kept their size; only the space around them changed.
Section order follows what you came for:
is what the modal opens on.
rarely touched.
Header & Panels gained two things. Every chip now carries the icon of the
button it switches on, so the list reads as the header itself rather than a
column of names, and a live preview sits above them: a scale model of the
app with a header bar, right-docked panels, a toolbar and floating windows,
rebuilt on every chip change so "what does this add" is answered in place,
before saving. The preview owns no icons of its own - it clones
.set-chip-icoout of the chip - so each icon has exactly one copy in index.html and a chip can
never drift from the button it previews.
Session Options and Add Case
Both moved onto the same surface. The
set-*rules went from#appSettingsModalto
:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal); an:is()list takes its most specific argument's specificity and all three are ids, so
every rule kept the weight it had and nothing downstream shifted in the cascade.
What they do NOT share is what the rail means. App Settings stays a table of
contents over one scrolling document; these two really switch, one section
visible and
.hiddenon the rest, because Summary owns its own scroller,Respawn is long, and Add Case is six independent forms. Phones get a horizontal
rail strip instead of App Settings' jump pill, which neither of them has.
They also take a size-up App Settings does not: 900px wide, 236px rail, and
height: autobetween a 560px floor and 88vh, so the shell is as tall as thepanel showing instead of a fixed box the content rattles in. They are short
panels you act on once, not a document you scan.
Respawn is ordered around what people open it for. Auto-resume on usage
limit is a callout again (accent card, icon, whole card as the hit target): it
is what turns a limit-halted overnight run back on, and as the first row of a
flat list it no longer looked like something you were meant to switch on. Loop
control (status + Enable/Stop) moved above the loop configuration. The three
cycle steps are real checkboxes, not chips - they are numbered steps of one
sequence. The Context tab is renamed Session (label only;
data-tab, thesection id and
switchOptionsTab('context')are unchanged) with groupsIdentity / Context window / Behavior.
Add Case keeps its legacy
.form-rowmarkup in all six panels, becauseevery id in them is read back by session-ui.js and restructuring the forms to
reach the row classes would be a lot of risk for no visual gain. An adapter
block scoped to
#createCaseModal .set-docpaints a form row as a row card, itslabel as a row label, its
.form-hintas a description, and<details class="advanced-options">as a collapsed group head.One trap worth naming:
summary { display: flex }drops the browser's owndisclosure triangle, so Clone options, Container settings, Advanced SSH,
Discover existing sessions and Advanced container settings rendered as plain
headings with nothing to say they open. They carry an explicit chevron now,
rotated on
[open], with the native marker suppressed in both spellings.With Add Case moved over,
.modal-tabs/.modal-tab-btn/.modal-tab-contenthave no users left, so their CSS is deleted from both stylesheets.
Tests and docs
test/session-options-structure.test.ts(new): rail-to-section pairing, theids
openSessionOptionsreads, the one-visible-section invariant, and thedata-claude-onlyentries external CLIs drop.test/app-settings-structure.test.ts: rail and document agree on order,Updates leads and carries only the version and the updater, every previewed
chip has both an icon to clone and a slot that exists, and the retired tab
classes stay gone.
test/admin-ui.test.ts: the load-order check matched a bareindexOf('session-ui.js'), which the new modal comments shadowed; it matchesthe script tag now.
docs/architecture-invariants.mdgains a "Settings surface" section, and thesettings paths that moved are repointed across README and docs.
Verification
Driven in a real browser against a live server on an isolated instance, not just
asserted in jsdom: all sections of all three modals render, rail clicks switch
and reset scroll, chips and the preview react live, Save applies to the real
header and survives a reload, an external-CLI session still loses Respawn and
Ralph and lands on Session, the callout toggles exactly once from both the text
and the switch, the collapsed blocks open from a click on the row, and phone
widths keep the horizontal rail strip. Checked on a light skin too, which is
what caught the preview frame painting itself a grey slab from hardcoded black
alphas (it uses skin tokens now).
npm run test:cigreen (4870 passed), plus typecheck, lint, prettier,check:lockfile, check:frontend-syntax and check:public-assets.