From 41a8302a0d7dbf006b195de8f956191a5a5a451f Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Fri, 14 Aug 2026 00:36:06 -0700 Subject: [PATCH] refactor(sidebar): group worktree-list files by domain Follow-up to #14465 / #14467. Keep the landed extract and reorganize the flat worktree-list dump into drag/, headers/, reveal/, rows/, scroll/, and viewport/. Fold tiny modules into their owners, move leftover sidebar-root files into the module, and retarget imports and source-path tests. Layout-only; no behavior change. --- config/max-lines-baseline.txt | 4 +- config/reliability-gates.jsonc | 10 ++-- .../worktree/workspace-pr-status-groups.ts | 2 +- .../src/components/WorktreeJumpPalette.tsx | 2 +- .../cmd-j/palette-filter-options.ts | 2 +- .../cmd-j/palette-project-results.ts | 2 +- ...ver-reveal-touch-action-visibility.test.ts | 3 + .../AiVaultSessionVirtualList.tsx | 2 +- .../FolderWorkspaceWorktreesPanel.test.tsx | 2 +- .../FolderWorkspaceWorktreesPanel.tsx | 2 +- .../right-sidebar/ai-vault-virtual-rows.ts | 2 +- .../sidebar/SidebarGroupByToggle.test.tsx | 2 +- .../sidebar/SidebarGroupByToggle.tsx | 2 +- .../WorktreeList.lineage-child-card.test.ts | 2 +- ...ktreeList.lineage-child-real-card.test.tsx | 2 +- .../src/components/sidebar/WorktreeList.tsx | 14 ++--- .../empty-project-placeholder-repos.ts | 2 +- .../sidebar/host-section-rows.test.ts | 2 +- .../components/sidebar/host-section-rows.ts | 2 +- .../imported-worktrees-card-candidates.ts | 2 +- ...new-external-worktrees-inbox-candidates.ts | 2 +- .../sidebar/project-group-header-dom.test.ts | 4 +- .../sidebar/project-group-header-drop.test.ts | 2 +- .../sidebar/project-group-header-drop.ts | 2 +- .../sidebar/project-header-drop.test.ts | 2 +- .../components/sidebar/project-header-drop.ts | 2 +- .../rendered-sidebar-worktree-order.ts | 6 +- .../sidebar/worktree-card-presentation.tsx | 2 +- .../components/sidebar/worktree-drag-units.ts | 2 +- .../worktree-header-section-boundaries.ts | 2 +- .../sidebar/worktree-keyboard-cycle.test.ts | 4 +- .../sidebar/worktree-keyboard-cycle.ts | 2 +- .../drop-commit-context.ts} | 14 ++--- .../groups.ts} | 6 +- .../pointer-commit.ts} | 24 +++----- .../pointer-flush.ts} | 15 ++--- .../row-state.ts} | 6 +- .../status-target.ts} | 13 ++--- .../use-document-drop.ts} | 10 ++-- .../use-header-drag.ts} | 28 ++++----- .../use-lineage-drop-commit.ts} | 16 +++--- .../use-native-autoscroll.ts} | 12 ++-- .../use-native-drag.ts} | 20 +++---- .../use-pointer-autoscroll.ts} | 8 +-- .../use-pointer-drag.ts} | 31 +++++----- .../use-pointer-window-events.ts} | 12 ++-- .../use-runtime.ts} | 12 ++-- .../use-session.ts} | 24 ++++---- .../use-status-row-drag.ts} | 16 +++--- .../FolderPathStatusIndicator.tsx | 2 +- .../{ => headers}/HostSectionHeader.tsx | 4 +- .../SectionHeader.tsx} | 30 +++++----- .../event-guards.ts} | 2 +- .../project-group-header-actions.tsx | 8 +-- .../repo-header-project-actions.tsx | 31 +++++++--- .../imported-worktrees-card-visibility.ts | 9 --- .../{worktree-option-dom.ts => option-dom.ts} | 0 .../worktree-list/render-row-item-rows.ts | 13 ----- .../ancestors.ts} | 8 +-- .../reveal/folder-reveal.test.ts} | 12 ++-- .../reveal/folder-reveal.ts} | 12 ++-- .../mounted-row.ts} | 4 +- .../pending-inputs.ts} | 24 ++++---- .../use-highlight.ts} | 0 .../use-pending.ts} | 14 ++--- .../use-requests.ts} | 8 +-- .../folder-row.tsx} | 28 ++++----- .../rows/groups.test.ts} | 26 ++++----- .../rows/groups.ts} | 41 ++++++------- .../rows/imported-rows.test.ts} | 14 ++--- .../rows/indentation.test.ts} | 2 +- .../rows/indentation.ts} | 0 .../item-row.tsx} | 18 +++--- .../notice-rows.tsx} | 22 ++++--- .../render-row-lookup.ts} | 8 +-- .../{ => rows}/renderable-rows.ts | 18 ++++-- .../virtual-row-context.ts} | 30 +++++----- .../virtual-row-dispatch.tsx} | 31 +++++----- .../scroll/hard-scroll-up.test.ts} | 2 +- .../scroll/hard-scroll-up.ts} | 0 .../scroll/scroll-adjustment.test.ts} | 10 ++-- .../tuning.ts} | 5 ++ .../scroll/use-scroll-to-top.test.ts} | 4 +- .../scroll/use-scroll-to-top.test.tsx} | 2 +- .../scroll/use-scroll-to-top.ts} | 2 +- .../use-suppression.ts} | 6 +- .../scroll/visible-refresh.test.ts} | 2 +- .../worktree-list/sidebar-editable-target.ts | 19 ------- .../stable-empty-store-slices.ts | 9 --- .../use-agent-send-target-worktree.ts | 15 ++--- .../use-effective-collapsed-groups.ts | 2 +- .../use-sidebar-host-visible-scope.ts | 2 +- .../worktree-list/use-sidebar-section-rows.ts | 2 +- .../use-sidebar-worktree-selection.ts | 2 +- .../use-sidebar-worktree-sort-order.ts | 2 +- ...s-mutations.ts => use-status-mutations.ts} | 2 +- .../use-visible-sidebar-worktrees.ts | 3 +- .../VirtualizedWorktreeViewport.tsx | 57 +++++++++---------- .../active-descendant-option.ts | 12 ++-- .../viewport/host-filtering.test.ts} | 6 +- .../viewport/host-filtering.ts} | 8 +-- .../viewport/review-cache-inputs.test.ts} | 4 +- .../viewport/review-cache-inputs.ts} | 4 +- .../viewport/sticky-headers.test.ts} | 12 ++-- .../use-active-row.ts} | 8 +-- .../use-folder-path-statuses.ts} | 8 +-- .../use-group-toggle.ts} | 2 +- .../use-keyboard.ts} | 33 ++++++++--- .../use-row-measurement.ts} | 8 +-- .../use-virtualizer.ts} | 8 +-- .../use-visible-review-refresh.ts} | 10 ++-- .../viewport-props.ts} | 24 ++++---- .../viewport/virtual-rows.test.ts} | 4 +- .../viewport/virtual-rows.ts} | 18 +++++- .../virtual-row-dom-attributes.ts | 15 ----- .../worktree-visibility-menu-label.ts | 18 ------ .../worktree-visible-refresh-listener.ts | 4 -- .../sidebar/worktree-sidebar-reveal.ts | 2 +- .../worktree-sidebar-row-preference.ts | 2 +- 119 files changed, 553 insertions(+), 586 deletions(-) rename src/renderer/src/components/sidebar/worktree-list/{worktree-drop-commit-context.ts => drag/drop-commit-context.ts} (74%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-drag-groups.ts => drag/groups.ts} (92%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-pointer-drag-commit.ts => drag/pointer-commit.ts} (87%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-pointer-drag-flush.ts => drag/pointer-flush.ts} (92%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-row-drag-state.ts => drag/row-state.ts} (94%) rename src/renderer/src/components/sidebar/worktree-list/{pointer-drop-status-target.ts => drag/status-target.ts} (80%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-document-drop.ts => drag/use-document-drop.ts} (90%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-sidebar-header-drag.ts => drag/use-header-drag.ts} (89%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-lineage-drop-commit.ts => drag/use-lineage-drop-commit.ts} (86%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-native-drag-autoscroll.ts => drag/use-native-autoscroll.ts} (89%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-native-drag.ts => drag/use-native-drag.ts} (90%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-pointer-drag-autoscroll.ts => drag/use-pointer-autoscroll.ts} (90%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-pointer-drag.ts => drag/use-pointer-drag.ts} (86%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-pointer-drag-window-events.ts => drag/use-pointer-window-events.ts} (86%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-drag-runtime.ts => drag/use-runtime.ts} (92%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-drag-session.ts => drag/use-session.ts} (92%) rename src/renderer/src/components/sidebar/worktree-list/{use-workspace-status-row-drag.ts => drag/use-status-row-drag.ts} (89%) rename src/renderer/src/components/sidebar/worktree-list/{ => headers}/FolderPathStatusIndicator.tsx (95%) rename src/renderer/src/components/sidebar/worktree-list/{ => headers}/HostSectionHeader.tsx (97%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-section-header-row.tsx => headers/SectionHeader.tsx} (94%) rename src/renderer/src/components/sidebar/worktree-list/{repo-header-event-guards.ts => headers/event-guards.ts} (93%) rename src/renderer/src/components/sidebar/worktree-list/{ => headers}/project-group-header-actions.tsx (93%) rename src/renderer/src/components/sidebar/worktree-list/{ => headers}/repo-header-project-actions.tsx (89%) delete mode 100644 src/renderer/src/components/sidebar/worktree-list/imported-worktrees-card-visibility.ts rename src/renderer/src/components/sidebar/worktree-list/{worktree-option-dom.ts => option-dom.ts} (100%) delete mode 100644 src/renderer/src/components/sidebar/worktree-list/render-row-item-rows.ts rename src/renderer/src/components/sidebar/worktree-list/{sidebar-row-reveal-ancestors.ts => reveal/ancestors.ts} (93%) rename src/renderer/src/components/sidebar/{worktree-list-folder-reveal.test.ts => worktree-list/reveal/folder-reveal.test.ts} (88%) rename src/renderer/src/components/sidebar/{worktree-list-folder-reveal.ts => worktree-list/reveal/folder-reveal.ts} (79%) rename src/renderer/src/components/sidebar/worktree-list/{mounted-row-reveal.ts => reveal/mounted-row.ts} (91%) rename src/renderer/src/components/sidebar/worktree-list/{pending-sidebar-reveal-inputs.ts => reveal/pending-inputs.ts} (80%) rename src/renderer/src/components/sidebar/worktree-list/{use-sidebar-reveal-highlight.ts => reveal/use-highlight.ts} (100%) rename src/renderer/src/components/sidebar/worktree-list/{use-pending-sidebar-reveal.ts => reveal/use-pending.ts} (95%) rename src/renderer/src/components/sidebar/worktree-list/{use-sidebar-reveal-requests.ts => reveal/use-requests.ts} (92%) rename src/renderer/src/components/sidebar/worktree-list/{folder-workspace-virtual-row.tsx => rows/folder-row.tsx} (82%) rename src/renderer/src/components/sidebar/{worktree-list-groups.test.ts => worktree-list/rows/groups.test.ts} (99%) rename src/renderer/src/components/sidebar/{worktree-list-groups.ts => worktree-list/rows/groups.ts} (97%) rename src/renderer/src/components/sidebar/{worktree-list-imported-rows.test.ts => worktree-list/rows/imported-rows.test.ts} (89%) rename src/renderer/src/components/sidebar/{worktree-list-indentation.test.ts => worktree-list/rows/indentation.test.ts} (99%) rename src/renderer/src/components/sidebar/{worktree-list-indentation.ts => worktree-list/rows/indentation.ts} (100%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-list-item-row.tsx => rows/item-row.tsx} (94%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-notice-virtual-rows.tsx => rows/notice-rows.tsx} (81%) rename src/renderer/src/components/sidebar/worktree-list/{render-row-worktree-lookup.ts => rows/render-row-lookup.ts} (90%) rename src/renderer/src/components/sidebar/worktree-list/{ => rows}/renderable-rows.ts (64%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-virtual-row-context.ts => rows/virtual-row-context.ts} (85%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-virtual-row-dispatch.tsx => rows/virtual-row-dispatch.tsx} (90%) rename src/renderer/src/components/sidebar/{worktree-list-hard-scroll-up.test.ts => worktree-list/scroll/hard-scroll-up.test.ts} (99%) rename src/renderer/src/components/sidebar/{worktree-list-hard-scroll-up.ts => worktree-list/scroll/hard-scroll-up.ts} (100%) rename src/renderer/src/components/sidebar/{worktree-list-scroll-adjustment.test.ts => worktree-list/scroll/scroll-adjustment.test.ts} (96%) rename src/renderer/src/components/sidebar/worktree-list/{worktree-sidebar-scroll-tuning.ts => scroll/tuning.ts} (85%) rename src/renderer/src/components/sidebar/{use-worktree-list-scroll-to-top.test.ts => worktree-list/scroll/use-scroll-to-top.test.ts} (97%) rename src/renderer/src/components/sidebar/{use-worktree-list-scroll-to-top.test.tsx => worktree-list/scroll/use-scroll-to-top.test.tsx} (97%) rename src/renderer/src/components/sidebar/{use-worktree-list-scroll-to-top.ts => worktree-list/scroll/use-scroll-to-top.ts} (99%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-sidebar-scroll-suppression.ts => scroll/use-suppression.ts} (96%) rename src/renderer/src/components/sidebar/{worktree-list-visible-refresh.test.ts => worktree-list/scroll/visible-refresh.test.ts} (95%) delete mode 100644 src/renderer/src/components/sidebar/worktree-list/sidebar-editable-target.ts delete mode 100644 src/renderer/src/components/sidebar/worktree-list/stable-empty-store-slices.ts rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-status-mutations.ts => use-status-mutations.ts} (98%) rename src/renderer/src/components/sidebar/worktree-list/{ => viewport}/VirtualizedWorktreeViewport.tsx (86%) rename src/renderer/src/components/sidebar/worktree-list/{ => viewport}/active-descendant-option.ts (84%) rename src/renderer/src/components/sidebar/{worktree-list-host-filtering.test.ts => worktree-list/viewport/host-filtering.test.ts} (95%) rename src/renderer/src/components/sidebar/{worktree-list-host-filtering.ts => worktree-list/viewport/host-filtering.ts} (95%) rename src/renderer/src/components/sidebar/{worktree-list-review-cache-inputs.test.ts => worktree-list/viewport/review-cache-inputs.test.ts} (95%) rename src/renderer/src/components/sidebar/{worktree-list-review-cache-inputs.ts => worktree-list/viewport/review-cache-inputs.ts} (91%) rename src/renderer/src/components/sidebar/{worktree-list-sticky-headers.test.ts => worktree-list/viewport/sticky-headers.test.ts} (89%) rename src/renderer/src/components/sidebar/worktree-list/{use-primary-active-worktree-row.ts => viewport/use-active-row.ts} (88%) rename src/renderer/src/components/sidebar/worktree-list/{use-folder-workspace-path-status-rows.ts => viewport/use-folder-path-statuses.ts} (94%) rename src/renderer/src/components/sidebar/worktree-list/{use-group-toggle-with-scroll-anchor.ts => viewport/use-group-toggle.ts} (92%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-list-keyboard-navigation.ts => viewport/use-keyboard.ts} (81%) rename src/renderer/src/components/sidebar/worktree-list/{use-virtual-row-measurement-sync.ts => viewport/use-row-measurement.ts} (93%) rename src/renderer/src/components/sidebar/worktree-list/{use-worktree-list-virtualizer.ts => viewport/use-virtualizer.ts} (96%) rename src/renderer/src/components/sidebar/worktree-list/{use-visible-pr-refresh-reporting.ts => viewport/use-visible-review-refresh.ts} (94%) rename src/renderer/src/components/sidebar/worktree-list/{virtualized-worktree-viewport-props.ts => viewport/viewport-props.ts} (84%) rename src/renderer/src/components/sidebar/{worktree-list-virtual-rows.test.ts => worktree-list/viewport/virtual-rows.test.ts} (99%) rename src/renderer/src/components/sidebar/{worktree-list-virtual-rows.ts => worktree-list/viewport/virtual-rows.ts} (94%) delete mode 100644 src/renderer/src/components/sidebar/worktree-list/virtual-row-dom-attributes.ts delete mode 100644 src/renderer/src/components/sidebar/worktree-list/worktree-visibility-menu-label.ts delete mode 100644 src/renderer/src/components/sidebar/worktree-list/worktree-visible-refresh-listener.ts diff --git a/config/max-lines-baseline.txt b/config/max-lines-baseline.txt index c1421ba2499..67bc9216ca9 100644 --- a/config/max-lines-baseline.txt +++ b/config/max-lines-baseline.txt @@ -235,8 +235,8 @@ inline src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx inline src/renderer/src/components/sidebar/WorktreeContextMenu.tsx inline src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts inline src/renderer/src/components/sidebar/use-workspace-kanban-area-selection.ts -inline src/renderer/src/components/sidebar/worktree-list-groups.test.ts -inline src/renderer/src/components/sidebar/worktree-list-groups.ts +inline src/renderer/src/components/sidebar/worktree-list/rows/groups.test.ts +inline src/renderer/src/components/sidebar/worktree-list/rows/groups.ts inline src/renderer/src/components/stats/usage-overview-model.ts inline src/renderer/src/components/status-bar/ResourceUsageStatusSegment.tsx inline src/renderer/src/components/status-bar/StatusBar.tsx diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc index 6d7292f3bf3..84cba84e153 100644 --- a/config/reliability-gates.jsonc +++ b/config/reliability-gates.jsonc @@ -4334,9 +4334,9 @@ "oracle": "Start with Active Server=Local desktop, connect and navigate Windows 2, then reveal a local terminal and assert it succeeds while the persisted preference remains local before and after restart. Repeat transient routing and selected-runtime Add Project operations with exact-ID host decoys and completion reordering. Separately pair the real web client to an isolated headed host, then make its sole still-listed runtime compatibility-blocked and unavailable. Require a null authoritative selection, a disabled host option, and disabled Browse, Clone, and Create actions with no local fallback. Repeat against headless serve. Run the byte-identical selector/action contract on latest main, candidate, and candidate with the eligibility seam disabled.", "commands": [ "pnpm exec vitest run --config config/vitest.config.ts src/main/ipc/settings.test.ts src/main/ipc/runtime-environments.test.ts src/renderer/src/store/slices/settings.test.ts src/renderer/src/store/slices/browser.test.ts src/renderer/src/components/settings/browser-session-host-selection.test.ts src/renderer/src/components/settings/RuntimeEnvironmentsPane.test.ts src/renderer/src/components/status-bar/SshStatusSegment.test.ts src/renderer/src/components/sidebar/use-add-repo-host-selection.test.ts src/renderer/src/hooks/useIpcEvents.test.ts src/renderer/src/web/web-preload-api.test.ts --maxWorkers=1", - "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts --maxWorkers=1", + "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts --maxWorkers=1", "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/use-add-repo-host-selection.test.ts src/renderer/src/components/sidebar/add-repo-browse-authority.test.ts src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/runtime/runtime-server-directory-browser.test.ts src/renderer/src/store/slices/repos-paired-runtime-add.test.ts --maxWorkers=1", - "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts src/renderer/src/runtime/web-runtime-session.test.ts --maxWorkers=1", + "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts src/renderer/src/runtime/web-runtime-session.test.ts --maxWorkers=1", "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/pr11346-selected-runtime-add.spec.ts --config tests/playwright.config.ts --project electron-headful --workers=1 --reporter=line", "pnpm run ensure:electron-runtime && npx playwright test tests/e2e/pr11346-selected-runtime-add.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1 --reporter=line", "SKIP_BUILD=1 ORCA_E2E_WEB_CLIENT=1 ORCA_E2E_FORCE_HEADFUL=1 pnpm exec playwright test tests/e2e/paired-web-add-project-unavailable-host.spec.ts --config tests/playwright.config.ts --project electron-headful --workers=1 --reporter=line", @@ -4366,7 +4366,7 @@ "src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts", "src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts", "src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts", - "src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts", + "src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts", "src/renderer/src/lib/resolved-worktree-execution-host.test.ts", "src/renderer/src/lib/worktree-runtime-owner.test.ts", "src/renderer/src/store/selectors.test.ts", @@ -4559,7 +4559,7 @@ "date": "2026-07-30", "runner": "local", "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts --maxWorkers=1", + "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts --maxWorkers=1", "result": "passed", "durationSeconds": 8.77, "summary": "Nineteen files and 386 tests passed after rebasing onto 94cf2f1422f30fc309cb47c5e864a831d516fa8b, including exact-ID repo/worktree/group/folder host identity in both catalog orders, fail-closed active-repo selection when a runtime row is unavailable, selected runtime and SSH owner capture through final Add/Clone/Create/folder activation, direct-SSH authoritative pruning, reversed catalog responses, reconnect generation fencing, local-after-runtime isolation, and missing-catalog worktree routing." @@ -4586,7 +4586,7 @@ "date": "2026-07-31", "runner": "local", "platform": "macos", - "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts src/renderer/src/runtime/web-runtime-session.test.ts --maxWorkers=1", + "command": "pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/components/sidebar/AddProjectFromFolderDialog.test.tsx src/renderer/src/components/sidebar/NonGitFolderDialog.test.tsx src/renderer/src/components/sidebar/AddRepoDialog.default-checkout.test.ts src/renderer/src/components/sidebar/AddRepoSteps.default-checkout.test.ts src/renderer/src/components/sidebar/project-added-default-checkout.test.ts src/renderer/src/components/sidebar/useAddRepoLocalFolderFlow.test.ts src/renderer/src/components/sidebar/useAddRepoNestedImportFlow.test.ts src/renderer/src/components/sidebar/useAddRepoServerPathFlow.test.ts src/renderer/src/components/sidebar/useAddRepoCloneFlow.test.ts src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts src/renderer/src/lib/resolved-worktree-execution-host.test.ts src/renderer/src/lib/worktree-runtime-owner.test.ts src/renderer/src/store/selectors.test.ts src/renderer/src/store/slices/repos-all-hosts-folder-workspaces.test.ts src/renderer/src/store/slices/repos-project-groups.test.ts src/renderer/src/store/slices/repos-selected-owner-routing.test.ts src/renderer/src/store/slices/selected-host-active-workspace-identity.test.ts src/renderer/src/store/slices/worktrees.test.ts src/renderer/src/runtime/web-runtime-session.test.ts --maxWorkers=1", "result": "passed", "durationSeconds": 7.1, "summary": "Twenty files and 430 tests passed on current main integration, including explicit runtime-owner preservation for terminal and browser session activation." diff --git a/mobile/src/worktree/workspace-pr-status-groups.ts b/mobile/src/worktree/workspace-pr-status-groups.ts index 998e1d4be1d..225b4e22e1e 100644 --- a/mobile/src/worktree/workspace-pr-status-groups.ts +++ b/mobile/src/worktree/workspace-pr-status-groups.ts @@ -1,6 +1,6 @@ import type { Worktree } from './workspace-list-sections' -// Why: matches desktop's PR_GROUP_META naming from worktree-list-groups.ts. +// Why: matches desktop's PR_GROUP_META naming from worktree-list/rows/groups.ts. // no PR/draft/unknown -> "In Progress", open -> "In Review", merged -> "Done", closed -> "Closed" export type PRGroupKey = 'done' | 'in-review' | 'in-progress' | 'closed' diff --git a/src/renderer/src/components/WorktreeJumpPalette.tsx b/src/renderer/src/components/WorktreeJumpPalette.tsx index 284299520b7..b7d7a9c98ea 100644 --- a/src/renderer/src/components/WorktreeJumpPalette.tsx +++ b/src/renderer/src/components/WorktreeJumpPalette.tsx @@ -132,7 +132,7 @@ import { getPaletteHostBadge, type PaletteHostBadge } from '@/components/cmd-j/p import PaletteFilterMenu from '@/components/cmd-j/PaletteFilterMenu' import PaletteFilterChips from '@/components/cmd-j/PaletteFilterChips' import { buildPaletteFilterModel } from '@/components/cmd-j/palette-filter-options' -import { getProjectGroupExecutionHostIdForRows } from '@/components/sidebar/worktree-list-host-filtering' +import { getProjectGroupExecutionHostIdForRows } from '@/components/sidebar/worktree-list/viewport/host-filtering' import { buildPaletteFilterPredicate, EMPTY_PALETTE_FILTER, diff --git a/src/renderer/src/components/cmd-j/palette-filter-options.ts b/src/renderer/src/components/cmd-j/palette-filter-options.ts index 2670eb80e28..d1143ffeed1 100644 --- a/src/renderer/src/components/cmd-j/palette-filter-options.ts +++ b/src/renderer/src/components/cmd-j/palette-filter-options.ts @@ -9,7 +9,7 @@ import type { Worktree } from '../../../../shared/worktree/types' import { getProjectHeaderRevealTarget, type ProjectGroupingModel -} from '../sidebar/worktree-list-groups' +} from '../sidebar/worktree-list/rows/groups' import type { SidebarHostOption } from '../sidebar/sidebar-host-options' import { buildPaletteFilterOptionSearchText } from './palette-filter-option-list' diff --git a/src/renderer/src/components/cmd-j/palette-project-results.ts b/src/renderer/src/components/cmd-j/palette-project-results.ts index e45e2a8e29f..dfc49166783 100644 --- a/src/renderer/src/components/cmd-j/palette-project-results.ts +++ b/src/renderer/src/components/cmd-j/palette-project-results.ts @@ -12,7 +12,7 @@ import { getProjectGroupHeaderKey, getProjectHeaderRevealTarget, type ProjectGroupingModel -} from '../sidebar/worktree-list-groups' +} from '../sidebar/worktree-list/rows/groups' export type CmdJProjectGroupResult = { id: string diff --git a/src/renderer/src/components/hover-reveal-touch-action-visibility.test.ts b/src/renderer/src/components/hover-reveal-touch-action-visibility.test.ts index 0f0c2e2b58b..e9ef47ee496 100644 --- a/src/renderer/src/components/hover-reveal-touch-action-visibility.test.ts +++ b/src/renderer/src/components/hover-reveal-touch-action-visibility.test.ts @@ -31,6 +31,9 @@ const HOVER_REVEAL_FILES = [ resolve(__dirname, 'sidebar/WorkspaceKanbanStatusLane.tsx'), resolve(__dirname, 'sidebar/WorktreeCardPorts.tsx'), resolve(__dirname, 'sidebar/WorktreeList.tsx'), + resolve(__dirname, 'sidebar/worktree-list/headers/HostSectionHeader.tsx'), + resolve(__dirname, 'sidebar/worktree-list/headers/SectionHeader.tsx'), + resolve(__dirname, 'sidebar/worktree-list/rows/item-row.tsx'), resolve(__dirname, 'status-bar/ResourceUsageStatusSegment.tsx'), resolve(__dirname, 'status-bar/ports-status-popover-rows.tsx'), resolve(__dirname, 'tab-bar/TabBarQuickCommandsMenu.tsx') diff --git a/src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsx b/src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsx index b56311e519b..14bdef20ff3 100644 --- a/src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsx +++ b/src/renderer/src/components/right-sidebar/AiVaultSessionVirtualList.tsx @@ -5,7 +5,7 @@ import type { AiVaultScope, AiVaultSession } from '../../../../shared/ai-vault-t import type { AiVaultResumeStartup } from '@/lib/ai-vault-resume-command' import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' -import { getActiveStickyHeaderIndexForScroll } from '../sidebar/worktree-list-virtual-rows' +import { getActiveStickyHeaderIndexForScroll } from '../sidebar/worktree-list/viewport/virtual-rows' import { VaultGroupHeader } from './AiVaultPanelControls' import { EmptyState, SessionLoadingState } from './AiVaultSessionListStates' import { VaultSessionRow } from './AiVaultSessionRow' diff --git a/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.test.tsx b/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.test.tsx index 2fcdd1cea93..675e5327a0b 100644 --- a/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.test.tsx +++ b/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.test.tsx @@ -10,7 +10,7 @@ import { folderWorkspaceKey, worktreeWorkspaceKey } from '../../../../shared/wor import { LINEAGE_CHILDREN_INLINE_OFFSET, getLineageChildrenInlineStyle -} from '@/components/sidebar/worktree-list-indentation' +} from '@/components/sidebar/worktree-list/rows/indentation' type MockStoreState = { activeWorktreeId: string | null diff --git a/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.tsx b/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.tsx index 94c46a545d2..fa93086304f 100644 --- a/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.tsx +++ b/src/renderer/src/components/right-sidebar/FolderWorkspaceWorktreesPanel.tsx @@ -2,7 +2,7 @@ import WorktreeCard from '@/components/sidebar/WorktreeCard' import { getLineageChildrenInlineStyle, getLineageNestedRowGeometry -} from '@/components/sidebar/worktree-list-indentation' +} from '@/components/sidebar/worktree-list/rows/indentation' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' import type { Worktree } from '../../../../shared/worktree/types' diff --git a/src/renderer/src/components/right-sidebar/ai-vault-virtual-rows.ts b/src/renderer/src/components/right-sidebar/ai-vault-virtual-rows.ts index c92e3f1e64e..08463c695f7 100644 --- a/src/renderer/src/components/right-sidebar/ai-vault-virtual-rows.ts +++ b/src/renderer/src/components/right-sidebar/ai-vault-virtual-rows.ts @@ -3,7 +3,7 @@ import type { Range } from '@tanstack/react-virtual' import { getActiveStickyHeaderIndex, getPreviousStickyHeaderIndex -} from '../sidebar/worktree-list-virtual-rows' +} from '../sidebar/worktree-list/viewport/virtual-rows' export const VAULT_GROUP_HEADER_ROW_HEIGHT = 32 export const VAULT_SESSION_ROW_HEIGHT = 98 diff --git a/src/renderer/src/components/sidebar/SidebarGroupByToggle.test.tsx b/src/renderer/src/components/sidebar/SidebarGroupByToggle.test.tsx index c9060644c8f..9b13a21b14c 100644 --- a/src/renderer/src/components/sidebar/SidebarGroupByToggle.test.tsx +++ b/src/renderer/src/components/sidebar/SidebarGroupByToggle.test.tsx @@ -4,7 +4,7 @@ import { act } from 'react' import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { SidebarGroupByToggle } from './SidebarGroupByToggle' -import type { WorktreeGroupBy } from './worktree-list-groups' +import type { WorktreeGroupBy } from './worktree-list/rows/groups' const roots: Root[] = [] diff --git a/src/renderer/src/components/sidebar/SidebarGroupByToggle.tsx b/src/renderer/src/components/sidebar/SidebarGroupByToggle.tsx index dddc8f46e4e..0476af842a7 100644 --- a/src/renderer/src/components/sidebar/SidebarGroupByToggle.tsx +++ b/src/renderer/src/components/sidebar/SidebarGroupByToggle.tsx @@ -1,5 +1,5 @@ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' -import type { WorktreeGroupBy } from './worktree-list-groups' +import type { WorktreeGroupBy } from './worktree-list/rows/groups' import { GROUP_BY_OPTIONS } from './sidebar-workspace-option-items' type SidebarGroupByToggleProps = { diff --git a/src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts b/src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts index a8c6045463d..bbfdce4e993 100644 --- a/src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts +++ b/src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts @@ -8,7 +8,7 @@ import type { Repo } from '../../../../shared/repo-types' import type { WorktreeLineage } from '../../../../shared/worktree/lineage-types' import type { Worktree } from '../../../../shared/worktree/types' import { folderWorkspaceKey } from '../../../../shared/workspace-scope' -import { getPinnedWorktreeRevealCollapsedGroupKeys } from './worktree-list/sidebar-row-reveal-ancestors' +import { getPinnedWorktreeRevealCollapsedGroupKeys } from './worktree-list/reveal/ancestors' import { cloneDefaultWorkspaceStatuses } from '../../../../shared/workspace-statuses' const mockStore = vi.hoisted(() => ({ diff --git a/src/renderer/src/components/sidebar/WorktreeList.lineage-child-real-card.test.tsx b/src/renderer/src/components/sidebar/WorktreeList.lineage-child-real-card.test.tsx index d36aa264f02..7b51d933d63 100644 --- a/src/renderer/src/components/sidebar/WorktreeList.lineage-child-real-card.test.tsx +++ b/src/renderer/src/components/sidebar/WorktreeList.lineage-child-real-card.test.tsx @@ -14,7 +14,7 @@ import { LINEAGE_IMMEDIATE_PARENT_STEP, LINEAGE_NESTED_ROW_SURFACE_INSET, WORKTREE_CARD_SURFACE_MARGIN -} from './worktree-list-indentation' +} from './worktree-list/rows/indentation' globalThis.IS_REACT_ACT_ENVIRONMENT = true diff --git a/src/renderer/src/components/sidebar/WorktreeList.tsx b/src/renderer/src/components/sidebar/WorktreeList.tsx index dbbee941a4f..6e380e8e355 100644 --- a/src/renderer/src/components/sidebar/WorktreeList.tsx +++ b/src/renderer/src/components/sidebar/WorktreeList.tsx @@ -14,29 +14,29 @@ import { getSettingsFocusedExecutionHostId } from '../../../../shared/execution-host' import { getActiveSidebarWorkspaceId } from '../../../../shared/workspace-scope' -import { getPinnedWorktreeDisplayPolicy } from './worktree-list-groups' -import { selectWorktreeListReviewCacheInputs } from './worktree-list-review-cache-inputs' +import { getPinnedWorktreeDisplayPolicy } from './worktree-list/rows/groups' +import { selectWorktreeListReviewCacheInputs } from './worktree-list/viewport/review-cache-inputs' import type { VirtualizedScrollAnchor } from '@/hooks/useVirtualizedScrollAnchor' import { SidebarWorktreeListDialogs } from './worktree-list/SidebarWorktreeListDialogs' import { SidebarWorktreeListEmptyState } from './worktree-list/SidebarWorktreeListEmptyState' -import { VirtualizedWorktreeViewport } from './worktree-list/VirtualizedWorktreeViewport' -import { markSidebarWorktreeActiveImmediately } from './worktree-list/worktree-option-dom' +import { VirtualizedWorktreeViewport } from './worktree-list/viewport/VirtualizedWorktreeViewport' +import { markSidebarWorktreeActiveImmediately } from './worktree-list/option-dom' import { EMPTY_PROJECT_GROUPS, NOOP_WORKSPACE_BOARD_DRAG_PREVIEW_CALLBACK -} from './worktree-list/virtualized-worktree-viewport-props' +} from './worktree-list/viewport/viewport-props' import { useAgentSendTargetWorktreeId } from './worktree-list/use-agent-send-target-worktree' import { useEffectiveCollapsedGroups } from './worktree-list/use-effective-collapsed-groups' import { useProjectGroupDialogs } from './worktree-list/use-project-group-dialogs' import { useSidebarExternalWorktreeCards } from './worktree-list/use-sidebar-external-worktree-cards' import { useSidebarHostVisibleScope } from './worktree-list/use-sidebar-host-visible-scope' -import { useSidebarRevealRequests } from './worktree-list/use-sidebar-reveal-requests' +import { useSidebarRevealRequests } from './worktree-list/reveal/use-requests' import { useSidebarSectionRows } from './worktree-list/use-sidebar-section-rows' import { useSidebarWorktreeFilters } from './worktree-list/use-sidebar-worktree-filters' import { useSidebarWorktreeSelection } from './worktree-list/use-sidebar-worktree-selection' import { useSidebarWorktreeSortOrder } from './worktree-list/use-sidebar-worktree-sort-order' import { useVisibleSidebarWorktrees } from './worktree-list/use-visible-sidebar-worktrees' -import { useWorktreeStatusMutations } from './worktree-list/use-worktree-status-mutations' +import { useWorktreeStatusMutations } from './worktree-list/use-status-mutations' type WorktreeListProps = { scrollOffsetRef: React.MutableRefObject diff --git a/src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts b/src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts index 29e30d196bf..b0ddcaf1646 100644 --- a/src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts +++ b/src/renderer/src/components/sidebar/empty-project-placeholder-repos.ts @@ -1,6 +1,6 @@ import type { Repo } from '../../../../shared/repo-types' import type { Worktree } from '../../../../shared/worktree/types' -import type { WorktreeGroupBy } from './worktree-list-groups' +import type { WorktreeGroupBy } from './worktree-list/rows/groups' export function getEmptyProjectPlaceholderRepoIds(args: { groupBy: WorktreeGroupBy diff --git a/src/renderer/src/components/sidebar/host-section-rows.test.ts b/src/renderer/src/components/sidebar/host-section-rows.test.ts index 1d8cc55a14b..7b658baa42d 100644 --- a/src/renderer/src/components/sidebar/host-section-rows.test.ts +++ b/src/renderer/src/components/sidebar/host-section-rows.test.ts @@ -3,7 +3,7 @@ import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' import type { ProjectGroup } from '../../../../shared/project-group-types' import type { Repo } from '../../../../shared/repo-types' import type { Worktree } from '../../../../shared/worktree/types' -import { PINNED_GROUP_KEY, type Row } from './worktree-list-groups' +import { PINNED_GROUP_KEY, type Row } from './worktree-list/rows/groups' import { addHostSectionRows, type HostSectionRow } from './host-section-rows' function repo(id: string, connectionId?: string | null): Repo { diff --git a/src/renderer/src/components/sidebar/host-section-rows.ts b/src/renderer/src/components/sidebar/host-section-rows.ts index 1371f57ed05..fd85d2a0e10 100644 --- a/src/renderer/src/components/sidebar/host-section-rows.ts +++ b/src/renderer/src/components/sidebar/host-section-rows.ts @@ -14,7 +14,7 @@ import type { SshConnectionStatus } from '../../../../shared/ssh-types' import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' import type { ProjectGroup } from '../../../../shared/project-group-types' import type { Repo } from '../../../../shared/repo-types' -import type { Row } from './worktree-list-groups' +import type { Row } from './worktree-list/rows/groups' export type HostHeaderRow = { type: 'host-header' diff --git a/src/renderer/src/components/sidebar/imported-worktrees-card-candidates.ts b/src/renderer/src/components/sidebar/imported-worktrees-card-candidates.ts index 71237a73149..ed41f843c9d 100644 --- a/src/renderer/src/components/sidebar/imported-worktrees-card-candidates.ts +++ b/src/renderer/src/components/sidebar/imported-worktrees-card-candidates.ts @@ -12,7 +12,7 @@ import { effectiveExternalWorktreeVisibility, isLegacyRepoForExternalWorktreeVisibility } from '../../../../shared/worktree/ownership' -import type { ImportedWorktreesCardCandidate } from './worktree-list-groups' +import type { ImportedWorktreesCardCandidate } from './worktree-list/rows/groups' export function getHiddenImportedWorktrees( detected: DetectedWorktreeListResult | undefined diff --git a/src/renderer/src/components/sidebar/new-external-worktrees-inbox-candidates.ts b/src/renderer/src/components/sidebar/new-external-worktrees-inbox-candidates.ts index c467707c4b0..2903e561707 100644 --- a/src/renderer/src/components/sidebar/new-external-worktrees-inbox-candidates.ts +++ b/src/renderer/src/components/sidebar/new-external-worktrees-inbox-candidates.ts @@ -8,7 +8,7 @@ import type { Repo } from '../../../../shared/repo-types' import type { DetectedWorktreeListResult, Worktree } from '../../../../shared/worktree/types' import { getNewExternalWorktreeInboxWorktrees } from '../../../../shared/external-worktree-inbox' import { isGitRepoKind } from '../../../../shared/repo-kind' -import type { NewExternalWorktreesInboxCandidate } from './worktree-list-groups' +import type { NewExternalWorktreesInboxCandidate } from './worktree-list/rows/groups' export function buildNewExternalWorktreesInboxCandidates(args: { repos: readonly Repo[] diff --git a/src/renderer/src/components/sidebar/project-group-header-dom.test.ts b/src/renderer/src/components/sidebar/project-group-header-dom.test.ts index 58da2de4511..a6d178f4d5f 100644 --- a/src/renderer/src/components/sidebar/project-group-header-dom.test.ts +++ b/src/renderer/src/components/sidebar/project-group-header-dom.test.ts @@ -4,14 +4,14 @@ import { describe, expect, it } from 'vitest' function readWorktreeListSource(): string { return readFileSync( - fileURLToPath(new URL('./worktree-list/worktree-section-header-row.tsx', import.meta.url)), + fileURLToPath(new URL('./worktree-list/headers/SectionHeader.tsx', import.meta.url)), 'utf8' ) } function readHeaderDragSource(): string { return readFileSync( - fileURLToPath(new URL('./worktree-list/use-worktree-sidebar-header-drag.ts', import.meta.url)), + fileURLToPath(new URL('./worktree-list/drag/use-header-drag.ts', import.meta.url)), 'utf8' ) } diff --git a/src/renderer/src/components/sidebar/project-group-header-drop.test.ts b/src/renderer/src/components/sidebar/project-group-header-drop.test.ts index 3e843cf7543..aa632730891 100644 --- a/src/renderer/src/components/sidebar/project-group-header-drop.test.ts +++ b/src/renderer/src/components/sidebar/project-group-header-drop.test.ts @@ -8,7 +8,7 @@ import { getSidebarOrderedProjectGroupHeaderIdsByBucket, mapSidebarProjectGroupDropIndexToSiblingInsertIndex } from './project-group-header-drop' -import type { Row } from './worktree-list-groups' +import type { Row } from './worktree-list/rows/groups' import type { ProjectGroup } from '../../../../shared/project-group-types' import type { Repo } from '../../../../shared/repo-types' diff --git a/src/renderer/src/components/sidebar/project-group-header-drop.ts b/src/renderer/src/components/sidebar/project-group-header-drop.ts index 73f4415cd00..83319ccab8e 100644 --- a/src/renderer/src/components/sidebar/project-group-header-drop.ts +++ b/src/renderer/src/components/sidebar/project-group-header-drop.ts @@ -2,7 +2,7 @@ import { computeWorktreeSidebarHeaderDropPreview, type WorktreeSidebarHeaderDropPreview } from './worktree-sidebar-header-drop-preview' -import type { Row } from './worktree-list-groups' +import type { Row } from './worktree-list/rows/groups' import type { ProjectGroup } from '../../../../shared/project-group-types' export type ProjectGroupHeaderDragBucketKey = string diff --git a/src/renderer/src/components/sidebar/project-header-drop.test.ts b/src/renderer/src/components/sidebar/project-header-drop.test.ts index 92baaf72b73..e0bb8725a82 100644 --- a/src/renderer/src/components/sidebar/project-header-drop.test.ts +++ b/src/renderer/src/components/sidebar/project-header-drop.test.ts @@ -11,7 +11,7 @@ import { mapSidebarProjectHeaderDropIndexToSiblingInsertIndex, mapSidebarRepoDropIndexToAllRepoInsertAt } from './project-header-drop' -import type { Row } from './worktree-list-groups' +import type { Row } from './worktree-list/rows/groups' import type { Repo } from '../../../../shared/repo-types' describe('getProjectHeaderDragBucketKey', () => { diff --git a/src/renderer/src/components/sidebar/project-header-drop.ts b/src/renderer/src/components/sidebar/project-header-drop.ts index c2de72bcaf3..eae08a5ebdf 100644 --- a/src/renderer/src/components/sidebar/project-header-drop.ts +++ b/src/renderer/src/components/sidebar/project-header-drop.ts @@ -3,7 +3,7 @@ import { computeWorktreeSidebarHeaderDropPreview, type WorktreeSidebarHeaderDropPreview } from './worktree-sidebar-header-drop-preview' -import type { Row } from './worktree-list-groups' +import type { Row } from './worktree-list/rows/groups' import type { Repo } from '../../../../shared/repo-types' export type ProjectHeaderDragBucketKey = string diff --git a/src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts b/src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts index bc2c038e8d2..587f8b0e130 100644 --- a/src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts +++ b/src/renderer/src/components/sidebar/rendered-sidebar-worktree-order.ts @@ -7,18 +7,18 @@ import { } from '../../../../shared/execution-host' import { getRepoMapFromState, getWorktreeMapFromState } from '@/store/selectors' import { getProjectHostSetupProjectionFromState } from '@/store/project-host-setup-selector' -import { buildRows, getPinnedWorktreeDisplayPolicy } from './worktree-list-groups' +import { buildRows, getPinnedWorktreeDisplayPolicy } from './worktree-list/rows/groups' import { addHostSectionRows } from './host-section-rows' import { orderHostSectionOptions } from './host-section-order' import { buildSidebarHostOptions } from './sidebar-host-options' import { getLogicalRepoOrderRankById } from './project-header-drop' import { getRenderedWorktreesInSidebarOrder } from './worktree-sidebar-row-preference' -import { selectWorktreeListReviewCacheInputs } from './worktree-list-review-cache-inputs' +import { selectWorktreeListReviewCacheInputs } from './worktree-list/viewport/review-cache-inputs' import { filterFolderWorkspacesForVisibleHosts, filterProjectGroupsForVisibleHosts, getVisibleSidebarHostIdSet -} from './worktree-list-host-filtering' +} from './worktree-list/viewport/host-filtering' const EMPTY_REPO_ID_SET: ReadonlySet = Object.freeze(new Set()) const EMPTY_IMPORTED_BY_REPO = Object.freeze(new Map()) as never diff --git a/src/renderer/src/components/sidebar/worktree-card-presentation.tsx b/src/renderer/src/components/sidebar/worktree-card-presentation.tsx index 5ce1e94501c..72aa34321c2 100644 --- a/src/renderer/src/components/sidebar/worktree-card-presentation.tsx +++ b/src/renderer/src/components/sidebar/worktree-card-presentation.tsx @@ -3,7 +3,7 @@ import React from 'react' import { getFlushWorktreeCardPaddingLeft, getNewCardStyleParentContentMarginLeft -} from './worktree-list-indentation' +} from './worktree-list/rows/indentation' import { hasWorktreeCardDetails, WorktreeCardDetailsHover, diff --git a/src/renderer/src/components/sidebar/worktree-drag-units.ts b/src/renderer/src/components/sidebar/worktree-drag-units.ts index c38d8b8643c..0f55bf25888 100644 --- a/src/renderer/src/components/sidebar/worktree-drag-units.ts +++ b/src/renderer/src/components/sidebar/worktree-drag-units.ts @@ -1,5 +1,5 @@ import type { WorktreeDragGroup } from './worktree-manual-order' -import { ALL_GROUP_KEY, PINNED_GROUP_KEY } from './worktree-list-groups' +import { ALL_GROUP_KEY, PINNED_GROUP_KEY } from './worktree-list/rows/groups' export type WorktreeDragUnitGroup = WorktreeDragGroup & { units: { worktreeId: string; worktreeIds: string[] }[] diff --git a/src/renderer/src/components/sidebar/worktree-header-section-boundaries.ts b/src/renderer/src/components/sidebar/worktree-header-section-boundaries.ts index 85ae0b73bf1..c531e25a304 100644 --- a/src/renderer/src/components/sidebar/worktree-header-section-boundaries.ts +++ b/src/renderer/src/components/sidebar/worktree-header-section-boundaries.ts @@ -1,4 +1,4 @@ -import { estimateRenderRowSize, type RenderRow } from './worktree-list-virtual-rows' +import { estimateRenderRowSize, type RenderRow } from './worktree-list/viewport/virtual-rows' function getEstimatedRenderRowStarts( rows: readonly RenderRow[], diff --git a/src/renderer/src/components/sidebar/worktree-keyboard-cycle.test.ts b/src/renderer/src/components/sidebar/worktree-keyboard-cycle.test.ts index edc4f41f47e..ccd97e44c76 100644 --- a/src/renderer/src/components/sidebar/worktree-keyboard-cycle.test.ts +++ b/src/renderer/src/components/sidebar/worktree-keyboard-cycle.test.ts @@ -132,9 +132,7 @@ describe('getCyclableWorktreeIds', () => { describe('WorktreeList keyboard cycling', () => { it('cycles over the rendered rows instead of rebuilding a parallel layout', () => { const source = readFileSync( - fileURLToPath( - new URL('./worktree-list/use-worktree-list-keyboard-navigation.ts', import.meta.url) - ), + fileURLToPath(new URL('./worktree-list/viewport/use-keyboard.ts', import.meta.url)), 'utf8' ) const navigateWorktree = source.slice( diff --git a/src/renderer/src/components/sidebar/worktree-keyboard-cycle.ts b/src/renderer/src/components/sidebar/worktree-keyboard-cycle.ts index a916e93024f..f2ad55b03b5 100644 --- a/src/renderer/src/components/sidebar/worktree-keyboard-cycle.ts +++ b/src/renderer/src/components/sidebar/worktree-keyboard-cycle.ts @@ -1,5 +1,5 @@ import type { HostSectionRow } from './host-section-rows' -import type { PinnedWorktreeDisplayPolicy, WorktreeRow } from './worktree-list-groups' +import type { PinnedWorktreeDisplayPolicy, WorktreeRow } from './worktree-list/rows/groups' import { getPreferredWorktreeRows } from './worktree-sidebar-row-preference' /** Worktree ids in sidebar order, taken from the rows the sidebar actually diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-drop-commit-context.ts b/src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts similarity index 74% rename from src/renderer/src/components/sidebar/worktree-list/worktree-drop-commit-context.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts index 2fee58ba4a4..5323529ebb2 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-drop-commit-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/drop-commit-context.ts @@ -2,13 +2,13 @@ import type React from 'react' import type { WorkspaceStatus, WorkspaceStatusDefinition -} from '../../../../../shared/worktree/types' -import type { WorktreeDragGroup } from '../worktree-manual-order' -import type { WorktreeDragUnitGroup } from '../worktree-drag-units' -import type { WorktreeSidebarDropPreview } from '../worktree-sidebar-drop-preview' -import type { WorktreeStatusDropAtIndexArgs } from './virtualized-worktree-viewport-props' -import type { WorktreeStatusDropRequest } from './use-worktree-drag-session' -import type { WorktreeSidebarLineageDropTarget } from './worktree-row-drag-state' +} from '../../../../../../shared/worktree/types' +import type { WorktreeDragGroup } from '../../worktree-manual-order' +import type { WorktreeDragUnitGroup } from '../../worktree-drag-units' +import type { WorktreeSidebarDropPreview } from '../../worktree-sidebar-drop-preview' +import type { WorktreeStatusDropAtIndexArgs } from '../viewport/viewport-props' +import type { WorktreeStatusDropRequest } from './use-session' +import type { WorktreeSidebarLineageDropTarget } from './row-state' // The shared surface every drop path (pointer, native drag, document capture) commits through. export type WorktreeDropCommitContext = { diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-drag-groups.ts b/src/renderer/src/components/sidebar/worktree-list/drag/groups.ts similarity index 92% rename from src/renderer/src/components/sidebar/worktree-list/worktree-drag-groups.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/groups.ts index eb93ecf617a..7b693203cfd 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-drag-groups.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/groups.ts @@ -1,6 +1,6 @@ -import { ALL_GROUP_KEY, PINNED_GROUP_KEY } from '../worktree-list-groups' -import type { HostSectionRow } from '../host-section-rows' -import type { WorktreeDragGroup } from '../worktree-manual-order' +import { ALL_GROUP_KEY, PINNED_GROUP_KEY } from '../rows/groups' +import type { HostSectionRow } from '../../host-section-rows' +import type { WorktreeDragGroup } from '../../worktree-manual-order' // A pinned duplicate of a worktree that also renders in its natural group is not its own drag slot. function getNaturalWorktreeIds(rows: readonly HostSectionRow[]): Set { diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-pointer-drag-commit.ts b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts similarity index 87% rename from src/renderer/src/components/sidebar/worktree-list/worktree-pointer-drag-commit.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts index 1e09a403885..0f2f9efcb87 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-pointer-drag-commit.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-commit.ts @@ -3,21 +3,15 @@ import { getWorkspaceKanbanSidebarDropTarget, isWorkspaceKanbanSidebarDropPointInBoard, resolveWorkspaceKanbanSidebarFullLaneDropIndex -} from '../workspace-kanban-sidebar-drop' -import { resolveWorkspaceKanbanCardDropCommitTarget } from '../workspace-kanban-card-pointer-drag-dom' -import { getFullDropIndexForWorktreeDragUnit } from '../worktree-drag-units' -import { resolveWorktreeSidebarStatusDropCommitTarget } from '../worktree-sidebar-drop-preview' -import { - getPointerDropStatusTarget, - shouldPreferSidebarStatusDropTarget -} from './pointer-drop-status-target' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import type { WorktreeStatusDropAtIndexArgs } from './virtualized-worktree-viewport-props' -import type { WorktreeSidebarStatusDropTarget } from '../worktree-sidebar-drop-preview' -import { - NO_WORKTREE_SIDEBAR_DROP_TARGET, - type WorktreePointerDrag -} from './worktree-row-drag-state' +} from '../../workspace-kanban-sidebar-drop' +import { resolveWorkspaceKanbanCardDropCommitTarget } from '../../workspace-kanban-card-pointer-drag-dom' +import { getFullDropIndexForWorktreeDragUnit } from '../../worktree-drag-units' +import { resolveWorktreeSidebarStatusDropCommitTarget } from '../../worktree-sidebar-drop-preview' +import { getPointerDropStatusTarget, shouldPreferSidebarStatusDropTarget } from './status-target' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import type { WorktreeStatusDropAtIndexArgs } from '../viewport/viewport-props' +import type { WorktreeSidebarStatusDropTarget } from '../../worktree-sidebar-drop-preview' +import { NO_WORKTREE_SIDEBAR_DROP_TARGET, type WorktreePointerDrag } from './row-state' type PointerDropCommitArgs = { event: PointerEvent diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-pointer-drag-flush.ts b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts similarity index 92% rename from src/renderer/src/components/sidebar/worktree-list/worktree-pointer-drag-flush.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts index 90d70d4534b..3baf8e81762 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-pointer-drag-flush.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/pointer-flush.ts @@ -1,17 +1,14 @@ import type React from 'react' -import type { WorkspaceStatus } from '../../../../../shared/worktree/types' +import type { WorkspaceStatus } from '../../../../../../shared/worktree/types' import { clearWorkspaceKanbanSidebarDropTargetVisual, hasWorkspaceKanbanSidebarDropBoard, isWorkspaceKanbanSidebarDropPointInBoard, updateWorkspaceKanbanSidebarDropTargetVisual -} from '../workspace-kanban-sidebar-drop' -import { updateSidebarDragPreviewPosition } from '../worktree-sidebar-pointer-drag-dom' -import { - getPointerDropStatusTarget, - shouldPreferSidebarStatusDropTarget -} from './pointer-drop-status-target' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' +} from '../../workspace-kanban-sidebar-drop' +import { updateSidebarDragPreviewPosition } from '../../worktree-sidebar-pointer-drag-dom' +import { getPointerDropStatusTarget, shouldPreferSidebarStatusDropTarget } from './status-target' +import type { WorktreeDropCommitContext } from './drop-commit-context' import { applyWorktreeDropPreview, clearWorktreeDropPreview, @@ -20,7 +17,7 @@ import { type WorktreePointerDrag, type WorktreeRowDragState, type WorktreeSidebarLineageDropTarget -} from './worktree-row-drag-state' +} from './row-state' export type WorktreePointerDragFrameArgs = { drag: WorktreePointerDrag diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-row-drag-state.ts b/src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts similarity index 94% rename from src/renderer/src/components/sidebar/worktree-list/worktree-row-drag-state.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts index 2bb47089e22..480f78d0a6e 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-row-drag-state.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/row-state.ts @@ -1,10 +1,10 @@ -import type { WorkspaceKanbanCardTrackedDropTarget } from '../workspace-kanban-card-pointer-drag-dom' -import type { WorktreeSidebarDragRect } from '../worktree-sidebar-drag-autoscroll' +import type { WorkspaceKanbanCardTrackedDropTarget } from '../../workspace-kanban-card-pointer-drag-dom' +import type { WorktreeSidebarDragRect } from '../../worktree-sidebar-drag-autoscroll' import type { WorktreeSidebarDropPreview, WorktreeSidebarStatusDropTarget, WorktreeSidebarTrackedStatusDropTarget -} from '../worktree-sidebar-drop-preview' +} from '../../worktree-sidebar-drop-preview' export type WorktreeRowDragState = { draggingWorktreeId: string | null diff --git a/src/renderer/src/components/sidebar/worktree-list/pointer-drop-status-target.ts b/src/renderer/src/components/sidebar/worktree-list/drag/status-target.ts similarity index 80% rename from src/renderer/src/components/sidebar/worktree-list/pointer-drop-status-target.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/status-target.ts index 46e20c56234..2d159f53bca 100644 --- a/src/renderer/src/components/sidebar/worktree-list/pointer-drop-status-target.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/status-target.ts @@ -1,14 +1,11 @@ import type { WorkspaceStatus, WorkspaceStatusDefinition -} from '../../../../../shared/worktree/types' -import { getWorkspaceStatusFromGroupKey } from '../workspace-status' -import { getWorktreeLineageDropTargetId } from '../worktree-lineage-drag-drop' -import type { WorktreeSidebarStatusDropTarget } from '../worktree-sidebar-drop-preview' -import { - NO_WORKTREE_SIDEBAR_DROP_TARGET, - type WorktreeSidebarLineageDropTarget -} from './worktree-row-drag-state' +} from '../../../../../../shared/worktree/types' +import { getWorkspaceStatusFromGroupKey } from '../../workspace-status' +import { getWorktreeLineageDropTargetId } from '../../worktree-lineage-drag-drop' +import type { WorktreeSidebarStatusDropTarget } from '../../worktree-sidebar-drop-preview' +import { NO_WORKTREE_SIDEBAR_DROP_TARGET, type WorktreeSidebarLineageDropTarget } from './row-state' export function getPointerDropStatusTarget(args: { container: HTMLElement diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-document-drop.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts similarity index 90% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-document-drop.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts index 7902f0647cb..7f9777aac48 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-document-drop.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-document-drop.ts @@ -1,10 +1,10 @@ import { useEffect } from 'react' import type React from 'react' -import { getFullDropIndexForWorktreeDragUnit } from '../worktree-drag-units' -import type { WorktreeSidebarDragSession } from '../worktree-sidebar-drag-autoscroll' -import { getPointerDropStatusTarget } from './pointer-drop-status-target' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import { NO_WORKTREE_SIDEBAR_DROP_TARGET } from './worktree-row-drag-state' +import { getFullDropIndexForWorktreeDragUnit } from '../../worktree-drag-units' +import type { WorktreeSidebarDragSession } from '../../worktree-sidebar-drag-autoscroll' +import { getPointerDropStatusTarget } from './status-target' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import { NO_WORKTREE_SIDEBAR_DROP_TARGET } from './row-state' // Why: a drop that lands outside the sidebar still belongs to the in-flight session, so the // capture-phase document listeners commit or abandon it before anything else sees the event. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-sidebar-header-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts similarity index 89% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-sidebar-header-drag.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts index 93be311bc8e..498e9c2d7aa 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-sidebar-header-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-header-drag.ts @@ -1,23 +1,23 @@ import { useCallback, useEffect, useMemo } from 'react' import type React from 'react' import { useAppStore } from '@/store' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { ProjectOrderBy } from '../../../../../shared/ui-chrome-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { ExecutionHostId } from '../../../../../shared/execution-host' -import type { HostHeaderRow, HostSectionRow } from '../host-section-rows' -import type { Row, WorktreeGroupBy } from '../worktree-list-groups' -import type { RenderRow } from '../worktree-list-virtual-rows' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { ProjectOrderBy } from '../../../../../../shared/ui-chrome-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { ExecutionHostId } from '../../../../../../shared/execution-host' +import type { HostHeaderRow, HostSectionRow } from '../../host-section-rows' +import type { Row, WorktreeGroupBy } from '../rows/groups' +import type { RenderRow } from '../viewport/virtual-rows' import { getProjectGroupHeaderSectionEndByGroupId, getRepoHeaderSectionEndByRepoId -} from '../worktree-header-section-boundaries' -import { useHostHeaderDrag } from '../host-header-drag' -import { useRepoHeaderDrag } from '../project-header-drag' -import { getSidebarOrderedRepoHeaderIdsByBucket } from '../project-header-drop' -import { useProjectGroupHeaderDrag } from '../project-group-header-drag' -import { getSidebarOrderedProjectGroupHeaderIdsByBucket } from '../project-group-header-drop' -import { USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS } from './worktree-sidebar-scroll-tuning' +} from '../../worktree-header-section-boundaries' +import { useHostHeaderDrag } from '../../host-header-drag' +import { useRepoHeaderDrag } from '../../project-header-drag' +import { getSidebarOrderedRepoHeaderIdsByBucket } from '../../project-header-drop' +import { useProjectGroupHeaderDrag } from '../../project-group-header-drag' +import { getSidebarOrderedProjectGroupHeaderIdsByBucket } from '../../project-group-header-drop' +import { USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS } from '../scroll/tuning' export type WorktreeSidebarHeaderDrag = ReturnType diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-lineage-drop-commit.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts similarity index 86% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-lineage-drop-commit.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts index 6629fe526e1..232f26058c1 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-lineage-drop-commit.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-lineage-drop-commit.ts @@ -2,14 +2,14 @@ import { useCallback, useMemo } from 'react' import { toast } from 'sonner' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' -import type { Repo } from '../../../../../shared/repo-types' -import type { Worktree } from '../../../../../shared/worktree/types' -import type { WorktreeLineage } from '../../../../../shared/worktree/lineage-types' -import { isEligibleWorktreeParent } from '../worktree-parent-candidates' -import { getCyclicProjectedWorktreeLineageIds } from '../worktree-lineage-projection' -import { getReorderedWorktreeIdsToUnnest } from '../worktree-lineage-drag-drop' -import type { WorktreeDragGroup } from '../worktree-manual-order' -import type { WorktreeSidebarLineageDropTarget } from './worktree-row-drag-state' +import type { Repo } from '../../../../../../shared/repo-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import type { WorktreeLineage } from '../../../../../../shared/worktree/lineage-types' +import { isEligibleWorktreeParent } from '../../worktree-parent-candidates' +import { getCyclicProjectedWorktreeLineageIds } from '../../worktree-lineage-projection' +import { getReorderedWorktreeIdsToUnnest } from '../../worktree-lineage-drag-drop' +import type { WorktreeDragGroup } from '../../worktree-manual-order' +import type { WorktreeSidebarLineageDropTarget } from './row-state' export type WorktreeLineageDropCommit = ReturnType diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-native-drag-autoscroll.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts similarity index 89% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-native-drag-autoscroll.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts index 339f1f8b768..c6c20fa8f4f 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-native-drag-autoscroll.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-autoscroll.ts @@ -1,11 +1,11 @@ import { useCallback } from 'react' import type React from 'react' -import { getWorktreeSidebarDragAutoscroll } from '../worktree-sidebar-drag-autoscroll' -import { getPointerDropStatusTarget } from './pointer-drop-status-target' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' -import type { WorktreeDragSession } from './use-worktree-drag-session' -import { applyWorktreeDropPreview, clearWorktreeDropPreview } from './worktree-row-drag-state' +import { getWorktreeSidebarDragAutoscroll } from '../../worktree-sidebar-drag-autoscroll' +import { getPointerDropStatusTarget } from './status-target' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import type { WorktreeDragRuntime } from './use-runtime' +import type { WorktreeDragSession } from './use-session' +import { applyWorktreeDropPreview, clearWorktreeDropPreview } from './row-state' // The HTML5 drag equivalent of pointer autoscroll: the browser gives no move events while // hovering the edge, so re-derive the drop preview from the last known point each frame. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-native-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts similarity index 90% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-native-drag.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts index b7fc693326e..b3f7f2f54b7 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-native-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-native-drag.ts @@ -1,20 +1,20 @@ import { useCallback } from 'react' import type React from 'react' -import { getWorkspaceKanbanSidebarDropTarget } from '../workspace-kanban-sidebar-drop' -import { getFullDropIndexForWorktreeDragUnit } from '../worktree-drag-units' -import { getWorktreeSidebarDragRectsForGroup } from '../worktree-sidebar-drag-autoscroll' -import { getWorktreeSidebarDragGrab } from '../worktree-sidebar-drag-geometry' -import { getPointerDropStatusTarget } from './pointer-drop-status-target' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' -import type { WorktreeDragSession } from './use-worktree-drag-session' -import { useWorktreeNativeDragAutoscroll } from './use-worktree-native-drag-autoscroll' +import { getWorkspaceKanbanSidebarDropTarget } from '../../workspace-kanban-sidebar-drop' +import { getFullDropIndexForWorktreeDragUnit } from '../../worktree-drag-units' +import { getWorktreeSidebarDragRectsForGroup } from '../../worktree-sidebar-drag-autoscroll' +import { getWorktreeSidebarDragGrab } from '../../worktree-sidebar-drag-geometry' +import { getPointerDropStatusTarget } from './status-target' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import type { WorktreeDragRuntime } from './use-runtime' +import type { WorktreeDragSession } from './use-session' +import { useWorktreeNativeDragAutoscroll } from './use-native-autoscroll' import { applyWorktreeDropPreview, clearWorktreeDropPreview, EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, NO_WORKTREE_SIDEBAR_DROP_TARGET -} from './worktree-row-drag-state' +} from './row-state' // The HTML5 drag path used by cards that start a native dragstart (board <-> sidebar transfers). export function useWorktreeNativeDrag(args: { diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag-autoscroll.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts similarity index 90% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag-autoscroll.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts index a2af2bd422d..0cc5fd9247d 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag-autoscroll.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-autoscroll.ts @@ -1,9 +1,9 @@ import { useCallback } from 'react' import type React from 'react' -import { getWorktreeSidebarDragAutoscroll } from '../worktree-sidebar-drag-autoscroll' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' -import type { WorktreeDragSession } from './use-worktree-drag-session' -import type { WorktreePointerDrag } from './worktree-row-drag-state' +import { getWorktreeSidebarDragAutoscroll } from '../../worktree-sidebar-drag-autoscroll' +import type { WorktreeDragRuntime } from './use-runtime' +import type { WorktreeDragSession } from './use-session' +import type { WorktreePointerDrag } from './row-state' // Scrolls the sidebar while a pointer drag hovers near its top or bottom edge, refreshing // the drag session on every scrolled frame so the insertion line stays on the right row. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts similarity index 86% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts index b61694d9bd4..9398d9a7942 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-drag.ts @@ -1,26 +1,23 @@ import { useCallback, useEffect } from 'react' import type React from 'react' -import type { WorkspaceStatus, Worktree } from '../../../../../shared/worktree/types' -import { hasWorkspaceKanbanSidebarDropBoard } from '../workspace-kanban-sidebar-drop' +import type { WorkspaceStatus, Worktree } from '../../../../../../shared/worktree/types' +import { hasWorkspaceKanbanSidebarDropBoard } from '../../workspace-kanban-sidebar-drop' import { createSidebarDragPreview, isSidebarPointerDragBlocked, setSidebarPointerDragDocumentStyles -} from '../worktree-sidebar-pointer-drag-dom' -import { getWorktreeSidebarDragRectsForGroup } from '../worktree-sidebar-drag-autoscroll' -import { getWorktreeSidebarDragGrab } from '../worktree-sidebar-drag-geometry' -import { NOOP_WORKSPACE_BOARD_DRAG_PREVIEW_CALLBACK } from './virtualized-worktree-viewport-props' -import type { WorktreeStatusDropAtIndexArgs } from './virtualized-worktree-viewport-props' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' -import type { WorktreeDragSession } from './use-worktree-drag-session' -import { useWorktreePointerDragAutoscroll } from './use-worktree-pointer-drag-autoscroll' -import { useWorktreePointerDragWindowEvents } from './use-worktree-pointer-drag-window-events' -import { flushWorktreePointerDragFrame } from './worktree-pointer-drag-flush' -import { - EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, - type WorktreePointerDrag -} from './worktree-row-drag-state' +} from '../../worktree-sidebar-pointer-drag-dom' +import { getWorktreeSidebarDragRectsForGroup } from '../../worktree-sidebar-drag-autoscroll' +import { getWorktreeSidebarDragGrab } from '../../worktree-sidebar-drag-geometry' +import { NOOP_WORKSPACE_BOARD_DRAG_PREVIEW_CALLBACK } from '../viewport/viewport-props' +import type { WorktreeStatusDropAtIndexArgs } from '../viewport/viewport-props' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import type { WorktreeDragRuntime } from './use-runtime' +import type { WorktreeDragSession } from './use-session' +import { useWorktreePointerDragAutoscroll } from './use-pointer-autoscroll' +import { useWorktreePointerDragWindowEvents } from './use-pointer-window-events' +import { flushWorktreePointerDragFrame } from './pointer-flush' +import { EMPTY_WORKTREE_DRAG_PREVIEW_OFFSETS, type WorktreePointerDrag } from './row-state' export function useWorktreePointerDrag(args: { ctx: WorktreeDropCommitContext diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag-window-events.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts similarity index 86% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag-window-events.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts index 745670dde4d..8e7a0fc1a91 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-pointer-drag-window-events.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-pointer-window-events.ts @@ -1,10 +1,10 @@ import { useEffect } from 'react' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import type { WorktreeStatusDropAtIndexArgs } from './virtualized-worktree-viewport-props' -import { commitWorktreePointerDrop } from './worktree-pointer-drag-commit' -import { SIDEBAR_POINTER_DRAG_THRESHOLD_PX } from './worktree-sidebar-scroll-tuning' -import type { WorktreePointerDrag } from './worktree-row-drag-state' +import type { WorktreeDragRuntime } from './use-runtime' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import type { WorktreeStatusDropAtIndexArgs } from '../viewport/viewport-props' +import { commitWorktreePointerDrop } from './pointer-commit' +import { SIDEBAR_POINTER_DRAG_THRESHOLD_PX } from '../scroll/tuning' +import type { WorktreePointerDrag } from './row-state' // Pointer drags escape the row that started them, so move/up/cancel are tracked on the // window in capture phase. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-drag-runtime.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-runtime.ts similarity index 92% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-drag-runtime.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-runtime.ts index 978c31c5cc3..a245cc26500 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-drag-runtime.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-runtime.ts @@ -1,18 +1,18 @@ import { useCallback, useMemo, useRef, useState } from 'react' import type React from 'react' -import type { WorkspaceStatus } from '../../../../../shared/worktree/types' -import { clearWorkspaceKanbanSidebarDropTargetVisual } from '../workspace-kanban-sidebar-drop' -import { setSidebarPointerDragDocumentStyles } from '../worktree-sidebar-pointer-drag-dom' +import type { WorkspaceStatus } from '../../../../../../shared/worktree/types' +import { clearWorkspaceKanbanSidebarDropTargetVisual } from '../../workspace-kanban-sidebar-drop' +import { setSidebarPointerDragDocumentStyles } from '../../worktree-sidebar-pointer-drag-dom' import type { WorktreeSidebarDragPoint, WorktreeSidebarDragSession -} from '../worktree-sidebar-drag-autoscroll' -import type { WorktreeSidebarDropAnchor } from '../worktree-sidebar-drag-geometry' +} from '../../worktree-sidebar-drag-autoscroll' +import type { WorktreeSidebarDropAnchor } from '../../worktree-sidebar-drag-geometry' import { WORKTREE_ROW_DRAG_INITIAL_STATE, type WorktreePointerDrag, type WorktreeRowDragState -} from './worktree-row-drag-state' +} from './row-state' export type WorktreeDragRuntime = ReturnType diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-drag-session.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts similarity index 92% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-drag-session.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts index f1077c71ff0..a6fe3d438a3 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-drag-session.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-session.ts @@ -1,29 +1,29 @@ import { useCallback, useMemo, useRef } from 'react' import type React from 'react' -import type { WorkspaceStatus } from '../../../../../shared/worktree/types' -import type { HostSectionRow } from '../host-section-rows' -import { PINNED_GROUP_KEY } from '../worktree-list-groups' -import { WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP } from '../worktree-list-virtual-rows' -import { getWorkspaceStatusGroupKey } from '../workspace-status' -import { expandDraggedWorktreeIdsForVisibleLineage } from '../worktree-manual-order' -import { getWorktreeDragUnitGroups } from '../worktree-drag-units' +import type { WorkspaceStatus } from '../../../../../../shared/worktree/types' +import type { HostSectionRow } from '../../host-section-rows' +import { PINNED_GROUP_KEY } from '../rows/groups' +import { WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP } from '../viewport/virtual-rows' +import { getWorkspaceStatusGroupKey } from '../../workspace-status' +import { expandDraggedWorktreeIdsForVisibleLineage } from '../../worktree-manual-order' +import { getWorktreeDragUnitGroups } from '../../worktree-drag-units' import { getWorktreeSidebarDragRectsForGroup, refreshWorktreeSidebarDragSession, type WorktreeSidebarDragRect, type WorktreeSidebarDragSession -} from '../worktree-sidebar-drag-autoscroll' +} from '../../worktree-sidebar-drag-autoscroll' import { shouldReevaluateWorktreeSidebarDropAnchor, type WorktreeSidebarDragGrab, type WorktreeSidebarDropAnchor -} from '../worktree-sidebar-drag-geometry' +} from '../../worktree-sidebar-drag-geometry' import { computeWorktreeSidebarDropPreview, type WorktreeSidebarDropPreview -} from '../worktree-sidebar-drop-preview' -import { getWorktreeDragGroups, getWorktreeDragIndexes } from './worktree-drag-groups' -import type { WorktreeItemRow } from './render-row-item-rows' +} from '../../worktree-sidebar-drop-preview' +import { getWorktreeDragGroups, getWorktreeDragIndexes } from './groups' +import type { WorktreeItemRow } from '../rows/renderable-rows' export type WorktreeStatusDropRequest = { pointerY: number diff --git a/src/renderer/src/components/sidebar/worktree-list/use-workspace-status-row-drag.ts b/src/renderer/src/components/sidebar/worktree-list/drag/use-status-row-drag.ts similarity index 89% rename from src/renderer/src/components/sidebar/worktree-list/use-workspace-status-row-drag.ts rename to src/renderer/src/components/sidebar/worktree-list/drag/use-status-row-drag.ts index cb45d99a93f..ab73b823cc6 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-workspace-status-row-drag.ts +++ b/src/renderer/src/components/sidebar/worktree-list/drag/use-status-row-drag.ts @@ -1,13 +1,13 @@ import { useCallback, useMemo } from 'react' import type React from 'react' -import type { WorkspaceStatus } from '../../../../../shared/worktree/types' -import type { HostSectionRow } from '../host-section-rows' -import { PINNED_GROUP_KEY, type WorktreeGroupBy } from '../worktree-list-groups' -import { hasWorkspaceDragData, readWorkspaceDragDataIds } from '../workspace-status' -import { useWorkspaceStatusDocumentDrop } from '../use-workspace-status-drop' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import type { WorktreeDragSession } from './use-worktree-drag-session' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' +import type { WorkspaceStatus } from '../../../../../../shared/worktree/types' +import type { HostSectionRow } from '../../host-section-rows' +import { PINNED_GROUP_KEY, type WorktreeGroupBy } from '../rows/groups' +import { hasWorkspaceDragData, readWorkspaceDragDataIds } from '../../workspace-status' +import { useWorkspaceStatusDocumentDrop } from '../../use-workspace-status-drop' +import type { WorktreeDropCommitContext } from './drop-commit-context' +import type { WorktreeDragSession } from './use-session' +import type { WorktreeDragRuntime } from './use-runtime' // Drag-and-drop onto a status section header or a status-grouped row, including the // document-level fallback used when the pointer leaves the sidebar mid-drag. diff --git a/src/renderer/src/components/sidebar/worktree-list/FolderPathStatusIndicator.tsx b/src/renderer/src/components/sidebar/worktree-list/headers/FolderPathStatusIndicator.tsx similarity index 95% rename from src/renderer/src/components/sidebar/worktree-list/FolderPathStatusIndicator.tsx rename to src/renderer/src/components/sidebar/worktree-list/headers/FolderPathStatusIndicator.tsx index 9b699c1086c..7e74055947e 100644 --- a/src/renderer/src/components/sidebar/worktree-list/FolderPathStatusIndicator.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/headers/FolderPathStatusIndicator.tsx @@ -9,7 +9,7 @@ import { import { isConfirmedStaleFolderPathStatus, type FolderWorkspacePathStatus -} from '../../../../../shared/folder-workspace-path-status' +} from '../../../../../../shared/folder-workspace-path-status' export function FolderPathStatusIndicator({ status diff --git a/src/renderer/src/components/sidebar/worktree-list/HostSectionHeader.tsx b/src/renderer/src/components/sidebar/worktree-list/headers/HostSectionHeader.tsx similarity index 97% rename from src/renderer/src/components/sidebar/worktree-list/HostSectionHeader.tsx rename to src/renderer/src/components/sidebar/worktree-list/headers/HostSectionHeader.tsx index 898719956f2..3df01f5fe7f 100644 --- a/src/renderer/src/components/sidebar/worktree-list/HostSectionHeader.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/headers/HostSectionHeader.tsx @@ -3,8 +3,8 @@ import { AlertTriangle, ChevronDown, Loader2, Server, ServerOff } from 'lucide-r import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' -import type { HostHeaderRow } from '../host-section-rows' -import { HostSectionHeaderMenu } from '../HostSectionHeaderMenu' +import type { HostHeaderRow } from '../../host-section-rows' +import { HostSectionHeaderMenu } from '../../HostSectionHeaderMenu' function formatSectionActivityLabel(count: number, label: string): string { return `${count} ${label}${count === 1 ? '' : 's'}` diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-section-header-row.tsx b/src/renderer/src/components/sidebar/worktree-list/headers/SectionHeader.tsx similarity index 94% rename from src/renderer/src/components/sidebar/worktree-list/worktree-section-header-row.tsx rename to src/renderer/src/components/sidebar/worktree-list/headers/SectionHeader.tsx index dd77e3dd0da..83e3a823b3b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-section-header-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/headers/SectionHeader.tsx @@ -5,24 +5,24 @@ import { cn } from '@/lib/utils' import type { AppState } from '@/store/types' import { RepoIconGlyph } from '@/components/repo/repo-icon' import { RepoForkIndicator } from '@/components/repo/repo-fork-indicator' -import type { FolderWorkspacePathStatus } from '../../../../../shared/folder-workspace-path-status' -import { isConfirmedStaleFolderPathStatus } from '../../../../../shared/folder-workspace-path-status' -import type { ProjectGroup } from '../../../../../shared/project-group-types' +import type { FolderWorkspacePathStatus } from '../../../../../../shared/folder-workspace-path-status' +import { isConfirmedStaleFolderPathStatus } from '../../../../../../shared/folder-workspace-path-status' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' import type { WorkspaceStatus, WorkspaceStatusDefinition -} from '../../../../../shared/worktree/types' -import type { GroupHeaderRow, WorktreeGroupBy } from '../worktree-list-groups' -import { PINNED_GROUP_KEY } from '../worktree-list-groups' -import { getWorkspaceStatusFromGroupKey } from '../workspace-status' -import { getVirtualRowTransform } from '../worktree-list-virtual-rows' -import { resolveProjectGroupHeaderColor } from '../project-header-color' -import { getRepoHeaderCreateState } from '../repo-header-create-state' -import { ProjectHeaderActions } from '../ProjectHeaderActions' +} from '../../../../../../shared/worktree/types' +import type { GroupHeaderRow, WorktreeGroupBy } from '../rows/groups' +import { PINNED_GROUP_KEY } from '../rows/groups' +import { getWorkspaceStatusFromGroupKey } from '../../workspace-status' +import { getVirtualRowTransform } from '../viewport/virtual-rows' +import { resolveProjectGroupHeaderColor } from '../../project-header-color' +import { getRepoHeaderCreateState } from '../../repo-header-create-state' +import { ProjectHeaderActions } from '../../ProjectHeaderActions' import { getProjectGroupHeaderPaddingLeft, WORKTREE_SECTION_HEADER_PADDING_LEFT -} from '../worktree-list-indentation' +} from '../rows/indentation' import { FolderPathStatusIndicator } from './FolderPathStatusIndicator' import { ProjectGroupCreateWorkspaceButton, @@ -36,9 +36,9 @@ import { import { handleRepoHeaderCollapseAffordancePointerDown, shouldIgnoreRepoHeaderToggle -} from './repo-header-event-guards' -import type { WorktreeSidebarHeaderDrag } from './use-worktree-sidebar-header-drag' -import { getWorktreeOptionId } from './worktree-option-dom' +} from './event-guards' +import type { WorktreeSidebarHeaderDrag } from '../drag/use-header-drag' +import { getWorktreeOptionId } from '../option-dom' export type SectionHeaderRowContext = { groupBy: WorktreeGroupBy diff --git a/src/renderer/src/components/sidebar/worktree-list/repo-header-event-guards.ts b/src/renderer/src/components/sidebar/worktree-list/headers/event-guards.ts similarity index 93% rename from src/renderer/src/components/sidebar/worktree-list/repo-header-event-guards.ts rename to src/renderer/src/components/sidebar/worktree-list/headers/event-guards.ts index c4ba87c3f50..19919703d59 100644 --- a/src/renderer/src/components/sidebar/worktree-list/repo-header-event-guards.ts +++ b/src/renderer/src/components/sidebar/worktree-list/headers/event-guards.ts @@ -1,5 +1,5 @@ import type React from 'react' -import { isRepoHeaderActionTarget } from '../project-header-drag' +import { isRepoHeaderActionTarget } from '../../project-header-drag' export function stopRepoHeaderKeyboardToggle(event: React.KeyboardEvent): void { if (event.key === 'Enter' || event.key === ' ') { diff --git a/src/renderer/src/components/sidebar/worktree-list/project-group-header-actions.tsx b/src/renderer/src/components/sidebar/worktree-list/headers/project-group-header-actions.tsx similarity index 93% rename from src/renderer/src/components/sidebar/worktree-list/project-group-header-actions.tsx rename to src/renderer/src/components/sidebar/worktree-list/headers/project-group-header-actions.tsx index fc9edf19114..62270c43742 100644 --- a/src/renderer/src/components/sidebar/worktree-list/project-group-header-actions.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/headers/project-group-header-actions.tsx @@ -11,14 +11,14 @@ import { import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' import { getFolderWorkspacePathStatusDescription } from '@/lib/folder-workspace-path-status' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { FolderWorkspacePathStatus } from '../../../../../shared/folder-workspace-path-status' -import { REPO_HEADER_ACTION_BUTTON_CLASS } from '../repo-header-action-button-class' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { FolderWorkspacePathStatus } from '../../../../../../shared/folder-workspace-path-status' +import { REPO_HEADER_ACTION_BUTTON_CLASS } from '../../repo-header-action-button-class' import { handleRepoHeaderActionPointerDown, stopRepoHeaderKeyboardToggle, stopRepoHeaderMenuEvent -} from './repo-header-event-guards' +} from './event-guards' export function ProjectGroupHeaderMenu({ groupId, diff --git a/src/renderer/src/components/sidebar/worktree-list/repo-header-project-actions.tsx b/src/renderer/src/components/sidebar/worktree-list/headers/repo-header-project-actions.tsx similarity index 89% rename from src/renderer/src/components/sidebar/worktree-list/repo-header-project-actions.tsx rename to src/renderer/src/components/sidebar/worktree-list/headers/repo-header-project-actions.tsx index c0a95f87c20..60a98017b7a 100644 --- a/src/renderer/src/components/sidebar/worktree-list/repo-header-project-actions.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/headers/repo-header-project-actions.tsx @@ -25,21 +25,36 @@ import { import { cn } from '@/lib/utils' import { translate } from '@/i18n/i18n' import { getRepositoryIconSectionId } from '@/components/settings/repository-settings-targets' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { WorktreeVisibilityDefaults } from '../../../../../shared/global-settings-types' -import { isGitRepoKind } from '../../../../../shared/repo-kind' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { WorktreeVisibilityDefaults } from '../../../../../../shared/global-settings-types' +import { isGitRepoKind } from '../../../../../../shared/repo-kind' +import { + effectiveExternalWorktreeVisibility, + isLegacyRepoForExternalWorktreeVisibility +} from '../../../../../../shared/worktree/ownership' import { REPO_HEADER_ACTION_BUTTON_CLASS, REPO_HEADER_ACTION_REVEAL_CLASS -} from '../repo-header-action-button-class' -import type { getRepoHeaderCreateState } from '../repo-header-create-state' +} from '../../repo-header-action-button-class' +import type { getRepoHeaderCreateState } from '../../repo-header-create-state' import { handleRepoHeaderActionPointerDown, stopRepoHeaderKeyboardToggle, stopRepoHeaderMenuEvent -} from './repo-header-event-guards' -import { getWorktreeVisibilityMenuLabel } from './worktree-visibility-menu-label' +} from './event-guards' + +function getWorktreeVisibilityMenuLabel( + repo: Repo, + visibilityDefaults?: WorktreeVisibilityDefaults +): string { + const visibility = effectiveExternalWorktreeVisibility( + repo, + isLegacyRepoForExternalWorktreeVisibility(repo), + visibilityDefaults + ) + return visibility === 'show' ? 'Hide non-Orca worktrees' : 'Show hidden worktrees' +} export type RepoHeaderProjectActions = { getWorktreeVisibilityDefaults: (repo: Repo) => WorktreeVisibilityDefaults | undefined diff --git a/src/renderer/src/components/sidebar/worktree-list/imported-worktrees-card-visibility.ts b/src/renderer/src/components/sidebar/worktree-list/imported-worktrees-card-visibility.ts deleted file mode 100644 index bfb7b38b039..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/imported-worktrees-card-visibility.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Row } from '../worktree-list-groups' -import type { ImportedWorktreeCardActionState } from '../imported-worktrees-card-actions' - -export function canKeepImportedWorktreesHidden( - row: Extract, - actionState: ImportedWorktreeCardActionState | undefined -): boolean { - return row.placement === 'repo-group' && actionState?.forceVisible !== true -} diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-option-dom.ts b/src/renderer/src/components/sidebar/worktree-list/option-dom.ts similarity index 100% rename from src/renderer/src/components/sidebar/worktree-list/worktree-option-dom.ts rename to src/renderer/src/components/sidebar/worktree-list/option-dom.ts diff --git a/src/renderer/src/components/sidebar/worktree-list/render-row-item-rows.ts b/src/renderer/src/components/sidebar/worktree-list/render-row-item-rows.ts deleted file mode 100644 index b0895ad6cd4..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/render-row-item-rows.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { PINNED_GROUP_KEY } from '../worktree-list-groups' -import type { HostSectionRow } from '../host-section-rows' - -export type WorktreeItemRow = Extract -export type FolderWorkspaceItemRow = Extract - -export function isWorktreeItemRow(row: HostSectionRow): row is WorktreeItemRow { - return row.type === 'item' -} - -export function isPinnedWorktreeRow(row: WorktreeItemRow): boolean { - return row.sectionKey === PINNED_GROUP_KEY -} diff --git a/src/renderer/src/components/sidebar/worktree-list/sidebar-row-reveal-ancestors.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/ancestors.ts similarity index 93% rename from src/renderer/src/components/sidebar/worktree-list/sidebar-row-reveal-ancestors.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/ancestors.ts index cc387daba32..6f09e8bdb3f 100644 --- a/src/renderer/src/components/sidebar/worktree-list/sidebar-row-reveal-ancestors.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/ancestors.ts @@ -1,11 +1,11 @@ -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { Worktree } from '../../../../../shared/worktree/types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { Worktree } from '../../../../../../shared/worktree/types' import { getProjectGroupHeaderKey, PINNED_GROUP_KEY, type ProjectGroupingModel -} from '../worktree-list-groups' +} from '../rows/groups' function getProjectIdFromHeaderRowKey(rowKey: string): string | null { if (!rowKey.startsWith('project:')) { diff --git a/src/renderer/src/components/sidebar/worktree-list-folder-reveal.test.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/folder-reveal.test.ts similarity index 88% rename from src/renderer/src/components/sidebar/worktree-list-folder-reveal.test.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/folder-reveal.test.ts index 61eca2889de..532c9fd6484 100644 --- a/src/renderer/src/components/sidebar/worktree-list-folder-reveal.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/folder-reveal.test.ts @@ -1,14 +1,14 @@ import { describe, expect, it } from 'vitest' -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../shared/project-group-types' -import type { Worktree } from '../../../../shared/worktree/types' -import { folderWorkspaceKey } from '../../../../shared/workspace-scope' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import { folderWorkspaceKey } from '../../../../../../shared/workspace-scope' import { getFolderWorkspaceRevealGroupKeys, getKnownSidebarWorktreeById, sidebarWorkspaceStillExists -} from './worktree-list-folder-reveal' -import { getProjectGroupHeaderKey } from './worktree-list-groups' +} from './folder-reveal' +import { getProjectGroupHeaderKey } from '../rows/groups' function makeFolderWorkspace(overrides: Partial = {}): FolderWorkspace { return { diff --git a/src/renderer/src/components/sidebar/worktree-list-folder-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/folder-reveal.ts similarity index 79% rename from src/renderer/src/components/sidebar/worktree-list-folder-reveal.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/folder-reveal.ts index de8ca2c278c..9694ce4552e 100644 --- a/src/renderer/src/components/sidebar/worktree-list-folder-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/folder-reveal.ts @@ -1,9 +1,9 @@ -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../shared/project-group-types' -import type { Worktree } from '../../../../shared/worktree/types' -import { folderWorkspaceToWorktree } from '../../../../shared/folder-workspace-worktree' -import { parseWorkspaceKey } from '../../../../shared/workspace-scope' -import { getProjectGroupHeaderKey } from './worktree-list-groups' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import { folderWorkspaceToWorktree } from '../../../../../../shared/folder-workspace-worktree' +import { parseWorkspaceKey } from '../../../../../../shared/workspace-scope' +import { getProjectGroupHeaderKey } from '../rows/groups' function findFolderWorkspaceByKey( worktreeId: string, diff --git a/src/renderer/src/components/sidebar/worktree-list/mounted-row-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/mounted-row.ts similarity index 91% rename from src/renderer/src/components/sidebar/worktree-list/mounted-row-reveal.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/mounted-row.ts index b3b1436211d..b22e55a5b0b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/mounted-row-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/mounted-row.ts @@ -1,5 +1,5 @@ -import { revealElementInScrollContainer } from '../worktree-sidebar-reveal' -import { getMountedWorktreeOptions, getWorktreeOptionId } from './worktree-option-dom' +import { revealElementInScrollContainer } from '../../worktree-sidebar-reveal' +import { getMountedWorktreeOptions, getWorktreeOptionId } from '../option-dom' export function revealMountedWorktreeElement( container: HTMLElement, diff --git a/src/renderer/src/components/sidebar/worktree-list/pending-sidebar-reveal-inputs.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/pending-inputs.ts similarity index 80% rename from src/renderer/src/components/sidebar/worktree-list/pending-sidebar-reveal-inputs.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/pending-inputs.ts index c29b08bd17f..ef19e779d58 100644 --- a/src/renderer/src/components/sidebar/worktree-list/pending-sidebar-reveal-inputs.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/pending-inputs.ts @@ -2,24 +2,24 @@ import type React from 'react' import type { Virtualizer } from '@tanstack/react-virtual' import type { AppState } from '@/store/types' import type { PendingSidebarRowReveal, PendingSidebarWorktreeReveal } from '@/store/slices/ui' -import type { FolderWorkspace } from '../../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { Repo } from '../../../../../shared/repo-types' -import type { WorkspaceStatusDefinition, Worktree } from '../../../../../shared/worktree/types' -import type { WorktreeLineage } from '../../../../../shared/worktree/lineage-types' -import type { ExecutionHostId } from '../../../../../shared/execution-host' -import { getWorktreeExecutionHostId } from '../../../../../shared/execution-host' -import type { RenderRow } from '../worktree-list-virtual-rows' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { WorkspaceStatusDefinition, Worktree } from '../../../../../../shared/worktree/types' +import type { WorktreeLineage } from '../../../../../../shared/worktree/lineage-types' +import type { ExecutionHostId } from '../../../../../../shared/execution-host' +import { getWorktreeExecutionHostId } from '../../../../../../shared/execution-host' +import type { RenderRow } from '../viewport/virtual-rows' import { getGroupKeysForWorktree, getLineageGroupKey, type PinnedWorktreeDisplayPolicy, type ProjectGroupingModel, type WorktreeGroupBy -} from '../worktree-list-groups' -import { getWorktreeLineageAncestors } from '../worktree-lineage-projection' -import { getFolderWorkspaceRevealGroupKeys } from '../worktree-list-folder-reveal' -import { getPinnedWorktreeRevealCollapsedGroupKeys } from './sidebar-row-reveal-ancestors' +} from '../rows/groups' +import { getWorktreeLineageAncestors } from '../../worktree-lineage-projection' +import { getFolderWorkspaceRevealGroupKeys } from './folder-reveal' +import { getPinnedWorktreeRevealCollapsedGroupKeys } from './ancestors' export const MAX_REVEAL_RETRIES = 8 diff --git a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-reveal-highlight.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/use-highlight.ts similarity index 100% rename from src/renderer/src/components/sidebar/worktree-list/use-sidebar-reveal-highlight.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/use-highlight.ts diff --git a/src/renderer/src/components/sidebar/worktree-list/use-pending-sidebar-reveal.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/use-pending.ts similarity index 95% rename from src/renderer/src/components/sidebar/worktree-list/use-pending-sidebar-reveal.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/use-pending.ts index 26cd2b352db..69cc396c28a 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-pending-sidebar-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/use-pending.ts @@ -2,21 +2,21 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { toast } from 'sonner' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' -import { getRenderRowOptionId } from './active-descendant-option' +import { getRenderRowOptionId } from '../viewport/active-descendant-option' import { findPreferredRenderRowIndexForWorktree, getRenderRowSidebarKey, rowKeyMatchesRenderRow -} from './render-row-worktree-lookup' -import { revealMountedSidebarRowElement, revealMountedWorktreeElement } from './mounted-row-reveal' -import { getSidebarRowRevealAncestorKeys } from './sidebar-row-reveal-ancestors' -import { sidebarWorkspaceStillExists } from '../worktree-list-folder-reveal' -import { resolvePendingSidebarReveal } from './worktree-sidebar-scroll-tuning' +} from '../rows/render-row-lookup' +import { revealMountedSidebarRowElement, revealMountedWorktreeElement } from './mounted-row' +import { getSidebarRowRevealAncestorKeys } from './ancestors' +import { sidebarWorkspaceStillExists } from './folder-reveal' +import { resolvePendingSidebarReveal } from '../scroll/tuning' import { expandGroupsForWorktreeReveal, MAX_REVEAL_RETRIES, type PendingSidebarRevealArgs -} from './pending-sidebar-reveal-inputs' +} from './pending-inputs' // Drives the store's two pending reveal requests (a worktree card, or any sidebar row) // from "expand the ancestors" through "scroll the mounted element into view". diff --git a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-reveal-requests.ts b/src/renderer/src/components/sidebar/worktree-list/reveal/use-requests.ts similarity index 92% rename from src/renderer/src/components/sidebar/worktree-list/use-sidebar-reveal-requests.ts rename to src/renderer/src/components/sidebar/worktree-list/reveal/use-requests.ts index 4737a7fc60a..9e57cc98885 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-reveal-requests.ts +++ b/src/renderer/src/components/sidebar/worktree-list/reveal/use-requests.ts @@ -4,10 +4,10 @@ import { SCROLL_TO_CURRENT_WORKSPACE_REVEAL_REQUEST_EVENT, type ScrollToCurrentWorkspaceRevealRequestDetail } from '@/lib/scroll-to-current-workspace-status' -import type { FolderWorkspace } from '../../../../../shared/folder-workspace-types' -import type { Worktree } from '../../../../../shared/worktree/types' -import type { WorktreeGroupBy } from '../worktree-list-groups' -import { getKnownSidebarWorktreeById } from '../worktree-list-folder-reveal' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import type { WorktreeGroupBy } from '../rows/groups' +import { getKnownSidebarWorktreeById } from './folder-reveal' // Turns a "show me the current workspace" request into whatever the sidebar must change // first — grouping mode, active filters — before the viewport can scroll to it. diff --git a/src/renderer/src/components/sidebar/worktree-list/folder-workspace-virtual-row.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx similarity index 82% rename from src/renderer/src/components/sidebar/worktree-list/folder-workspace-virtual-row.tsx rename to src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx index eb3dc2f3556..0a4f9f91e80 100644 --- a/src/renderer/src/components/sidebar/worktree-list/folder-workspace-virtual-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/folder-row.tsx @@ -1,23 +1,23 @@ import React from 'react' import type { VirtualItem } from '@tanstack/react-virtual' import type { AppState } from '@/store/types' -import type { Repo } from '../../../../../shared/repo-types' +import type { Repo } from '../../../../../../shared/repo-types' import type { WorkspaceLineage, WorktreeLineage -} from '../../../../../shared/worktree/lineage-types' -import type { Worktree } from '../../../../../shared/worktree/types' -import type { FolderWorkspacePathStatus } from '../../../../../shared/folder-workspace-path-status' -import { isConfirmedStaleFolderPathStatus } from '../../../../../shared/folder-workspace-path-status' -import { folderWorkspaceToWorktree } from '../../../../../shared/folder-workspace-worktree' -import WorktreeCard from '../WorktreeCard' -import type { WorktreeGroupBy } from '../worktree-list-groups' -import { getVirtualRowTransform } from '../worktree-list-virtual-rows' -import { getFolderWorkspaceRowGeometry } from '../worktree-list-indentation' -import { getFolderWorkspaceCardPrDisplay } from '../folder-workspace-card-pr-display' -import { FolderPathStatusIndicator } from './FolderPathStatusIndicator' -import type { FolderWorkspaceItemRow } from './render-row-item-rows' -import { getWorktreeOptionId } from './worktree-option-dom' +} from '../../../../../../shared/worktree/lineage-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import type { FolderWorkspacePathStatus } from '../../../../../../shared/folder-workspace-path-status' +import { isConfirmedStaleFolderPathStatus } from '../../../../../../shared/folder-workspace-path-status' +import { folderWorkspaceToWorktree } from '../../../../../../shared/folder-workspace-worktree' +import WorktreeCard from '../../WorktreeCard' +import type { WorktreeGroupBy } from './groups' +import { getVirtualRowTransform } from '../viewport/virtual-rows' +import { getFolderWorkspaceRowGeometry } from './indentation' +import { getFolderWorkspaceCardPrDisplay } from '../../folder-workspace-card-pr-display' +import { FolderPathStatusIndicator } from '../headers/FolderPathStatusIndicator' +import type { FolderWorkspaceItemRow } from './renderable-rows' +import { getWorktreeOptionId } from '../option-dom' export type FolderWorkspaceRowContext = { groupBy: WorktreeGroupBy diff --git a/src/renderer/src/components/sidebar/worktree-list-groups.test.ts b/src/renderer/src/components/sidebar/worktree-list/rows/groups.test.ts similarity index 99% rename from src/renderer/src/components/sidebar/worktree-list-groups.test.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/groups.test.ts index 7e6fe4b3b58..73813108206 100644 --- a/src/renderer/src/components/sidebar/worktree-list-groups.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/groups.test.ts @@ -2,8 +2,8 @@ import { readFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' -import { getExecutionHostLabel } from '../../../../shared/execution-host' -import { projectHostSetupProjectionFromRepos } from '../../../../shared/project-host-setup-projection' +import { getExecutionHostLabel } from '../../../../../../shared/execution-host' +import { projectHostSetupProjectionFromRepos } from '../../../../../../shared/project-host-setup-projection' import { ALL_GROUP_META, buildRows, @@ -14,18 +14,18 @@ import { getPRGroupKey, PINNED_GROUP_KEY, type PendingCreationRef -} from './worktree-list-groups' +} from './groups' import { REPO_HEADER_ACTION_BUTTON_CLASS, REPO_HEADER_ACTION_REVEAL_CLASS -} from './repo-header-action-button-class' -import { getWorktreeLineageAncestors } from './worktree-lineage-projection' -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../shared/project-group-types' -import type { Project, ProjectHostSetup } from '../../../../shared/project-types' -import type { Repo } from '../../../../shared/repo-types' -import type { WorktreeLineage } from '../../../../shared/worktree/lineage-types' -import type { DetectedWorktree, Worktree } from '../../../../shared/worktree/types' +} from '../../repo-header-action-button-class' +import { getWorktreeLineageAncestors } from '../../worktree-lineage-projection' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Project, ProjectHostSetup } from '../../../../../../shared/project-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { WorktreeLineage } from '../../../../../../shared/worktree/lineage-types' +import type { DetectedWorktree, Worktree } from '../../../../../../shared/worktree/types' const LOCAL_HOST_LABEL = getExecutionHostLabel('local') @@ -60,12 +60,12 @@ const worktree: Worktree = { const repoMap = new Map([[repo.id, repo]]) function readWorktreeListSource(): string { - return readFileSync(fileURLToPath(new URL('./WorktreeList.tsx', import.meta.url)), 'utf8') + return readFileSync(fileURLToPath(new URL('../../WorktreeList.tsx', import.meta.url)), 'utf8') } function readSectionHeaderRowSource(): string { return readFileSync( - fileURLToPath(new URL('./worktree-list/worktree-section-header-row.tsx', import.meta.url)), + fileURLToPath(new URL('../headers/SectionHeader.tsx', import.meta.url)), 'utf8' ) } diff --git a/src/renderer/src/components/sidebar/worktree-list-groups.ts b/src/renderer/src/components/sidebar/worktree-list/rows/groups.ts similarity index 97% rename from src/renderer/src/components/sidebar/worktree-list-groups.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/groups.ts index 068da94edb7..67b36fda423 100644 --- a/src/renderer/src/components/sidebar/worktree-list-groups.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/groups.ts @@ -1,36 +1,39 @@ /* eslint-disable max-lines -- Why: sidebar row construction keeps every grouping mode in one pure module so reveal, virtualized rendering, and tests share the same flat row contract. */ import { CircleX, FolderTree, List, Pin } from 'lucide-react' import type React from 'react' -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../shared/project-group-types' -import type { Project, ProjectHostSetup } from '../../../../shared/project-types' -import type { Repo } from '../../../../shared/repo-types' -import type { ProjectOrderBy } from '../../../../shared/ui-chrome-types' -import type { WorktreeLineage } from '../../../../shared/worktree/lineage-types' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Project, ProjectHostSetup } from '../../../../../../shared/project-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { ProjectOrderBy } from '../../../../../../shared/ui-chrome-types' +import type { WorktreeLineage } from '../../../../../../shared/worktree/lineage-types' import type { DetectedWorktree, WorkspaceStatusDefinition, Worktree -} from '../../../../shared/worktree/types' -import { branchName } from '../../lib/git-utils' +} from '../../../../../../shared/worktree/types' +import { branchName } from '../../../../lib/git-utils' import { getWorkspaceStatus, getWorkspaceStatusFromGroupKey, getWorkspaceStatusGroupKey, getWorkspaceStatusVisualMeta -} from './workspace-status' +} from '../../workspace-status' import { ConductorDoneIcon, ConductorProgressIcon, ConductorReviewIcon -} from './workspace-status-icons' +} from '../../workspace-status-icons' import { getEffectiveProjectGroupManualRank, UNGROUPED_PROJECT_GROUP_KEY -} from '../../../../shared/project-groups' -import { cloneDefaultWorkspaceStatuses } from '../../../../shared/workspace-statuses' -import type { AppState } from '../../store/types' -import { getGitHubPRCacheKey, getLegacyGitHubPRCacheKey } from '../../store/slices/github-cache-key' +} from '../../../../../../shared/project-groups' +import { cloneDefaultWorkspaceStatuses } from '../../../../../../shared/workspace-statuses' +import type { AppState } from '../../../../store/types' +import { + getGitHubPRCacheKey, + getLegacyGitHubPRCacheKey +} from '../../../../store/slices/github-cache-key' import { getRepoDisplayLabelKey, getRepoDisplayLabelsByPath } from '@/lib/repo-display-labels' import { translate } from '@/i18n/i18n' import { @@ -40,18 +43,18 @@ import { getWorktreeExecutionHostId, toSshExecutionHostId, type ExecutionHostId -} from '../../../../shared/execution-host' -import { parseWslUncPath } from '../../../../shared/wsl-paths' +} from '../../../../../../shared/execution-host' +import { parseWslUncPath } from '../../../../../../shared/wsl-paths' import { isWindowsAbsolutePathLike, normalizeRuntimePathForComparison -} from '../../../../shared/cross-platform-path' +} from '../../../../../../shared/cross-platform-path' import { getCyclicProjectedWorktreeLineageIds, getLineageRenderInfo -} from './worktree-lineage-projection' +} from '../../worktree-lineage-projection' -export { getLineageRenderInfo } from './worktree-lineage-projection' +export { getLineageRenderInfo } from '../../worktree-lineage-projection' export { branchName } diff --git a/src/renderer/src/components/sidebar/worktree-list-imported-rows.test.ts b/src/renderer/src/components/sidebar/worktree-list/rows/imported-rows.test.ts similarity index 89% rename from src/renderer/src/components/sidebar/worktree-list-imported-rows.test.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/imported-rows.test.ts index 552515ff936..0799488647e 100644 --- a/src/renderer/src/components/sidebar/worktree-list-imported-rows.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/imported-rows.test.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from 'vitest' -import { canKeepImportedWorktreesHidden } from './worktree-list/imported-worktrees-card-visibility' -import { getRenderRowKey } from './worktree-list-virtual-rows' -import { getWorktreeDragGroups, getWorktreeDragIndexes } from './worktree-list/worktree-drag-groups' -import { renderRowContainsWorktree } from './worktree-list/render-row-worktree-lookup' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' -import type { Row } from './worktree-list-groups' +import { canKeepImportedWorktreesHidden } from './notice-rows' +import { getRenderRowKey } from '../viewport/virtual-rows' +import { getWorktreeDragGroups, getWorktreeDragIndexes } from '../drag/groups' +import { renderRowContainsWorktree } from './render-row-lookup' +import type { Repo } from '../../../../../../shared/repo-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import type { Row } from './groups' const repo: Repo = { id: 'repo-1', diff --git a/src/renderer/src/components/sidebar/worktree-list-indentation.test.ts b/src/renderer/src/components/sidebar/worktree-list/rows/indentation.test.ts similarity index 99% rename from src/renderer/src/components/sidebar/worktree-list-indentation.test.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/indentation.test.ts index cb9d12aeaf5..55eb0711d90 100644 --- a/src/renderer/src/components/sidebar/worktree-list-indentation.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/indentation.test.ts @@ -20,7 +20,7 @@ import { getProjectGroupHeaderPaddingLeft, getWorktreeCardContentIndent, getWorktreeCardSurfaceInset -} from './worktree-list-indentation' +} from './indentation' function getFlushCardContentStart(args: { surfaceInset: number diff --git a/src/renderer/src/components/sidebar/worktree-list-indentation.ts b/src/renderer/src/components/sidebar/worktree-list/rows/indentation.ts similarity index 100% rename from src/renderer/src/components/sidebar/worktree-list-indentation.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/indentation.ts diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-list-item-row.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx similarity index 94% rename from src/renderer/src/components/sidebar/worktree-list/worktree-list-item-row.tsx rename to src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx index 8e0a5c070c5..3b24e11b2ad 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-list-item-row.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/item-row.tsx @@ -1,9 +1,9 @@ import React from 'react' import { cn } from '@/lib/utils' import type { AppState } from '@/store/types' -import type { Worktree } from '../../../../../shared/worktree/types' -import WorktreeCard, { type ActiveSurfaceVariant } from '../WorktreeCard' -import { PINNED_GROUP_KEY, type WorktreeGroupBy } from '../worktree-list-groups' +import type { Worktree } from '../../../../../../shared/worktree/types' +import WorktreeCard, { type ActiveSurfaceVariant } from '../../WorktreeCard' +import { PINNED_GROUP_KEY, type WorktreeGroupBy } from './groups' import { getFolderBackedRepoWorktreeCardContentIndent, getFolderBackedRepoWorktreeCardSurfaceInset, @@ -12,12 +12,12 @@ import { getWorktreeCardContentIndent, getWorktreeCardSurfaceInset, LINEAGE_CHILDREN_INLINE_OFFSET -} from '../worktree-list-indentation' -import type { LineageToggleHandler } from '../worktree-lineage-toggle-handler-cache' -import { stopNestedWorktreeCardBubble } from './repo-header-event-guards' -import type { WorktreeItemRow } from './render-row-item-rows' -import { getWorktreeOptionId } from './worktree-option-dom' -import type { WorktreePointerDrag, WorktreeRowDragState } from './worktree-row-drag-state' +} from './indentation' +import type { LineageToggleHandler } from '../../worktree-lineage-toggle-handler-cache' +import { stopNestedWorktreeCardBubble } from '../headers/event-guards' +import type { WorktreeItemRow } from './renderable-rows' +import { getWorktreeOptionId } from '../option-dom' +import type { WorktreePointerDrag, WorktreeRowDragState } from '../drag/row-state' export type WorktreeItemRowContext = { settings: AppState['settings'] diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-notice-virtual-rows.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/notice-rows.tsx similarity index 81% rename from src/renderer/src/components/sidebar/worktree-list/worktree-notice-virtual-rows.tsx rename to src/renderer/src/components/sidebar/worktree-list/rows/notice-rows.tsx index d997548f4bd..0882e4d86a7 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-notice-virtual-rows.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/notice-rows.tsx @@ -1,13 +1,19 @@ import React from 'react' import type { VirtualItem } from '@tanstack/react-virtual' -import type { Row } from '../worktree-list-groups' -import { getVirtualRowTransform } from '../worktree-list-virtual-rows' -import { PendingWorktreeRow } from '../PendingWorktreeRow' -import ImportedWorktreesVisibilityLine from '../ImportedWorktreesVisibilityLine' -import NewExternalWorktreesInboxLine from '../NewExternalWorktreesInboxLine' -import type { ImportedWorktreeCardActionState } from '../imported-worktrees-card-actions' -import type { NewExternalWorktreesInboxActionState } from '../new-external-worktrees-inbox-actions' -import { canKeepImportedWorktreesHidden } from './imported-worktrees-card-visibility' +import type { Row } from './groups' +import { getVirtualRowTransform } from '../viewport/virtual-rows' +import { PendingWorktreeRow } from '../../PendingWorktreeRow' +import ImportedWorktreesVisibilityLine from '../../ImportedWorktreesVisibilityLine' +import NewExternalWorktreesInboxLine from '../../NewExternalWorktreesInboxLine' +import type { ImportedWorktreeCardActionState } from '../../imported-worktrees-card-actions' +import type { NewExternalWorktreesInboxActionState } from '../../new-external-worktrees-inbox-actions' + +export function canKeepImportedWorktreesHidden( + row: Extract, + actionState: ImportedWorktreeCardActionState | undefined +): boolean { + return row.placement === 'repo-group' && actionState?.forceVisible !== true +} type NoticeRowFrameProps = { vItem: VirtualItem diff --git a/src/renderer/src/components/sidebar/worktree-list/render-row-worktree-lookup.ts b/src/renderer/src/components/sidebar/worktree-list/rows/render-row-lookup.ts similarity index 90% rename from src/renderer/src/components/sidebar/worktree-list/render-row-worktree-lookup.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/render-row-lookup.ts index 4605058d231..be74eaabf85 100644 --- a/src/renderer/src/components/sidebar/worktree-list/render-row-worktree-lookup.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/render-row-lookup.ts @@ -1,7 +1,7 @@ -import { folderWorkspaceKey } from '../../../../../shared/workspace-scope' -import type { RenderRow } from '../worktree-list-virtual-rows' -import type { PinnedWorktreeDisplayPolicy } from '../worktree-list-groups' -import { isPinnedWorktreeRow, type WorktreeItemRow } from './render-row-item-rows' +import { folderWorkspaceKey } from '../../../../../../shared/workspace-scope' +import type { RenderRow } from '../viewport/virtual-rows' +import type { PinnedWorktreeDisplayPolicy } from './groups' +import { isPinnedWorktreeRow, type WorktreeItemRow } from './renderable-rows' export function getRenderRowSidebarKey(row: RenderRow): string | null { if (row.type === 'header') { diff --git a/src/renderer/src/components/sidebar/worktree-list/renderable-rows.ts b/src/renderer/src/components/sidebar/worktree-list/rows/renderable-rows.ts similarity index 64% rename from src/renderer/src/components/sidebar/worktree-list/renderable-rows.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/renderable-rows.ts index cc7c5e7870b..1e897f9d3ee 100644 --- a/src/renderer/src/components/sidebar/worktree-list/renderable-rows.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/renderable-rows.ts @@ -1,7 +1,17 @@ -import { getLineageGroupKey } from '../worktree-list-groups' -import type { HostSectionRow } from '../host-section-rows' -import type { RenderRow } from '../worktree-list-virtual-rows' -import { isWorktreeItemRow, type WorktreeItemRow } from './render-row-item-rows' +import { getLineageGroupKey, PINNED_GROUP_KEY } from './groups' +import type { HostSectionRow } from '../../host-section-rows' +import type { RenderRow } from '../viewport/virtual-rows' + +export type WorktreeItemRow = Extract +export type FolderWorkspaceItemRow = Extract + +export function isWorktreeItemRow(row: HostSectionRow): row is WorktreeItemRow { + return row.type === 'item' +} + +export function isPinnedWorktreeRow(row: WorktreeItemRow): boolean { + return row.sectionKey === PINNED_GROUP_KEY +} // Collapse a parent and its visible lineage descendants into one virtual row so the card renders them inline. export function buildRenderableRows(rows: HostSectionRow[]): RenderRow[] { diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-virtual-row-context.ts b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-context.ts similarity index 85% rename from src/renderer/src/components/sidebar/worktree-list/worktree-virtual-row-context.ts rename to src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-context.ts index 2e0d2defa45..4390538289a 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-virtual-row-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-context.ts @@ -1,20 +1,20 @@ import type React from 'react' import type { AppState } from '@/store/types' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { RenderRow } from '../worktree-list-virtual-rows' -import type { LineageToggleHandler } from '../worktree-lineage-toggle-handler-cache' -import type { SidebarRevealHighlight } from './use-sidebar-reveal-highlight' -import type { WorktreeDragRuntime } from './use-worktree-drag-runtime' -import type { WorktreeDragSession } from './use-worktree-drag-session' -import type { useFolderWorkspacePathStatusRows } from './use-folder-workspace-path-status-rows' -import type { usePrimaryActiveWorktreeRow } from './use-primary-active-worktree-row' -import type { useWorkspaceStatusRowDrag } from './use-workspace-status-row-drag' -import type { useWorktreeNativeDrag } from './use-worktree-native-drag' -import type { WorktreeSidebarHeaderDrag } from './use-worktree-sidebar-header-drag' -import type { WorktreeListVirtualizer } from './use-worktree-list-virtualizer' -import type { VirtualizedWorktreeViewportProps } from './virtualized-worktree-viewport-props' -import type { WorktreeVirtualRowContext } from './worktree-virtual-row-dispatch' -import { getRepoOwnerWorktreeVisibilityDefaults } from '../../../store/worktree-visibility-defaults-by-host' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { RenderRow } from '../viewport/virtual-rows' +import type { LineageToggleHandler } from '../../worktree-lineage-toggle-handler-cache' +import type { SidebarRevealHighlight } from '../reveal/use-highlight' +import type { WorktreeDragRuntime } from '../drag/use-runtime' +import type { WorktreeDragSession } from '../drag/use-session' +import type { useFolderWorkspacePathStatusRows } from '../viewport/use-folder-path-statuses' +import type { usePrimaryActiveWorktreeRow } from '../viewport/use-active-row' +import type { useWorkspaceStatusRowDrag } from '../drag/use-status-row-drag' +import type { useWorktreeNativeDrag } from '../drag/use-native-drag' +import type { WorktreeSidebarHeaderDrag } from '../drag/use-header-drag' +import type { WorktreeListVirtualizer } from '../viewport/use-virtualizer' +import type { VirtualizedWorktreeViewportProps } from '../viewport/viewport-props' +import type { WorktreeVirtualRowContext } from './virtual-row-dispatch' +import { getRepoOwnerWorktreeVisibilityDefaults } from '../../../../store/worktree-visibility-defaults-by-host' type BuildArgs = { props: VirtualizedWorktreeViewportProps diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-virtual-row-dispatch.tsx b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx similarity index 90% rename from src/renderer/src/components/sidebar/worktree-list/worktree-virtual-row-dispatch.tsx rename to src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx index b17c81439d9..dbed069e22e 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-virtual-row-dispatch.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/rows/virtual-row-dispatch.tsx @@ -4,38 +4,35 @@ import { cn } from '@/lib/utils' import type { WorkspaceStatus, WorkspaceStatusDefinition -} from '../../../../../shared/worktree/types' -import { getWorkspaceStatus } from '../workspace-status' -import type { WorktreeGroupBy } from '../worktree-list-groups' +} from '../../../../../../shared/worktree/types' +import { getWorkspaceStatus } from '../../workspace-status' +import type { WorktreeGroupBy } from './groups' import { getVirtualRowTransform, shouldUseHeaderTopSpacing, type RenderRow -} from '../worktree-list-virtual-rows' -import type { ImportedWorktreeCardActionState } from '../imported-worktrees-card-actions' -import type { NewExternalWorktreesInboxActionState } from '../new-external-worktrees-inbox-actions' -import type { useHostHeaderDrag } from '../host-header-drag' -import { HostSectionHeader } from './HostSectionHeader' -import { - renderFolderWorkspaceVirtualRow, - type FolderWorkspaceRowContext -} from './folder-workspace-virtual-row' +} from '../viewport/virtual-rows' +import type { ImportedWorktreeCardActionState } from '../../imported-worktrees-card-actions' +import type { NewExternalWorktreesInboxActionState } from '../../new-external-worktrees-inbox-actions' +import type { useHostHeaderDrag } from '../../host-header-drag' +import { HostSectionHeader } from '../headers/HostSectionHeader' +import { renderFolderWorkspaceVirtualRow, type FolderWorkspaceRowContext } from './folder-row' import { renderImportedWorktreesVirtualRow, renderNewExternalWorktreesInboxVirtualRow, renderPendingCreationVirtualRow -} from './worktree-notice-virtual-rows' +} from './notice-rows' import { renderWorktreeItemRow, renderWorktreeLineageDescendants, type WorktreeItemRowContext -} from './worktree-list-item-row' +} from './item-row' import { renderWorktreeSectionHeaderRow, type SectionHeaderRowContext -} from './worktree-section-header-row' -import { getWorktreeVirtualRowTransform } from './virtual-row-dom-attributes' -import type { WorktreeRowDragState } from './worktree-row-drag-state' +} from '../headers/SectionHeader' +import { getWorktreeVirtualRowTransform } from '../viewport/virtual-rows' +import type { WorktreeRowDragState } from '../drag/row-state' export type WorktreeVirtualRowContext = { renderRows: RenderRow[] diff --git a/src/renderer/src/components/sidebar/worktree-list-hard-scroll-up.test.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/hard-scroll-up.test.ts similarity index 99% rename from src/renderer/src/components/sidebar/worktree-list-hard-scroll-up.test.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/hard-scroll-up.test.ts index c3a51731e52..83128038ec2 100644 --- a/src/renderer/src/components/sidebar/worktree-list-hard-scroll-up.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/hard-scroll-up.test.ts @@ -7,7 +7,7 @@ import { reduceHardScrollUpOnIdle, reduceHardScrollUpOnScroll, reduceHardScrollUpOnWheel -} from './worktree-list-hard-scroll-up' +} from './hard-scroll-up' const DEEP = { scrollTop: 1200, diff --git a/src/renderer/src/components/sidebar/worktree-list-hard-scroll-up.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/hard-scroll-up.ts similarity index 100% rename from src/renderer/src/components/sidebar/worktree-list-hard-scroll-up.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/hard-scroll-up.ts diff --git a/src/renderer/src/components/sidebar/worktree-list-scroll-adjustment.test.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/scroll-adjustment.test.ts similarity index 96% rename from src/renderer/src/components/sidebar/worktree-list-scroll-adjustment.test.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/scroll-adjustment.test.ts index dd6f7c0689a..73ca1cf1c90 100644 --- a/src/renderer/src/components/sidebar/worktree-list-scroll-adjustment.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/scroll-adjustment.test.ts @@ -3,19 +3,19 @@ import { countRecordKeysByReference, resolvePendingSidebarReveal, shouldAdjustWorktreeSidebarMeasuredRowScroll -} from './worktree-list/worktree-sidebar-scroll-tuning' +} from './tuning' import { getScrollTopToRevealBounds, WORKTREE_SIDEBAR_REVEAL_TOP_INSET -} from './worktree-sidebar-reveal' +} from '../../worktree-sidebar-reveal' import { extractWorktreeVirtualRowIndexes, estimateRenderRowSize, GROUP_HEADER_ROW_HEIGHT, getActiveStickyHeaderIndexForScroll -} from './worktree-list-virtual-rows' -import type { Repo } from '../../../../shared/repo-types' -import type { Row } from './worktree-list-groups' +} from '../viewport/virtual-rows' +import type { Repo } from '../../../../../../shared/repo-types' +import type { Row } from '../rows/groups' const repo: Repo = { id: 'repo-1', diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-sidebar-scroll-tuning.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/tuning.ts similarity index 85% rename from src/renderer/src/components/sidebar/worktree-list/worktree-sidebar-scroll-tuning.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/tuning.ts index bb404507e26..6a163d36acc 100644 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-sidebar-scroll-tuning.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/tuning.ts @@ -40,3 +40,8 @@ export function resolvePendingSidebarReveal(args: { } return args.targetWorktreeStillExists ? 'keep-pending' : 'clear' } + +export function installWorktreeVisibleRefreshVisibilityListener(onChange: () => void): () => void { + document.addEventListener('visibilitychange', onChange) + return () => document.removeEventListener('visibilitychange', onChange) +} diff --git a/src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.test.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.test.ts similarity index 97% rename from src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.test.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.test.ts index 286221602e1..e68c6c5c182 100644 --- a/src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.test.ts @@ -1,8 +1,8 @@ // @vitest-environment happy-dom import { act, renderHook } from '@testing-library/react' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { useWorktreeListScrollToTop } from './use-worktree-list-scroll-to-top' -import { HARD_SCROLL_UP } from './worktree-list-hard-scroll-up' +import { useWorktreeListScrollToTop } from './use-scroll-to-top' +import { HARD_SCROLL_UP } from './hard-scroll-up' let clockMs = 0 diff --git a/src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.test.tsx b/src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.test.tsx similarity index 97% rename from src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.test.tsx rename to src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.test.tsx index f35bde00ffc..a7337c4f954 100644 --- a/src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.test.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.test.tsx @@ -2,7 +2,7 @@ import { act, cleanup, renderHook } from '@testing-library/react' import { afterEach, describe, expect, it, vi } from 'vitest' -import { useWorktreeListScrollToTop } from './use-worktree-list-scroll-to-top' +import { useWorktreeListScrollToTop } from './use-scroll-to-top' function createScroller(): HTMLElement { const element = document.createElement('div') diff --git a/src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.ts similarity index 99% rename from src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.ts index 3199c69543a..d732aa71cec 100644 --- a/src/renderer/src/components/sidebar/use-worktree-list-scroll-to-top.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/use-scroll-to-top.ts @@ -7,7 +7,7 @@ import { reduceHardScrollUpOnScroll, reduceHardScrollUpOnWheel, type HardScrollUpDetectorState -} from './worktree-list-hard-scroll-up' +} from './hard-scroll-up' function readViewport(element: HTMLElement): { scrollTop: number; maxScroll: number } { const maxScroll = Math.max(0, element.scrollHeight - element.clientHeight) diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-sidebar-scroll-suppression.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/use-suppression.ts similarity index 96% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-sidebar-scroll-suppression.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/use-suppression.ts index e606c270a70..442dc36400f 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-sidebar-scroll-suppression.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/use-suppression.ts @@ -1,15 +1,15 @@ import { useCallback, useEffect, useRef } from 'react' import type React from 'react' -import { SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT } from '../WorktreeCardAgents' +import { SUPPRESS_WORKTREE_LIST_SCROLL_ADJUSTMENT_EVENT } from '../../WorktreeCardAgents' import { createPendingRevealScroll, isRevealScrollSettling, type PendingRevealScroll -} from '../worktree-sidebar-reveal-scroll-settle' +} from '../../worktree-sidebar-reveal-scroll-settle' import { EXPANDING_CARD_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS, USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS -} from './worktree-sidebar-scroll-tuning' +} from './tuning' export type WorktreeSidebarScrollSuppression = ReturnType< typeof useWorktreeSidebarScrollSuppression diff --git a/src/renderer/src/components/sidebar/worktree-list-visible-refresh.test.ts b/src/renderer/src/components/sidebar/worktree-list/scroll/visible-refresh.test.ts similarity index 95% rename from src/renderer/src/components/sidebar/worktree-list-visible-refresh.test.ts rename to src/renderer/src/components/sidebar/worktree-list/scroll/visible-refresh.test.ts index b99c0957d52..aea1c032d04 100644 --- a/src/renderer/src/components/sidebar/worktree-list-visible-refresh.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/scroll/visible-refresh.test.ts @@ -1,5 +1,5 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' -import { installWorktreeVisibleRefreshVisibilityListener } from './worktree-list/worktree-visible-refresh-listener' +import { installWorktreeVisibleRefreshVisibilityListener } from './tuning' describe('installWorktreeVisibleRefreshVisibilityListener', () => { beforeEach(() => { diff --git a/src/renderer/src/components/sidebar/worktree-list/sidebar-editable-target.ts b/src/renderer/src/components/sidebar/worktree-list/sidebar-editable-target.ts deleted file mode 100644 index 390df9dae2f..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/sidebar-editable-target.ts +++ /dev/null @@ -1,19 +0,0 @@ -export function isEditableTarget(target: EventTarget | null): boolean { - if (!(target instanceof HTMLElement)) { - return false - } - - // xterm's hidden input textarea isn't a real text field; treating it as one would block sidebar shortcuts. - if (target.classList.contains('xterm-helper-textarea')) { - return false - } - - if (target.isContentEditable) { - return true - } - - return ( - target.closest('input, textarea, select, [contenteditable=""], [contenteditable="true"]') !== - null - ) -} diff --git a/src/renderer/src/components/sidebar/worktree-list/stable-empty-store-slices.ts b/src/renderer/src/components/sidebar/worktree-list/stable-empty-store-slices.ts deleted file mode 100644 index 49cae96a88b..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/stable-empty-store-slices.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { AppState } from '@/store/types' - -// Why: selectors that opt out of a slice must return one stable reference, or every store tick looks like a change. -export const EMPTY_AGENT_STATUS_BY_PANE_KEY: AppState['agentStatusByPaneKey'] = {} -export const EMPTY_WORKTREE_ID_SET: ReadonlySet = new Set() -export const EMPTY_TABS_BY_WORKTREE: AppState['tabsByWorktree'] = {} -export const EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID: AppState['terminalLayoutsByTabId'] = {} -export const EMPTY_PTY_IDS_BY_TAB_ID: AppState['ptyIdsByTabId'] = {} -export const EMPTY_RUNTIME_PANE_TITLES_BY_TAB_ID: AppState['runtimePaneTitlesByTabId'] = {} diff --git a/src/renderer/src/components/sidebar/worktree-list/use-agent-send-target-worktree.ts b/src/renderer/src/components/sidebar/worktree-list/use-agent-send-target-worktree.ts index 61269d78225..50fce3f1ed8 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-agent-send-target-worktree.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-agent-send-target-worktree.ts @@ -1,13 +1,14 @@ import { useMemo } from 'react' import { useAppStore } from '@/store' +import type { AppState } from '@/store/types' import { deriveRunningAgentSendTargets } from '@/lib/running-agent-targets' -import { - EMPTY_AGENT_STATUS_BY_PANE_KEY, - EMPTY_PTY_IDS_BY_TAB_ID, - EMPTY_RUNTIME_PANE_TITLES_BY_TAB_ID, - EMPTY_TABS_BY_WORKTREE, - EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID -} from './stable-empty-store-slices' + +// Why: selectors that opt out of a slice must return one stable reference, or every store tick looks like a change. +const EMPTY_AGENT_STATUS_BY_PANE_KEY: AppState['agentStatusByPaneKey'] = {} +const EMPTY_TABS_BY_WORKTREE: AppState['tabsByWorktree'] = {} +const EMPTY_TERMINAL_LAYOUTS_BY_TAB_ID: AppState['terminalLayoutsByTabId'] = {} +const EMPTY_PTY_IDS_BY_TAB_ID: AppState['ptyIdsByTabId'] = {} +const EMPTY_RUNTIME_PANE_TITLES_BY_TAB_ID: AppState['runtimePaneTitlesByTabId'] = {} // The agent send picker forces its target workspace visible, but only while a running // agent there can actually receive the message. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-effective-collapsed-groups.ts b/src/renderer/src/components/sidebar/worktree-list/use-effective-collapsed-groups.ts index 681056b5fe6..d8c89febdee 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-effective-collapsed-groups.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-effective-collapsed-groups.ts @@ -10,7 +10,7 @@ import { PINNED_GROUP_KEY, type ProjectGroupingModel, type WorktreeGroupBy -} from '../worktree-list-groups' +} from './rows/groups' import { getWorktreeLineageAncestors } from '../worktree-lineage-projection' // While the agent send picker targets a workspace, force open every section that hides it. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-host-visible-scope.ts b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-host-visible-scope.ts index 0a0b4cea44a..7de2a2fa1a4 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-host-visible-scope.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-host-visible-scope.ts @@ -9,7 +9,7 @@ import { filterFolderWorkspacesForVisibleHosts, filterProjectGroupsForVisibleHosts, getVisibleSidebarHostIdSet -} from '../worktree-list-host-filtering' +} from './viewport/host-filtering' // Narrows repos, project groups, and folder workspaces to the hosts (and devices) the // current host filter admits. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-section-rows.ts b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-section-rows.ts index 9b139e88719..8ca71a0245c 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-section-rows.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-section-rows.ts @@ -17,7 +17,7 @@ import { type ProjectGroupingModel, type Row, type WorktreeGroupBy -} from '../worktree-list-groups' +} from './rows/groups' import { getLogicalRepoOrderRankById } from '../project-header-drop' import { getEmptyProjectPlaceholderRepoIds } from '../empty-project-placeholder-repos' import { addHostSectionRows } from '../host-section-rows' diff --git a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-selection.ts b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-selection.ts index 426385c6063..74e365b6865 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-selection.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-selection.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from 'reac import type React from 'react' import type { Worktree } from '../../../../../shared/worktree/types' import type { HostSectionRow } from '../host-section-rows' -import type { PinnedWorktreeDisplayPolicy } from '../worktree-list-groups' +import type { PinnedWorktreeDisplayPolicy } from './rows/groups' import { getRenderedWorktreesInSidebarOrder } from '../worktree-sidebar-row-preference' import { setVisibleWorktreeIds } from '../visible-worktrees' import { diff --git a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-sort-order.ts b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-sort-order.ts index 64b2e426abf..ed276cdbf49 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-sort-order.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-sidebar-worktree-sort-order.ts @@ -14,7 +14,7 @@ import { type WorktreeAttention } from '../smart-attention' import { useReusedArrayIdentity } from './use-reused-array-identity' -import { SORT_SETTLE_MS } from './worktree-sidebar-scroll-tuning' +import { SORT_SETTLE_MS } from './scroll/tuning' function trackSmartClassDistribution(attention: ReadonlyMap): void { let class1 = 0 diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-status-mutations.ts b/src/renderer/src/components/sidebar/worktree-list/use-status-mutations.ts similarity index 98% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-status-mutations.ts rename to src/renderer/src/components/sidebar/worktree-list/use-status-mutations.ts index 77f9cc38b3f..aa58afa10a2 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-status-mutations.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-status-mutations.ts @@ -15,7 +15,7 @@ import { } from '../worktree-manual-order' import { buildWorkspaceKanbanSidebarDropUpdates } from '../workspace-kanban-sidebar-drop' import type { SortBy } from '../smart-sort' -import type { WorktreeStatusDropAtIndexArgs } from './virtualized-worktree-viewport-props' +import type { WorktreeStatusDropAtIndexArgs } from './viewport/viewport-props' function buildRankByWorktreeId( groups: readonly WorktreeDragGroup[], diff --git a/src/renderer/src/components/sidebar/worktree-list/use-visible-sidebar-worktrees.ts b/src/renderer/src/components/sidebar/worktree-list/use-visible-sidebar-worktrees.ts index bd1f37fc708..7f560d20af0 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-visible-sidebar-worktrees.ts +++ b/src/renderer/src/components/sidebar/worktree-list/use-visible-sidebar-worktrees.ts @@ -17,9 +17,10 @@ import { } from '../visible-worktree-activity-inputs' import type { SortBy } from '../smart-sort' import type { SidebarWorktreeFilters } from './use-sidebar-worktree-filters' -import { EMPTY_WORKTREE_ID_SET } from './stable-empty-store-slices' import { useReusedArrayIdentity } from './use-reused-array-identity' +const EMPTY_WORKTREE_ID_SET: ReadonlySet = new Set() + // Applies every sidebar filter to the sorted id stream. Flatten/filter/sort goes through the // shared utility so card order matches Cmd+1–9 numbering. export function useVisibleSidebarWorktrees(args: { diff --git a/src/renderer/src/components/sidebar/worktree-list/VirtualizedWorktreeViewport.tsx b/src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx similarity index 86% rename from src/renderer/src/components/sidebar/worktree-list/VirtualizedWorktreeViewport.tsx rename to src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx index 8e1d8f8a7d1..4b87d668701 100644 --- a/src/renderer/src/components/sidebar/worktree-list/VirtualizedWorktreeViewport.tsx +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/VirtualizedWorktreeViewport.tsx @@ -1,37 +1,34 @@ import React, { useCallback, useMemo, useRef, useState } from 'react' import { useAppStore } from '@/store' import { translate } from '@/i18n/i18n' -import { WorktreeSidebarDropIndicator } from '../WorktreeSidebarDropIndicator' -import { WorktreeListScrollToTopButton } from '../WorktreeListScrollToTopButton' -import { useWorktreeListScrollToTop } from '../use-worktree-list-scroll-to-top' +import { WorktreeSidebarDropIndicator } from '../../WorktreeSidebarDropIndicator' +import { WorktreeListScrollToTopButton } from '../../WorktreeListScrollToTopButton' +import { useWorktreeListScrollToTop } from '../scroll/use-scroll-to-top' import { getActiveDescendantOptionId } from './active-descendant-option' -import { buildRenderableRows } from './renderable-rows' -import { useFolderWorkspacePathStatusRows } from './use-folder-workspace-path-status-rows' -import { useGroupToggleWithScrollAnchor } from './use-group-toggle-with-scroll-anchor' -import { usePendingSidebarReveal } from './use-pending-sidebar-reveal' -import { usePrimaryActiveWorktreeRow } from './use-primary-active-worktree-row' -import { useSidebarRevealHighlight } from './use-sidebar-reveal-highlight' -import { useVirtualRowMeasurementSync } from './use-virtual-row-measurement-sync' -import { useVisiblePrRefreshReporting } from './use-visible-pr-refresh-reporting' -import { useWorkspaceStatusRowDrag } from './use-workspace-status-row-drag' -import { useWorktreeDragRuntime } from './use-worktree-drag-runtime' -import { useWorktreeDragSession } from './use-worktree-drag-session' -import { useWorktreeDocumentDrop } from './use-worktree-document-drop' -import { useWorktreeLineageDropCommit } from './use-worktree-lineage-drop-commit' -import { useWorktreeListKeyboardNavigation } from './use-worktree-list-keyboard-navigation' -import { useWorktreeListVirtualizer } from './use-worktree-list-virtualizer' -import { useWorktreeNativeDrag } from './use-worktree-native-drag' -import { useWorktreePointerDrag } from './use-worktree-pointer-drag' -import { useWorktreeSidebarHeaderDrag } from './use-worktree-sidebar-header-drag' -import { useWorktreeSidebarScrollSuppression } from './use-worktree-sidebar-scroll-suppression' -import { - EMPTY_PROJECT_GROUPS, - type VirtualizedWorktreeViewportProps -} from './virtualized-worktree-viewport-props' -import { WORKTREE_SIDEBAR_SCROLL_STYLE } from './worktree-sidebar-scroll-tuning' -import type { WorktreeDropCommitContext } from './worktree-drop-commit-context' -import { buildWorktreeVirtualRowContext } from './worktree-virtual-row-context' -import { renderWorktreeVirtualRow } from './worktree-virtual-row-dispatch' +import { buildRenderableRows } from '../rows/renderable-rows' +import { useFolderWorkspacePathStatusRows } from './use-folder-path-statuses' +import { useGroupToggleWithScrollAnchor } from './use-group-toggle' +import { usePendingSidebarReveal } from '../reveal/use-pending' +import { usePrimaryActiveWorktreeRow } from './use-active-row' +import { useSidebarRevealHighlight } from '../reveal/use-highlight' +import { useVirtualRowMeasurementSync } from './use-row-measurement' +import { useVisiblePrRefreshReporting } from './use-visible-review-refresh' +import { useWorkspaceStatusRowDrag } from '../drag/use-status-row-drag' +import { useWorktreeDragRuntime } from '../drag/use-runtime' +import { useWorktreeDragSession } from '../drag/use-session' +import { useWorktreeDocumentDrop } from '../drag/use-document-drop' +import { useWorktreeLineageDropCommit } from '../drag/use-lineage-drop-commit' +import { useWorktreeListKeyboardNavigation } from './use-keyboard' +import { useWorktreeListVirtualizer } from './use-virtualizer' +import { useWorktreeNativeDrag } from '../drag/use-native-drag' +import { useWorktreePointerDrag } from '../drag/use-pointer-drag' +import { useWorktreeSidebarHeaderDrag } from '../drag/use-header-drag' +import { useWorktreeSidebarScrollSuppression } from '../scroll/use-suppression' +import { EMPTY_PROJECT_GROUPS, type VirtualizedWorktreeViewportProps } from './viewport-props' +import { WORKTREE_SIDEBAR_SCROLL_STYLE } from '../scroll/tuning' +import type { WorktreeDropCommitContext } from '../drag/drop-commit-context' +import { buildWorktreeVirtualRowContext } from '../rows/virtual-row-context' +import { renderWorktreeVirtualRow } from '../rows/virtual-row-dispatch' export const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktreeViewport( props: VirtualizedWorktreeViewportProps diff --git a/src/renderer/src/components/sidebar/worktree-list/active-descendant-option.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/active-descendant-option.ts similarity index 84% rename from src/renderer/src/components/sidebar/worktree-list/active-descendant-option.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/active-descendant-option.ts index b52614cfe4c..0d802ec2fd3 100644 --- a/src/renderer/src/components/sidebar/worktree-list/active-descendant-option.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/active-descendant-option.ts @@ -1,9 +1,9 @@ -import { folderWorkspaceKey } from '../../../../../shared/workspace-scope' -import type { RenderRow } from '../worktree-list-virtual-rows' -import type { PinnedWorktreeDisplayPolicy } from '../worktree-list-groups' -import { isPinnedWorktreeRow } from './render-row-item-rows' -import { getRenderRowWorktreeItem, renderRowContainsWorktree } from './render-row-worktree-lookup' -import { getWorktreeOptionId } from './worktree-option-dom' +import { folderWorkspaceKey } from '../../../../../../shared/workspace-scope' +import type { RenderRow } from './virtual-rows' +import type { PinnedWorktreeDisplayPolicy } from '../rows/groups' +import { isPinnedWorktreeRow } from '../rows/renderable-rows' +import { getRenderRowWorktreeItem, renderRowContainsWorktree } from '../rows/render-row-lookup' +import { getWorktreeOptionId } from '../option-dom' export function getRenderRowOptionId( row: RenderRow | undefined, diff --git a/src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts similarity index 95% rename from src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts index 179e3ee8379..5f059a4267b 100644 --- a/src/renderer/src/components/sidebar/worktree-list-host-filtering.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.test.ts @@ -1,12 +1,12 @@ import { describe, expect, it } from 'vitest' -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../shared/project-group-types' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' import { getFolderPathStatusRouteOptionsForRows, getFolderWorkspaceExecutionHostIdForRows, getProjectGroupExecutionHostIdForRows, getRuntimeEnvironmentIdForFolderPathStatusHost -} from './worktree-list-host-filtering' +} from './host-filtering' function group(overrides: Partial = {}): ProjectGroup { return { diff --git a/src/renderer/src/components/sidebar/worktree-list-host-filtering.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.ts similarity index 95% rename from src/renderer/src/components/sidebar/worktree-list-host-filtering.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.ts index 8071b56bcb7..0b8e27c0f12 100644 --- a/src/renderer/src/components/sidebar/worktree-list-host-filtering.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/host-filtering.ts @@ -5,10 +5,10 @@ import { toSshExecutionHostId, type ExecutionHostId, type ExecutionHostScope -} from '../../../../shared/execution-host' -import type { FolderWorkspacePathStatusRequest } from '../../../../shared/folder-workspace-path-status' -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../shared/project-group-types' +} from '../../../../../../shared/execution-host' +import type { FolderWorkspacePathStatusRequest } from '../../../../../../shared/folder-workspace-path-status' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' /** null means "no host filter" — every host is visible. */ export function getVisibleSidebarHostIdSet( diff --git a/src/renderer/src/components/sidebar/worktree-list-review-cache-inputs.test.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/review-cache-inputs.test.ts similarity index 95% rename from src/renderer/src/components/sidebar/worktree-list-review-cache-inputs.test.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/review-cache-inputs.test.ts index 714fe0e64b4..8e7cc283365 100644 --- a/src/renderer/src/components/sidebar/worktree-list-review-cache-inputs.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/review-cache-inputs.test.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from 'vitest' import { shallow } from 'zustand/shallow' -import type { FolderWorkspace } from '../../../../shared/folder-workspace-types' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' import { EMPTY_WORKTREE_LIST_REVIEW_CACHE_INPUTS, selectWorktreeListReviewCacheInputs, type WorktreeListReviewCacheState -} from './worktree-list-review-cache-inputs' +} from './review-cache-inputs' const EMPTY_STATE: WorktreeListReviewCacheState = { folderWorkspaces: [], diff --git a/src/renderer/src/components/sidebar/worktree-list-review-cache-inputs.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/review-cache-inputs.ts similarity index 91% rename from src/renderer/src/components/sidebar/worktree-list-review-cache-inputs.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/review-cache-inputs.ts index 690da091f3c..d581b1c5194 100644 --- a/src/renderer/src/components/sidebar/worktree-list-review-cache-inputs.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/review-cache-inputs.ts @@ -1,6 +1,6 @@ import type { AppState } from '@/store/types' -import type { WorktreeCardProperty } from '../../../../shared/ui-chrome-types' -import type { WorktreeGroupBy } from './worktree-list-groups' +import type { WorktreeCardProperty } from '../../../../../../shared/ui-chrome-types' +import type { WorktreeGroupBy } from '../rows/groups' export type WorktreeListReviewCacheState = Pick< AppState, diff --git a/src/renderer/src/components/sidebar/worktree-list-sticky-headers.test.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/sticky-headers.test.ts similarity index 89% rename from src/renderer/src/components/sidebar/worktree-list-sticky-headers.test.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/sticky-headers.test.ts index 1c8efdfe9a0..37277f945ee 100644 --- a/src/renderer/src/components/sidebar/worktree-list-sticky-headers.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/sticky-headers.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it } from 'vitest' -import { buildRows } from './worktree-list-groups' -import { getStickyHeaderIndexes } from './worktree-list-virtual-rows' -import type { ProjectGroup } from '../../../../shared/project-group-types' -import type { Repo } from '../../../../shared/repo-types' -import type { Worktree } from '../../../../shared/worktree/types' -import type { Row } from './worktree-list-groups' +import { buildRows } from '../rows/groups' +import { getStickyHeaderIndexes } from './virtual-rows' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Repo } from '../../../../../../shared/repo-types' +import type { Worktree } from '../../../../../../shared/worktree/types' +import type { Row } from '../rows/groups' const repo: Repo = { id: 'repo-1', diff --git a/src/renderer/src/components/sidebar/worktree-list/use-primary-active-worktree-row.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-active-row.ts similarity index 88% rename from src/renderer/src/components/sidebar/worktree-list/use-primary-active-worktree-row.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-active-row.ts index e3e30642c8e..b9308714401 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-primary-active-worktree-row.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-active-row.ts @@ -1,8 +1,8 @@ import { useCallback, useLayoutEffect, useState } from 'react' -import type { ActiveSurfaceVariant } from '../WorktreeCard' -import type { HostSectionRow } from '../host-section-rows' -import type { PinnedWorktreeDisplayPolicy } from '../worktree-list-groups' -import { isPinnedWorktreeRow, type WorktreeItemRow } from './render-row-item-rows' +import type { ActiveSurfaceVariant } from '../../WorktreeCard' +import type { HostSectionRow } from '../../host-section-rows' +import type { PinnedWorktreeDisplayPolicy } from '../rows/groups' +import { isPinnedWorktreeRow, type WorktreeItemRow } from '../rows/renderable-rows' // A worktree can render in more than one section; the row the user actually clicked owns // the primary active surface so its duplicates stay visually secondary. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-folder-workspace-path-status-rows.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-folder-path-statuses.ts similarity index 94% rename from src/renderer/src/components/sidebar/worktree-list/use-folder-workspace-path-status-rows.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-folder-path-statuses.ts index 8e5bd17b81d..825c9726898 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-folder-workspace-path-status-rows.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-folder-path-statuses.ts @@ -2,11 +2,11 @@ import { useCallback, useEffect, useMemo } from 'react' import { useShallow } from 'zustand/react/shallow' import { useAppStore } from '@/store' import { useFolderWorkspacePathStatusCacheExpiryTick } from '@/lib/folder-workspace-path-status-cache-expiry' -import type { FolderWorkspace } from '../../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { Repo } from '../../../../../shared/repo-types' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { Repo } from '../../../../../../shared/repo-types' import type { AppState } from '@/store/types' -import { getFolderPathStatusRouteOptionsForRows } from '../worktree-list-host-filtering' +import { getFolderPathStatusRouteOptionsForRows } from './host-filtering' type FolderPathStatusRequest = Parameters[0] diff --git a/src/renderer/src/components/sidebar/worktree-list/use-group-toggle-with-scroll-anchor.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-group-toggle.ts similarity index 92% rename from src/renderer/src/components/sidebar/worktree-list/use-group-toggle-with-scroll-anchor.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-group-toggle.ts index 044c2a2651f..d434ce67628 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-group-toggle-with-scroll-anchor.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-group-toggle.ts @@ -1,7 +1,7 @@ import { useCallback, useMemo } from 'react' import type React from 'react' import { VIRTUALIZED_SCROLL_ANCHOR_RECORD_EVENT } from '@/hooks/useVirtualizedScrollAnchor' -import { createLineageToggleHandlerCache } from '../worktree-lineage-toggle-handler-cache' +import { createLineageToggleHandlerCache } from '../../worktree-lineage-toggle-handler-cache' // Collapsing a section changes total height, so snapshot the anchor first or the viewport jumps. export function useGroupToggleWithScrollAnchor(args: { diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-list-keyboard-navigation.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard.ts similarity index 81% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-list-keyboard-navigation.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard.ts index 4a9fc419703..6a737d70c1d 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-list-keyboard-navigation.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-keyboard.ts @@ -4,13 +4,32 @@ import type { Virtualizer } from '@tanstack/react-virtual' import { useAppStore } from '@/store' import { activateAndRevealWorktree } from '@/lib/worktree-activation' import { getShortcutPlatform } from '@/lib/shortcut-platform' -import { keybindingMatchesAction } from '../../../../../shared/keybindings' -import type { HostSectionRow } from '../host-section-rows' -import type { PinnedWorktreeDisplayPolicy } from '../worktree-list-groups' -import type { RenderRow } from '../worktree-list-virtual-rows' -import { getCyclableWorktreeIds, resolveCycledWorktreeId } from '../worktree-keyboard-cycle' -import { findPreferredRenderRowIndexForWorktree } from './render-row-worktree-lookup' -import { isEditableTarget } from './sidebar-editable-target' +import { keybindingMatchesAction } from '../../../../../../shared/keybindings' +import type { HostSectionRow } from '../../host-section-rows' +import type { PinnedWorktreeDisplayPolicy } from '../rows/groups' +import type { RenderRow } from './virtual-rows' +import { getCyclableWorktreeIds, resolveCycledWorktreeId } from '../../worktree-keyboard-cycle' +import { findPreferredRenderRowIndexForWorktree } from '../rows/render-row-lookup' + +function isEditableTarget(target: EventTarget | null): boolean { + if (!(target instanceof HTMLElement)) { + return false + } + + // xterm's hidden input textarea isn't a real text field; treating it as one would block sidebar shortcuts. + if (target.classList.contains('xterm-helper-textarea')) { + return false + } + + if (target.isContentEditable) { + return true + } + + return ( + target.closest('input, textarea, select, [contenteditable=""], [contenteditable="true"]') !== + null + ) +} export function useWorktreeListKeyboardNavigation(args: { rows: HostSectionRow[] diff --git a/src/renderer/src/components/sidebar/worktree-list/use-virtual-row-measurement-sync.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts similarity index 93% rename from src/renderer/src/components/sidebar/worktree-list/use-virtual-row-measurement-sync.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts index 79f0d53b429..709bf05d85b 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-virtual-row-measurement-sync.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-row-measurement.ts @@ -11,10 +11,10 @@ import { getRenderRowKey, pruneStaleVirtualRowElementCache, type RenderRow -} from '../worktree-list-virtual-rows' -import { getVirtualRowKey } from './virtual-row-dom-attributes' -import { countRecordKeysByReference } from './worktree-sidebar-scroll-tuning' -import type { WorktreeListVirtualizer } from './use-worktree-list-virtualizer' +} from './virtual-rows' +import { getVirtualRowKey } from './virtual-rows' +import { countRecordKeysByReference } from '../scroll/tuning' +import type { WorktreeListVirtualizer } from './use-virtualizer' // Re-measures only rows whose DOM node still matches its virtual key, and publishes the // sticky-header slots the render pass reads out of refs. diff --git a/src/renderer/src/components/sidebar/worktree-list/use-worktree-list-virtualizer.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts similarity index 96% rename from src/renderer/src/components/sidebar/worktree-list/use-worktree-list-virtualizer.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts index 52bd92358ce..ea41bcd83a3 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-worktree-list-virtualizer.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-virtualizer.ts @@ -12,13 +12,13 @@ import { getStickyHeaderIndexes, WORKTREE_SIDEBAR_VIRTUAL_ROW_GAP, type RenderRow -} from '../worktree-list-virtual-rows' -import { WORKTREE_SIDEBAR_REVEAL_TOP_INSET } from '../worktree-sidebar-reveal' -import { getVirtualRowIndex } from './virtual-row-dom-attributes' +} from './virtual-rows' +import { WORKTREE_SIDEBAR_REVEAL_TOP_INSET } from '../../worktree-sidebar-reveal' +import { getVirtualRowIndex } from './virtual-rows' import { shouldAdjustWorktreeSidebarMeasuredRowScroll, USER_SCROLL_MEASUREMENT_ADJUSTMENT_SUPPRESS_MS -} from './worktree-sidebar-scroll-tuning' +} from '../scroll/tuning' export type WorktreeListVirtualizer = ReturnType diff --git a/src/renderer/src/components/sidebar/worktree-list/use-visible-pr-refresh-reporting.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts similarity index 94% rename from src/renderer/src/components/sidebar/worktree-list/use-visible-pr-refresh-reporting.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts index ba6bafc0b5a..ac3cb9eb071 100644 --- a/src/renderer/src/components/sidebar/worktree-list/use-visible-pr-refresh-reporting.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/use-visible-review-refresh.ts @@ -3,11 +3,11 @@ import type React from 'react' import type { VirtualItem } from '@tanstack/react-virtual' import { useAppStore } from '@/store' import { rightSidebarShowsPullRequestData } from '@/lib/right-sidebar-visibility' -import type { Worktree } from '../../../../../shared/worktree/types' -import type { WorktreeGroupBy } from '../worktree-list-groups' -import type { RenderRow } from '../worktree-list-virtual-rows' -import type { WorktreeItemRow } from './render-row-item-rows' -import { installWorktreeVisibleRefreshVisibilityListener } from './worktree-visible-refresh-listener' +import type { Worktree } from '../../../../../../shared/worktree/types' +import type { WorktreeGroupBy } from '../rows/groups' +import type { RenderRow } from './virtual-rows' +import type { WorktreeItemRow } from '../rows/renderable-rows' +import { installWorktreeVisibleRefreshVisibilityListener } from '../scroll/tuning' const DOCUMENT_HIDDEN_KEY = '__document_hidden__' const NOTHING_TO_TRACK_KEY = '__hidden__' diff --git a/src/renderer/src/components/sidebar/worktree-list/virtualized-worktree-viewport-props.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/viewport-props.ts similarity index 84% rename from src/renderer/src/components/sidebar/worktree-list/virtualized-worktree-viewport-props.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/viewport-props.ts index 2aaacf85795..8513e34c2cb 100644 --- a/src/renderer/src/components/sidebar/worktree-list/virtualized-worktree-viewport-props.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/viewport-props.ts @@ -2,29 +2,29 @@ import type React from 'react' import type { AppState } from '@/store/types' import type { VirtualizedScrollAnchor } from '@/hooks/useVirtualizedScrollAnchor' import type { PendingSidebarRowReveal, PendingSidebarWorktreeReveal } from '@/store/slices/ui' -import type { FolderWorkspace } from '../../../../../shared/folder-workspace-types' -import type { ProjectGroup } from '../../../../../shared/project-group-types' -import type { ProjectOrderBy } from '../../../../../shared/ui-chrome-types' -import type { Repo } from '../../../../../shared/repo-types' +import type { FolderWorkspace } from '../../../../../../shared/folder-workspace-types' +import type { ProjectGroup } from '../../../../../../shared/project-group-types' +import type { ProjectOrderBy } from '../../../../../../shared/ui-chrome-types' +import type { Repo } from '../../../../../../shared/repo-types' import type { WorkspaceLineage, WorktreeLineage -} from '../../../../../shared/worktree/lineage-types' +} from '../../../../../../shared/worktree/lineage-types' import type { WorkspaceStatus, WorkspaceStatusDefinition, Worktree -} from '../../../../../shared/worktree/types' -import type { ExecutionHostId } from '../../../../../shared/execution-host' -import type { HostSectionRow } from '../host-section-rows' -import type { ImportedWorktreeCardActionState } from '../imported-worktrees-card-actions' -import type { NewExternalWorktreesInboxActionState } from '../new-external-worktrees-inbox-actions' -import type { WorktreeDragGroup } from '../worktree-manual-order' +} from '../../../../../../shared/worktree/types' +import type { ExecutionHostId } from '../../../../../../shared/execution-host' +import type { HostSectionRow } from '../../host-section-rows' +import type { ImportedWorktreeCardActionState } from '../../imported-worktrees-card-actions' +import type { NewExternalWorktreesInboxActionState } from '../../new-external-worktrees-inbox-actions' +import type { WorktreeDragGroup } from '../../worktree-manual-order' import type { PinnedWorktreeDisplayPolicy, ProjectGroupingModel, WorktreeGroupBy -} from '../worktree-list-groups' +} from '../rows/groups' export const EMPTY_PROJECT_GROUPS: readonly ProjectGroup[] = [] export const NOOP_WORKSPACE_BOARD_DRAG_PREVIEW_CALLBACK = (): void => {} diff --git a/src/renderer/src/components/sidebar/worktree-list-virtual-rows.test.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.test.ts similarity index 99% rename from src/renderer/src/components/sidebar/worktree-list-virtual-rows.test.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.test.ts index 67b6147765e..71d9066cd11 100644 --- a/src/renderer/src/components/sidebar/worktree-list-virtual-rows.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import type { VirtualItem } from '@tanstack/react-virtual' -import type { ExecutionHostId } from '../../../../shared/execution-host' +import type { ExecutionHostId } from '../../../../../../shared/execution-host' import { HOST_STICKY_PINNED_HEIGHT, buildLineageRowRekeyMap, @@ -10,7 +10,7 @@ import { getStickyHeaderIndexes, pruneStaleVirtualRowElementCache, type RenderRow -} from './worktree-list-virtual-rows' +} from './virtual-rows' function hostRow(hostId: ExecutionHostId): RenderRow { return { diff --git a/src/renderer/src/components/sidebar/worktree-list-virtual-rows.ts b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts similarity index 94% rename from src/renderer/src/components/sidebar/worktree-list-virtual-rows.ts rename to src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts index 4d6b0b7162c..b3e868e9893 100644 --- a/src/renderer/src/components/sidebar/worktree-list-virtual-rows.ts +++ b/src/renderer/src/components/sidebar/worktree-list/viewport/virtual-rows.ts @@ -1,7 +1,7 @@ import { defaultRangeExtractor } from '@tanstack/react-virtual' import type { Range, VirtualItem } from '@tanstack/react-virtual' -import type { HostSectionRow } from './host-section-rows' -import { getLineageGroupKey, PINNED_GROUP_KEY } from './worktree-list-groups' +import type { HostSectionRow } from '../../host-section-rows' +import { getLineageGroupKey, PINNED_GROUP_KEY } from '../rows/groups' export const GROUP_HEADER_ROW_HEIGHT = 28 export const HOST_HEADER_ROW_HEIGHT = 32 @@ -134,6 +134,20 @@ export function getVirtualRowTransform(start: number): string { return `translateY(${start}px)` } +export function getVirtualRowIndex(element: Element): number | null { + const index = Number.parseInt(element.getAttribute('data-index') ?? '', 10) + return Number.isNaN(index) ? null : index +} + +export function getVirtualRowKey(element: Element): string | null { + return element.getAttribute('data-worktree-virtual-row-key') +} + +export function getWorktreeVirtualRowTransform(start: number, previewOffset: number): string { + const base = getVirtualRowTransform(start) + return previewOffset === 0 ? base : `${base} translateY(${previewOffset}px)` +} + type VirtualRowElementCache = { elementsCache: Map measureElement: (node: TElement | null) => void diff --git a/src/renderer/src/components/sidebar/worktree-list/virtual-row-dom-attributes.ts b/src/renderer/src/components/sidebar/worktree-list/virtual-row-dom-attributes.ts deleted file mode 100644 index 7523418e7c1..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/virtual-row-dom-attributes.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { getVirtualRowTransform } from '../worktree-list-virtual-rows' - -export function getVirtualRowIndex(element: Element): number | null { - const index = Number.parseInt(element.getAttribute('data-index') ?? '', 10) - return Number.isNaN(index) ? null : index -} - -export function getVirtualRowKey(element: Element): string | null { - return element.getAttribute('data-worktree-virtual-row-key') -} - -export function getWorktreeVirtualRowTransform(start: number, previewOffset: number): string { - const base = getVirtualRowTransform(start) - return previewOffset === 0 ? base : `${base} translateY(${previewOffset}px)` -} diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-visibility-menu-label.ts b/src/renderer/src/components/sidebar/worktree-list/worktree-visibility-menu-label.ts deleted file mode 100644 index 93d3acdabc2..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-visibility-menu-label.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Repo } from '../../../../../shared/repo-types' -import type { WorktreeVisibilityDefaults } from '../../../../../shared/global-settings-types' -import { - effectiveExternalWorktreeVisibility, - isLegacyRepoForExternalWorktreeVisibility -} from '../../../../../shared/worktree/ownership' - -export function getWorktreeVisibilityMenuLabel( - repo: Repo, - visibilityDefaults?: WorktreeVisibilityDefaults -): string { - const visibility = effectiveExternalWorktreeVisibility( - repo, - isLegacyRepoForExternalWorktreeVisibility(repo), - visibilityDefaults - ) - return visibility === 'show' ? 'Hide non-Orca worktrees' : 'Show hidden worktrees' -} diff --git a/src/renderer/src/components/sidebar/worktree-list/worktree-visible-refresh-listener.ts b/src/renderer/src/components/sidebar/worktree-list/worktree-visible-refresh-listener.ts deleted file mode 100644 index b9e77fefb83..00000000000 --- a/src/renderer/src/components/sidebar/worktree-list/worktree-visible-refresh-listener.ts +++ /dev/null @@ -1,4 +0,0 @@ -export function installWorktreeVisibleRefreshVisibilityListener(onChange: () => void): () => void { - document.addEventListener('visibilitychange', onChange) - return () => document.removeEventListener('visibilitychange', onChange) -} diff --git a/src/renderer/src/components/sidebar/worktree-sidebar-reveal.ts b/src/renderer/src/components/sidebar/worktree-sidebar-reveal.ts index ffc8bd7f8df..c0830f0e264 100644 --- a/src/renderer/src/components/sidebar/worktree-sidebar-reveal.ts +++ b/src/renderer/src/components/sidebar/worktree-sidebar-reveal.ts @@ -1,4 +1,4 @@ -import { GROUP_HEADER_ROW_HEIGHT } from './worktree-list-virtual-rows' +import { GROUP_HEADER_ROW_HEIGHT } from './worktree-list/viewport/virtual-rows' const WORKTREE_REVEAL_TOP_CLEARANCE = 6 export const WORKTREE_SIDEBAR_REVEAL_TOP_INSET = diff --git a/src/renderer/src/components/sidebar/worktree-sidebar-row-preference.ts b/src/renderer/src/components/sidebar/worktree-sidebar-row-preference.ts index 0495375b691..99f7b9ceece 100644 --- a/src/renderer/src/components/sidebar/worktree-sidebar-row-preference.ts +++ b/src/renderer/src/components/sidebar/worktree-sidebar-row-preference.ts @@ -5,7 +5,7 @@ import { PINNED_GROUP_KEY, type PinnedWorktreeDisplayPolicy, type WorktreeRow -} from './worktree-list-groups' +} from './worktree-list/rows/groups' export function getPreferredWorktreeRows( rows: readonly WorktreeRow[],