## Summary Restores green **Full Suite (non-blocking)** runs on `main`. Recent main merges left i18n key parity, schema baseline bookkeeping (0011→0012), heartbeat tool inventory (FN-8058 `fn_task_logs_read`), and merger whitespace-classification mocks (execFile `git diff -p -w :2: :3:`) out of date, so all four test shards failed. ## Root causes observed on main - **Shard 4 / `@fusion/i18n`**: missing `skipConfirmationDialogs*` + `reviewBudgetExhausted` in non-en locales; orphan `awaitingApprovalPlanReviewReplanCap` - **Shard 3 / `@fusion/core`**: `SCHEMA_BASELINE_VERSION` advanced to `0012` while tests still equated it with `OWNER_PROJECT_ID_SPLIT_VERSION` (`0011`) and omitted `0012` from applied-migration lists - **Shards 1–2 / `@fusion/engine`**: tool count/snapshot drift for `fn_task_logs_read`; merger tests still mocked `git diff-tree` for trivial classification after the execFile `:2:`/`:3:` cutover; mock provider `updateTask` arity drift ## Changes - Locale catalogs: add missing keys, drop orphan key - Schema applier tests: immutable 0011 identity + baseline 0012 lists - Heartbeat + gating snapshots: include `fn_task_logs_read` - Merger unit mocks: recognize `git diff -p -w :2:path :3:path` - Mock provider: accept optional third `updateTask` arg ## Test plan - [x] `pnpm --filter @fusion/i18n exec vitest run` — 23/23 - [x] `pnpm --filter @fusion/core exec vitest run src/__tests__/postgres/schema-applier.test.ts` (immutable + automation upgrade) — pass - [x] `pnpm --filter @fusion/core exec vitest run` project-identity + satellite-fusiondir — pass - [x] Engine suites from failed CI shards (file-scoped, hermes/openclaw/paperclip/grok, reliability post-finalize/mission, heartbeat, gating, merger recovery/prompt, mock-provider, etc.) — pass - [ ] Full Suite workflow green on merge to main <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved project data isolation across backend operations. - Added safer optional toast handling when UI components render outside the full application shell. - Added support for reading task logs during agent heartbeat sessions. - **Bug Fixes** - Prevented runtime probes from hanging and avoided scanning large binary files. - Improved path handling for workspaces with missing descendants. - Corrected task retry state resets and GitHub import/issue-close behavior. - **Style** - Improved chat, terminal, and settings spacing. - Added clearer accessibility labeling for the auto-merge control. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
3184 lines
83 KiB
CSS
3184 lines
83 KiB
CSS
/* FNXC:SettingsStyling 2026-07-15-17:35: Settings mixed px, rem, em, and calc()-over-spacing font sizes across ~10 distinct values with no scale. Declarations now name scale rungs by role — 2xs for badges/pills, xs for help and caption copy, sm for control labels and body, base/md for headings — so type is retuned from the token definitions rather than by sweeping this file. The mobile `font-size: 16px` overrides stay raw on purpose: they are iOS's focus-zoom threshold, not a type rung, and must hold at 16px even if the root font size shrinks. */
|
|
/* === SettingsModal: all settings-related layout, panel, and component styles ===
|
|
Extracted from styles.css as part of the Sweep-3 CSS extraction effort.
|
|
Imported by SettingsModal.tsx (and MemoryView.tsx for shared classes). */
|
|
|
|
/* === Settings Modal header action buttons (Star on GitHub, Discord) === */
|
|
.settings-header-actions {
|
|
--settings-header-action-height: calc(var(--space-md) * 2 + var(--space-xs) / 2);
|
|
|
|
display: flex;
|
|
align-items: stretch;
|
|
gap: var(--space-xs);
|
|
margin-left: auto;
|
|
margin-right: var(--space-sm);
|
|
}
|
|
|
|
/* Keep the GitHub Star and Help buttons at matching heights regardless of
|
|
their differing icon sizes (provider icon 16px vs. HelpCircle 13px). */
|
|
.settings-header-actions > .settings-github-star-btn,
|
|
.settings-header-actions > .settings-header-discord-btn {
|
|
height: var(--settings-header-action-height);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.settings-header-discord-btn,
|
|
.settings-footer-help-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
/* These are <a> tags styled as .btn — .btn never resets link underline, so without this the browser's default underline paints under the icon+label box. */
|
|
text-decoration: none;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
/* FN-4375: keep heading, actions, and close in one row on mobile; supersedes FN-4281/FN-4331/FN-4363 wrap/order behavior. */
|
|
.settings-modal .modal-header {
|
|
flex-wrap: nowrap;
|
|
align-items: center;
|
|
column-gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-modal-heading {
|
|
flex: 1 1 0;
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-header-actions {
|
|
margin-left: auto;
|
|
margin-right: var(--space-sm);
|
|
}
|
|
|
|
/*
|
|
FNXC:Settings 2026-07-11-00:00:
|
|
FN-7848: Mobile Settings must still show the GitHub star count. FN-4375 kept the header in one row by collapsing promotional action labels; keep the count visible and rely on .settings-modal-heading min-width: 0 to truncate the title before the pill wraps. Only the redundant "Star" word stays collapsed to save horizontal room.
|
|
*/
|
|
.settings-header-actions .settings-github-star-btn__action,
|
|
.settings-header-actions .settings-header-discord-btn {
|
|
width: 40px;
|
|
min-width: 40px;
|
|
padding: 0;
|
|
justify-content: center;
|
|
font-size: 0;
|
|
}
|
|
|
|
.settings-header-actions .settings-github-star-btn__action {
|
|
gap: 0;
|
|
}
|
|
}
|
|
|
|
/* GitHub star button — split-pill layout: [Star half | Count half] */
|
|
.settings-github-star-btn {
|
|
display: inline-flex;
|
|
align-items: stretch;
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-pill);
|
|
background: var(--card);
|
|
color: var(--text);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 500;
|
|
text-decoration: none;
|
|
overflow: hidden;
|
|
transition:
|
|
border-color var(--transition-fast),
|
|
background var(--transition-fast);
|
|
}
|
|
|
|
.settings-github-star-btn:hover {
|
|
border-color: var(--text-muted);
|
|
background: var(--card-hover);
|
|
}
|
|
|
|
.settings-github-star-btn:focus-visible {
|
|
outline: none;
|
|
box-shadow: var(--focus-ring-strong);
|
|
border-color: var(--todo);
|
|
}
|
|
|
|
.settings-github-star-btn__action {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
padding: var(--space-xs) calc(var(--space-sm) + var(--space-xs) / 2);
|
|
}
|
|
|
|
.settings-github-star-btn__count {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: var(--space-xs) calc(var(--space-sm) + var(--space-xs) / 4);
|
|
border-left: var(--btn-border-width) solid var(--border);
|
|
background: color-mix(in srgb, var(--surface) 60%, var(--card));
|
|
color: var(--text-muted);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* === Settings Modal: sizing + resizability === */
|
|
.settings-modal {
|
|
width: min(95vw, 1100px);
|
|
max-width: 95vw;
|
|
min-width: 520px;
|
|
height: 80vh;
|
|
min-height: 480px;
|
|
max-height: calc(100dvh - var(--overlay-padding-top, 10vh) - 16px);
|
|
overflow: hidden;
|
|
resize: both;
|
|
}
|
|
|
|
/*
|
|
FNXC:Settings 2026-06-22-00:00:
|
|
Embedded settings is a main-content destination (taskView === "settings"), mirroring Planning/Command Center. The host (.settings-embedded.right-dock-embedded-view) fills the content pane and the inner panel (.settings-modal--embedded) sheds all dialog chrome — fixed dialog sizing, shadow, border, radius, resize grip — so the view sits flush on the project-content surface. Theme tokens only; no hardcoded colors.
|
|
|
|
FNXC:Settings 2026-06-22-14:36:
|
|
The Settings page should match other main-content views with no extra outer inset around the whole box. Keep breathing room inside each settings screen instead so controls never sit directly against the content edge.
|
|
*/
|
|
.settings-embedded.right-dock-embedded-view {
|
|
display: flex;
|
|
flex: 1;
|
|
width: 100%;
|
|
height: 100%;
|
|
min-height: 0;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.settings-embedded .settings-modal--embedded {
|
|
flex: 1;
|
|
width: 100%;
|
|
max-width: none;
|
|
min-width: 0;
|
|
height: 100%;
|
|
min-height: 0;
|
|
max-height: none;
|
|
resize: none;
|
|
box-shadow: none;
|
|
border: none;
|
|
border-radius: 0;
|
|
background: transparent;
|
|
position: static;
|
|
}
|
|
|
|
/*
|
|
FNXC:Settings 2026-06-22-00:00:
|
|
The embedded title reads like other embedded-view titles (Planning modal-header--embedded, Command Center cc-title): a plain heading with no tinted modal-header bar, no bottom divider, no close button, aligned to the content edge, at the shared 1.125rem embedded-title size. Header actions (Star/Discord) and footer remain so the full-width panel keeps its controls.
|
|
*/
|
|
/* FNXC:Settings 2026-06-23-04:45: The embedded Settings header now matches the canonical ViewHeader/other sidebar views exactly — same min-height, space-lg/space-xl padding, surface bg, no bottom divider, var(--todo) icon, 1.125rem/600 title, actions pinned right. */
|
|
.settings-modal--embedded .modal-header--embedded {
|
|
box-sizing: border-box;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
min-height: var(--view-header-min-height);
|
|
padding: var(--space-lg) var(--space-xl);
|
|
background: var(--surface);
|
|
border-bottom: none;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.settings-modal--embedded .modal-header--embedded .settings-header-actions {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.settings-modal--embedded .modal-header--embedded .settings-modal-heading h3 {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
font-size: var(--font-size-md);
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
letter-spacing: normal;
|
|
}
|
|
|
|
.settings-modal--embedded .modal-header--embedded .settings-modal-heading svg {
|
|
color: var(--todo);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
/* Embedded footer actions sit flush on the content surface (no tinted modal bar).
|
|
FNXC:Settings 2026-06-23-04:45: Trim the footer's bottom padding so the space BELOW the Save/Help/Version/Import-Export row matches the space above it (the base .modal-actions var(--modal-padding) left too large a gap under the row in the embedded full-height view). */
|
|
.settings-modal--embedded .modal-actions {
|
|
background: transparent;
|
|
flex-shrink: 0;
|
|
padding-bottom: var(--space-sm);
|
|
}
|
|
|
|
/* Mobile: full-screen sheet. The desktop `min-width: 520px` was forcing the
|
|
modal wider than narrow viewports, pushing it off-screen; the desktop
|
|
`height: 80vh` left awkward strips of overlay above and below. Drop the
|
|
overlay's default top padding so the modal actually fills the viewport,
|
|
and disable resize (touchscreen users can't drag the grip anyway).
|
|
FNXC:Settings 2026-06-22-16:00: scope the viewport-takeover rules to the
|
|
NON-embedded (dialog) presentation only via :not(.settings-modal--embedded).
|
|
The embedded panel lives inside the main-content pane (between the mobile
|
|
Header and MobileNavBar); without the guard its base .settings-modal class
|
|
matched these 100vw/100dvh rules and covered the whole screen, hiding the
|
|
app header/footer. The embedded view's own fill-the-pane sizing is handled
|
|
below. */
|
|
@media (max-width: 768px) {
|
|
.modal-overlay.settings-modal-overlay,
|
|
.modal-overlay:has(.settings-modal:not(.settings-modal--embedded)) {
|
|
padding: 0;
|
|
inset: 0;
|
|
align-items: stretch;
|
|
justify-content: stretch;
|
|
}
|
|
|
|
.modal.settings-modal:not(.settings-modal--embedded) {
|
|
width: 100vw;
|
|
min-width: 0;
|
|
max-width: 100vw;
|
|
height: 100dvh;
|
|
min-height: 100dvh;
|
|
max-height: 100dvh;
|
|
margin: 0;
|
|
border: none;
|
|
border-radius: 0;
|
|
resize: none;
|
|
flex: 1 1 auto;
|
|
}
|
|
|
|
.modal.settings-modal:not(.settings-modal--embedded)[style*="--keyboard-overlap"] {
|
|
height: var(--vv-height, 100dvh);
|
|
min-height: var(--vv-height, 100dvh);
|
|
max-height: var(--vv-height, 100dvh);
|
|
transform: translateY(var(--vv-offset-top, 0px));
|
|
will-change: transform;
|
|
}
|
|
|
|
/* FNXC:Settings 2026-06-22-16:00: on mobile the embedded settings view fills
|
|
only its own content pane (not the viewport) and scrolls internally so the
|
|
app Header + MobileNavBar stay visible. Trim the outer host padding so the
|
|
panel and its section navigation sit edge-to-edge in the narrow pane. */
|
|
.settings-embedded.right-dock-embedded-view {
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.settings-embedded .settings-modal--embedded {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/*
|
|
FNXC:Settings 2026-07-07-00:00:
|
|
Mobile embedded Settings has no left sidebar to exit through (only the bottom MobileNavBar), so the header needs
|
|
an explicit close affordance. Reuse the shared .modal-close sizing/hover and pin it to the header's trailing
|
|
edge, after the Star/Discord actions, without displacing them. Desktop/tablet embedded and the standalone modal
|
|
presentation are untouched by this rule (scoped to .settings-modal--embedded .modal-header--embedded).
|
|
*/
|
|
/*
|
|
FNXC:Settings 2026-07-09-00:00:
|
|
FN-7752 makes the mobile Settings header and footer shorter at ≤768px only. Keep the FN-4375 one-line header invariant while trimming vertical padding in both standalone modal and embedded SettingsView presentations.
|
|
|
|
FNXC:Settings 2026-07-10-00:00:
|
|
FN-7773 further tightens only the mobile footer rail so Version, Help, Import/Export, Reset, and Save read as one contained row without affecting desktop/tablet sizing.
|
|
*/
|
|
.settings-modal .modal-header {
|
|
padding-block: var(--space-sm);
|
|
padding-inline: var(--space-md);
|
|
}
|
|
|
|
.settings-modal .modal-actions {
|
|
padding-block: var(--space-xs);
|
|
padding-inline: var(--space-md);
|
|
}
|
|
|
|
.settings-modal--embedded .modal-header--embedded {
|
|
min-height: 0;
|
|
}
|
|
|
|
.settings-modal--embedded .modal-header--embedded .settings-embedded-mobile-close {
|
|
--settings-embedded-mobile-close-touch-target: calc(var(--space-lg) + var(--space-lg) + var(--space-xs));
|
|
|
|
flex-shrink: 0;
|
|
margin-left: var(--space-xs);
|
|
min-height: var(--settings-embedded-mobile-close-touch-target);
|
|
min-width: var(--settings-embedded-mobile-close-touch-target);
|
|
}
|
|
}
|
|
|
|
/* === Settings Layout === */
|
|
.settings-modal-heading {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.settings-modal-heading h3 {
|
|
margin: 0;
|
|
}
|
|
|
|
.settings-modal-version {
|
|
margin: 0;
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-muted);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.settings-modal-footer-version {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
margin-right: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-update-check {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsLayout 2026-07-10-12:25:
|
|
First-run review video showed the footer Save button clipped at the panel edge. The base `.modal-actions` row is a no-wrap flex rail of non-shrinking buttons (Help + Version + Export/Import/Reset + Save, plus the optional update-check result text); on narrow desktop panes its intrinsic width exceeds the panel and, because `.settings-modal`/`.modal` clip overflow, the rightmost Save button gets cut off (at ~770px pane width Save already sat exactly at the padding edge — any extra footer text or font scaling clipped it).
|
|
Fix the invariant for BOTH presentations (standalone modal + embedded SettingsView share `.settings-modal .modal-actions`): let the desktop footer wrap onto additional rows instead of clipping. The ≤768px mobile block below (FN-7752/FN-7773) intentionally re-asserts a single nowrap rail with horizontal scroll and overrides this inside its media query.
|
|
*/
|
|
.settings-modal .modal-actions {
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
row-gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
/*
|
|
FNXC:Settings 2026-07-09-00:00:
|
|
FN-7752 requires the mobile Settings footer to remain a single horizontal row in both standalone and embedded presentations. Keep Version/Help, Export/Import/Reset, and Cancel/Save on one nowrap rail that can scroll horizontally instead of stacking into multiple rows.
|
|
|
|
FNXC:Settings 2026-07-10-00:00:
|
|
FN-7773 fixes the observed mobile overlap by centering every footer sub-region on the rail and making the Version text a non-wrapping inline box. Keep these selectors under the mobile media query so desktop/tablet footer flow remains unchanged.
|
|
*/
|
|
.settings-modal .modal-actions {
|
|
flex-wrap: nowrap;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
}
|
|
|
|
.settings-modal .modal-actions-left {
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.settings-modal .modal-actions-right {
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.settings-modal .settings-modal-footer-version {
|
|
align-items: center;
|
|
align-self: center;
|
|
flex: 0 0 auto;
|
|
margin-right: 0;
|
|
min-width: max-content;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsUpdate 2026-07-16-00:00:
|
|
FN-8138 adds a restart control and manual-restart explanation after an update.
|
|
Let this inner result flow wrap on narrow screens while the existing footer rail
|
|
remains horizontally scrollable, so the new controls never create a clipped shell.
|
|
*/
|
|
.settings-modal .settings-update-check {
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
row-gap: var(--space-xs);
|
|
}
|
|
|
|
.settings-modal .settings-update-result {
|
|
align-self: center;
|
|
flex: 0 1 auto;
|
|
line-height: normal;
|
|
white-space: normal;
|
|
}
|
|
|
|
.settings-modal .settings-footer-help-btn {
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.settings-modal .settings-version-check-btn {
|
|
align-items: center;
|
|
flex-shrink: 0;
|
|
line-height: 1;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.settings-modal .settings-modal-version {
|
|
align-items: center;
|
|
display: inline-flex;
|
|
flex-shrink: 0;
|
|
line-height: 1;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/*
|
|
FNXC:Settings 2026-07-16-00:20:
|
|
Help/Export/Import/Reset/Cancel/Save all share `.btn-sm`, which the global mobile
|
|
touch-target rule (`.btn:not(...):not(.btn-sm)...{min-height:36px}` in styles.css)
|
|
deliberately skips so dense table/card rows of small buttons stay compact. That
|
|
exclusion also caught this footer, so these rendered at their ~24px unpadded height
|
|
on mobile — visibly smaller and misaligned against the 36px auth Test/Disable buttons
|
|
sitting on the same screen. Re-apply the 36px touch target scoped to this footer only,
|
|
so the exclusion keeps holding everywhere else that intentionally wants compact `.btn-sm`.
|
|
*/
|
|
.settings-modal .modal-actions-left .btn-sm,
|
|
.settings-modal .modal-actions-right .btn-sm,
|
|
.settings-modal .settings-footer-help-btn {
|
|
min-height: 36px;
|
|
}
|
|
}
|
|
|
|
.settings-version-check-btn {
|
|
--settings-inline-touch-target: calc(var(--space-lg) + var(--space-lg) + var(--space-xs));
|
|
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
margin: 0;
|
|
min-height: var(--settings-inline-touch-target);
|
|
padding: var(--space-xs) var(--space-sm);
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
transition: opacity var(--transition-fast), box-shadow var(--transition-fast);
|
|
}
|
|
|
|
.settings-version-check-btn:hover:not(:disabled) {
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.settings-version-check-btn:focus-visible {
|
|
outline: none;
|
|
box-shadow: var(--focus-ring-strong);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.settings-version-check-btn:disabled {
|
|
cursor: default;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.settings-version-check-btn svg {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-version-check-btn svg.spinning {
|
|
animation: settings-update-spin 1s linear infinite;
|
|
}
|
|
|
|
.settings-update-result {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-xs);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 500;
|
|
}
|
|
|
|
.settings-update-result--up-to-date {
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.settings-update-result--available {
|
|
color: var(--color-info);
|
|
}
|
|
|
|
.settings-update-result--error {
|
|
color: var(--color-error);
|
|
}
|
|
|
|
.settings-update-result-link {
|
|
color: var(--color-info);
|
|
text-decoration: underline;
|
|
transition: color var(--transition-fast), box-shadow var(--transition-fast);
|
|
}
|
|
|
|
.settings-update-result-link:hover {
|
|
color: color-mix(in srgb, var(--color-info) 80%, var(--text));
|
|
}
|
|
|
|
.settings-update-result-link:focus-visible {
|
|
outline: none;
|
|
box-shadow: var(--focus-ring-strong);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.settings-update-now-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
min-height: calc(var(--space-lg) + var(--space-sm));
|
|
color: var(--text);
|
|
}
|
|
|
|
.settings-update-now-btn:disabled {
|
|
color: var(--color-warning);
|
|
}
|
|
|
|
.settings-update-now-btn svg.spinning {
|
|
animation: settings-update-spin 1s linear infinite;
|
|
}
|
|
|
|
.settings-update-install-succeeded {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.settings-update-install-status {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-update-install-status--success {
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.settings-update-install-status--error {
|
|
color: var(--color-error);
|
|
}
|
|
|
|
@keyframes settings-update-spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* === Settings Backups: summary stats + backup list === */
|
|
.backup-stats {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-lg);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--surface) 85%, transparent);
|
|
}
|
|
|
|
.backup-stat {
|
|
display: flex;
|
|
flex: 1 1 0;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
.backup-stat-value {
|
|
color: var(--text);
|
|
font-size: var(--font-size-base);
|
|
font-weight: 600;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.backup-stat-label {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
letter-spacing: 0.04em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.backup-list {
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
.backup-list summary {
|
|
padding: var(--space-sm) 0;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
transition: color var(--transition-fast);
|
|
}
|
|
|
|
.backup-list summary:hover {
|
|
color: var(--text);
|
|
}
|
|
|
|
.backup-list summary:focus-visible {
|
|
outline: none;
|
|
border-radius: var(--radius-sm);
|
|
box-shadow: var(--focus-ring-strong);
|
|
}
|
|
|
|
.backup-list ul {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
margin: 0;
|
|
margin-top: var(--space-sm);
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.backup-list li {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm) 0;
|
|
border-top: var(--btn-border-width) solid color-mix(in srgb, var(--border) 80%, transparent);
|
|
}
|
|
|
|
.backup-list li code {
|
|
min-width: 0;
|
|
color: var(--text);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-xs);
|
|
white-space: nowrap;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.backup-size {
|
|
color: var(--text-muted);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-xs);
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.backup-stats {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.backup-stat {
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.backup-list ul {
|
|
max-height: calc(var(--space-xl) * 9 + var(--space-md));
|
|
overflow-y: auto;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
.backup-list li code {
|
|
white-space: normal;
|
|
overflow-wrap: anywhere;
|
|
word-break: normal;
|
|
}
|
|
}
|
|
|
|
.settings-layout {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: row;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsSimplification 2026-07-11-00:42:
|
|
The Settings rail is one navigation surface. The legacy translucent background on both the parent rail and nested section list compounded into a visibly lighter second tone; keep the parent opaque and the nested scroller transparent in every theme.
|
|
|
|
FNXC:Settings 2026-07-11-18:45:
|
|
FN-7825 removes the hard divider between the Settings rail and content, keeps section rows single-line at the rail's full width, and makes this parent rail the authoritative width owner so the upcoming desktop resize handle can update one CSS custom property without fighting nested sidebar rules.
|
|
*/
|
|
.settings-navigation {
|
|
--settings-nav-width: calc(var(--space-xl) * 10 + var(--space-sm));
|
|
width: var(--settings-nav-width);
|
|
min-width: var(--settings-nav-width);
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
background: var(--surface);
|
|
}
|
|
|
|
.settings-navigation .settings-sidebar {
|
|
background: transparent;
|
|
}
|
|
|
|
.settings-nav-resize-handle {
|
|
flex: 0 0 var(--space-xs);
|
|
margin-inline: calc(var(--space-xs) * -0.5);
|
|
cursor: col-resize;
|
|
background: transparent;
|
|
border: 0;
|
|
outline: none;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.settings-nav-resize-handle::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset-block: var(--space-sm);
|
|
inset-inline: calc(50% - (var(--btn-border-width) / 2));
|
|
width: var(--btn-border-width);
|
|
border-radius: var(--radius-pill);
|
|
background: transparent;
|
|
transition: background var(--transition-fast);
|
|
}
|
|
|
|
.settings-nav-resize-handle:hover::before,
|
|
.settings-nav-resize-handle:focus-visible::before {
|
|
background: var(--border);
|
|
}
|
|
|
|
.settings-nav-resize-handle:focus-visible {
|
|
box-shadow: var(--focus-ring-strong);
|
|
}
|
|
|
|
.settings-search {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
padding: var(--space-sm);
|
|
border-bottom: var(--btn-border-width) solid var(--border);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsSimplification 2026-07-10-23:24:
|
|
The Advanced settings preference is a navigation-level disclosure, so keep it visually adjacent to section search/picking in both desktop and mobile layouts and retain a full-row click target.
|
|
*/
|
|
.settings-advanced-toggle {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm);
|
|
border-bottom: var(--btn-border-width) solid var(--border);
|
|
color: var(--text);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.settings-advanced-toggle input {
|
|
margin: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* FNXC:Settings 2026-07-09-12:00: the row wraps label + input + results; on desktop it always renders (see .settings-search-toggle below, hidden here). On mobile the inline section-picker toggle controls whether this wrapper mounts below the picker, avoiding an empty padded shell above the picker while collapsed. */
|
|
.settings-search-row {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
/* Desktop/tablet: search row is always visible, so the toggle icon never renders here. */
|
|
.settings-search-toggle {
|
|
display: none;
|
|
}
|
|
|
|
.settings-search-label {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.settings-search-input-wrap {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-search-input {
|
|
min-width: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.settings-search-clear {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.settings-search-results,
|
|
.settings-search-empty-hint {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
margin: 0;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsSearch 2026-07-15-17:35:
|
|
Per-setting search results. Sits between the search input and the section nav because it answers the question the operator actually asked — which setting, not which section — and the nav below stays filtered as the coarse view.
|
|
Scrolls at a fixed max-height rather than growing: the list is capped at 8 entries, but long labels still wrap, and an unbounded block would push the section nav out of view on a short viewport.
|
|
Sizes name the shared scale (label = xs, section = 2xs) so results read as the same UI as the rows they navigate to.
|
|
*/
|
|
.settings-search-hits {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
max-height: calc(var(--space-2xl) * 6);
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.settings-search-hit {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 1px;
|
|
width: 100%;
|
|
padding: var(--space-xs) var(--space-sm);
|
|
background: none;
|
|
border: none;
|
|
border-radius: var(--radius-sm);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
color: var(--text);
|
|
}
|
|
|
|
.settings-search-hit:hover,
|
|
.settings-search-hit:focus-visible {
|
|
background: var(--bg);
|
|
}
|
|
|
|
.settings-search-hit-label {
|
|
font-size: var(--font-size-xs);
|
|
line-height: var(--line-height-tight);
|
|
}
|
|
|
|
/* The owning section, so two similarly-named settings are distinguishable before navigating. */
|
|
.settings-search-hit-section {
|
|
font-size: var(--font-size-2xs);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-search-hits-more {
|
|
padding: var(--space-xs) var(--space-sm);
|
|
font-size: var(--font-size-2xs);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-search-empty {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-md);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-search-empty p,
|
|
.settings-search-content-empty p {
|
|
margin: 0;
|
|
}
|
|
|
|
.settings-search-content-empty {
|
|
gap: var(--space-md);
|
|
}
|
|
|
|
.settings-mobile-section-picker {
|
|
display: none;
|
|
}
|
|
|
|
.settings-sidebar {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
padding: var(--space-sm) var(--space-sm);
|
|
gap: calc(var(--space-xs) / 2);
|
|
scrollbar-color: var(--border) transparent;
|
|
scrollbar-width: thin;
|
|
}
|
|
|
|
.settings-sidebar::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.settings-sidebar::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.settings-sidebar::-webkit-scrollbar-thumb {
|
|
background: var(--border);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.settings-sidebar::-webkit-scrollbar-thumb:hover {
|
|
background: var(--text-muted);
|
|
}
|
|
|
|
.settings-research-source-locked {
|
|
cursor: default;
|
|
color: var(--text-muted) !important;
|
|
}
|
|
|
|
.settings-research-source-locked input[type="checkbox"] {
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.settings-research-source-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-source-grid > * {
|
|
min-width: 0;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsResearch 2026-07-14-19:45:
|
|
Source options stack the control label and default-state hint so hints stay visible without joining the checkbox accessible name.
|
|
*/
|
|
.settings-research-source-option {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: calc(var(--space-xs) / 2);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-limits-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-limits-grid > * {
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-limit-field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-limits-error {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.settings-research-source-grid,
|
|
.settings-research-limits-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
.settings-nav-item {
|
|
display: block;
|
|
width: 100%;
|
|
padding: var(--space-sm) var(--space-md);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 500;
|
|
color: var(--text-muted);
|
|
background: none;
|
|
border: none;
|
|
border-left: 3px solid transparent;
|
|
border-radius: 0 var(--radius-md) var(--radius-md) 0;
|
|
cursor: pointer;
|
|
text-align: left;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
transition:
|
|
background var(--transition-fast),
|
|
color var(--transition-fast),
|
|
border-color var(--transition-fast);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsSimplification 2026-07-11-00:17:
|
|
The simplified Settings surface needs one consistent reading rhythm across legacy and typed sections. Use a wider navigation rail to prevent routine labels wrapping, align section content to one gutter, normalize control height/type, and keep helper copy readable without changing the dashboard's existing tokens or font family.
|
|
*/
|
|
.settings-sidebar {
|
|
padding-block: var(--space-md);
|
|
}
|
|
|
|
.settings-nav-item {
|
|
min-height: 36px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
line-height: 1.25;
|
|
}
|
|
|
|
.settings-content {
|
|
padding: var(--space-lg) calc(var(--space-xl) + var(--space-sm)) calc(var(--space-xl) + var(--space-md));
|
|
}
|
|
|
|
.settings-content > .settings-gitlab-disclosure {
|
|
margin-inline: 0;
|
|
}
|
|
|
|
.settings-content .settings-section-heading {
|
|
padding-inline: 0;
|
|
color: var(--text);
|
|
line-height: 1.3;
|
|
text-wrap: balance;
|
|
}
|
|
|
|
.settings-content h4.settings-section-heading {
|
|
font-size: var(--font-size-base);
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
.settings-content h5.settings-section-heading {
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsStyling 2026-07-15-20:30:
|
|
Bespoke rows keep the same vertical rhythm as migrated ones.
|
|
`.settings-field-row` spaces itself with `padding-block: var(--space-sm)`, while a `.form-group` spaced itself with `margin-top: var(--space-md)`. Mixed in one section — which is now the normal case, since some rows deliberately stay bespoke — that produced three different gaps depending on which idioms happened to be adjacent: 16px between two migrated rows, 12px between two bespoke ones, and 20px where they met.
|
|
Matching the primitive's `padding-block` (rather than giving the primitive a margin) keeps one idiom: every settings row owns its own space and margins never collapse or compound between them.
|
|
`padding-inline: 0` stays — `.settings-content` already owns the horizontal inset, and `.form-group`'s global `0 var(--space-xl)` would double-indent every bespoke row.
|
|
*/
|
|
.settings-content .form-group {
|
|
padding-inline: 0;
|
|
margin-top: 0;
|
|
padding-block: var(--space-sm);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsStyling 2026-07-15-20:30:
|
|
Every label in Settings reads the same way, whether its row is migrated or still bespoke.
|
|
Some rows deliberately stay hand-rolled — a `data-testid` the primitives have no slot for, help copy that interleaves `<code>` or a link, a repeating-row editor — and those inherit the GLOBAL `.form-group label` treatment: 12px, weight 600, uppercase, letter-spaced, muted. So a section rendered "PLAN APPROVAL MODE" in caps directly above "Auto-merge conflict retries" in sentence case. Two idioms on one screen is the exact inconsistency the migration set out to remove, and migrating every last row is not achievable (nor desirable — see the bespoke rows' own FNXC notes).
|
|
Retuning them here instead of at the global rule is deliberate: `.form-group` is dashboard-wide across 35 non-settings files, and its uppercase treatment is that context's convention, not a bug. This selector is settings-scoped, so it changes Settings only.
|
|
Declarations mirror `.settings-field-row-label` — one contract, expressed at whatever specificity each markup idiom needs.
|
|
*/
|
|
.settings-content .form-group label:not(.checkbox-label) {
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 500;
|
|
line-height: var(--line-height-tight);
|
|
color: var(--text);
|
|
text-transform: none;
|
|
letter-spacing: normal;
|
|
margin-bottom: var(--space-xs);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsStyling 2026-07-15-20:30:
|
|
Bespoke help text (`<small>` inside a form-group) matches `.settings-field-row-help`: same rung, same colour, same measure. Without this a bespoke row's help sat at 12px/1.4 beside a migrated row's at 12.8px/1.5 — close enough to look like a rendering glitch rather than a choice.
|
|
Still applies to the copy that legitimately stays inline: validation messages (`.field-error`) and the explanatory blurbs that describe a whole block rather than one control.
|
|
*/
|
|
.settings-content .form-group small,
|
|
.settings-content .form-group .form-text {
|
|
font-size: var(--font-size-xs);
|
|
line-height: 1.5;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsHelp 2026-07-15-21:40:
|
|
The label line for a row that is NOT on the shared primitive, so its help can hang off a "?" exactly like a migrated row's.
|
|
A bespoke row is `<label>` + control + `<small>`; the tip has to sit beside the label, and it cannot go INSIDE the `<label>` — a nested button would both swallow the label's click-to-focus and put an interactive element inside a label, which is invalid. This wrapper puts them on one line as siblings instead.
|
|
Mirrors `.settings-field-row-head` (same gap, same wrap) so the two idioms produce an identical label line.
|
|
*/
|
|
.settings-field-label-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* The label inside the wrapper must not also claim the row's full width, or the tip wraps to its own line. */
|
|
.settings-content .form-group .settings-field-label-row label {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.settings-content .checkbox-label {
|
|
min-height: 32px;
|
|
line-height: 1.35;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsStyling 2026-07-16-12:40:
|
|
Every checkbox in Settings resolves to ONE appearance, whatever markup it sits in.
|
|
The global `.form-group input[type="checkbox"]` rule themes only checkboxes that happen to sit inside a form-group, so the Advanced-settings header toggle, SettingsToggleRow's `.settings-toggle`, the ntfy/webhook card-header enables, and the MCP card toggle all fell back to the browser-default accent — observed as a mix of default-blue and themed checkboxes in one section on Android.
|
|
Scoped to `.settings-modal` (both the dialog and the embedded `settings-modal--embedded` panel carry it) and declaration-identical to the global form-group rule, so cascade ties between the two are harmless.
|
|
*/
|
|
.settings-modal input[type="checkbox"] {
|
|
width: 16px;
|
|
height: 16px;
|
|
flex-shrink: 0;
|
|
padding: 0;
|
|
margin: 0;
|
|
accent-color: var(--todo);
|
|
cursor: pointer;
|
|
border-radius: var(--radius-sm);
|
|
transition: box-shadow var(--transition-fast);
|
|
}
|
|
.settings-modal input[type="checkbox"]:focus-visible {
|
|
box-shadow: var(--focus-ring-strong);
|
|
outline: none;
|
|
}
|
|
.settings-modal input[type="checkbox"]:disabled {
|
|
cursor: default;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsStyling 2026-07-15-18:52:
|
|
Every text-entry control in Settings resolves to ONE appearance, whatever markup it sits in.
|
|
The dashboard ships two competing input treatments: `.input`/`.select` (6px 10px at 13px) and the global `.form-group input` rule (`var(--space-sm) var(--space-md)` = 8px 12px at 14px). A control's padding therefore depended on whether an ancestor happened to be a `.form-group` — and because `.form-group input` (0,1,1) out-specifies `.input` (0,1,0), adding the standard class to a control inside one did nothing. Migrated rows sit outside `.form-group` and got 6px 10px; the bespoke rows still inside one got 8px 12px, side by side in the same section.
|
|
Normalized here rather than by editing the global `.form-group` rule, which 35 non-settings files depend on: this selector is already settings-scoped and already targets exactly the right set (form-group children AND `.input`/`.select`/`.form-input`), so it is the one place that can reconcile them without reaching outside Settings.
|
|
Values intentionally mirror `.input` in styles.css — it is the dashboard-wide control appearance across ~300 call sites, so Settings matches the rest of the app rather than inventing a third treatment. If `.input` is ever tokenized, this pairs with it.
|
|
*/
|
|
.settings-content .form-group :is(input:not([type="checkbox"]), select, textarea),
|
|
.settings-content :is(.input, .select, .form-input) {
|
|
min-height: 38px;
|
|
box-sizing: border-box;
|
|
padding: 6px 10px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.settings-content :is(.form-group small, .settings-description, .settings-section-description) {
|
|
max-width: 78ch;
|
|
line-height: 1.5;
|
|
text-wrap: pretty;
|
|
}
|
|
.settings-nav-item:hover {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
border-left-color: var(--border);
|
|
}
|
|
.settings-nav-item:focus-visible {
|
|
box-shadow: var(--focus-ring-strong);
|
|
outline: none;
|
|
}
|
|
.settings-nav-item.active {
|
|
background: var(--bg);
|
|
color: var(--todo);
|
|
font-weight: 600;
|
|
border-left-color: var(--todo);
|
|
}
|
|
|
|
/* Settings group headers - visual separators between global and project sections */
|
|
.settings-group-header {
|
|
font-size: var(--font-size-2xs);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: var(--text-muted);
|
|
padding: var(--space-md) var(--space-md) var(--space-xs);
|
|
margin-top: var(--space-sm);
|
|
user-select: none;
|
|
}
|
|
.settings-group-header:first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.settings-content {
|
|
flex: 1;
|
|
overflow-x: hidden;
|
|
overflow-y: auto;
|
|
padding: var(--space-md) var(--space-xl) var(--space-lg);
|
|
scrollbar-color: var(--border) transparent;
|
|
scrollbar-width: thin;
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsSimplification 2026-07-11-00:04:
|
|
Basic Settings mode also removes low-frequency controls inside otherwise common sections. Keep the fields mounted so toggling Advanced settings never discards unsaved edits, but remove them from layout and accessibility navigation until the browser-local disclosure is enabled.
|
|
*/
|
|
.settings-content[data-show-advanced="false"] :is(
|
|
.form-group:has(#globalGitlabApiBaseUrl),
|
|
.form-group:has(#persistAgentToolOutput),
|
|
.form-group:has(#persistAgentThinkingLogPermanent),
|
|
.form-group:has(#fnBinaryCheckEnabled),
|
|
.form-group:has(#updateCheckFrequency),
|
|
.form-group:has(#autoReloadOnVersionChange),
|
|
.form-group:has(#taskDetailChatFirst),
|
|
.form-group:has(#sessionBannersHidden),
|
|
.form-group:has(#failureNotificationDelayMs),
|
|
.form-group:has(#webhookFormat),
|
|
.form-group:has(#allowAbsoluteFileBrowserPaths),
|
|
.form-group:has(#operationalLogRetentionDays),
|
|
.form-group:has(#chatRoomRecentVerbatimMessages),
|
|
.form-group:has(#chatRoomCompactionFetchLimit),
|
|
.form-group:has(#chatRoomSummaryMaxChars),
|
|
.form-group:has(#capacityRiskTodoThresholdGeneral),
|
|
.form-group:has(#githubTrackingDedupEnabled),
|
|
.form-group:has(#gitlabApiBaseUrl),
|
|
.form-group:has(#worktreeInitCommand),
|
|
.form-group:has(#recycleWorktrees),
|
|
.form-group:has(#showWorktreeGrouping),
|
|
.form-group:has(#worktree-copy-files-list),
|
|
.form-group:has(#executorAllowSiblingBranchRename),
|
|
.form-group:has(#worktreeNaming),
|
|
.form-group:has(#worktreesDir),
|
|
.form-group:has(#worktreeRebaseRemote),
|
|
.form-group:has(#worktreeRebaseLocalBase),
|
|
.form-group:has(#worktrunkBinaryPath),
|
|
.form-group:has(#worktrunkOnFailure),
|
|
.form-group:has(#pollIntervalMs),
|
|
.form-group:has(#heartbeatScopeDiscipline),
|
|
.form-group:has(#taskStuckTimeoutMs),
|
|
.form-group:has(#buildTimeoutMs),
|
|
.form-group:has(#staleHighFanoutBlockerAgeThresholdMs),
|
|
.form-group:has(#preserveProgressOnStuckRequeue),
|
|
.form-group:has(#specStalenessEnabled),
|
|
.form-group:has(#specStalenessMaxAgeMs),
|
|
.form-group:has(#autoArchiveDoneAfterMs),
|
|
.form-group:has(#archiveAgentLogMode),
|
|
.form-group:has(#autoArchiveDuplicateTasksEnabled),
|
|
.form-group:has(#maxStuckKills),
|
|
.form-group:has(#ignoreHiddenOverlapPaths),
|
|
.form-group:has(.settings-overlap-ignore-row),
|
|
.form-group:has(#maxAutoMergeRetries),
|
|
#legacy-automerge-stamp-cleanup-panel,
|
|
.form-group:has(#mergerMaxReviewPasses),
|
|
.form-group:has(#mergerAllowDirtyLocalCheckoutSync),
|
|
.form-group:has(#testMode),
|
|
.form-group:has(#directMergeCommitStrategy),
|
|
.form-group:has(#mergeIntegrationWorktree),
|
|
.form-group:has(#mergeAdvanceAutoSync),
|
|
.form-group:has(#commitAuthorEnabled),
|
|
.form-group:has(#commitAuthorName),
|
|
.form-group:has(#commitAuthorEmail),
|
|
.form-group:has(#autoResolveConflicts),
|
|
.form-group:has(#smartConflictResolution),
|
|
.form-group:has(#mergeConflictStrategy),
|
|
.form-group:has(#mergeStrategyOverlapBehavior),
|
|
.form-group:has(#postMergeAuditMode),
|
|
.form-group:has(#pushAfterMerge),
|
|
.form-group:has(#pushRemote),
|
|
.form-group:has(#project-model-lane-title-summarizer-fallback),
|
|
.form-group:has(#autoSelectModelPreset),
|
|
.form-group:has(#autoSummarizeTitles),
|
|
.form-group:has(#useAiMergeCommitSummary),
|
|
.form-group:has(#prTitlePromptInstructions),
|
|
.form-group:has(#prDescriptionPromptInstructions),
|
|
.model-pricing-section,
|
|
details:has(#openrouterAppAttributionReferer),
|
|
.form-group:has(#openrouterModelSync),
|
|
.form-group:has(#opencodeGoModelSync)
|
|
) {
|
|
display: none;
|
|
}
|
|
|
|
.settings-content::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.settings-content::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.settings-content::-webkit-scrollbar-thumb {
|
|
background: var(--border);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.settings-content::-webkit-scrollbar-thumb:hover {
|
|
background: var(--text-muted);
|
|
}
|
|
.settings-content > * {
|
|
animation: settingsFadeIn var(--duration-normal) ease;
|
|
}
|
|
@keyframes settingsFadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(4px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsMobile 2026-06-23-09:02:
|
|
Settings section headings should preserve hierarchy through spacing and type only. Avoid per-heading divider borders so mobile and desktop shared Settings sections keep the lighter scrollbar-focused chrome contract.
|
|
*/
|
|
.settings-section-heading {
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 600;
|
|
padding: var(--space-lg) 0 var(--space-md);
|
|
margin: 0 0 var(--space-md);
|
|
color: var(--text);
|
|
}
|
|
|
|
/* First heading inside the section drops top padding to remove a redundant
|
|
gap stacked on top of the settings-content container's own top padding. */
|
|
.settings-content > .settings-section-heading:first-child,
|
|
.settings-modal-section > .settings-section-heading:first-child {
|
|
padding-top: var(--space-md);
|
|
}
|
|
|
|
/* Spacing modifier for settings-section-heading that need extra top margin */
|
|
.settings-section-heading--spaced {
|
|
margin-top: var(--space-lg);
|
|
}
|
|
|
|
.settings-section-description {
|
|
margin: 0;
|
|
margin-bottom: var(--space-sm);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.settings-plugins-subsection-toggle {
|
|
display: inline-flex;
|
|
gap: var(--space-xs);
|
|
margin: var(--space-md) 0;
|
|
}
|
|
|
|
.settings-plugins-subsection-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-pill);
|
|
background: var(--surface);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 600;
|
|
padding: var(--space-xs) var(--space-md);
|
|
cursor: pointer;
|
|
transition:
|
|
background var(--transition-fast),
|
|
color var(--transition-fast),
|
|
border-color var(--transition-fast),
|
|
box-shadow var(--transition-fast);
|
|
}
|
|
|
|
.settings-plugins-subsection-btn:hover {
|
|
background: var(--surface-hover, color-mix(in srgb, var(--text) 6%, transparent));
|
|
color: var(--text);
|
|
}
|
|
|
|
.settings-plugins-subsection-btn:focus-visible {
|
|
outline: none;
|
|
box-shadow: var(--focus-ring-strong);
|
|
}
|
|
|
|
.settings-plugins-subsection-btn.active {
|
|
background: color-mix(in srgb, var(--todo) 14%, transparent);
|
|
border-color: color-mix(in srgb, var(--todo) 45%, var(--border));
|
|
color: var(--todo);
|
|
}
|
|
|
|
.settings-plugins-subsection-panel {
|
|
padding-bottom: var(--space-md);
|
|
}
|
|
|
|
/* Scope indicators in sidebar nav items */
|
|
.settings-scope-icon {
|
|
margin-right: 6px;
|
|
display: inline-flex;
|
|
vertical-align: middle;
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Scope banner above section content */
|
|
|
|
/* Helper note styling for Settings sections — aligns with form-group horizontal gutters */
|
|
.settings-note {
|
|
display: block;
|
|
padding: 0 var(--space-xl);
|
|
margin-top: var(--space-xs);
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/* Inline text input + secondary action ("Use dropdown") for fields that
|
|
sometimes want a free-text fallback. */
|
|
.form-inline-group {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
.form-inline-group .input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
.btn-link {
|
|
background: none;
|
|
border: none;
|
|
padding: 0;
|
|
font-size: var(--font-size-xs);
|
|
color: var(--accent, #4a90e2);
|
|
cursor: pointer;
|
|
text-decoration: underline;
|
|
white-space: nowrap;
|
|
}
|
|
.btn-link:hover {
|
|
text-decoration: none;
|
|
}
|
|
|
|
.settings-overlap-ignore-group code {
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.settings-button-row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-sm);
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
.settings-raw-output {
|
|
margin: var(--space-sm) 0 0;
|
|
padding: var(--space-sm);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--surface);
|
|
color: var(--text-muted);
|
|
max-height: calc(var(--space-2xl) * 7);
|
|
overflow: auto;
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.settings-url-output {
|
|
display: block;
|
|
margin-top: var(--space-xs);
|
|
padding: var(--space-sm);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--surface);
|
|
color: var(--text-muted);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-xs);
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
overflow-x: auto;
|
|
max-width: 100%;
|
|
}
|
|
|
|
.settings-qr-preview {
|
|
margin-top: var(--space-sm);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-qr-preview-label {
|
|
margin: 0;
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-qr-preview-image-wrap {
|
|
width: fit-content;
|
|
max-width: 100%;
|
|
padding: var(--space-sm);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--card);
|
|
}
|
|
|
|
.settings-qr-preview-image {
|
|
display: block;
|
|
width: min(calc(var(--space-2xl) * 6), 100%);
|
|
height: auto;
|
|
aspect-ratio: 1 / 1;
|
|
}
|
|
|
|
.remote-provider-selector {
|
|
display: flex;
|
|
gap: var(--space-xs);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
padding: var(--space-xs);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.remote-provider-option {
|
|
position: relative;
|
|
flex: 1;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.remote-provider-option input {
|
|
position: absolute;
|
|
opacity: 0;
|
|
width: 0;
|
|
height: 0;
|
|
}
|
|
|
|
.remote-provider-option span {
|
|
display: block;
|
|
padding: var(--space-sm) var(--space-md);
|
|
text-align: center;
|
|
border-radius: var(--radius-sm);
|
|
border: var(--btn-border-width) solid transparent;
|
|
color: var(--text-muted);
|
|
transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
|
|
}
|
|
|
|
.remote-provider-option-content {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.remote-provider-option input:checked + span {
|
|
background: var(--card);
|
|
color: var(--text);
|
|
border-color: var(--border);
|
|
}
|
|
|
|
.remote-provider-option input:focus-visible + span {
|
|
box-shadow: var(--focus-ring-strong);
|
|
}
|
|
|
|
.remote-status-bar {
|
|
--status-color: var(--text-muted);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border-radius: var(--radius-md);
|
|
margin: 0 var(--space-xl) var(--space-md);
|
|
background: color-mix(in srgb, var(--status-color) 10%, transparent);
|
|
}
|
|
|
|
.remote-status-bar--running {
|
|
--status-color: var(--color-success);
|
|
}
|
|
|
|
.remote-status-bar--starting {
|
|
--status-color: var(--color-warning);
|
|
}
|
|
|
|
.remote-status-bar--error {
|
|
--status-color: var(--color-error);
|
|
}
|
|
|
|
.remote-status-bar--stopped {
|
|
--status-color: var(--text-muted);
|
|
}
|
|
|
|
.remote-share-block {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
margin: 0 var(--space-xl) var(--space-md);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--text-muted) 5%, transparent);
|
|
}
|
|
|
|
.remote-share-row {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.remote-share-row .settings-qr-preview-image {
|
|
margin-top: var(--space-xs);
|
|
}
|
|
|
|
.remote-status-dot {
|
|
width: var(--space-sm);
|
|
height: var(--space-sm);
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.remote-status-dot--running {
|
|
background: var(--color-success);
|
|
}
|
|
|
|
.remote-status-dot--starting {
|
|
background: var(--color-warning);
|
|
}
|
|
|
|
.remote-status-dot--error {
|
|
background: var(--color-error);
|
|
}
|
|
|
|
.remote-status-dot--stopped {
|
|
background: var(--text-muted);
|
|
}
|
|
|
|
.remote-status-url {
|
|
margin-left: auto;
|
|
font-family: var(--font-mono);
|
|
overflow-wrap: anywhere;
|
|
word-break: break-word;
|
|
user-select: all;
|
|
}
|
|
|
|
.remote-provider-settings {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.remote-cli-detection {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--space-sm);
|
|
margin: 0 var(--space-xl) var(--space-md);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.remote-cli-detection--available {
|
|
background: color-mix(in srgb, var(--color-success) 10%, transparent);
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.remote-cli-detection--missing {
|
|
background: color-mix(in srgb, var(--color-warning) 10%, transparent);
|
|
color: var(--color-warning);
|
|
}
|
|
|
|
.remote-cli-detection-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
color: var(--text);
|
|
}
|
|
|
|
.remote-cli-detection .btn {
|
|
align-self: flex-start;
|
|
}
|
|
|
|
.remote-cli-install-error {
|
|
color: var(--color-error);
|
|
}
|
|
|
|
.remote-cli-manual {
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.remote-external-tunnel-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
margin: 0 var(--space-xl) var(--space-md);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--color-info) 10%, transparent);
|
|
}
|
|
|
|
.remote-external-tunnel-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
color: var(--color-info);
|
|
}
|
|
|
|
.remote-external-tunnel-qr {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.remote-external-tunnel-actions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.remote-advanced-details {
|
|
margin: var(--space-lg) var(--space-xl) 0;
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
padding: var(--space-md);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.remote-advanced-details > summary {
|
|
cursor: pointer;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
list-style: none;
|
|
}
|
|
|
|
.remote-advanced-details > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.remote-advanced-details > summary::before {
|
|
content: "▸";
|
|
margin-right: var(--space-xs);
|
|
}
|
|
|
|
.remote-advanced-details[open] > summary::before {
|
|
content: "▾";
|
|
}
|
|
|
|
.settings-option-details {
|
|
margin-top: var(--space-xs);
|
|
}
|
|
|
|
.settings-research-provider-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-provider-advanced-details {
|
|
min-width: 0;
|
|
padding-inline-start: var(--space-md);
|
|
}
|
|
|
|
.settings-research-provider-advanced-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
padding-top: var(--space-sm);
|
|
}
|
|
|
|
.settings-research-provider-advanced-body > .form-group {
|
|
margin-top: 0;
|
|
padding: 0;
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-provider-advanced-body .input {
|
|
width: 100%;
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-research-empty-state {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.settings-research-provider-advanced-details {
|
|
padding-inline-start: 0;
|
|
}
|
|
}
|
|
|
|
.settings-option-details > summary {
|
|
cursor: pointer;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
list-style: none;
|
|
}
|
|
|
|
.settings-option-details > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.settings-option-details > summary::before {
|
|
content: "▸";
|
|
margin-right: var(--space-xs);
|
|
}
|
|
|
|
.settings-option-details[open] > summary::before {
|
|
content: "▾";
|
|
}
|
|
|
|
/*
|
|
FNXC:GitLabSettings 2026-07-02-00:00:
|
|
FN-7453 keeps GitLab's enable switch visible while hiding noisy URL/token fields behind native details disclosure. The layout uses token spacing and native summary semantics so collapsed and disabled states do not leave empty icon-button shells or focusable hidden fields.
|
|
*/
|
|
/*
|
|
FNXC:SettingsLayout 2026-07-10-12:20:
|
|
First-run review video showed the GitLab Configuration box (and its right-flushed "Enable GitLab integration" checkbox) leaking past the alignment line of every other General control, reading as overflow at the panel/viewport edge.
|
|
Root cause: settings controls get their horizontal gutter from the base `.form-group { padding: 0 var(--space-xl) }`, but this disclosure is a direct `.settings-content` child that carried no gutter of its own, so its bordered box spanned the full content width.
|
|
Fix the invariant: bordered boxes that are direct settings-content children align their OUTER edge to the form-group gutter via horizontal margins, desktop `var(--space-xl)` and mobile `var(--space-sm)` to mirror the mobile `.form-group` gutter below.
|
|
*/
|
|
.settings-gitlab-disclosure {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
margin: var(--space-md) var(--space-xl) 0;
|
|
padding: var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--surface-secondary);
|
|
}
|
|
|
|
.settings-gitlab-disclosure > summary {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: var(--space-md);
|
|
cursor: pointer;
|
|
list-style: none;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.settings-gitlab-disclosure > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.settings-gitlab-disclosure > summary::before {
|
|
content: "▸";
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-gitlab-disclosure[open] > summary::before {
|
|
content: "▾";
|
|
}
|
|
|
|
.settings-gitlab-disclosure__title {
|
|
flex: 1 1 auto;
|
|
font-weight: var(--font-weight-semibold);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-gitlab-disclosure__toggle {
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.settings-gitlab-disclosure__body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
margin-top: var(--space-sm);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-gitlab-disclosure__body > .form-group {
|
|
margin-top: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
/* FNXC:SettingsLayout 2026-07-10-12:20: mobile keeps the disclosure aligned with the mobile `.form-group { padding: 0 var(--space-sm) }` gutter. */
|
|
.settings-gitlab-disclosure {
|
|
margin-inline: var(--space-sm);
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.settings-gitlab-disclosure > summary {
|
|
align-items: flex-start;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-gitlab-disclosure__toggle {
|
|
flex-basis: 100%;
|
|
}
|
|
}
|
|
|
|
.remote-cf-advanced-details {
|
|
margin-top: var(--space-xs);
|
|
}
|
|
|
|
.remote-cf-advanced-details > summary {
|
|
cursor: pointer;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
list-style: none;
|
|
}
|
|
|
|
.remote-cf-advanced-details > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.remote-cf-advanced-details > summary::before {
|
|
content: "▸";
|
|
margin-right: var(--space-xs);
|
|
}
|
|
|
|
.remote-cf-advanced-details[open] > summary::before {
|
|
content: "▾";
|
|
}
|
|
|
|
.remote-cf-advanced-fields {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
.remote-tunnel-actions {
|
|
margin-top: var(--space-md);
|
|
}
|
|
|
|
.remote-tunnel-actions .btn {
|
|
width: 100%;
|
|
}
|
|
|
|
.settings-overlap-ignore-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
margin: var(--space-sm) 0;
|
|
}
|
|
|
|
.settings-overlap-ignore-row {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
gap: var(--space-sm);
|
|
align-items: center;
|
|
}
|
|
|
|
.settings-overlap-ignore-path-controls {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
gap: var(--space-sm);
|
|
align-items: center;
|
|
}
|
|
|
|
.settings-overlap-path-picker-modal {
|
|
max-width: min(960px, calc(100vw - var(--space-2xl)));
|
|
}
|
|
|
|
.settings-overlap-path-picker-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
max-height: min(70vh, 720px);
|
|
}
|
|
|
|
.settings-overlap-path-picker-note {
|
|
margin: 0;
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-section-divider {
|
|
border-top: 1px solid var(--border);
|
|
margin: var(--space-lg) var(--space-xl);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsReset 2026-07-04-00:45:
|
|
Reset Settings confirmation dialog (FN-7506). Reuses .modal/.modal-md/.modal-overlay/
|
|
.modal-actions chrome; only the choice-list layout and destructive-reason hint are new.
|
|
Design tokens only (spacing/radius/color vars), no hardcoded px besides 0.
|
|
*/
|
|
.settings-reset-dialog__choice {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
margin-block-end: var(--space-md);
|
|
}
|
|
|
|
.settings-reset-dialog__choice-btn {
|
|
align-self: flex-start;
|
|
width: 100%;
|
|
}
|
|
|
|
.settings-reset-dialog__ineligible-reason {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.settings-reset-dialog {
|
|
max-width: calc(100vw - var(--space-md));
|
|
}
|
|
|
|
.settings-reset-dialog__choice-btn {
|
|
/* Full-width tappable target at narrow widths; avoids horizontal overflow. */
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.settings-note {
|
|
padding: 0 var(--space-sm);
|
|
}
|
|
|
|
.settings-overlap-ignore-row {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
}
|
|
|
|
.settings-overlap-ignore-path-controls {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
}
|
|
|
|
.settings-overlap-ignore-row > .btn {
|
|
justify-self: start;
|
|
}
|
|
|
|
.settings-overlap-path-picker-modal {
|
|
max-width: calc(100vw - var(--space-md));
|
|
}
|
|
|
|
.remote-provider-selector {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.remote-status-bar {
|
|
flex-wrap: wrap;
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
|
|
.remote-share-block {
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
|
|
.remote-status-url {
|
|
width: 100%;
|
|
margin-left: 0;
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.remote-provider-option {
|
|
min-height: 36px;
|
|
}
|
|
|
|
.remote-tunnel-actions .btn {
|
|
min-height: 36px;
|
|
}
|
|
|
|
.remote-cli-detection {
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
|
|
.remote-external-tunnel-panel {
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
}
|
|
|
|
/* === Notifications Settings === */
|
|
.ntfy-advanced-disclosure {
|
|
margin-top: var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.ntfy-advanced-disclosure > summary {
|
|
cursor: pointer;
|
|
list-style: none;
|
|
padding: var(--space-sm) var(--space-md);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
}
|
|
|
|
.ntfy-advanced-disclosure > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.ntfy-advanced-content {
|
|
padding: 0 var(--space-md) var(--space-md);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-inline-link {
|
|
color: var(--todo);
|
|
text-decoration: none;
|
|
transition: text-decoration var(--transition-fast);
|
|
}
|
|
|
|
.settings-inline-link:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.settings-inline-link:focus-visible {
|
|
outline: none;
|
|
box-shadow: var(--focus-ring-strong);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
/**
|
|
* Notification provider cards:
|
|
* reusable container pattern for per-provider notification configuration
|
|
* (ntfy, webhook, and future providers).
|
|
*/
|
|
.notification-provider-card {
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-lg);
|
|
background: var(--surface);
|
|
margin: 0 var(--space-xl) var(--space-lg);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.notification-provider-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: var(--space-md) var(--space-lg);
|
|
border-bottom: var(--btn-border-width) solid var(--border);
|
|
}
|
|
|
|
.notification-provider-body {
|
|
padding: var(--space-lg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-lg);
|
|
}
|
|
|
|
.notification-provider-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-sm);
|
|
padding-top: var(--space-md);
|
|
border-top: var(--btn-border-width) solid var(--border);
|
|
margin-top: var(--space-md);
|
|
}
|
|
|
|
.notification-test-feedback {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
.notification-test-feedback-item {
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
padding: var(--space-xs) var(--space-sm);
|
|
}
|
|
|
|
.notification-test-feedback-item--success {
|
|
border-color: color-mix(in srgb, var(--color-success) 45%, var(--border));
|
|
background: color-mix(in srgb, var(--color-success) 8%, transparent);
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.notification-test-feedback-item--error {
|
|
border-color: color-mix(in srgb, var(--color-error) 45%, var(--border));
|
|
background: color-mix(in srgb, var(--color-error) 8%, transparent);
|
|
color: var(--color-error);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.notification-provider-actions {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.notification-provider-actions .btn {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/* === Memory Settings === */
|
|
.memory-status-message {
|
|
display: flex;
|
|
gap: var(--space-md);
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
.memory-status-message span {
|
|
min-width: 0;
|
|
}
|
|
|
|
.memory-retrieval-test {
|
|
margin-top: var(--space-lg);
|
|
}
|
|
|
|
.memory-test-result {
|
|
margin: 0 var(--space-xl) var(--space-lg);
|
|
padding: var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--card);
|
|
}
|
|
|
|
.memory-test-result strong,
|
|
.memory-test-result span {
|
|
color: var(--text);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.memory-test-result small {
|
|
display: block;
|
|
margin-top: var(--space-xs);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.memory-test-result ul {
|
|
margin: var(--space-md) 0 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.memory-test-result li {
|
|
padding-top: var(--space-sm);
|
|
border-top: var(--btn-border-width) solid var(--border);
|
|
}
|
|
|
|
.memory-test-result li + li {
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
.memory-test-result p {
|
|
margin: var(--space-xs) 0 0;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
line-height: 1.5;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.memory-editor-section {
|
|
margin-top: var(--space-lg);
|
|
display: flex;
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.memory-editor-form-group {
|
|
display: flex;
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.memory-file-summary {
|
|
display: grid;
|
|
grid-template-columns: auto minmax(0, 1fr);
|
|
gap: var(--space-xs) var(--space-md);
|
|
align-items: center;
|
|
margin: 0 var(--space-xl) var(--space-md);
|
|
padding: var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--card);
|
|
}
|
|
|
|
.memory-file-summary span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: var(--space-xs) var(--space-sm);
|
|
border-radius: var(--radius-pill);
|
|
color: var(--color-info);
|
|
background: color-mix(in srgb, var(--color-info) 12%, transparent);
|
|
font-size: var(--font-size-2xs);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.memory-file-summary strong {
|
|
min-width: 0;
|
|
overflow-wrap: anywhere;
|
|
color: var(--text);
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.memory-file-summary small {
|
|
grid-column: 2;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
.memory-editor-frame {
|
|
min-height: 50vh;
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* === Auth Provider Cards === */
|
|
|
|
/* Horizontal gutter wrapper for the auth section body. Tighter than the
|
|
default form-group gutter so each provider card gets more horizontal room
|
|
— the cards are dense enough that the wider --space-xl gutter wasted
|
|
visible width without any visual benefit. */
|
|
.auth-panel-body {
|
|
padding-inline: var(--space-md);
|
|
}
|
|
|
|
.auth-section-hint {
|
|
padding: var(--space-md) var(--space-lg);
|
|
margin-bottom: var(--space-md);
|
|
background: var(--bg-tertiary);
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-muted);
|
|
border-left: 3px solid var(--text-muted);
|
|
}
|
|
.auth-provider-group {
|
|
margin-bottom: calc(var(--space-sm) + var(--space-xs) / 2);
|
|
}
|
|
.auth-group-label {
|
|
font-size: var(--font-size-2xs);
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
color: var(--text-muted);
|
|
margin-bottom: var(--space-xs);
|
|
padding: 0 4px;
|
|
}
|
|
.auth-provider-card {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
margin-bottom: var(--space-xs);
|
|
overflow: hidden;
|
|
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
|
|
}
|
|
.auth-provider-card:hover {
|
|
border-color: var(--text-dim);
|
|
}
|
|
.auth-provider-card--authenticated {
|
|
border-color: color-mix(in srgb, var(--color-success) 40%, var(--border));
|
|
background: color-mix(in srgb, var(--color-success) 5%, var(--surface));
|
|
}
|
|
.auth-provider-card--authenticated:hover {
|
|
border-color: color-mix(in srgb, var(--color-success) 60%, var(--border));
|
|
}
|
|
.auth-provider-header {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: var(--space-sm) var(--space-md);
|
|
gap: 12px;
|
|
}
|
|
.auth-provider-info {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
.auth-provider-info strong {
|
|
font-weight: 500;
|
|
color: var(--text);
|
|
}
|
|
.auth-provider-icon-slot {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
inline-size: calc(var(--space-md) + var(--space-xs) * 2);
|
|
block-size: calc(var(--space-md) + var(--space-xs) * 2);
|
|
flex-shrink: 0;
|
|
}
|
|
.auth-provider-icon-slot .provider-icon {
|
|
display: inline-flex;
|
|
}
|
|
.auth-hint {
|
|
display: block;
|
|
padding: 12px 4px 0;
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
border-top: 1px solid var(--border);
|
|
margin-top: 8px;
|
|
}
|
|
.auth-apikey-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
align-items: flex-end;
|
|
flex-shrink: 0;
|
|
}
|
|
.auth-apikey-input-row {
|
|
display: flex;
|
|
gap: 6px;
|
|
align-items: center;
|
|
}
|
|
.auth-apikey-input {
|
|
background: var(--surface-1);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
color: var(--text);
|
|
padding: 6px 10px;
|
|
font-size: var(--font-size-sm);
|
|
width: 180px;
|
|
font-family: monospace;
|
|
}
|
|
.auth-apikey-input:focus {
|
|
outline: none;
|
|
border-color: var(--todo);
|
|
box-shadow: var(--focus-ring);
|
|
}
|
|
.auth-apikey-input:disabled {
|
|
opacity: 0.6;
|
|
}
|
|
.auth-apikey-progress {
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
padding-right: 4px;
|
|
}
|
|
.auth-apikey-error {
|
|
font-size: var(--font-size-2xs);
|
|
color: var(--color-error);
|
|
padding-right: 4px;
|
|
}
|
|
.auth-provider-actions-row {
|
|
display: flex;
|
|
width: 100%;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.auth-device-code-panel {
|
|
width: 100%;
|
|
margin-top: var(--space-sm);
|
|
padding: var(--space-sm);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--surface) 85%, transparent);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.auth-device-code-pill {
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-sm);
|
|
letter-spacing: var(--space-xs);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border-radius: var(--radius-pill);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
background: var(--card);
|
|
color: var(--text);
|
|
display: flex;
|
|
align-self: stretch;
|
|
}
|
|
|
|
/* === Key Hint === */
|
|
.auth-key-hint {
|
|
font-family: var(--font-mono);
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
display: inline-block;
|
|
margin-top: var(--space-xs);
|
|
user-select: none;
|
|
}
|
|
/* === Model Lane Status Badges === */
|
|
.settings-lane-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 2px 8px;
|
|
border-radius: 10px;
|
|
font-size: var(--font-size-2xs);
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.settings-lane-badge--override {
|
|
background-color: color-mix(in srgb, var(--todo) 20%, transparent);
|
|
color: var(--todo);
|
|
}
|
|
|
|
.settings-lane-badge--inherited {
|
|
background-color: color-mix(in srgb, var(--text-muted) 15%, transparent);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsScopeGrouping 2026-06-25-10:42:
|
|
Mobile settings must make clear which controls are global (all projects) vs project-scoped; group scheduling fields under labeled Global/This-project subheadings with a scope badge so operators don't mistake the global concurrency cap for a per-project setting.
|
|
The header row wraps so the badge drops below the heading on narrow widths instead of overflowing.
|
|
*/
|
|
.settings-scope-group {
|
|
margin-top: var(--space-md);
|
|
}
|
|
|
|
.settings-scope-group-header {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-scope-group-header .settings-section-heading {
|
|
padding-bottom: 0;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.settings-scope-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 2px 8px;
|
|
border-radius: 10px;
|
|
font-size: var(--font-size-2xs);
|
|
font-weight: 500;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.settings-scope-badge--global {
|
|
background-color: color-mix(in srgb, var(--accent, #4a90e2) 18%, transparent);
|
|
color: var(--accent, #4a90e2);
|
|
}
|
|
|
|
.settings-scope-badge--project {
|
|
background-color: color-mix(in srgb, var(--text-muted) 15%, transparent);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.settings-scope-caption {
|
|
display: block;
|
|
margin: var(--space-xs) 0 var(--space-md);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-xs);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.settings-description {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-dim);
|
|
padding-inline: var(--space-xl);
|
|
margin-block: 0 var(--space-md);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.settings-token-cap-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-model-lane-label-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
margin-bottom: var(--space-xs);
|
|
}
|
|
|
|
.settings-model-lane-control-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-model-lane-control-main {
|
|
flex: 1;
|
|
}
|
|
|
|
.settings-model-lane-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-md) var(--space-xl) var(--space-lg);
|
|
margin-block: var(--space-sm) var(--space-lg);
|
|
border-bottom: var(--btn-border-width) solid var(--border);
|
|
}
|
|
|
|
.settings-error {
|
|
color: var(--color-error);
|
|
}
|
|
|
|
.settings-summarization-actions {
|
|
justify-content: flex-start;
|
|
}
|
|
/* === Settings: Model Presets === */
|
|
.settings-model-presets {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-md);
|
|
}
|
|
|
|
.settings-preset-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: var(--space-md);
|
|
padding: var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--card);
|
|
}
|
|
|
|
.settings-preset-item-meta {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-preset-item-meta strong {
|
|
color: var(--text);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.settings-preset-summary {
|
|
font-size: var(--font-size-xs);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.settings-preset-item-actions {
|
|
display: flex;
|
|
gap: var(--space-xs);
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.settings-preset-actions {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.settings-preset-editor {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-md);
|
|
padding: var(--space-md);
|
|
border: var(--btn-border-width) solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--surface) 85%, var(--card));
|
|
}
|
|
|
|
.settings-preset-editor-fields {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-editor .form-group {
|
|
padding: 0;
|
|
}
|
|
|
|
.settings-preset-editor-actions {
|
|
justify-content: flex-start;
|
|
margin-top: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.settings-preset-auto-select {
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-size-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-size-row {
|
|
padding: 0;
|
|
}
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
/* Settings modal: use the section picker as the only mobile navigation */
|
|
.settings-layout {
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.settings-navigation {
|
|
width: 100%;
|
|
min-width: 0;
|
|
flex: 0 0 auto;
|
|
border-right: none;
|
|
border-bottom: var(--btn-border-width) solid var(--border);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.settings-nav-resize-handle {
|
|
display: none;
|
|
}
|
|
|
|
.settings-search {
|
|
padding: var(--space-sm) var(--space-md) var(--space-sm);
|
|
}
|
|
|
|
.settings-advanced-toggle {
|
|
padding: 0 var(--space-md) var(--space-sm);
|
|
border-bottom: none;
|
|
}
|
|
|
|
/* FNXC:Settings 2026-07-09-12:00: mobile-only collapse-by-default toggle sits in the section picker control row so search and section navigation share one horizontal affordance row. The row starts collapsed and expands to the full search row below the picker on tap. */
|
|
.settings-search-toggle {
|
|
--settings-search-toggle-touch-target: calc(var(--space-lg) + var(--space-lg) + var(--space-xs));
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: var(--settings-search-toggle-touch-target);
|
|
height: var(--settings-search-toggle-touch-target);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.settings-search-row {
|
|
margin-top: var(--space-xs);
|
|
}
|
|
|
|
.settings-search-input-wrap {
|
|
align-items: stretch;
|
|
}
|
|
|
|
.settings-search-input {
|
|
font-size: 16px;
|
|
}
|
|
|
|
/*
|
|
FNXC:Settings 2026-07-09-00:00:
|
|
FN-7752 trims mobile-only Settings section chrome: after removing the visible picker label, use smaller horizontal padding for the picker and internal section text so narrow viewports devote more width to controls without changing desktop/tablet rules.
|
|
|
|
FNXC:Settings 2026-07-10-00:00:
|
|
FN-7785 makes the Settings content pages uniformly tighter at mobile widths. The `.settings-content` scroll container is the primary lever so standalone and embedded Settings share the same reduced gutter, while section wrappers step down to token spacing without changing desktop/tablet rules.
|
|
*/
|
|
.settings-mobile-section-picker {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
padding: var(--space-sm) var(--space-md) var(--space-md);
|
|
border-bottom: none;
|
|
background: var(--surface);
|
|
}
|
|
|
|
.settings-mobile-section-picker-control-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
min-width: 0;
|
|
}
|
|
|
|
.settings-mobile-section-picker select {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.settings-mobile-section-picker .settings-search-empty-hint {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
width: 100%;
|
|
}
|
|
|
|
.settings-mobile-section-picker select:focus-visible {
|
|
outline: none;
|
|
border-color: var(--todo);
|
|
box-shadow: var(--focus-ring-strong);
|
|
}
|
|
|
|
.settings-sidebar {
|
|
display: none;
|
|
}
|
|
|
|
.settings-nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--space-xs);
|
|
border-left: none;
|
|
border-bottom: 2px solid transparent;
|
|
border-radius: var(--radius-md) var(--radius-md) 0 0;
|
|
padding: calc(var(--space-xs) + var(--space-xs) / 2) var(--space-md);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.settings-nav-item:hover {
|
|
border-left-color: transparent;
|
|
border-bottom-color: var(--border);
|
|
}
|
|
|
|
.settings-nav-item.active {
|
|
border-left-color: transparent;
|
|
border-bottom-color: var(--todo);
|
|
}
|
|
|
|
/* Hide group headers on mobile - they don't fit well in horizontal scroll row */
|
|
.settings-group-header {
|
|
display: none;
|
|
}
|
|
|
|
.notification-provider-card {
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
|
|
.notification-provider-header {
|
|
padding: var(--space-sm) var(--space-md);
|
|
}
|
|
|
|
.notification-provider-body {
|
|
padding: var(--space-md);
|
|
}
|
|
|
|
.settings-scope-icon {
|
|
margin-right: 0;
|
|
}
|
|
|
|
.settings-content {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow-x: hidden;
|
|
overflow-y: auto;
|
|
padding: var(--space-md) var(--space-md) var(--space-lg);
|
|
}
|
|
|
|
.settings-content .form-group small {
|
|
overflow-wrap: break-word;
|
|
word-break: normal;
|
|
}
|
|
|
|
.settings-content input,
|
|
.settings-content select,
|
|
.settings-content textarea {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.settings-section-heading {
|
|
padding: var(--space-md) 0 var(--space-sm);
|
|
margin: 0 0 var(--space-sm);
|
|
}
|
|
|
|
.settings-plugins-subsection-toggle {
|
|
display: flex;
|
|
width: 100%;
|
|
padding: 0 var(--space-sm);
|
|
}
|
|
|
|
.settings-plugins-subsection-btn {
|
|
flex: 1;
|
|
min-height: 36px;
|
|
}
|
|
|
|
.settings-plugins-subsection-panel {
|
|
padding-left: var(--space-sm);
|
|
padding-right: var(--space-sm);
|
|
}
|
|
|
|
.form-group {
|
|
padding: 0;
|
|
}
|
|
|
|
|
|
.memory-editor-frame {
|
|
min-height: 65vh;
|
|
}
|
|
|
|
.memory-file-summary {
|
|
grid-template-columns: 1fr;
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.memory-status-message {
|
|
align-items: stretch;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.memory-file-summary span {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.memory-file-summary small {
|
|
grid-column: auto;
|
|
}
|
|
|
|
.settings-description {
|
|
padding: 0;
|
|
}
|
|
|
|
.settings-empty-state {
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.theme-selector {
|
|
padding: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
|
|
.settings-model-lane-actions {
|
|
align-items: stretch;
|
|
flex-direction: column;
|
|
margin-block: var(--space-xs) var(--space-md);
|
|
padding: var(--space-sm) var(--space-sm) var(--space-md);
|
|
}
|
|
|
|
.settings-content .btn,
|
|
.settings-preset-item-actions .btn {
|
|
min-height: 36px;
|
|
}
|
|
|
|
.auth-provider-row {
|
|
flex-wrap: wrap;
|
|
padding: var(--space-sm);
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.auth-section-hint {
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.auth-provider-group {
|
|
margin-bottom: var(--space-sm);
|
|
}
|
|
|
|
.auth-group-label {
|
|
padding: 0 var(--space-sm);
|
|
}
|
|
|
|
.auth-provider-card {
|
|
margin: 0 var(--space-sm) var(--space-sm);
|
|
}
|
|
|
|
/*
|
|
FNXC:SettingsLayout 2026-07-16-12:34:
|
|
On mobile, auth-panel provider cards and their hint and group-label siblings must share the
|
|
Authentication section header's left edge. Remove the stacked panel/card horizontal inset only
|
|
inside .auth-panel-body so Custom Provider cards outside the panel keep their existing gutter.
|
|
*/
|
|
.auth-panel-body {
|
|
padding-inline: 0;
|
|
}
|
|
|
|
.auth-panel-body .auth-section-hint {
|
|
margin: 0 0 var(--space-sm);
|
|
}
|
|
|
|
.auth-panel-body .auth-group-label {
|
|
padding: 0;
|
|
}
|
|
|
|
.auth-panel-body .auth-provider-card {
|
|
margin: 0 0 var(--space-sm);
|
|
}
|
|
|
|
.auth-provider-header {
|
|
flex-wrap: wrap;
|
|
padding: var(--space-sm);
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.auth-provider-header > div:not(.auth-provider-info):not(.auth-apikey-section) {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.auth-provider-actions-row {
|
|
width: 100%;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.auth-device-code-panel {
|
|
width: 100%;
|
|
}
|
|
|
|
.auth-device-code-pill {
|
|
max-width: 100%;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.auth-provider-info {
|
|
width: 100%;
|
|
flex-basis: 100%;
|
|
}
|
|
|
|
.auth-apikey-section {
|
|
width: 100%;
|
|
flex-basis: 100%;
|
|
align-items: flex-end;
|
|
}
|
|
|
|
.auth-apikey-input-row {
|
|
width: 100%;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.auth-apikey-input {
|
|
flex: 1;
|
|
min-width: 120px;
|
|
width: auto;
|
|
}
|
|
|
|
.auth-apikey-input-row .btn {
|
|
flex-shrink: 0;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.auth-hint {
|
|
padding: var(--space-sm) var(--space-sm) 0;
|
|
}
|
|
|
|
/* Settings modal: model preset controls stack cleanly on narrow screens */
|
|
.settings-model-presets {
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-item {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-item-actions {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.settings-preset-editor {
|
|
padding: var(--space-sm);
|
|
}
|
|
|
|
.settings-preset-editor-actions {
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.settings-preset-size-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--space-xs);
|
|
}
|
|
|
|
.settings-node-routing-note {
|
|
padding: var(--space-sm);
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
}
|
|
|
|
/* Node Routing section */
|
|
.settings-node-routing-note {
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
padding: var(--space-md) var(--space-lg);
|
|
margin-top: var(--space-sm);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.settings-node-status {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-xs);
|
|
margin-top: var(--space-sm);
|
|
font-size: var(--font-size-xs);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.auth-advanced-disclosure {
|
|
margin-top: var(--space-md);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-md);
|
|
background: var(--surface);
|
|
}
|
|
|
|
.auth-advanced-disclosure > summary {
|
|
cursor: pointer;
|
|
padding: var(--space-sm) var(--space-md);
|
|
}
|
|
|
|
.auth-advanced-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-sm);
|
|
padding: 0 var(--space-md) var(--space-md);
|
|
}
|
|
|
|
.auth-custom-provider-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.auth-custom-provider-actions {
|
|
display: flex;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
.settings-warning-banner {
|
|
margin-top: var(--space-sm);
|
|
padding: var(--space-sm) var(--space-md);
|
|
border: 1px solid var(--color-warning);
|
|
border-radius: var(--radius-md);
|
|
background: color-mix(in srgb, var(--color-warning) 12%, transparent);
|
|
color: var(--text);
|
|
font-size: var(--font-size-sm);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.settings-warning-banner strong {
|
|
color: var(--color-warning);
|
|
margin-right: var(--space-xs);
|
|
}
|
|
|
|
/* KTD-8: informational note at the bottom of the Node Sync section. */
|
|
.settings-sync-workflow-note {
|
|
margin-top: var(--space-md);
|
|
font-size: var(--font-size-sm);
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.auth-custom-provider-item {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.settings-warning-banner {
|
|
padding: var(--space-sm);
|
|
}
|
|
}
|
|
|
|
/*
|
|
FNXC:DashboardShortcuts 2026-07-04-00:00:
|
|
FN-7553's dedicated Keyboard Shortcuts section groups every action under a category heading and gives each row a press-to-record capture control (input + Record + Clear) instead of the old bare text input. Token spacing/colors only; wraps to a single column under 768px so the record/clear buttons never overflow the row on mobile.
|
|
*/
|
|
.shortcut-category {
|
|
margin-top: var(--space-lg);
|
|
}
|
|
|
|
.shortcut-category:first-of-type {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.shortcut-row {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
padding: var(--space-sm) 0;
|
|
border-bottom: 1px solid var(--color-border);
|
|
}
|
|
|
|
.shortcut-row:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
/*
|
|
FNXC:DashboardShortcuts 2026-07-05-00:00:
|
|
FN-7602 fixes an overlap bug: Record/Clear previously used the icon-only `btn-icon` class
|
|
(line-height:0 + mobile 36px square), which clipped/overlapped their text labels
|
|
("Record"/"Recording…"/"Clear") against the input and each other (IMG_1305). The input
|
|
keeps `flex: 1 1 auto; min-width: 0;` so it shrinks first, while the buttons get
|
|
`flex-shrink: 0; white-space: nowrap;` so their content-sized width (including the longer
|
|
"Recording…" label) is never crushed or allowed to overlap a neighbor on desktop. Below
|
|
768px the row stacks to a column so the buttons sit on their own row under the full-width
|
|
input, still non-overlapping.
|
|
*/
|
|
.shortcut-capture {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.shortcut-capture__input {
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
}
|
|
|
|
.shortcut-capture__input--invalid {
|
|
border-color: var(--color-error);
|
|
}
|
|
|
|
.shortcut-capture__record,
|
|
.shortcut-capture__clear {
|
|
flex-shrink: 0;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.shortcut-capture__record--active {
|
|
color: var(--color-warning);
|
|
}
|
|
|
|
.shortcut-conflict-banner {
|
|
color: var(--color-error);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.shortcut-capture {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.shortcut-capture__input {
|
|
width: 100%;
|
|
}
|
|
}
|