Skip to content

refactor: extract command palette controller and split ContentView#106

Closed
arzafran wants to merge 7 commits into
mainfrom
nr/cv
Closed

refactor: extract command palette controller and split ContentView#106
arzafran wants to merge 7 commits into
mainfrom
nr/cv

Conversation

@arzafran

@arzafran arzafran commented Jul 9, 2026

Copy link
Copy Markdown
Member

What this does

ContentView.swift drops from 13,605 to about 7,772 lines. Command-palette state moves off the view onto a controller, and four self-contained domains get their own files.

Summary

  • CommandPaletteController (ObservableObject) now owns palette state; ContentView holds one @StateObject
  • pure moves: TabItemView.swift (1,965 lines), CommandPaletteSearchEngine.swift (993), FileDropOverlayView.swift (489), SidebarDragDrop.swift (926), SidebarVisuals.swift (1,533)
  • TabItemView moved verbatim: Equatable, precomputed params, and the .equatable() call site untouched (verified)
  • access widenings (private dropped) for cross-file symbols, called out per commit

Remainder of #94 (WindowChrome, SidebarResizer, WorkspaceHandoff extractions) is not in this PR; the issue stays open for it.

Fixes #88. Refs #94.

Test Plan

  • build + build-for-testing green locally
  • CI green

arzafran added 6 commits July 9, 2026 13:51
Move TabItemView and its exclusive helper-view cluster
(SidebarWorkspaceDescriptionText, SidebarMarkdownRenderer,
SidebarMetadataRows/EntryRow/MarkdownBlocks/BlockRow) out of
ContentView.swift into a new file, verbatim.

Access-level widening: TabItemView was file-private (private struct);
widened to internal (dropped the private modifier) because
VerticalTabsSidebar, which stays in ContentView.swift, constructs it
at its ForEach call site. No other behavior change. Equatable
conformance, precomputed let parameters, and the .equatable() call
site are untouched.

Refs #94.
…move)

Move the FileDropOverlayView NSView class out of ContentView.swift
into its own file, verbatim (no access-level changes needed — it was
already internal).

Refs #94.
…anical move)

Move the command-palette fuzzy-search engine (switcher search
metadata/indexer, fuzzy matcher, search corpus entry/result types,
search engine) out of ContentView.swift into its own file, verbatim.
Zero coupling to ContentView state.

Refs #94.
…moves

TabItemView.swift, FileDropOverlayView.swift, and CommandPaletteSearchEngine.swift
now live in their own files, so file-private symbols they depend on (or that
depend on them) that remain in ContentView.swift need at least internal
visibility:

- SidebarDragAutoScrollController, SidebarTabItemSettingsSnapshot,
  SidebarTabDragPayload, BonsplitTabDragPayload, SidebarBonsplitTabDropDelegate,
  SidebarTabDropDelegate, MiddleClickCapture, MiddleClickCaptureView — used by
  the extracted TabItemView.
- coloredCircleImage, debugCommandPaletteTextPreview, the Color(hex:)
  initializer — top-level helpers used by the extracted TabItemView.

FileDropOverlayView.swift needs `import Bonsplit` for dlog() (per CLAUDE.md's
dlog-needs-bonsplit-import note).

No behavior changes — access-level widening only.

Refs #94.
…ls.swift

Consolidate the drag-and-drop domain (DragOverlayRoutingPolicy, drag payload
enums, drop delegates, drop edge/indicator/planner, auto-scroll planner, drag
lifecycle/failsafe types) and the sidebar visual chrome (footer/help-menu/
scrim/blur/material/tint/preset enums, visual-effect/backdrop views, NSColor
extension), both previously fragmented across several non-contiguous locations
in ContentView.swift, into two new files. Done together in one pass since both
extractions needed the same verification cycle.

Access-level widening (dropped `private`, no other behavior change):
- SidebarDragFailsafeMonitor, SidebarExternalDropOverlay — constructed from
  VerticalTabsSidebar / ContentView, which stay in ContentView.swift.
- SidebarFooter, SidebarTopScrim, SidebarScrollViewResolver,
  SidebarScrollViewResolverView, SidebarEmptyArea, ClearScrollBackground,
  DraggableFolderIcon, TitlebarLeadingInsetReader, SidebarTrailingBorder,
  SidebarBackdrop — same reason.

The fileDropOverlayKey/commandPaletteWindowOverlayKey/
tmuxWorkspacePaneWindowOverlayKey associated-object keys and their overlay
container identifiers stay in ContentView.swift (they back
WindowCommandPaletteOverlayController / WindowTmuxWorkspacePaneOverlayController,
not the drag-drop domain) — they only transited SidebarDragDrop.swift briefly
during the mechanical move and were moved back.

Refs #94.
…tate ownership

Move all 34 command-palette-only @State properties plus the 2 @AppStorage
settings (commandPaletteQuery, commandPaletteMode, search corpus/results,
rename/workspace-description drafts, focus-restore targets, usage history,
etc.) off ContentView and onto a new CommandPaletteController: ObservableObject,
as @Published/@AppStorage properties. ContentView keeps exactly one
`@StateObject private var commandPaletteController`.

ContentView's existing (unqualified) call sites — the ~4000 lines of palette
orchestration (registerCommandPaletteHandlers, commandPaletteCommandContributions,
rename/workspace-description flows, focus-restore, usage-history persistence,
the view body) — are preserved unchanged: each former @State name becomes a
thin computed proxy on ContentView that forwards get/set to the controller
(`private var commandPaletteQuery: String { get { commandPaletteController.
commandPaletteQuery } nonmutating set { ... } }`). This is a real ownership
transfer (the controller is the only place these values are stored; ContentView
holds no @State backing them anymore), done without touching the orchestration
method bodies, which keeps this change mechanically verifiable.

5 properties that were bound via SwiftUI's $name projection at their call
sites (commandPaletteQuery, commandPaletteRenameDraft,
commandPaletteWorkspaceDescriptionDraft, commandPaletteWorkspaceDescriptionHeight,
commandPaletteShouldFocusWorkspaceDescriptionEditor) get a matching
`nameBinding: Binding<T>` proxy instead, since computed properties can't
project a $-binding; call sites were updated to use .

The 2 @focusstate properties (isCommandPaletteSearchFocused,
isCommandPaletteRenameFocused) stay on ContentView — @focusstate is a SwiftUI
View-only property wrapper and cannot be hosted on an ObservableObject. This
is the actual reason the file's previous "CV1" extraction attempt called a
full split impossible; it wasn't, once the @Published-storage-plus-proxy
shape is used instead of moving @State verbatim.

Access-level widening (dropped `private`, no other behavior change) for
nested types now referenced from CommandPaletteController.swift (a different
file): CommandPaletteMode, CommandPaletteRestoreFocusTarget,
CommandPaletteTextSelectionBehavior, CommandPaletteMultilineTextEditorRepresentable,
CommandPaletteRenameTarget, CommandPaletteWorkspaceDescriptionTarget (enum-case
associated types must be at least as visible as the enum), and their AppKit
backing views CommandPaletteMultilineTextEditorView / CommandPaletteMultilineTextView
(NSViewRepresentable makeNSView/updateNSView must be at least as visible as
the representable struct).

The ~4000-line orchestration block and the ContentView+CommandPalette.swift
pure helpers were NOT physically relocated onto the controller in this pass —
they continue to compile and run correctly against the controller through the
proxies, but remain textually in ContentView.swift/ContentView+CommandPalette.swift.
Physically moving that method block is left for a follow-up; the state
ownership change (the core ask) is complete and verified.

Refs #88.
@arzafran

arzafran commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

Superseded by #116, which consolidates the seven remaining nuclear-review PRs into one CI run per maintainer request. Every commit from this branch is contained in #116 verbatim.

@arzafran arzafran closed this Jul 9, 2026
arzafran added a commit that referenced this pull request Jul 9, 2026
Nuclear review: consolidated fixes and splits (supersedes #106-#112)
@arzafran arzafran deleted the nr/cv branch July 9, 2026 21:59
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.

Finish the command-palette extraction: 3,900 lines and 30 @State properties never left ContentView

1 participant