@kabmo-labs/kabmo 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/client/index.html +1 -1
- package/dist/client/index.js +403 -384
- package/dist/client/static/js/{index-0wvbca0b.js → index-bpptfkz2.js} +905 -875
- package/dist/client/static/js/index-k315p1e8.css +1 -0
- package/package.json +10 -1
- package/src/client/components/common/code-surface/documents.test.ts +120 -0
- package/src/client/components/common/code-surface/documents.ts +201 -0
- package/src/client/components/common/code-surface/extensions.test.ts +110 -0
- package/src/client/components/common/code-surface/extensions.ts +162 -0
- package/src/client/components/common/code-surface/handle.ts +102 -0
- package/src/client/components/common/code-surface/index.tsx +253 -314
- package/src/client/components/common/code-surface/keymap.test.ts +133 -0
- package/src/client/components/common/code-surface/keymap.ts +267 -0
- package/src/client/components/common/code-surface/marks.ts +74 -0
- package/src/client/components/common/code-surface/shiki.ts +234 -0
- package/src/client/components/common/code-surface/sync.test.ts +185 -0
- package/src/client/components/common/code-surface/sync.ts +58 -0
- package/src/client/components/common/code-surface/theme.test.ts +202 -0
- package/src/client/components/common/code-surface/theme.ts +111 -0
- package/src/client/components/common/editor-view-buttons/index.test.tsx +148 -0
- package/src/client/components/common/editor-view-buttons/index.tsx +109 -0
- package/src/client/components/common/header-controls/index.tsx +24 -1
- package/src/client/components/common/header-controls/shell-chrome.test.tsx +272 -0
- package/src/client/components/common/shell-update-button/Modal.tsx +163 -0
- package/src/client/components/common/shell-update-button/index.test.tsx +251 -0
- package/src/client/components/common/shell-update-button/index.tsx +125 -0
- package/src/client/components/common/toolbar-row/index.test.tsx +166 -0
- package/src/client/components/common/toolbar-row/index.tsx +189 -0
- package/src/client/components/common/workspace-options-menu/index.tsx +19 -1
- package/src/client/components/layout/desktop-layout/TopNavbar.tsx +5 -6
- package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +41 -78
- package/src/client/components/layout/desktop-layout/index.tsx +55 -69
- package/src/client/components/layout/desktop-layout/resizer/index.tsx +6 -3
- package/src/client/components/layout/session-sidebar/CategoryHeader.tsx +24 -0
- package/src/client/components/layout/session-sidebar/CategoryHeader.worktree.test.tsx +126 -0
- package/src/client/components/layout/session-sidebar/CategoryItem.tsx +4 -0
- package/src/client/components/layout/session-sidebar/Footer.tsx +8 -15
- package/src/client/components/layout/session-sidebar/SessionItem.tsx +12 -0
- package/src/client/components/layout/session-sidebar/SessionList.tsx +4 -0
- package/src/client/components/layout/session-sidebar/SessionsView.tsx +24 -0
- package/src/client/components/layout/session-sidebar/WorktreeBadge.test.ts +48 -0
- package/src/client/components/layout/session-sidebar/WorktreeBadge.tsx +55 -0
- package/src/client/components/mobile/LayoutWrapper.tsx +23 -16
- package/src/client/components/mobile/RightSidebar.tsx +21 -10
- package/src/client/components/mobile/SessionSidebar.tsx +8 -0
- package/src/client/components/mobile/header-titlebar.test.tsx +172 -0
- package/src/client/components/mobile/mobile-main-view/Header.tsx +10 -1
- package/src/client/components/mobile/mobile-right-sidebar/FullDiff.tsx +3 -0
- package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +63 -38
- package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.test.tsx +137 -0
- package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.tsx +14 -3
- package/src/client/components/mobile/mobile-session-sidebar/Footer.tsx +9 -21
- package/src/client/components/mobile/mobile-session-sidebar/Header.tsx +6 -2
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +16 -0
- package/src/client/components/mobile/mobile-session-sidebar/List.tsx +4 -0
- package/src/client/components/mobile/mobile-session-sidebar/SessionRow.tsx +5 -0
- package/src/client/components/mobile/mobile-session-sidebar/WorktreeDialog.tsx +49 -0
- package/src/client/components/mobile/worktree-session.ts +51 -0
- package/src/client/components/settings/Modal.tsx +8 -0
- package/src/client/components/settings/categories/AppearanceSettings.tsx +3 -0
- package/src/client/components/settings/categories/ProjectSettings.tsx +22 -8
- package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.test.tsx +167 -0
- package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.tsx +184 -0
- package/src/client/components/settings/categories/project-settings/WorktreeSettings.tsx +146 -0
- package/src/client/components/settings/categories/project-settings/worktree-row.test.ts +83 -0
- package/src/client/components/settings/categories/project-settings/worktree-row.ts +47 -0
- package/src/client/components/workspace/chat-timeline/EmptyWorkspacePrompt.tsx +36 -55
- package/src/client/components/workspace/chat-timeline/PendingChatView.tsx +8 -0
- package/src/client/components/workspace/chat-timeline/SystemNotice.tsx +9 -4
- package/src/client/components/workspace/chat-timeline/TaskResultContent.tsx +9 -7
- package/src/client/components/workspace/chat-timeline/index.tsx +5 -0
- package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.test.tsx +132 -0
- package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.tsx +120 -0
- package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.test.tsx +319 -0
- package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.tsx +285 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.test.ts +2 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/hashline-patch/index.tsx +34 -29
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/AstEdit.tsx +11 -9
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Bash.tsx +10 -7
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Edit.tsx +25 -8
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Eval.test.tsx +146 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Eval.tsx +32 -17
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.test.ts +114 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.tsx +111 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Read.tsx +7 -7
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/SearchTool.tsx +9 -59
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ArtifactBody.tsx +2 -7
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.test.ts +27 -28
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.tsx +39 -26
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ExcerptCode.tsx +8 -8
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/HighlightedCode.tsx +35 -0
- package/src/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock.tsx +4 -4
- package/src/client/components/workspace/diff-panel/MergeDiffView.tsx +179 -0
- package/src/client/components/workspace/diff-panel/ThemedMergeDiffView.tsx +24 -0
- package/src/client/components/workspace/diff-panel/Toolbar.tsx +224 -186
- package/src/client/components/workspace/diff-panel/ToolbarSegments.tsx +98 -0
- package/src/client/components/workspace/diff-panel/index.tsx +76 -145
- package/src/client/components/workspace/diff-panel/merge.ts +315 -0
- package/src/client/components/workspace/editor/Body.tsx +4 -7
- package/src/client/components/workspace/editor/CommandPalette.tsx +2 -2
- package/src/client/components/workspace/editor/FileDocument.tsx +58 -63
- package/src/client/components/workspace/editor/FilePane.tsx +4 -7
- package/src/client/components/workspace/editor/FindWidget.tsx +7 -2
- package/src/client/components/workspace/editor/Toolbar.tsx +220 -123
- package/src/client/components/workspace/editor/index.tsx +7 -9
- package/src/client/components/workspace/file-explorer/ContextMenu.tsx +2 -1
- package/src/client/components/workspace/file-explorer/Modals.tsx +40 -12
- package/src/client/components/workspace/file-explorer/OutlineSection.test.tsx +18 -0
- package/src/client/components/workspace/file-explorer/OutlineSection.tsx +10 -3
- package/src/client/components/workspace/file-explorer/RootMenu.tsx +14 -5
- package/src/client/components/workspace/file-explorer/Toolbar.tsx +74 -0
- package/src/client/components/workspace/file-explorer/TreeItem.tsx +38 -3
- package/src/client/components/workspace/file-explorer/filter.ts +47 -0
- package/src/client/components/workspace/file-explorer/index.tsx +77 -64
- package/src/client/components/workspace/file-explorer/local-edit.test.ts +135 -0
- package/src/client/components/workspace/search-panel/Body.tsx +1 -1
- package/src/client/components/workspace/worktree-modal/index.test.tsx +118 -0
- package/src/client/components/workspace/worktree-modal/index.tsx +261 -0
- package/src/client/hooks/chat/omp/reconnect.test.ts +338 -0
- package/src/client/hooks/chat/omp/reconnect.ts +187 -0
- package/src/client/hooks/chat/omp/run-liveness.test.ts +71 -0
- package/src/client/hooks/chat/omp/run-liveness.ts +65 -0
- package/src/client/hooks/chat/omp/session-list.activity.test.ts +114 -0
- package/src/client/hooks/chat/omp/session-list.test-util.ts +164 -0
- package/src/client/hooks/chat/omp/session-list.test.ts +30 -150
- package/src/client/hooks/chat/omp/session-list.tsx +69 -9
- package/src/client/hooks/chat/timeline/generating.ts +19 -2
- package/src/client/hooks/chat/timeline/index.ts +27 -28
- package/src/client/hooks/chat/timeline/lost-run.test.ts +329 -0
- package/src/client/hooks/chat/timeline/lost-run.ts +177 -0
- package/src/client/hooks/chat/timeline/send-execute.test.ts +2 -0
- package/src/client/hooks/chat/timeline/send-steer.test.ts +2 -0
- package/src/client/hooks/chat/timeline/send.test.ts +2 -0
- package/src/client/hooks/chat/timeline/send.ts +11 -1
- package/src/client/hooks/chat/timeline/session-load.test-util.ts +205 -0
- package/src/client/hooks/chat/timeline/session-load.test.ts +61 -162
- package/src/client/hooks/chat/timeline/session-load.ts +69 -37
- package/src/client/hooks/chat/timeline/session-switch.test.ts +65 -0
- package/src/client/hooks/chat/timeline/session-switch.ts +63 -0
- package/src/client/hooks/chat/timeline/spawn-target.ts +52 -0
- package/src/client/hooks/chat/timeline/tool-open.test.ts +74 -0
- package/src/client/hooks/chat/timeline/tool-open.ts +52 -8
- package/src/client/hooks/code/deferred-highlight.test.ts +182 -0
- package/src/client/hooks/code/deferred-highlight.ts +129 -0
- package/src/client/hooks/editor/find-state.ts +1 -1
- package/src/client/hooks/editor/theme.test.ts +206 -0
- package/src/client/hooks/editor/theme.ts +82 -0
- package/src/client/hooks/editor/use-editor-drop.test.ts +101 -0
- package/src/client/hooks/editor/use-editor-drop.ts +67 -14
- package/src/client/hooks/editor/use-editor-find.ts +7 -7
- package/src/client/hooks/editor/use-outline-view.test.ts +182 -0
- package/src/client/hooks/editor/use-outline-view.ts +94 -7
- package/src/client/hooks/editor/use-scroll-restore.test.ts +55 -2
- package/src/client/hooks/editor/use-scroll-restore.ts +124 -4
- package/src/client/hooks/editor/use-search-reveal.ts +1 -1
- package/src/client/hooks/ui/long-press.test.ts +174 -0
- package/src/client/hooks/ui/long-press.ts +112 -0
- package/src/client/hooks/ui/settings-modal.ts +56 -0
- package/src/client/hooks/ui/updates/shell-download.test.ts +75 -0
- package/src/client/hooks/ui/updates/shell-download.ts +153 -28
- package/src/client/hooks/ui/updates/shell-offer.test.ts +213 -0
- package/src/client/hooks/ui/updates/shell-update.test.ts +140 -0
- package/src/client/hooks/ui/updates/shell-update.ts +119 -0
- package/src/client/hooks/workspace/diff-file.ts +29 -10
- package/src/client/hooks/workspace/diff-git-actions.ts +147 -0
- package/src/client/hooks/workspace/editor-view.ts +61 -0
- package/src/client/hooks/workspace/file-create.ts +41 -11
- package/src/client/hooks/workspace/file-listing.ts +56 -1
- package/src/client/hooks/workspace/file-tabs.test.ts +62 -5
- package/src/client/hooks/workspace/file-tabs.ts +24 -11
- package/src/client/hooks/workspace/file-tree-actions.ts +37 -5
- package/src/client/hooks/workspace/navbar-session-actions.ts +72 -0
- package/src/client/hooks/workspace/worktree/index.ts +165 -0
- package/src/client/lib/desktop/bridge.ts +45 -0
- package/src/client/lib/search-reveal.ts +1 -1
- package/src/client/main.tsx +5 -1
- package/src/client/tailwind.css +56 -8
- package/src/server/index.ts +7 -2
- package/src/server/lib/auth/guard.test.ts +28 -0
- package/src/server/lib/auth/guard.ts +10 -0
- package/src/server/lib/bundler/titlebar-css.test.ts +42 -1
- package/src/server/lib/db/schema.ts +4 -0
- package/src/server/lib/db/worktree-registry.server.ts +91 -0
- package/src/server/lib/fs/canonical.ts +52 -0
- package/src/server/lib/fs/relative-path.ts +48 -0
- package/src/server/lib/fs/root-worktree.test.ts +116 -0
- package/src/server/lib/fs/root.ts +39 -10
- package/src/server/lib/fs/worktree/carry.ts +134 -0
- package/src/server/lib/fs/worktree/create.test.ts +249 -0
- package/src/server/lib/fs/worktree/create.ts +262 -0
- package/src/server/lib/fs/worktree/errors.ts +43 -0
- package/src/server/lib/fs/worktree/list.test.ts +207 -0
- package/src/server/lib/fs/worktree/list.ts +142 -0
- package/src/server/lib/fs/worktree/location.test.ts +206 -0
- package/src/server/lib/fs/worktree/location.ts +114 -0
- package/src/server/lib/fs/worktree/project-id.ts +53 -0
- package/src/server/lib/fs/worktree/remove.test.ts +242 -0
- package/src/server/lib/fs/worktree/remove.ts +152 -0
- package/src/server/lib/fs/worktree/settings.server.ts +56 -0
- package/src/server/lib/fs/worktree/topology.ts +142 -0
- package/src/server/lib/lifecycle/claim-port.test.ts +156 -0
- package/src/server/lib/lifecycle/port-guard.test.ts +20 -164
- package/src/server/lib/lifecycle/port-guard.ts +39 -7
- package/src/server/lib/lifecycle/probe-health.test.ts +166 -0
- package/src/server/lib/lifecycle/probe.test.ts +77 -164
- package/src/server/lib/lifecycle/probe.ts +121 -13
- package/src/server/lib/omp/rpc/frame-fold-signals.test.ts +112 -0
- package/src/server/lib/omp/rpc/frame-fold.ts +10 -5
- package/src/server/lib/omp/session/sidebar-activity.server.test.ts +162 -0
- package/src/server/lib/omp/session/sidebar-activity.server.ts +70 -0
- package/src/server/lib/omp/session/sidebar-data.canonical.test.ts +155 -0
- package/src/server/lib/omp/session/sidebar-data.server.ts +28 -1
- package/src/server/lib/omp/session/telemetry/compute.ts +13 -1
- package/src/server/lib/omp/session/telemetry.test.ts +33 -0
- package/src/server/lib/panels/local-edits.server.ts +33 -0
- package/src/server/lib/panels/sync.fixture.test-util.ts +92 -0
- package/src/server/lib/panels/sync.local-edits.test.ts +150 -0
- package/src/server/lib/panels/sync.server.test.ts +125 -147
- package/src/server/lib/panels/sync.server.ts +153 -48
- package/src/server/lib/realtime/signals.server.ts +10 -0
- package/src/server/lib/realtime/topics/catalog.server.ts +4 -1
- package/src/server/lib/realtime/topics.server.ts +16 -0
- package/src/server/lib/updates/shell-assets.test.ts +114 -0
- package/src/server/lib/updates/shell-assets.ts +200 -0
- package/src/server/lib/updates/shell-cache.ts +105 -0
- package/src/server/lib/updates/shell-pages.test.ts +187 -0
- package/src/server/lib/updates/shell-pages.ts +157 -0
- package/src/server/lib/updates/shell-release-desktop.test.ts +166 -0
- package/src/server/lib/updates/shell-release.test.ts +59 -71
- package/src/server/lib/updates/shell-release.ts +178 -198
- package/src/server/routes/fs/action-create.test.ts +18 -13
- package/src/server/routes/fs/action-nested.test.ts +184 -0
- package/src/server/routes/fs/action.ts +47 -16
- package/src/server/routes/fs/git-merge.test.ts +11 -1
- package/src/server/routes/fs/read.test.ts +24 -7
- package/src/server/routes/fs/scope.test.ts +15 -10
- package/src/server/routes/fs/scope.ts +3 -14
- package/src/server/routes/index.ts +2 -0
- package/src/server/routes/updates/android.ts +6 -1
- package/src/server/routes/updates/desktop.ts +59 -0
- package/src/server/routes/updates/index.ts +2 -0
- package/src/server/routes/updates/ios.ts +5 -1
- package/src/server/routes/worktrees/create.ts +138 -0
- package/src/server/routes/worktrees/index.ts +25 -0
- package/src/server/routes/worktrees/list.ts +134 -0
- package/src/server/routes/worktrees/remove.ts +102 -0
- package/src/server/routes/worktrees/routes.test.ts +326 -0
- package/src/server/routes/worktrees/shared.ts +90 -0
- package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +36 -0
- package/src/shared/lib/chat/timeline/omp-callbacks.ts +18 -0
- package/src/shared/lib/chat/timeline/run-signals.test.ts +2 -0
- package/src/shared/lib/code/editor/comment-syntax.test.ts +37 -0
- package/src/shared/lib/code/editor/comment-syntax.ts +55 -0
- package/src/shared/lib/code/editor/find-replace.test.ts +134 -0
- package/src/shared/lib/code/editor/{find.ts → find-replace.ts} +23 -107
- package/src/shared/lib/code/editor/{find.test.ts → find-scan.test.ts} +19 -73
- package/src/shared/lib/code/editor/find-scan.ts +177 -0
- package/src/shared/lib/code/editor/keymap.ts +3 -3
- package/src/shared/lib/code/editor/lines.test.ts +7 -55
- package/src/shared/lib/code/editor/lines.ts +8 -48
- package/src/shared/lib/code/editor/marks.test.ts +4 -5
- package/src/shared/lib/code/editor/marks.ts +13 -25
- package/src/shared/lib/code/editor/theme-loader.ts +97 -0
- package/src/shared/lib/code/editor/themes.generated.ts +156 -0
- package/src/shared/lib/code/editor/themes.test.ts +165 -0
- package/src/shared/lib/code/editor/themes.ts +165 -0
- package/src/shared/lib/code/highlight-memo.test.ts +103 -0
- package/src/shared/lib/code/highlight-memo.ts +149 -0
- package/src/shared/lib/code/highlighter-warmup.ts +112 -0
- package/src/shared/lib/code/highlighter.ts +3 -1
- package/src/shared/lib/code/idle.ts +37 -0
- package/src/shared/lib/code/outline/rows.test.ts +96 -0
- package/src/shared/lib/code/outline/rows.ts +94 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +1 -79
- package/src/shared/lib/code/syntax-highlight.ts +2 -3
- package/src/shared/lib/fs/file-kind-classify.test.ts +25 -11
- package/src/shared/lib/fs/file-kind.ts +47 -13
- package/src/shared/lib/fs/file-tree.test.ts +183 -0
- package/src/shared/lib/fs/file-tree.ts +159 -0
- package/src/shared/lib/fs/file-url.test.ts +52 -0
- package/src/shared/lib/fs/file-url.ts +37 -0
- package/src/shared/lib/fs/patch-sides.test.ts +114 -0
- package/src/shared/lib/fs/patch-sides.ts +139 -0
- package/src/shared/lib/fs/paths.test.ts +24 -1
- package/src/shared/lib/fs/paths.ts +17 -0
- package/src/shared/lib/fs/worktree/settings-keys.ts +18 -0
- package/src/shared/lib/omp/session/prewarm.ts +7 -0
- package/src/shared/lib/realtime/protocol.ts +16 -0
- package/src/shared/lib/ui/toolbar-overflow.test.ts +83 -0
- package/src/shared/lib/ui/toolbar-overflow.ts +86 -0
- package/src/shared/lib/updates/shell-arch.ts +54 -0
- package/src/shared/lib/updates/shell-ua.ts +24 -4
- package/src/shared/lib/workspace/active-project.ts +23 -0
- package/src/shared/lib/workspace/editor-view-mode.test.ts +70 -0
- package/src/shared/lib/workspace/editor-view-mode.ts +48 -0
- package/src/shared/lib/workspace/panel-widths.test.ts +30 -0
- package/src/shared/lib/workspace/panel-widths.ts +25 -5
- package/src/shared/lib/workspace/session-state/cache.ts +159 -0
- package/src/shared/lib/workspace/session-state/harness.test-util.ts +55 -0
- package/src/shared/lib/workspace/session-state/merge.ts +100 -0
- package/src/shared/lib/workspace/session-state/store-load.test.ts +206 -0
- package/src/shared/lib/workspace/session-state/store.test.ts +17 -139
- package/src/shared/lib/workspace/session-state/store.ts +92 -130
- package/src/shared/lib/workspace/workspace-modules.test.ts +49 -1
- package/src/shared/lib/workspace/workspace-panel-widths.ts +124 -0
- package/src/shared/lib/workspace/worktree/branch-name.test.ts +95 -0
- package/src/shared/lib/workspace/worktree/branch-name.ts +115 -0
- package/src/shared/lib/workspace/worktree/types.ts +104 -0
- package/src/shared/types/fs.ts +10 -0
- package/src/shared/types/realtime.ts +29 -0
- package/src/shared/types/settings/state.ts +10 -0
- package/src/shared/types/workspace.ts +14 -0
- package/src/test-support/file-explorer-disk.test-util.ts +76 -0
- package/src/test-support/file-explorer.test-util.ts +71 -40
- package/src/test-support/loopback.ts +183 -0
- package/dist/client/static/js/index-nzyjf92m.css +0 -1
- package/src/client/components/common/code-editor/handler.test.ts +0 -274
- package/src/client/components/common/code-editor/handler.ts +0 -175
- package/src/client/components/common/code-editor/index.tsx +0 -314
- package/src/client/components/common/code-surface/measure.ts +0 -148
- package/src/client/components/workspace/diff-panel/SplitView.tsx +0 -209
- package/src/client/components/workspace/diff-panel/UnifiedView.tsx +0 -150
- package/src/client/hooks/editor/scroll-container.test.ts +0 -102
- package/src/client/hooks/editor/scroll-container.ts +0 -26
- package/src/client/hooks/editor/use-code-window.ts +0 -100
- package/src/client/hooks/editor/use-editor-history.test.ts +0 -216
- package/src/client/hooks/editor/use-editor-history.ts +0 -134
- package/src/client/hooks/editor/use-occurrences.test.ts +0 -236
- package/src/client/hooks/editor/use-occurrences.ts +0 -98
- package/src/shared/lib/code/editor/commands.test.ts +0 -155
- package/src/shared/lib/code/editor/commands.ts +0 -260
- package/src/shared/lib/code/editor/edits.test.ts +0 -100
- package/src/shared/lib/code/editor/edits.ts +0 -122
- package/src/shared/lib/code/editor/history.test.ts +0 -118
- package/src/shared/lib/code/editor/history.ts +0 -101
- package/src/shared/lib/code/editor/layers.test.ts +0 -77
- package/src/shared/lib/code/editor/layers.ts +0 -67
- package/src/shared/lib/code/editor/line-edits.test.ts +0 -131
- package/src/shared/lib/code/editor/line-edits.ts +0 -159
- package/src/shared/lib/code/editor/occurrences.test.ts +0 -117
- package/src/shared/lib/code/editor/occurrences.ts +0 -111
- package/src/shared/lib/code/editor/reset-css.test.ts +0 -35
- package/src/shared/lib/code/editor/reset-css.ts +0 -32
- package/src/shared/lib/code/editor/text-commands.test.ts +0 -178
- package/src/shared/lib/code/editor/text-commands.ts +0 -154
- package/src/shared/lib/code/lazy-window.test.ts +0 -161
- package/src/shared/lib/code/lazy-window.ts +0 -156
- package/src/shared/lib/code/windowed-highlight.ts +0 -114
- package/src/shared/lib/fs/diff-parser.test.ts +0 -116
- package/src/shared/lib/fs/diff-parser.ts +0 -213
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The code surface's type and chrome, in CodeMirror's own theme format.
|
|
8
|
+
*
|
|
9
|
+
* Type is INHERITED, not restated: the host element carries the font family,
|
|
10
|
+
* size and line box (the zoom control and the editor-font setting both write
|
|
11
|
+
* there), and `.cm-editor` inherits them. That is what keeps a zoom step a pure
|
|
12
|
+
* CSS change — a theme extension carrying a pixel size would have to be
|
|
13
|
+
* reconfigured on every step, and every reconfigure re-creates the editor's
|
|
14
|
+
* plugins (including the tokenizer).
|
|
15
|
+
*
|
|
16
|
+
* Colours are read through `--editor-*` custom properties with the app's
|
|
17
|
+
* palette as the FALLBACK:
|
|
18
|
+
*
|
|
19
|
+
* - `auto` (the default) writes none of them, so every declaration resolves to
|
|
20
|
+
* `--theme-*` and an app theme flip repaints the editor with zero JS — the
|
|
21
|
+
* same property the Shiki decorations rely on.
|
|
22
|
+
* - An explicit editor theme writes them on the surface's own host element
|
|
23
|
+
* (`editorThemeVars`), so the editor paints Dracula while the console around
|
|
24
|
+
* it stays on the app palette. Because the values arrive as CSS, switching
|
|
25
|
+
* between two editor themes is also a repaint: nothing here is reconfigured.
|
|
26
|
+
*
|
|
27
|
+
* The one rule that cannot be a variable is the TOKEN colour. Shiki emits both
|
|
28
|
+
* palettes on every token, and which one paints is decided by the app's variant
|
|
29
|
+
* attribute — except an explicit editor theme tokenizes with ITSELF in both
|
|
30
|
+
* slots, so the two custom properties carry identical values and either one is
|
|
31
|
+
* correct. That is why the dark rule below is safe to state unconditionally.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import type { Extension } from '@codemirror/state';
|
|
35
|
+
import { EditorView } from '@codemirror/view';
|
|
36
|
+
|
|
37
|
+
/** The surface's own CSS. Everything here is structural, never per-layout. */
|
|
38
|
+
export function surfaceTheme(): Extension {
|
|
39
|
+
return EditorView.theme({
|
|
40
|
+
'&': {
|
|
41
|
+
height: '100%',
|
|
42
|
+
backgroundColor: 'var(--editor-bg, transparent)',
|
|
43
|
+
color: 'var(--editor-fg, var(--theme-ink))',
|
|
44
|
+
fontSize: 'inherit',
|
|
45
|
+
fontFamily: 'inherit',
|
|
46
|
+
lineHeight: 'inherit',
|
|
47
|
+
},
|
|
48
|
+
'&.cm-focused': { outline: 'none' },
|
|
49
|
+
'.cm-scroller': {
|
|
50
|
+
overflow: 'auto',
|
|
51
|
+
fontFamily: 'inherit',
|
|
52
|
+
fontSize: 'inherit',
|
|
53
|
+
lineHeight: 'inherit',
|
|
54
|
+
},
|
|
55
|
+
'.cm-content': {
|
|
56
|
+
// CM6 reads these two back as the document's padding, which is why the
|
|
57
|
+
// breathing room belongs here and not on a wrapper: a scroll container's
|
|
58
|
+
// own padding is not part of its scrollable overflow.
|
|
59
|
+
paddingTop: 'var(--surface-pad, 16px)',
|
|
60
|
+
paddingBottom: 'var(--surface-pad, 16px)',
|
|
61
|
+
caretColor: 'var(--editor-cursor, var(--theme-ink))',
|
|
62
|
+
},
|
|
63
|
+
'.cm-line': { padding: '0 var(--surface-pad, 16px)' },
|
|
64
|
+
// The gutter is CM6's own (lineNumbers()), styled to the panel's band. An
|
|
65
|
+
// explicit editor theme paints it in the theme's own surface, which is what
|
|
66
|
+
// VS Code does for the 46 themes that ship no `editorGutter.background`.
|
|
67
|
+
'.cm-gutters': {
|
|
68
|
+
backgroundColor: 'var(--editor-bg, var(--theme-canvas))',
|
|
69
|
+
color: 'var(--editor-gutter-fg, color-mix(in srgb, var(--theme-ink) 30%, transparent))',
|
|
70
|
+
border: 'none',
|
|
71
|
+
borderRight: '1px solid var(--editor-border, color-mix(in srgb, var(--theme-ink) 10%, transparent))',
|
|
72
|
+
fontFamily: 'inherit',
|
|
73
|
+
fontSize: 'inherit',
|
|
74
|
+
lineHeight: 'inherit',
|
|
75
|
+
},
|
|
76
|
+
'.cm-lineNumbers .cm-gutterElement': {
|
|
77
|
+
padding: '0 12px 0 16px',
|
|
78
|
+
minWidth: '1.5rem',
|
|
79
|
+
textAlign: 'right',
|
|
80
|
+
},
|
|
81
|
+
// The selection is drawn by `drawSelection()`; the native one is left to
|
|
82
|
+
// the browser when the editor is not focused.
|
|
83
|
+
'.cm-selectionBackground, &.cm-focused .cm-selectionBackground': {
|
|
84
|
+
backgroundColor: 'var(--editor-selection, color-mix(in srgb, var(--theme-ink) 20%, transparent))',
|
|
85
|
+
},
|
|
86
|
+
'.cm-cursor, .cm-dropCursor': {
|
|
87
|
+
borderLeftColor: 'var(--editor-cursor, var(--theme-ink))',
|
|
88
|
+
},
|
|
89
|
+
// A find match, painted by the decoration field (`marks.ts`). The geometry
|
|
90
|
+
// rules are the ones the old markup layer needed: horizontal edges only, or
|
|
91
|
+
// a match spanning several tokens paints a seam between them.
|
|
92
|
+
'.cm-content .find-match': {
|
|
93
|
+
color: 'inherit',
|
|
94
|
+
backgroundColor: 'var(--editor-find, color-mix(in srgb, var(--theme-warning) 32%, transparent))',
|
|
95
|
+
},
|
|
96
|
+
'.cm-content .find-match[data-find-current]': {
|
|
97
|
+
backgroundColor:
|
|
98
|
+
'var(--editor-find-current, color-mix(in srgb, var(--theme-warning) 55%, transparent))',
|
|
99
|
+
borderTop: '1px solid color-mix(in srgb, var(--theme-warning) 75%, transparent)',
|
|
100
|
+
borderBottom: '1px solid color-mix(in srgb, var(--theme-warning) 75%, transparent)',
|
|
101
|
+
},
|
|
102
|
+
// Shiki tokens. The colour itself comes from the custom properties the
|
|
103
|
+
// decoration carries; `tailwind.css` holds the same pair for every other
|
|
104
|
+
// surface, and this block is the editor's copy of it. The `&`-less form
|
|
105
|
+
// below is CM6's base-class prefix, and the second rule is why an app theme
|
|
106
|
+
// flip repaints tokens with no re-tokenize.
|
|
107
|
+
'.cm-content .shiki-tok': { color: 'var(--shiki-light)' },
|
|
108
|
+
'html[data-theme-variant="dark"] & .cm-content .shiki-tok': { color: 'var(--shiki-dark)' },
|
|
109
|
+
'.cm-placeholder': { color: 'color-mix(in srgb, var(--editor-fg, var(--theme-ink)) 35%, transparent)' },
|
|
110
|
+
});
|
|
111
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The two width-mode buttons, driven through the REAL session-state provider.
|
|
8
|
+
*
|
|
9
|
+
* Three properties only a mount can see, and each fails silently:
|
|
10
|
+
*
|
|
11
|
+
* - The two toolbars draw the SAME state. `useSessionState` hands every caller
|
|
12
|
+
* a private copy, so a version reading it would leave the diff toolbar
|
|
13
|
+
* showing `normal` after the editor toolbar entered compact view — two
|
|
14
|
+
* buttons disagreeing about one layout.
|
|
15
|
+
* - Each button reports `aria-pressed` for its own mode and nothing else, so
|
|
16
|
+
* the pair reads as a mode selection rather than as two unrelated switches.
|
|
17
|
+
* - Clicking the ACTIVE mode returns to `normal`, which is the only way back to
|
|
18
|
+
* a layout with the chat on screen without hunting for the other button.
|
|
19
|
+
*
|
|
20
|
+
* Rendered with `h()` against happy-dom, over a stubbed `fetch` that answers
|
|
21
|
+
* the session-state blob (the provider loads it before anything is ready).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { afterAll, afterEach, beforeAll, describe, expect, test } from 'bun:test';
|
|
25
|
+
import { Window } from 'happy-dom';
|
|
26
|
+
import { h, render } from 'preact';
|
|
27
|
+
import { act } from 'preact/test-utils';
|
|
28
|
+
import { EditorViewButtons } from '@/client/components/common/editor-view-buttons';
|
|
29
|
+
import { SessionStateProvider } from '@/client/components/common/session-state-provider/index';
|
|
30
|
+
import { forgetSession } from '@/shared/lib/workspace/session-state/store';
|
|
31
|
+
import { installDomGlobals, restoreDomGlobals } from '@/test-support/pristine-globals';
|
|
32
|
+
|
|
33
|
+
const SESSION = 'new-editor-view-buttons';
|
|
34
|
+
/** The runner's own fetch, reached through `Bun` so a leaked stub cannot be mistaken for it. */
|
|
35
|
+
const nativeFetch = Bun.fetch;
|
|
36
|
+
|
|
37
|
+
let container: HTMLElement;
|
|
38
|
+
|
|
39
|
+
beforeAll(() => {
|
|
40
|
+
installDomGlobals(new Window({ url: 'http://localhost' }));
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
if (container) {
|
|
45
|
+
render(null, container);
|
|
46
|
+
container.remove();
|
|
47
|
+
}
|
|
48
|
+
forgetSession(SESSION);
|
|
49
|
+
(globalThis as unknown as Record<string, unknown>).fetch = nativeFetch;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterAll(() => {
|
|
53
|
+
restoreDomGlobals();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Mount TWO button pairs under one provider — the editor toolbar's and the diff
|
|
58
|
+
* toolbar's — because "they share one slot" is the claim, and a single pair
|
|
59
|
+
* could pass with private copies.
|
|
60
|
+
*/
|
|
61
|
+
async function mount(stored: Record<string, unknown> = {}): Promise<void> {
|
|
62
|
+
const target = globalThis as unknown as Record<string, unknown>;
|
|
63
|
+
target.fetch = async () => {
|
|
64
|
+
const json = (body: unknown) =>
|
|
65
|
+
new Response(JSON.stringify(body), { status: 200, headers: { 'content-type': 'application/json' } });
|
|
66
|
+
return json({ sessionId: SESSION, state: stored });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
container = document.createElement('div');
|
|
70
|
+
document.body.appendChild(container);
|
|
71
|
+
await act(async () => {
|
|
72
|
+
render(
|
|
73
|
+
h(SessionStateProvider, {
|
|
74
|
+
sessionId: SESSION,
|
|
75
|
+
children: h(
|
|
76
|
+
'div',
|
|
77
|
+
null,
|
|
78
|
+
h('div', { id: 'editor-toolbar' }, h(EditorViewButtons, { size: 14 })),
|
|
79
|
+
h('div', { id: 'diff-toolbar' }, h(EditorViewButtons, { size: 13 })),
|
|
80
|
+
),
|
|
81
|
+
}),
|
|
82
|
+
container,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
// The provider's blob load settles across a tick.
|
|
86
|
+
for (let i = 0; i < 10; i += 1) await act(async () => {});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The named button inside one of the two toolbar hosts. */
|
|
90
|
+
function button(toolbar: 'editor-toolbar' | 'diff-toolbar', name: string): HTMLButtonElement {
|
|
91
|
+
const host = container.querySelector(`#${toolbar}`);
|
|
92
|
+
const found = [...(host?.querySelectorAll('button') ?? [])].find(
|
|
93
|
+
(el) => el.getAttribute('aria-label') === name,
|
|
94
|
+
);
|
|
95
|
+
if (!found) throw new Error(`no "${name}" button in #${toolbar}`);
|
|
96
|
+
return found as HTMLButtonElement;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function click(el: HTMLButtonElement): Promise<void> {
|
|
100
|
+
await act(async () => {
|
|
101
|
+
el.dispatchEvent(new window.MouseEvent('click', { bubbles: true }) as never);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
describe('EditorViewButtons', () => {
|
|
106
|
+
test('both toolbars offer both modes, and neither is pressed by default', async () => {
|
|
107
|
+
await mount();
|
|
108
|
+
|
|
109
|
+
for (const toolbar of ['editor-toolbar', 'diff-toolbar'] as const) {
|
|
110
|
+
expect(button(toolbar, 'Compact view').getAttribute('aria-pressed')).toBe('false');
|
|
111
|
+
expect(button(toolbar, 'Full view').getAttribute('aria-pressed')).toBe('false');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('a click enters the mode, and the OTHER toolbar shows the same state', async () => {
|
|
116
|
+
await mount();
|
|
117
|
+
await click(button('editor-toolbar', 'Compact view'));
|
|
118
|
+
|
|
119
|
+
expect(button('editor-toolbar', 'Compact view').getAttribute('aria-pressed')).toBe('true');
|
|
120
|
+
expect(button('diff-toolbar', 'Compact view').getAttribute('aria-pressed')).toBe('true');
|
|
121
|
+
expect(button('diff-toolbar', 'Full view').getAttribute('aria-pressed')).toBe('false');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('the two modes are exclusive: entering one clears the other', async () => {
|
|
125
|
+
await mount();
|
|
126
|
+
await click(button('editor-toolbar', 'Compact view'));
|
|
127
|
+
await click(button('diff-toolbar', 'Full view'));
|
|
128
|
+
|
|
129
|
+
expect(button('editor-toolbar', 'Compact view').getAttribute('aria-pressed')).toBe('false');
|
|
130
|
+
expect(button('editor-toolbar', 'Full view').getAttribute('aria-pressed')).toBe('true');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('clicking the active mode returns to the ordinary layout', async () => {
|
|
134
|
+
await mount();
|
|
135
|
+
await click(button('editor-toolbar', 'Full view'));
|
|
136
|
+
await click(button('editor-toolbar', 'Full view'));
|
|
137
|
+
|
|
138
|
+
expect(button('editor-toolbar', 'Full view').getAttribute('aria-pressed')).toBe('false');
|
|
139
|
+
expect(button('editor-toolbar', 'Compact view').getAttribute('aria-pressed')).toBe('false');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('a mode stored for this session is restored on mount', async () => {
|
|
143
|
+
await mount({ 'layout.editorViewMode': 'full' });
|
|
144
|
+
|
|
145
|
+
expect(button('editor-toolbar', 'Full view').getAttribute('aria-pressed')).toBe('true');
|
|
146
|
+
expect(button('diff-toolbar', 'Full view').getAttribute('aria-pressed')).toBe('true');
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { MessageSquareOff, PanelLeftClose } from 'lucide-preact';
|
|
2
|
+
import type { LucideIcon } from 'lucide-preact';
|
|
3
|
+
import { useSharedSessionState } from '@/client/hooks/workspace/session-state';
|
|
4
|
+
import type { ToolbarOverflowAction } from '@/client/components/common/toolbar-row';
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_EDITOR_VIEW_MODE,
|
|
7
|
+
EDITOR_VIEW_MODE_KEY,
|
|
8
|
+
isEditorViewMode,
|
|
9
|
+
toggledEditorViewMode,
|
|
10
|
+
type EditorViewMode,
|
|
11
|
+
type EditorViewModeTarget,
|
|
12
|
+
} from '@/shared/lib/workspace/editor-view-mode';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The two width modes, as data.
|
|
16
|
+
*
|
|
17
|
+
* Each is a row so the buttons, their labels and their icons cannot drift
|
|
18
|
+
* apart: the label states exactly which columns disappear, because "Compact"
|
|
19
|
+
* and "Full" are words that could mean anything, and the icons are DIFFERENT
|
|
20
|
+
* SHAPES rather than two spellings of the same one — the third width control
|
|
21
|
+
* beside them (full SCREEN, which covers the right panel too) keeps its own
|
|
22
|
+
* `Maximize` glyph.
|
|
23
|
+
*/
|
|
24
|
+
export const EDITOR_VIEW_MODES: Array<{
|
|
25
|
+
target: EditorViewModeTarget;
|
|
26
|
+
label: string;
|
|
27
|
+
hint: string;
|
|
28
|
+
Icon: LucideIcon;
|
|
29
|
+
}> = [
|
|
30
|
+
{
|
|
31
|
+
target: 'compact',
|
|
32
|
+
label: 'Compact view',
|
|
33
|
+
hint: 'Editor takes the chat width — chat hidden, sidebar and right panel kept',
|
|
34
|
+
Icon: MessageSquareOff,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
target: 'full',
|
|
38
|
+
label: 'Full view',
|
|
39
|
+
hint: 'Editor takes the chat and sidebar width — chat and sidebar hidden, right panel kept',
|
|
40
|
+
Icon: PanelLeftClose,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/** The mode in force, and the setter the buttons share. */
|
|
45
|
+
export function useEditorViewMode(): { mode: EditorViewMode; setMode: (next: EditorViewMode) => void } {
|
|
46
|
+
const [stored, setMode] = useSharedSessionState<EditorViewMode>(EDITOR_VIEW_MODE_KEY, DEFAULT_EDITOR_VIEW_MODE);
|
|
47
|
+
return { mode: isEditorViewMode(stored) ? stored : DEFAULT_EDITOR_VIEW_MODE, setMode };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The same two modes as `⋯` menu rows, for a toolbar too narrow to draw them.
|
|
52
|
+
*
|
|
53
|
+
* A hook rather than a plain function because the menu row has to CLICK the same
|
|
54
|
+
* slot the buttons write, and a row built from a stale mode would enter the mode
|
|
55
|
+
* already in force (which the toggle reads as "leave it").
|
|
56
|
+
*/
|
|
57
|
+
export function useEditorViewActions(): ToolbarOverflowAction[] {
|
|
58
|
+
const { mode, setMode } = useEditorViewMode();
|
|
59
|
+
return EDITOR_VIEW_MODES.map(({ target, label, Icon }) => ({
|
|
60
|
+
// A menu row has no hover title, so the sentence that explains the mode is
|
|
61
|
+
// part of what the row says rather than lost with the button's tooltip.
|
|
62
|
+
label,
|
|
63
|
+
icon: Icon,
|
|
64
|
+
active: mode === target,
|
|
65
|
+
onSelect: () => setMode(toggledEditorViewMode(mode, target)),
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface EditorViewButtonsProps {
|
|
70
|
+
/** Glyph size: the editor toolbar draws 14px, the diff toolbar 13px. */
|
|
71
|
+
size?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The two width-mode buttons, drawn by BOTH the editor and the diff toolbar.
|
|
76
|
+
*
|
|
77
|
+
* One component because the two toolbars are the same surface on two tab kinds:
|
|
78
|
+
* a mode entered from the diff and a mode entered from a file must be the same
|
|
79
|
+
* mode, and a second copy of these rules is how the diff ends up offering a
|
|
80
|
+
* width the layout does not implement. The state is the shared session slot
|
|
81
|
+
* (see `useEditorViewLayout`) so the layout collapses the columns on the same
|
|
82
|
+
* render.
|
|
83
|
+
*/
|
|
84
|
+
export function EditorViewButtons({ size = 14 }: EditorViewButtonsProps) {
|
|
85
|
+
const { mode, setMode } = useEditorViewMode();
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<>
|
|
89
|
+
{EDITOR_VIEW_MODES.map(({ target, label, hint, Icon }) => {
|
|
90
|
+
const active = mode === target;
|
|
91
|
+
return (
|
|
92
|
+
<button
|
|
93
|
+
key={target}
|
|
94
|
+
type="button"
|
|
95
|
+
onClick={() => setMode(toggledEditorViewMode(mode, target))}
|
|
96
|
+
className={`flex items-center justify-center p-1.5 rounded transition-colors cursor-pointer flex-shrink-0 ${
|
|
97
|
+
active ? 'bg-ink/10 text-ink' : 'text-ink/50 hover:text-ink hover:bg-ink/5'
|
|
98
|
+
}`}
|
|
99
|
+
title={hint}
|
|
100
|
+
aria-label={label}
|
|
101
|
+
aria-pressed={active}
|
|
102
|
+
>
|
|
103
|
+
<Icon size={size} />
|
|
104
|
+
</button>
|
|
105
|
+
);
|
|
106
|
+
})}
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -6,6 +6,8 @@ import { ContextUsageButton } from '@/client/components/common/context-usage-but
|
|
|
6
6
|
import { HeaderPanelButtons } from '@/client/components/common/header-panel-buttons';
|
|
7
7
|
import { useHeaderEntries } from '@/client/components/common/header-panel-buttons/entries';
|
|
8
8
|
import { HeaderMoreMenu } from '@/client/components/common/header-controls/MoreMenu';
|
|
9
|
+
import { WindowControls } from '@/client/components/common/window-controls';
|
|
10
|
+
import { drawsWindowControls } from '@/client/lib/desktop/titlebar';
|
|
9
11
|
import { useAuthEnabled, useAuthState } from '@/client/hooks/ui/auth';
|
|
10
12
|
import { useViewportAtLeast } from '@/client/hooks/ui/media-query';
|
|
11
13
|
import { withChord } from '@/client/lib/ui/with-chord';
|
|
@@ -111,7 +113,17 @@ export function HeaderControls({
|
|
|
111
113
|
const headerEntries = useHeaderEntries(layout);
|
|
112
114
|
|
|
113
115
|
return (
|
|
114
|
-
|
|
116
|
+
// `titlebar-no-drag` belongs HERE, on the cluster's own root, not on each
|
|
117
|
+
// call site. `app-region` INHERITS (WebView2's own spec says so, and
|
|
118
|
+
// Chromium 152 began enforcing it), so a header that marks itself a drag
|
|
119
|
+
// region makes every descendant a drag handle unless something below it
|
|
120
|
+
// says otherwise — a control inside one is unclickable, silently. The
|
|
121
|
+
// desktop navbar wrapped this cluster in a `titlebar-no-drag` div and the
|
|
122
|
+
// PHONE's header did not, so on Windows every control in a phone-width
|
|
123
|
+
// window (reload, context, sign out, the right panel, `⋯`) swallowed its
|
|
124
|
+
// own click. Stating it on the cluster is what makes the next layout that
|
|
125
|
+
// draws it correct by construction.
|
|
126
|
+
<div className="flex items-center space-x-1 titlebar-no-drag">
|
|
115
127
|
{layout === 'desktop' && <PWAInstallButton />}
|
|
116
128
|
<HeaderPanelButtons layout={layout} entries={headerEntries.inline} />
|
|
117
129
|
{/* The cluster's ORDER is fixed; what changes on a narrow phone is that
|
|
@@ -169,6 +181,17 @@ export function HeaderControls({
|
|
|
169
181
|
<Smartphone size={size} />
|
|
170
182
|
</button>
|
|
171
183
|
)}
|
|
184
|
+
{/* The Windows and Linux shells draw no caption buttons of their own —
|
|
185
|
+
their webview is the whole window — so the PAGE owns minimise,
|
|
186
|
+
maximise and close. This lives in the shared cluster rather than in
|
|
187
|
+
the desktop navbar for a measured reason: a desktop shell window can
|
|
188
|
+
be NARROWER than the layout breakpoint (`App` switches to the mobile
|
|
189
|
+
layout below 768px, and all three shells allow a 720px window), and
|
|
190
|
+
the phone header drew no window controls at all. A user who shrank
|
|
191
|
+
the window lost close/minimise/maximise entirely, with no title bar
|
|
192
|
+
behind them to fall back on. macOS never reaches this branch: its
|
|
193
|
+
traffic lights are the system's, so `drawsWindowControls` is false. */}
|
|
194
|
+
{drawsWindowControls() && <WindowControls />}
|
|
172
195
|
</div>
|
|
173
196
|
);
|
|
174
197
|
}
|