mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 16:09:43 +02:00
feat(settings): put Session Options on the same surface as App Settings
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.
This commit is contained in:
+104
-61
@@ -3170,21 +3170,21 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
stack of separate cards: at 390px the per-card borders were most of the pixels.
|
||||
============================================================================ */
|
||||
@media (max-width: 860px) {
|
||||
#appSettingsModal .modal-content.modal-lg {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .modal-content.modal-lg {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-body {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* Rail keeps only its search field, laid out as a bar */
|
||||
#appSettingsModal .set-rail {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-rail {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-right: 0;
|
||||
@@ -3195,37 +3195,37 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-rail-items,
|
||||
#appSettingsModal .set-rail-foot {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-rail-items,
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-rail-foot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-search {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-search {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-search input {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-search input {
|
||||
padding: 9px 10px 9px 30px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* Save moves into the header; the bottom action bar would cost 60px */
|
||||
#appSettingsModal .set-head-save {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-head-save {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-foot {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-foot {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-doc {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-doc {
|
||||
padding: 0 14px 34px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ── jump control ──────────────────────────────────────────────────── */
|
||||
#appSettingsModal .set-jump {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump {
|
||||
display: flex;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@@ -3246,30 +3246,30 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-ico {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-ico {
|
||||
color: var(--accent);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-label {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-label {
|
||||
font-weight: 580;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-chev {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-chev {
|
||||
margin-left: auto;
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.18s;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump[aria-expanded='true'] .set-jump-chev {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump[aria-expanded='true'] .set-jump-chev {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-veil {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-veil {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -3277,7 +3277,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
background: rgba(4, 8, 13, 0.62);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-menu {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
@@ -3299,7 +3299,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
display: block;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-row {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
@@ -3314,43 +3314,43 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-row svg {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-row svg {
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-row .set-jump-count {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-row .set-jump-count {
|
||||
margin-left: auto;
|
||||
font-size: 0.62rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-row.active {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-row.active {
|
||||
background: rgba(var(--accent-rgb), 0.14);
|
||||
color: var(--text);
|
||||
font-weight: 570;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-jump-row.active svg {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-jump-row.active svg {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* ── sections step down: the jump pill already names the current one ── */
|
||||
#appSettingsModal .set-section {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-section + .set-section {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section + .set-section {
|
||||
border-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-section-head {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section-head {
|
||||
gap: 7px;
|
||||
margin: 18px 0 2px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-section-head svg {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section-head svg {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
@@ -3359,7 +3359,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-section-head h2 {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section-head h2 {
|
||||
font-size: 0.6rem;
|
||||
font-weight: 640;
|
||||
letter-spacing: 0.1em;
|
||||
@@ -3367,45 +3367,45 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-section-head::after {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section-head::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, var(--border), transparent);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-section-blurb {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-section-blurb {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── live layout preview ───────────────────────────────────────────── */
|
||||
#appSettingsModal .set-preview {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-preview {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-preview-stage {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-preview-stage {
|
||||
min-height: 62px;
|
||||
}
|
||||
|
||||
/* ── inset grouped list ────────────────────────────────────────────── */
|
||||
#appSettingsModal .set-group {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group + .set-group {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group + .set-group {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-head {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-head {
|
||||
margin-bottom: 7px;
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-hint {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-hint {
|
||||
padding: 0 3px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-body {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body {
|
||||
gap: 0;
|
||||
background: rgba(255, 255, 255, 0.035);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
@@ -3413,7 +3413,7 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-body > .set-row {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > .set-row {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
@@ -3421,104 +3421,147 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-body > .set-row + .set-row {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > .set-row + .set-row {
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.055);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-body > .set-chips,
|
||||
#appSettingsModal .set-group-body > .set-modelgrid,
|
||||
#appSettingsModal .set-group-body > .set-minigrid,
|
||||
#appSettingsModal .set-group-body > #appSettingsShortcutsList {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > .set-chips,
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > .set-modelgrid,
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > .set-minigrid,
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > #appSettingsShortcutsList {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-group-body > .event-type-grid {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-group-body > .event-type-grid {
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-row-label {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row-label {
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-row-desc {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row-desc {
|
||||
font-size: 0.69rem;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
/* Fields go full width under their label instead of fighting for the row */
|
||||
#appSettingsModal .set-row.has-field {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row.has-field {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-row.has-field .set-select,
|
||||
#appSettingsModal .set-row.has-field .set-input {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row.has-field .set-select,
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row.has-field .set-input {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-row-actions-wide {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row-actions-wide {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-row-actions-wide .set-input {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-row-actions-wide .set-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-num {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-num {
|
||||
width: 76px;
|
||||
}
|
||||
|
||||
/* Bigger touch targets for the toggles and chips */
|
||||
#appSettingsModal .switch-sm {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .switch-sm {
|
||||
width: 40px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#appSettingsModal .switch-sm .slider:before {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .switch-sm .slider:before {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
#appSettingsModal .switch-sm input:checked + .slider:before {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .switch-sm input:checked + .slider:before {
|
||||
transform: translateX(16px);
|
||||
}
|
||||
|
||||
#appSettingsModal .set-chip {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-chip {
|
||||
font-size: 0.78rem;
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-modelgrid {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-modelgrid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-minigrid {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-minigrid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-mini .set-select {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-mini .set-select {
|
||||
width: 148px;
|
||||
}
|
||||
|
||||
/* One scrollable line beats a ragged two-row wrap for 7 effort levels */
|
||||
#appSettingsModal .set-segment {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-segment {
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-segment::-webkit-scrollbar {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-segment::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#appSettingsModal .set-segment button {
|
||||
:is(#appSettingsModal, #sessionOptionsModal) .set-segment button {
|
||||
flex: 0 0 auto;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Session Options, compact layout (<= 860px)
|
||||
|
||||
App Settings collapses its rail and hands navigation to the sticky
|
||||
#appSettingsJump pill. Session Options has no such pill (and no search), so
|
||||
its rail stays put and becomes a horizontal, scrollable strip — which is
|
||||
what its tab bar was before the two modals started sharing a surface.
|
||||
============================================================================ */
|
||||
@media (max-width: 860px) {
|
||||
#sessionOptionsModal .set-rail {
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
#sessionOptionsModal .set-rail-items {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
#sessionOptionsModal .set-rail-items::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sessionOptionsModal .set-rail-item {
|
||||
white-space: nowrap;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
/* The active marker is a left bar in the vertical rail; horizontally that
|
||||
reads as a stray tick, so the strip uses a filled pill instead. */
|
||||
#sessionOptionsModal .set-rail-item.active::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sessionOptionsModal .set-rail-item.active {
|
||||
background: rgba(var(--accent-rgb), 0.13);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user