From 9b22927f3cd43c01506e9bc4fc5cce83ea8cfb2a Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 10 Jul 2026 23:44:45 -0700 Subject: [PATCH] feat(settings): simplify section details and layout --- .changeset/calm-settings-navigation.md | 4 +- .../settings-advanced/desktop-advanced.png | 4 +- .../settings-advanced/desktop-basic.png | 4 +- .../settings-advanced/mobile-advanced.png | 4 +- .../settings-advanced/mobile-basic.png | 4 +- .../app/components/SettingsModal.css | 176 +++++++++++++++++- .../app/components/SettingsModal.tsx | 6 +- .../__tests__/SettingsModal.general.test.tsx | 9 +- .../settings/sections/AppearanceSection.tsx | 4 +- 9 files changed, 195 insertions(+), 20 deletions(-) diff --git a/.changeset/calm-settings-navigation.md b/.changeset/calm-settings-navigation.md index c2e943425c..dbbe4b0d91 100644 --- a/.changeset/calm-settings-navigation.md +++ b/.changeset/calm-settings-navigation.md @@ -2,6 +2,6 @@ "@runfusion/fusion": minor --- -summary: Add a persistent Advanced settings toggle that keeps uncommon Settings sections hidden by default. +summary: Add a persistent Advanced settings toggle that keeps uncommon Settings sections and controls hidden by default. category: feature -dev: The browser-local disclosure applies to desktop, mobile, and Settings search without changing saved settings. +dev: The browser-local disclosure applies to navigation, search, and field-level controls without changing saved settings. diff --git a/docs/screenshots/settings-advanced/desktop-advanced.png b/docs/screenshots/settings-advanced/desktop-advanced.png index 121a43d4c6..846db46146 100644 --- a/docs/screenshots/settings-advanced/desktop-advanced.png +++ b/docs/screenshots/settings-advanced/desktop-advanced.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:674d24e02998d9ccdf85ffbc2dc8dc4fa1c070964ef4096b33aed8f8e3d56459 -size 189060 +oid sha256:b5b6ca940b18f3c99bcd33b5cf5965e0ea2335c6c8c4af145fe96398c7b9e762 +size 183359 diff --git a/docs/screenshots/settings-advanced/desktop-basic.png b/docs/screenshots/settings-advanced/desktop-basic.png index dac9349202..f7ecd67d1f 100644 --- a/docs/screenshots/settings-advanced/desktop-basic.png +++ b/docs/screenshots/settings-advanced/desktop-basic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b2d0d6d3bddc2f06f178658b6162a088f73154c86b1f3d515728ac0f8169673 -size 182222 +oid sha256:3acb4caab9fcbd284ac9c2e307b39d0e375e1b2a7b5426eae486d7fd5dc47c27 +size 169598 diff --git a/docs/screenshots/settings-advanced/mobile-advanced.png b/docs/screenshots/settings-advanced/mobile-advanced.png index 7c27793aa7..0bea7e7a62 100644 --- a/docs/screenshots/settings-advanced/mobile-advanced.png +++ b/docs/screenshots/settings-advanced/mobile-advanced.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:429955254d3ee2cac2e0b40016c158e00d37034488b69b2cb0d4411231c3354f -size 258818 +oid sha256:3bb749fca2154fcf081dab21c54fbb93280303bc325455392c83b9d16d72502e +size 233320 diff --git a/docs/screenshots/settings-advanced/mobile-basic.png b/docs/screenshots/settings-advanced/mobile-basic.png index 189e07c840..998fa39a15 100644 --- a/docs/screenshots/settings-advanced/mobile-basic.png +++ b/docs/screenshots/settings-advanced/mobile-basic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b9441fcaa82f557113c685622b792f1a8c898971b2709f256ba6e5f9c1dd945 -size 258732 +oid sha256:6ed9dc75baaac936b7476e3cf2fec34914de19d79f5febd44ec4e9f8e8847318 +size 233318 diff --git a/packages/dashboard/app/components/SettingsModal.css b/packages/dashboard/app/components/SettingsModal.css index f92152d2f9..88b0f5261e 100644 --- a/packages/dashboard/app/components/SettingsModal.css +++ b/packages/dashboard/app/components/SettingsModal.css @@ -44,6 +44,24 @@ margin-left: auto; margin-right: var(--space-sm); } + + /* Keep the mobile title readable by reducing promotional header actions to their already-labeled icons. */ + .settings-header-actions .settings-github-star-btn__count { + display: none; + } + + .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] */ @@ -624,8 +642,8 @@ Fix the invariant for BOTH presentations (standalone modal + embedded SettingsVi } .settings-navigation { - width: calc(var(--space-xl) * 7 + var(--space-sm)); - min-width: calc(var(--space-xl) * 7 + var(--space-sm)); + width: calc(var(--space-xl) * 10 + var(--space-sm)); + min-width: calc(var(--space-xl) * 10 + var(--space-sm)); border-right: var(--btn-border-width) solid var(--border); display: flex; flex-direction: column; @@ -822,6 +840,72 @@ The Advanced settings preference is a navigation-level disclosure, so keep it vi 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; + 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 > :is(.settings-scope-banner, .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: 0.95rem; + margin-bottom: var(--space-md); +} + +.settings-content h5.settings-section-heading { + font-size: 0.82rem; +} + +.settings-content .form-group { + padding-inline: 0; + margin-top: var(--space-md); +} + +.settings-content .form-group label:not(.checkbox-label) { + font-size: 0.72rem; + line-height: 1.35; +} + +.settings-content .checkbox-label { + min-height: 32px; + line-height: 1.35; +} + +.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; +} + +.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); @@ -862,6 +946,86 @@ The Advanced settings preference is a navigation-level disclosure, so keep it vi 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; } @@ -2424,7 +2588,7 @@ The header row wraps so the badge drops below the heading on narrow widths inste min-height: 0; overflow-x: hidden; overflow-y: auto; - padding: var(--space-sm) var(--space-sm) var(--space-md); + padding: var(--space-md) var(--space-md) var(--space-lg); } .settings-content .form-group small { @@ -2439,7 +2603,7 @@ The header row wraps so the badge drops below the heading on narrow widths inste } .settings-section-heading { - padding: var(--space-md) var(--space-sm) var(--space-sm); + padding: var(--space-md) 0 var(--space-sm); margin: 0 0 var(--space-sm); } @@ -2460,7 +2624,7 @@ The header row wraps so the badge drops below the heading on narrow widths inste } .form-group { - padding: 0 var(--space-sm); + padding: 0; } .settings-scope-banner { @@ -2492,7 +2656,7 @@ The header row wraps so the badge drops below the heading on narrow widths inste } .settings-description { - padding: 0 var(--space-sm); + padding: 0; } .settings-empty-state { diff --git a/packages/dashboard/app/components/SettingsModal.tsx b/packages/dashboard/app/components/SettingsModal.tsx index 41ddfb9c20..587edd98eb 100644 --- a/packages/dashboard/app/components/SettingsModal.tsx +++ b/packages/dashboard/app/components/SettingsModal.tsx @@ -3895,7 +3895,11 @@ export function SettingsModal({ )} -
+
{hasSettingsSearchResults ? renderSectionFields() : (

{t("settings.search.noResults", "No settings sections match \"{{query}}\".", { query: settingsSearchQuery.trim() })}

diff --git a/packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx b/packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx index 1737850431..8e9620199b 100644 --- a/packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx +++ b/packages/dashboard/app/components/__tests__/SettingsModal.general.test.tsx @@ -1,4 +1,4 @@ -import { describe, it, expect, vi } from "vitest"; +import { beforeEach, describe, it, expect, vi } from "vitest"; import { render, screen, fireEvent, waitFor, within, cleanup } from "@testing-library/react"; import path from "path"; import { SettingsModal } from "../SettingsModal"; @@ -238,11 +238,13 @@ describe("SettingsModal", () => { const toggle = screen.getByRole("checkbox", { name: "Advanced settings" }); expect(toggle).not.toBeChecked(); + expect(document.querySelector(".settings-content")).toHaveAttribute("data-show-advanced", "false"); expect(screen.queryByRole("button", { name: /^Node Sync$/ })).not.toBeInTheDocument(); expect(screen.queryByRole("button", { name: /^Experimental Features$/ })).not.toBeInTheDocument(); expect(screen.getByRole("button", { name: /^Appearance$/ })).toBeInTheDocument(); await settingsModalUser.click(toggle); + expect(document.querySelector(".settings-content")).toHaveAttribute("data-show-advanced", "true"); expect(localStorage.getItem("fusion:settings:show-advanced")).toBe("true"); expect(screen.getByRole("button", { name: /^Node Sync$/ })).toBeInTheDocument(); expect(screen.getByRole("button", { name: /^Experimental Features$/ })).toBeInTheDocument(); @@ -550,6 +552,7 @@ describe("SettingsModal", () => { it("persists the legacy sibling branch rename escape hatch in worktree settings", async () => { renderModal(); await waitForSettingsModalReady(); + await settingsModalUser.click(screen.getByRole("checkbox", { name: "Advanced settings" })); await settingsModalUser.click(screen.getByRole("button", { name: /^Worktrees$/ })); @@ -617,6 +620,10 @@ describe("SettingsModal", () => { }); describe("Global General", () => { + beforeEach(() => { + localStorage.setItem("fusion:settings:show-advanced", "true"); + }); + // Read-only default-render assertions are merged into one rendered // instance to avoid re-rendering the full modal per pure-display check. it("renders default global logging fields, helper text, and tracking repo control", async () => { diff --git a/packages/dashboard/app/components/settings/sections/AppearanceSection.tsx b/packages/dashboard/app/components/settings/sections/AppearanceSection.tsx index f32e4c3944..3b748b8678 100644 --- a/packages/dashboard/app/components/settings/sections/AppearanceSection.tsx +++ b/packages/dashboard/app/components/settings/sections/AppearanceSection.tsx @@ -55,14 +55,14 @@ export function AppearanceSection({ scopeBanner, form, setForm, themeMode, color
{/* FNXC:TaskDetailActivityFirst 2026-06-30-23:59: The project setting is opt-in because task details now default to Activity-first; explicit Activity/Chat/Logs links keep their destination regardless of this checkbox. */} {t("settings.appearance.taskDetailChatFirstHelp", "Off by default: task details list Activity first and omitted non-done opens land on Activity. Turn on to restore Chat-first order/default; explicit Chat links still work either way.")}
{t("settings.appearance.suppressTheLdquoNeedsYourInputRdquoBanner", " Suppress the “needs your input” banner that appears when AI sessions are awaiting input or have failed. ")}