Skip to content

Scaffold redesigned Settings: sidebar container behind feature flag#356

Merged
FuJacob merged 2 commits into
mainfrom
feat/settings-redesign-scaffold
May 28, 2026
Merged

Scaffold redesigned Settings: sidebar container behind feature flag#356
FuJacob merged 2 commits into
mainfrom
feat/settings-redesign-scaffold

Conversation

@FuJacob
Copy link
Copy Markdown
Owner

@FuJacob FuJacob commented May 28, 2026

Summary

First PR in the Settings sidebar-redesign stack. Adds the new container view, sidebar list, pane scaffold, placeholder panes, and a feature flag in SettingsCoordinator. Default routes to the legacy SettingsView; flipping defaults write com.jacobfu.tabby cotabbySettingsRedesignEnabled -bool YES (or the cotabbySettingsRedesignEnabled key) routes to the new container. Subsequent PRs replace placeholders with real panes; the final PR in the stack flips the flag default.

Validation

xcodegen generate + xcodebuild -project Cotabby.xcodeproj -scheme Cotabby -destination 'platform=macOS' build CODE_SIGNING_ALLOWED=NO
** BUILD SUCCEEDED **

swiftlint lint --quiet → no violations.

Manual: with the flag flipped, open Settings and confirm the sidebar renders with eight rows under the planned sections, selection persists across reopens, and the title bar updates to "Settings — ". With the flag off, behavior is unchanged.

Linked issues

(none filed)

Risk / rollout notes

  • Default routing unchanged. Every existing user sees the legacy single-form layout until later PRs land and the final PR flips the flag.
  • New window autosave name CotabbySettingsWindowV2 is intentional so the redesigned layout starts from its own default frame rather than inheriting a smaller saved frame from the legacy window.
  • .id(selection) is applied preemptively on the detail pane to work around the documented macOS 14 NavigationSplitView selection bug where the first sidebar tap doesn't always trigger a detail re-render.
  • The SettingsCategory enum includes both engineAndModel (parent overview pane) and appleIntelligence / openSource (sub-rows). The parent pane will own the engine picker when PR 3 lands so the user has a single place to switch engines.
  • xcodegen generate was rerun and the resulting Cotabby.xcodeproj/project.pbxproj is committed.

Greptile Summary

This PR scaffolds the redesigned Settings window behind a UserDefaults feature flag in SettingsCoordinator. The legacy SettingsView remains the default; flipping cotabbySettingsRedesignEnabled routes to the new SettingsContainerView, a NavigationSplitView shell with a categorized sidebar and placeholder detail panes that subsequent PRs will fill in.

  • SettingsCoordinator: adds isRedesignEnabled branch that selects the right hosting controller, initial frame, min size, and autosave name (CotabbySettingsWindowV2) per variant, leaving existing window-reuse and delegate teardown logic untouched.
  • SettingsContainerView / SettingsSidebarView: NavigationSplitView with @AppStorage-persisted selection, syncWindowTitle called unconditionally in onAppear and on every selection change, and .id(selection) as the documented macOS 14 split-view workaround.
  • SettingsCategory / SettingsSidebarSection / SettingsPaneScaffold / PlaceholderPaneView: new enum catalog, section grouping, shared pane chrome with optional callout banner, and a temporary placeholder rendered for every category until real panes land.

Confidence Score: 5/5

Safe to merge. The redesign is fully gated behind a feature flag that defaults to off, so all existing users continue on the legacy settings path until a future PR flips the default.

All changes are additive and behind a UserDefaults flag that is off by default. The legacy window path is unchanged. The new container view, sidebar, pane scaffold, and placeholder panes are all scaffolding that only activates for developers who opt in via defaults write. No existing behavior is affected.

SettingsContainerView.swift — the @AppStorage/@State two-variable sync pattern is worth simplifying before real panes land, but it has no user-visible impact while all panes are placeholders.

Important Files Changed

Filename Overview
Cotabby/App/Coordinators/SettingsCoordinator.swift Adds feature-flag branch (isRedesignEnabled) to route between legacy SettingsView and new SettingsContainerView, with matching frame/minSize/autosave name per variant. Clean refactor; existing window-reuse logic and delegate teardown unchanged.
Cotabby/UI/Settings/SettingsContainerView.swift New root view for redesigned Settings; uses NavigationSplitView + AppStorage-persisted selection. A redundant @State/@AppStorage two-variable pattern is used where a single @AppStorage on SettingsCategory directly would suffice, causing the detail pane to render once with .general before onAppear corrects it to the stored category.
Cotabby/UI/Settings/SettingsSidebarView.swift Renders the sidebar List grouped by SettingsSidebarSection. Uses SettingsSidebarSection.allCases ordering and .tag() for selection, with visual-only leading padding for sub-rows. Straightforward and correct.
Cotabby/UI/Settings/SettingsCategory.swift Defines the SettingsCategory and SettingsSidebarSection enums that drive sidebar structure. All cases covered, section mapping matches PR description, isSubRow correctly targets only engine sub-rows.
Cotabby/UI/Settings/Components/SettingsPaneScaffold.swift Reusable chrome for detail panes: scrollable Form with optional inline callout banner. Well-structured with clear tone/styling split; no issues.
Cotabby/UI/Settings/Panes/PlaceholderPaneView.swift Temporary stand-in pane rendered for all SettingsCategory cases; intentional scaffold until subsequent PRs replace each placeholder. No issues.

Sequence Diagram

sequenceDiagram
    participant User
    participant SettingsCoordinator
    participant NSWindow
    participant SettingsContainerView
    participant SettingsSidebarView
    participant AppStorage

    User->>SettingsCoordinator: showSettings()
    SettingsCoordinator->>SettingsCoordinator: isRedesignEnabled?
    alt redesign flag ON
        SettingsCoordinator->>NSWindow: create(frame: 960x680, autosave: V2)
        SettingsCoordinator->>SettingsContainerView: NSHostingController
    else redesign flag OFF
        SettingsCoordinator->>NSWindow: create(frame: 700x620, autosave: legacy)
        SettingsCoordinator->>NSWindow: SettingsView (legacy)
    end
    NSWindow->>User: makeKeyAndOrderFront

    User->>SettingsContainerView: onAppear
    AppStorage-->>SettingsContainerView: storedCategoryRawValue
    SettingsContainerView->>SettingsContainerView: "selection = stored ?? .general"
    SettingsContainerView->>NSWindow: syncWindowTitle(selection)

    User->>SettingsSidebarView: tap row
    SettingsSidebarView->>SettingsContainerView: $selection binding updated
    SettingsContainerView->>AppStorage: persist rawValue
    SettingsContainerView->>NSWindow: syncWindowTitle(newValue)
    SettingsContainerView->>SettingsContainerView: detailPane re-renders (.id workaround)
Loading

Fix All in Codex Fix All in Claude Code

Reviews (2): Last reviewed commit: "Address Greptile feedback: initial title..." | Re-trigger Greptile

Comment thread Cotabby/UI/Settings/SettingsContainerView.swift
Comment thread Cotabby/UI/Settings/SettingsContainerView.swift
@FuJacob FuJacob force-pushed the feat/settings-redesign-scaffold branch from 0a7dc0d to fb8c68b Compare May 28, 2026 10:28
@FuJacob FuJacob merged commit db9d899 into main May 28, 2026
4 checks passed
@FuJacob FuJacob deleted the feat/settings-redesign-scaffold branch May 28, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant