@pennyfarthing/cyclist 10.4.0 → 11.0.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/dist/api/agent-load.d.ts +1 -2
- package/dist/api/agent-load.d.ts.map +1 -1
- package/dist/api/agent-load.js +2 -123
- package/dist/api/agent-load.js.map +1 -1
- package/dist/api/audit-log.d.ts +1 -17
- package/dist/api/audit-log.d.ts.map +1 -1
- package/dist/api/audit-log.js +2 -162
- package/dist/api/audit-log.js.map +1 -1
- package/dist/api/background-tasks.d.ts +1 -26
- package/dist/api/background-tasks.d.ts.map +1 -1
- package/dist/api/background-tasks.js +2 -55
- package/dist/api/background-tasks.js.map +1 -1
- package/dist/api/bell.d.ts +1 -18
- package/dist/api/bell.d.ts.map +1 -1
- package/dist/api/bell.js +2 -33
- package/dist/api/bell.js.map +1 -1
- package/dist/api/code-markers.d.ts +1 -8
- package/dist/api/code-markers.d.ts.map +1 -1
- package/dist/api/code-markers.js +2 -61
- package/dist/api/code-markers.js.map +1 -1
- package/dist/api/complexity.d.ts +1 -2
- package/dist/api/complexity.d.ts.map +1 -1
- package/dist/api/complexity.js +2 -46
- package/dist/api/complexity.js.map +1 -1
- package/dist/api/context.d.ts +1 -37
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +2 -143
- package/dist/api/context.js.map +1 -1
- package/dist/api/dead-code.d.ts +1 -2
- package/dist/api/dead-code.d.ts.map +1 -1
- package/dist/api/dead-code.js +2 -69
- package/dist/api/dead-code.js.map +1 -1
- package/dist/api/dependencies.d.ts +1 -2
- package/dist/api/dependencies.d.ts.map +1 -1
- package/dist/api/dependencies.js +2 -42
- package/dist/api/dependencies.js.map +1 -1
- package/dist/api/evaluation.d.ts +1 -19
- package/dist/api/evaluation.d.ts.map +1 -1
- package/dist/api/evaluation.js +2 -127
- package/dist/api/evaluation.js.map +1 -1
- package/dist/api/file-browser.d.ts +1 -8
- package/dist/api/file-browser.d.ts.map +1 -1
- package/dist/api/file-browser.js +2 -114
- package/dist/api/file-browser.js.map +1 -1
- package/dist/api/git.d.ts +1 -46
- package/dist/api/git.d.ts.map +1 -1
- package/dist/api/git.js +2 -354
- package/dist/api/git.js.map +1 -1
- package/dist/api/health-score.d.ts +1 -2
- package/dist/api/health-score.d.ts.map +1 -1
- package/dist/api/health-score.js +2 -46
- package/dist/api/health-score.js.map +1 -1
- package/dist/api/hook-request.d.ts +1 -40
- package/dist/api/hook-request.d.ts.map +1 -1
- package/dist/api/hook-request.js +2 -277
- package/dist/api/hook-request.js.map +1 -1
- package/dist/api/hotspots.d.ts +1 -2
- package/dist/api/hotspots.d.ts.map +1 -1
- package/dist/api/hotspots.js +2 -61
- package/dist/api/hotspots.js.map +1 -1
- package/dist/api/identity.d.ts +1 -16
- package/dist/api/identity.d.ts.map +1 -1
- package/dist/api/identity.js +2 -78
- package/dist/api/identity.js.map +1 -1
- package/dist/api/index.d.ts +1 -34
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -44
- package/dist/api/index.js.map +1 -1
- package/dist/api/mode.d.ts +1 -22
- package/dist/api/mode.d.ts.map +1 -1
- package/dist/api/mode.js +2 -37
- package/dist/api/mode.js.map +1 -1
- package/dist/api/otlp.d.ts +1 -2
- package/dist/api/otlp.d.ts.map +1 -1
- package/dist/api/otlp.js +2 -46
- package/dist/api/otlp.js.map +1 -1
- package/dist/api/permissions.d.ts +1 -15
- package/dist/api/permissions.d.ts.map +1 -1
- package/dist/api/permissions.js +2 -66
- package/dist/api/permissions.js.map +1 -1
- package/dist/api/persona.d.ts +1 -8
- package/dist/api/persona.d.ts.map +1 -1
- package/dist/api/persona.js +2 -67
- package/dist/api/persona.js.map +1 -1
- package/dist/api/portrait.d.ts +1 -5
- package/dist/api/portrait.d.ts.map +1 -1
- package/dist/api/portrait.js +2 -27
- package/dist/api/portrait.js.map +1 -1
- package/dist/api/settings.d.ts +1 -53
- package/dist/api/settings.d.ts.map +1 -1
- package/dist/api/settings.js +2 -464
- package/dist/api/settings.js.map +1 -1
- package/dist/api/spans.d.ts +1 -16
- package/dist/api/spans.d.ts.map +1 -1
- package/dist/api/spans.js +2 -244
- package/dist/api/spans.js.map +1 -1
- package/dist/api/stats.d.ts +1 -12
- package/dist/api/stats.d.ts.map +1 -1
- package/dist/api/stats.js +2 -84
- package/dist/api/stats.js.map +1 -1
- package/dist/api/story.d.ts +1 -2
- package/dist/api/story.d.ts.map +1 -1
- package/dist/api/story.js +2 -14
- package/dist/api/story.js.map +1 -1
- package/dist/api/telemetry.d.ts +1 -18
- package/dist/api/telemetry.d.ts.map +1 -1
- package/dist/api/telemetry.js +2 -164
- package/dist/api/telemetry.js.map +1 -1
- package/dist/api/theme-agents.d.ts +1 -60
- package/dist/api/theme-agents.d.ts.map +1 -1
- package/dist/api/theme-agents.js +2 -213
- package/dist/api/theme-agents.js.map +1 -1
- package/dist/api/todos.d.ts +1 -32
- package/dist/api/todos.d.ts.map +1 -1
- package/dist/api/todos.js +2 -43
- package/dist/api/todos.js.map +1 -1
- package/dist/api/token-stats.d.ts +1 -7
- package/dist/api/token-stats.d.ts.map +1 -1
- package/dist/api/token-stats.js +2 -35
- package/dist/api/token-stats.js.map +1 -1
- package/dist/api/welcome.d.ts +1 -21
- package/dist/api/welcome.d.ts.map +1 -1
- package/dist/api/welcome.js +2 -34
- package/dist/api/welcome.js.map +1 -1
- package/dist/bikerack.js +2 -2
- package/dist/bikerack.js.map +1 -1
- package/dist/env.d.ts +6 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/focus.d.ts +53 -0
- package/dist/focus.d.ts.map +1 -0
- package/dist/focus.js +122 -0
- package/dist/focus.js.map +1 -0
- package/dist/git-cache.d.ts +1 -0
- package/dist/git-cache.d.ts.map +1 -1
- package/dist/git-cache.js +3 -1
- package/dist/git-cache.js.map +1 -1
- package/dist/menu-builder.d.ts.map +1 -1
- package/dist/menu-builder.js +0 -1
- package/dist/menu-builder.js.map +1 -1
- package/dist/prime.d.ts +3 -3
- package/dist/prime.d.ts.map +1 -1
- package/dist/prime.js +38 -14
- package/dist/prime.js.map +1 -1
- package/dist/public/css/react.css +1 -1
- package/dist/public/js/react/react.js +53 -61
- package/dist/server.d.ts +18 -85
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +105 -405
- package/dist/server.js.map +1 -1
- package/dist/sprint-data.d.ts +1 -1
- package/dist/sprint-data.d.ts.map +1 -1
- package/dist/sprint-data.js +2 -2
- package/dist/sprint-data.js.map +1 -1
- package/dist/theme-metadata.d.ts +3 -3
- package/dist/theme-metadata.d.ts.map +1 -1
- package/dist/theme-metadata.js +4 -4
- package/dist/theme-metadata.js.map +1 -1
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +53 -75
- package/dist/websocket.js.map +1 -1
- package/package.json +2 -6
- package/portraits/hogans-heroes/large/burkhalter-35312.png +0 -0
- package/portraits/hogans-heroes/large/carter-34352.png +0 -0
- package/portraits/hogans-heroes/large/hochstetter-45314.png +0 -0
- package/portraits/hogans-heroes/large/hogan-44541.png +0 -0
- package/portraits/hogans-heroes/large/kinch-35241.png +0 -0
- package/portraits/hogans-heroes/large/klink-23434.png +0 -0
- package/portraits/hogans-heroes/large/lebeau-45443.png +0 -0
- package/portraits/hogans-heroes/large/marya-53543.png +0 -0
- package/portraits/hogans-heroes/large/newkirk-54432.png +0 -0
- package/portraits/hogans-heroes/large/schultz-42453.png +0 -0
- package/portraits/hogans-heroes/large/underground-55131.png +0 -0
- package/portraits/hogans-heroes/medium/burkhalter-35312.png +0 -0
- package/portraits/hogans-heroes/medium/carter-34352.png +0 -0
- package/portraits/hogans-heroes/medium/hochstetter-45314.png +0 -0
- package/portraits/hogans-heroes/medium/hogan-44541.png +0 -0
- package/portraits/hogans-heroes/medium/kinch-35241.png +0 -0
- package/portraits/hogans-heroes/medium/klink-23434.png +0 -0
- package/portraits/hogans-heroes/medium/lebeau-45443.png +0 -0
- package/portraits/hogans-heroes/medium/marya-53543.png +0 -0
- package/portraits/hogans-heroes/medium/newkirk-54432.png +0 -0
- package/portraits/hogans-heroes/medium/schultz-42453.png +0 -0
- package/portraits/hogans-heroes/medium/underground-55131.png +0 -0
- package/portraits/monty-python/large/announcer-44441.png +0 -0
- package/portraits/monty-python/large/arguer-35412.png +0 -0
- package/portraits/monty-python/large/bicycle-repair-man-35241.png +0 -0
- package/portraits/monty-python/large/colonel-35423.png +0 -0
- package/portraits/monty-python/large/counsellor-45341.png +0 -0
- package/portraits/monty-python/large/gumbys-23524.png +0 -0
- package/portraits/monty-python/large/nudge-43533.png +0 -0
- package/portraits/monty-python/large/praline-45413.png +0 -0
- package/portraits/monty-python/large/silly-walks-55322.png +0 -0
- package/portraits/monty-python/large/wensleydale-54451.png +0 -0
- package/portraits/monty-python/large/xim-nez-43534.png +0 -0
- package/portraits/monty-python/medium/announcer-44441.png +0 -0
- package/portraits/monty-python/medium/arguer-35412.png +0 -0
- package/portraits/monty-python/medium/bicycle-repair-man-35241.png +0 -0
- package/portraits/monty-python/medium/colonel-35423.png +0 -0
- package/portraits/monty-python/medium/counsellor-45341.png +0 -0
- package/portraits/monty-python/medium/gumbys-23524.png +0 -0
- package/portraits/monty-python/medium/nudge-43533.png +0 -0
- package/portraits/monty-python/medium/praline-45413.png +0 -0
- package/portraits/monty-python/medium/silly-walks-55322.png +0 -0
- package/portraits/monty-python/medium/wensleydale-54451.png +0 -0
- package/portraits/monty-python/medium/xim-nez-43534.png +0 -0
- package/portraits/stephen-king/large/andy-55231.png +0 -0
- package/portraits/stephen-king/large/christine-25112.png +0 -0
- package/portraits/stephen-king/large/danny-53243.png +0 -0
- package/portraits/stephen-king/large/flagg-55311.png +0 -0
- package/portraits/stephen-king/large/gaunt-54421.png +0 -0
- package/portraits/stephen-king/large/jack-44224.png +0 -0
- package/portraits/stephen-king/large/johnny-44353.png +0 -0
- package/portraits/stephen-king/large/margaret-15415.png +0 -0
- package/portraits/stephen-king/large/paul-45233.png +0 -0
- package/portraits/stephen-king/large/pennywise-54411.png +0 -0
- package/portraits/stephen-king/large/roland-35121.png +0 -0
- package/portraits/stephen-king/medium/andy-55231.png +0 -0
- package/portraits/stephen-king/medium/christine-25112.png +0 -0
- package/portraits/stephen-king/medium/danny-53243.png +0 -0
- package/portraits/stephen-king/medium/flagg-55311.png +0 -0
- package/portraits/stephen-king/medium/gaunt-54421.png +0 -0
- package/portraits/stephen-king/medium/jack-44224.png +0 -0
- package/portraits/stephen-king/medium/johnny-44353.png +0 -0
- package/portraits/stephen-king/medium/margaret-15415.png +0 -0
- package/portraits/stephen-king/medium/paul-45233.png +0 -0
- package/portraits/stephen-king/medium/pennywise-54411.png +0 -0
- package/portraits/stephen-king/medium/roland-35121.png +0 -0
- package/portraits/star-trek-tng/large/beverly-44352.png +0 -0
- package/portraits/star-trek-tng/large/data-55241.png +0 -0
- package/portraits/star-trek-tng/large/deanna-43353.png +0 -0
- package/portraits/star-trek-tng/large/geordi-54342.png +0 -0
- package/portraits/star-trek-tng/large/jean-luc-45342.png +0 -0
- package/portraits/star-trek-tng/large/kathryn-45332.png +0 -0
- package/portraits/star-trek-tng/large/miles-35342.png +0 -0
- package/portraits/star-trek-tng/large/q-53521.png +0 -0
- package/portraits/star-trek-tng/large/spock-45231.png +0 -0
- package/portraits/star-trek-tng/large/troi-44352.png +0 -0
- package/portraits/star-trek-tng/medium/beverly-44352.png +0 -0
- package/portraits/star-trek-tng/medium/data-55241.png +0 -0
- package/portraits/star-trek-tng/medium/deanna-43353.png +0 -0
- package/portraits/star-trek-tng/medium/geordi-54342.png +0 -0
- package/portraits/star-trek-tng/medium/jean-luc-45342.png +0 -0
- package/portraits/star-trek-tng/medium/kathryn-45332.png +0 -0
- package/portraits/star-trek-tng/medium/miles-35342.png +0 -0
- package/portraits/star-trek-tng/medium/q-53521.png +0 -0
- package/portraits/star-trek-tng/medium/spock-45231.png +0 -0
- package/portraits/star-trek-tng/medium/troi-44352.png +0 -0
- package/src/public/App.tsx +0 -340
- package/src/public/components/AgentLoadDialog.tsx +0 -202
- package/src/public/components/AgentPopup.tsx +0 -308
- package/src/public/components/ApprovalModal/ApprovalModal.css +0 -35
- package/src/public/components/ApprovalModal/index.tsx +0 -632
- package/src/public/components/BikeRackIndex.tsx +0 -54
- package/src/public/components/BikeRackWorkspace.tsx +0 -142
- package/src/public/components/CommandPalette.tsx +0 -555
- package/src/public/components/ConfirmDialog.tsx +0 -168
- package/src/public/components/ContextIndicator/ContextIndicator.css +0 -85
- package/src/public/components/ContextIndicator/index.tsx +0 -330
- package/src/public/components/ContextSparkline.tsx +0 -56
- package/src/public/components/ControlBar.tsx +0 -636
- package/src/public/components/DeadCodeDialog.tsx +0 -169
- package/src/public/components/DiffViewer.tsx +0 -585
- package/src/public/components/DockviewWorkspace.tsx +0 -737
- package/src/public/components/Editor.tsx +0 -630
- package/src/public/components/ErrorBoundary.tsx +0 -67
- package/src/public/components/FileTree.tsx +0 -379
- package/src/public/components/FontPicker/FontPicker.css +0 -276
- package/src/public/components/FontPicker/index.tsx +0 -430
- package/src/public/components/FullFileTree.tsx +0 -237
- package/src/public/components/HealthGauge.tsx +0 -181
- package/src/public/components/Message.tsx +0 -225
- package/src/public/components/MessageList.tsx +0 -98
- package/src/public/components/MessageView.tsx +0 -400
- package/src/public/components/ModeSwitch/ModeSwitch.css +0 -165
- package/src/public/components/ModeSwitch/index.tsx +0 -372
- package/src/public/components/PersonaHeader.tsx +0 -240
- package/src/public/components/QuickActions.tsx +0 -267
- package/src/public/components/SpanTimeline.tsx +0 -352
- package/src/public/components/StandalonePanel.tsx +0 -84
- package/src/public/components/StatsStrip.tsx +0 -162
- package/src/public/components/StreamingContent.tsx +0 -77
- package/src/public/components/SubagentSpan.tsx +0 -180
- package/src/public/components/TandemPortrait.tsx +0 -72
- package/src/public/components/ThemePalette/ThemePalette.css +0 -179
- package/src/public/components/ThemePalette/index.tsx +0 -326
- package/src/public/components/ToolCallBlock.tsx +0 -252
- package/src/public/components/ToolStack.tsx +0 -209
- package/src/public/components/ToolStatus.tsx +0 -57
- package/src/public/components/dialogs/CodeMarkersDialog.tsx +0 -169
- package/src/public/components/dialogs/ComplexityDialog.tsx +0 -163
- package/src/public/components/dialogs/DependenciesDialog.tsx +0 -120
- package/src/public/components/dialogs/HotspotsDialog.tsx +0 -451
- package/src/public/components/dialogs/ToolDialog.tsx +0 -43
- package/src/public/components/panel-registry.ts +0 -11
- package/src/public/components/panels/ACPanel.tsx +0 -93
- package/src/public/components/panels/AcceptanceCriteriaPanel.tsx +0 -104
- package/src/public/components/panels/AuditLogPanel.tsx +0 -465
- package/src/public/components/panels/BackgroundPanel.tsx +0 -115
- package/src/public/components/panels/BikeLanePanel.tsx +0 -214
- package/src/public/components/panels/ChangedPanel.tsx +0 -65
- package/src/public/components/panels/DebugPanel.tsx +0 -344
- package/src/public/components/panels/DiffsPanel.tsx +0 -155
- package/src/public/components/panels/GitPanel.tsx +0 -216
- package/src/public/components/panels/HotspotsPanel.tsx +0 -365
- package/src/public/components/panels/MessagePanel.tsx +0 -497
- package/src/public/components/panels/SettingsPanel.tsx +0 -453
- package/src/public/components/panels/SprintPanel.tsx +0 -670
- package/src/public/components/panels/TTYPanel.tsx +0 -299
- package/src/public/components/panels/TodoPanel.tsx +0 -142
- package/src/public/components/panels/WorkflowPanel.tsx +0 -224
- package/src/public/components/panels/index.ts +0 -24
- package/src/public/components/ui/alert-dialog.tsx +0 -139
- package/src/public/components/ui/badge.tsx +0 -36
- package/src/public/components/ui/button.tsx +0 -57
- package/src/public/components/ui/checkbox.tsx +0 -28
- package/src/public/components/ui/collapsible.tsx +0 -9
- package/src/public/components/ui/command.tsx +0 -151
- package/src/public/components/ui/dialog.tsx +0 -120
- package/src/public/components/ui/popover.tsx +0 -31
- package/src/public/components/ui/progress.tsx +0 -28
- package/src/public/components/ui/scroll-area.tsx +0 -46
- package/src/public/components/ui/select.tsx +0 -157
- package/src/public/components/ui/separator.tsx +0 -29
- package/src/public/components/ui/skeleton.tsx +0 -15
- package/src/public/components/ui/switch.tsx +0 -27
- package/src/public/components/ui/toggle-group.tsx +0 -59
- package/src/public/components/ui/toggle.tsx +0 -43
- package/src/public/components/ui/tooltip.tsx +0 -30
- package/src/public/contexts/ClaudeContext.tsx +0 -311
- package/src/public/contexts/MessageQueueContext.tsx +0 -143
- package/src/public/css/theme-browser.css +0 -550
- package/src/public/css/theme-system.css +0 -630
- package/src/public/hooks/index.ts +0 -49
- package/src/public/hooks/useAgentLoad.ts +0 -105
- package/src/public/hooks/useBackgroundTasks.ts +0 -131
- package/src/public/hooks/useClaude.ts +0 -234
- package/src/public/hooks/useCodeMarkers.ts +0 -101
- package/src/public/hooks/useColorScheme.ts +0 -42
- package/src/public/hooks/useCommandHistory.ts +0 -99
- package/src/public/hooks/useComplexity.ts +0 -80
- package/src/public/hooks/useDeadCode.ts +0 -99
- package/src/public/hooks/useDependencies.ts +0 -82
- package/src/public/hooks/useDiffs.ts +0 -143
- package/src/public/hooks/useFileBrowser.ts +0 -71
- package/src/public/hooks/useGitStatus.ts +0 -233
- package/src/public/hooks/useHealthScore.ts +0 -69
- package/src/public/hooks/useHotspots.ts +0 -123
- package/src/public/hooks/useLayoutPersistence.ts +0 -138
- package/src/public/hooks/useMarkdownParser.ts +0 -36
- package/src/public/hooks/useMarkerActions.ts +0 -234
- package/src/public/hooks/useMessageQueue.ts +0 -380
- package/src/public/hooks/useMessageStream.ts +0 -131
- package/src/public/hooks/usePersona.ts +0 -112
- package/src/public/hooks/usePlanModeExit.ts +0 -105
- package/src/public/hooks/useResponsiveLayout.ts +0 -173
- package/src/public/hooks/useSprint.ts +0 -147
- package/src/public/hooks/useStatsStrip.ts +0 -204
- package/src/public/hooks/useStory.ts +0 -135
- package/src/public/hooks/useSubagentHelper.ts +0 -64
- package/src/public/hooks/useSyntaxHighlighter.ts +0 -52
- package/src/public/hooks/useTabCompletion.ts +0 -124
- package/src/public/hooks/useTodos.ts +0 -93
- package/src/public/hooks/useUserAvatar.ts +0 -54
- package/src/public/index.tsx +0 -10
- package/src/public/lib/utils.ts +0 -6
- package/src/public/styles/dockview-theme.css +0 -459
- package/src/public/styles/tailwind.css +0 -4396
- package/src/public/types/electron.d.ts +0 -18
- package/src/public/types/message.ts +0 -51
- package/src/public/utils/avatar-service.ts +0 -73
- package/src/public/utils/color-presets.ts +0 -940
- package/src/public/utils/font-presets.ts +0 -362
- package/src/public/utils/formatDuration.ts +0 -14
- package/src/public/utils/markdown.ts +0 -249
- package/src/public/utils/messageFilters.ts +0 -128
- package/src/public/utils/slash-commands.ts +0 -353
- package/src/public/utils/subagent-display.ts +0 -146
- package/src/public/utils/syntax.ts +0 -219
- package/src/public/utils/toolIntentSummarizer.ts +0 -199
- package/src/public/utils/toolStackGrouper.ts +0 -106
- package/src/public/utils/toolTypeColors.ts +0 -45
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BikeRackWorkspace - Dockview-based panel layout for BikeRack mode
|
|
3
|
-
*
|
|
4
|
-
* Story MSSCI-14877: Migrate BikeRack from index page to Dockview layout
|
|
5
|
-
* Epic: 102 (BikeRack Follow-up)
|
|
6
|
-
*
|
|
7
|
-
* Replaces BikeRackIndex with a proper Dockview layout.
|
|
8
|
-
* No MessagePanel (sacred center) — BikeRack is a monitoring dashboard.
|
|
9
|
-
* Single Dockview group — users can freely rearrange panels.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import React, { useCallback, useRef } from 'react';
|
|
13
|
-
import {
|
|
14
|
-
DockviewReact,
|
|
15
|
-
DockviewReadyEvent,
|
|
16
|
-
DockviewApi,
|
|
17
|
-
IDockviewPanelProps,
|
|
18
|
-
} from 'dockview-react';
|
|
19
|
-
import 'dockview-react/dist/styles/dockview.css';
|
|
20
|
-
import { ErrorBoundary } from './ErrorBoundary';
|
|
21
|
-
import { panelRegistry } from './panel-registry';
|
|
22
|
-
import PersonaHeader from './PersonaHeader.js';
|
|
23
|
-
import '../styles/dockview-theme.css';
|
|
24
|
-
|
|
25
|
-
// =============================================================================
|
|
26
|
-
// BikeRack Panel Definitions
|
|
27
|
-
// =============================================================================
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Panels included in BikeRack Dockview mode.
|
|
31
|
-
* Unlike base Cyclist, BikeRack does NOT include MessagePanel.
|
|
32
|
-
*/
|
|
33
|
-
export const BIKERACK_PANELS: string[] = [
|
|
34
|
-
'sprint',
|
|
35
|
-
'git',
|
|
36
|
-
'diffs',
|
|
37
|
-
'todo',
|
|
38
|
-
'workflow',
|
|
39
|
-
'background',
|
|
40
|
-
'audit-log',
|
|
41
|
-
'changed',
|
|
42
|
-
'ac',
|
|
43
|
-
'debug',
|
|
44
|
-
'settings',
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
const PANEL_TITLES: Record<string, string> = {
|
|
48
|
-
sprint: 'Sprint',
|
|
49
|
-
git: 'Git',
|
|
50
|
-
diffs: 'Diffs',
|
|
51
|
-
todo: 'Todo',
|
|
52
|
-
workflow: 'Workflow',
|
|
53
|
-
background: 'Subagents',
|
|
54
|
-
'audit-log': 'Audit Log',
|
|
55
|
-
changed: 'Changed',
|
|
56
|
-
ac: 'AC',
|
|
57
|
-
debug: 'Debug',
|
|
58
|
-
settings: 'Settings',
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
// =============================================================================
|
|
62
|
-
// Panel Adapter
|
|
63
|
-
// =============================================================================
|
|
64
|
-
|
|
65
|
-
interface PanelAdapterParams {
|
|
66
|
-
panelId: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function PanelAdapter({ params }: IDockviewPanelProps<PanelAdapterParams>): React.ReactElement | null {
|
|
70
|
-
const Component = panelRegistry.get(params.panelId);
|
|
71
|
-
|
|
72
|
-
if (!Component) {
|
|
73
|
-
return (
|
|
74
|
-
<div data-testid={`panel-${params.panelId}`} className="dockview-panel-content">
|
|
75
|
-
<div style={{ padding: '16px', color: 'var(--text-secondary, #94a3b8)' }}>
|
|
76
|
-
{PANEL_TITLES[params.panelId] || params.panelId}
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return (
|
|
83
|
-
<div data-testid={`panel-${params.panelId}`} className="dockview-panel-content">
|
|
84
|
-
<ErrorBoundary panelName={params.panelId}>
|
|
85
|
-
<div className="error-boundary-wrapper">
|
|
86
|
-
<Component />
|
|
87
|
-
</div>
|
|
88
|
-
</ErrorBoundary>
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// =============================================================================
|
|
94
|
-
// BikeRackWorkspace Component
|
|
95
|
-
// =============================================================================
|
|
96
|
-
|
|
97
|
-
export function BikeRackWorkspace(): React.ReactElement {
|
|
98
|
-
const apiRef = useRef<DockviewApi | null>(null);
|
|
99
|
-
|
|
100
|
-
const onReady = useCallback((event: DockviewReadyEvent) => {
|
|
101
|
-
const api = event.api;
|
|
102
|
-
apiRef.current = api;
|
|
103
|
-
|
|
104
|
-
// Single group — all panels as tabs, user can rearrange freely
|
|
105
|
-
const first = api.addPanel({
|
|
106
|
-
id: BIKERACK_PANELS[0],
|
|
107
|
-
component: 'PanelAdapter',
|
|
108
|
-
params: { panelId: BIKERACK_PANELS[0] },
|
|
109
|
-
title: PANEL_TITLES[BIKERACK_PANELS[0]],
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
for (let i = 1; i < BIKERACK_PANELS.length; i++) {
|
|
113
|
-
api.addPanel({
|
|
114
|
-
id: BIKERACK_PANELS[i],
|
|
115
|
-
component: 'PanelAdapter',
|
|
116
|
-
params: { panelId: BIKERACK_PANELS[i] },
|
|
117
|
-
position: { referencePanel: first.id },
|
|
118
|
-
title: PANEL_TITLES[BIKERACK_PANELS[i]],
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
|
-
const components = { PanelAdapter };
|
|
124
|
-
|
|
125
|
-
return (
|
|
126
|
-
<div className="cyclist-app cyclist-dockview" style={{ height: '100vh', width: '100vw', display: 'flex', flexDirection: 'column' }}>
|
|
127
|
-
<div data-testid="bikerack-portrait-anchor" style={{ flexShrink: 0 }}>
|
|
128
|
-
<PersonaHeader />
|
|
129
|
-
</div>
|
|
130
|
-
<div className="flex-1" style={{ flexGrow: 1, minHeight: 0 }}>
|
|
131
|
-
<DockviewReact
|
|
132
|
-
className="dockview-container"
|
|
133
|
-
onReady={onReady}
|
|
134
|
-
components={components}
|
|
135
|
-
watermarkComponent={() => null}
|
|
136
|
-
/>
|
|
137
|
-
</div>
|
|
138
|
-
</div>
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
export default BikeRackWorkspace;
|
|
@@ -1,555 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CommandPalette Component
|
|
3
|
-
*
|
|
4
|
-
* Story MSSCI-12721 - Command Palette for keyboard-driven navigation
|
|
5
|
-
*
|
|
6
|
-
* Features:
|
|
7
|
-
* - Cmd+Shift+P (Mac) / Ctrl+Shift+P (Windows) to open
|
|
8
|
-
* - Fuzzy search filtering (via cmdk)
|
|
9
|
-
* - Category grouping (Panels, Navigation, Settings, Agents)
|
|
10
|
-
* - Keyboard shortcuts display
|
|
11
|
-
* - Recent commands tracking
|
|
12
|
-
*
|
|
13
|
-
* Built on shadcn Command (cmdk) + Dialog primitives.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
|
|
17
|
-
import {
|
|
18
|
-
Command as CommandPrimitive,
|
|
19
|
-
CommandInput,
|
|
20
|
-
CommandList,
|
|
21
|
-
CommandGroup,
|
|
22
|
-
CommandItem as ShadcnCommandItem,
|
|
23
|
-
CommandEmpty,
|
|
24
|
-
CommandShortcut,
|
|
25
|
-
} from '@/components/ui/command';
|
|
26
|
-
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
|
27
|
-
|
|
28
|
-
// =============================================================================
|
|
29
|
-
// Types
|
|
30
|
-
// =============================================================================
|
|
31
|
-
|
|
32
|
-
export interface Command {
|
|
33
|
-
id: string;
|
|
34
|
-
name: string;
|
|
35
|
-
category: string;
|
|
36
|
-
shortcut?: string;
|
|
37
|
-
handler?: () => void;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export type CommandRegistry = Map<string, Command>;
|
|
41
|
-
|
|
42
|
-
// Runtime exports for testing (TypeScript interfaces don't exist at runtime)
|
|
43
|
-
export const Command = {} as const;
|
|
44
|
-
export const CommandRegistry = {} as const;
|
|
45
|
-
|
|
46
|
-
// =============================================================================
|
|
47
|
-
// Constants
|
|
48
|
-
// =============================================================================
|
|
49
|
-
|
|
50
|
-
export const COMMAND_PALETTE_TRIGGER = {
|
|
51
|
-
key: 'p',
|
|
52
|
-
metaKey: true,
|
|
53
|
-
shiftKey: true,
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
export const COMMAND_CATEGORIES = ['Panels', 'Navigation', 'Settings', 'Agents'] as const;
|
|
57
|
-
|
|
58
|
-
export const MODAL_OVERLAY_CLASS = 'command-palette-overlay';
|
|
59
|
-
export const SEARCH_INPUT_ID = 'command-palette-search';
|
|
60
|
-
export const RESULTS_LIST_ID = 'command-palette-results';
|
|
61
|
-
export const CATEGORY_HEADER_CLASS = 'command-palette-category-header';
|
|
62
|
-
export const SHORTCUT_DISPLAY_CLASS = 'command-palette-shortcut';
|
|
63
|
-
export const RECENT_SECTION_CLASS = 'command-palette-recent';
|
|
64
|
-
export const RECENT_COMMANDS_KEY = 'cyclist:recent-commands';
|
|
65
|
-
export const MAX_RECENT_COMMANDS = 5;
|
|
66
|
-
|
|
67
|
-
// =============================================================================
|
|
68
|
-
// Default Commands
|
|
69
|
-
// =============================================================================
|
|
70
|
-
|
|
71
|
-
export const DEFAULT_COMMANDS: Command[] = [
|
|
72
|
-
// Panels
|
|
73
|
-
{ id: 'toggle-changed-panel', name: 'Toggle Changed Files', category: 'Panels', shortcut: 'Cmd+Shift+1' },
|
|
74
|
-
{ id: 'toggle-diffs-panel', name: 'Toggle Diffs', category: 'Panels', shortcut: 'Cmd+Shift+2' },
|
|
75
|
-
{ id: 'toggle-message-panel', name: 'Toggle Messages', category: 'Panels', shortcut: 'Cmd+Shift+3' },
|
|
76
|
-
{ id: 'toggle-debug-panel', name: 'Toggle Debug', category: 'Panels' },
|
|
77
|
-
|
|
78
|
-
// Navigation
|
|
79
|
-
{ id: 'focus-input', name: 'Focus Input', category: 'Navigation', shortcut: 'Cmd+L' },
|
|
80
|
-
{ id: 'scroll-to-bottom', name: 'Scroll to Bottom', category: 'Navigation', shortcut: 'Cmd+End' },
|
|
81
|
-
{ id: 'scroll-to-top', name: 'Scroll to Top', category: 'Navigation', shortcut: 'Cmd+Home' },
|
|
82
|
-
|
|
83
|
-
// Settings
|
|
84
|
-
{ id: 'open-settings', name: 'Open Settings', category: 'Settings', shortcut: 'Cmd+,' },
|
|
85
|
-
{ id: 'toggle-theme', name: 'Toggle Theme', category: 'Settings' },
|
|
86
|
-
|
|
87
|
-
// Agents
|
|
88
|
-
{ id: 'run-sm', name: 'Start Scrum Master', category: 'Agents' },
|
|
89
|
-
{ id: 'run-tea', name: 'Start TEA', category: 'Agents' },
|
|
90
|
-
{ id: 'run-dev', name: 'Start Dev', category: 'Agents' },
|
|
91
|
-
{ id: 'run-reviewer', name: 'Start Reviewer', category: 'Agents' },
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
// =============================================================================
|
|
95
|
-
// Selection State (module-level for imperative access)
|
|
96
|
-
// =============================================================================
|
|
97
|
-
|
|
98
|
-
let selectedIndex = 0;
|
|
99
|
-
|
|
100
|
-
export function getSelectedIndex(): number {
|
|
101
|
-
return selectedIndex;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function setSelectedIndex(index: number): void {
|
|
105
|
-
selectedIndex = index;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function handleArrowDown(listLength: number): void {
|
|
109
|
-
selectedIndex = (selectedIndex + 1) % listLength;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function handleArrowUp(listLength: number): void {
|
|
113
|
-
selectedIndex = selectedIndex === 0 ? listLength - 1 : selectedIndex - 1;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function scrollSelectedIntoView(): void {
|
|
117
|
-
const selectedEl = document.querySelector(`[data-command-index="${selectedIndex}"]`);
|
|
118
|
-
selectedEl?.scrollIntoView({ block: 'nearest' });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// =============================================================================
|
|
122
|
-
// Platform Detection
|
|
123
|
-
// =============================================================================
|
|
124
|
-
|
|
125
|
-
export function detectPlatform(): 'mac' | 'windows' | 'linux' {
|
|
126
|
-
if (typeof navigator === 'undefined') return 'mac';
|
|
127
|
-
const platform = navigator.platform?.toLowerCase() || '';
|
|
128
|
-
if (platform.includes('mac')) return 'mac';
|
|
129
|
-
if (platform.includes('win')) return 'windows';
|
|
130
|
-
return 'linux';
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// =============================================================================
|
|
134
|
-
// Keyboard Trigger Detection
|
|
135
|
-
// =============================================================================
|
|
136
|
-
|
|
137
|
-
export function isCommandPaletteTrigger(event: KeyboardEvent): boolean {
|
|
138
|
-
const isP = event.key.toLowerCase() === 'p';
|
|
139
|
-
const hasModifier = event.metaKey || event.ctrlKey;
|
|
140
|
-
const hasShift = event.shiftKey;
|
|
141
|
-
return isP && hasModifier && hasShift;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// =============================================================================
|
|
145
|
-
// Shortcut Formatting
|
|
146
|
-
// =============================================================================
|
|
147
|
-
|
|
148
|
-
export function formatShortcut(shortcut: string, platform: 'mac' | 'windows' | 'linux'): string {
|
|
149
|
-
if (platform === 'mac') {
|
|
150
|
-
return shortcut
|
|
151
|
-
.replace(/Cmd/g, '\u2318')
|
|
152
|
-
.replace(/Shift/g, '\u21E7')
|
|
153
|
-
.replace(/Alt/g, '\u2325')
|
|
154
|
-
.replace(/Ctrl/g, '\u2303')
|
|
155
|
-
.replace(/\+/g, '');
|
|
156
|
-
}
|
|
157
|
-
// Windows/Linux: Keep text but replace Cmd with Ctrl
|
|
158
|
-
return shortcut.replace(/Cmd/g, 'Ctrl');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// =============================================================================
|
|
162
|
-
// Command Filtering
|
|
163
|
-
// =============================================================================
|
|
164
|
-
|
|
165
|
-
export function filterCommands(commands: Command[], query: string): Command[] {
|
|
166
|
-
if (!query.trim()) return commands;
|
|
167
|
-
|
|
168
|
-
const lowerQuery = query.toLowerCase();
|
|
169
|
-
|
|
170
|
-
// Score each command for fuzzy matching
|
|
171
|
-
const scored = commands.map(cmd => {
|
|
172
|
-
const lowerName = cmd.name.toLowerCase();
|
|
173
|
-
const lowerId = cmd.id.toLowerCase();
|
|
174
|
-
|
|
175
|
-
// Exact match in name or id
|
|
176
|
-
if (lowerName.includes(lowerQuery) || lowerId.includes(lowerQuery)) {
|
|
177
|
-
// Prioritize matches at start
|
|
178
|
-
const startsWithBonus = lowerName.startsWith(lowerQuery) ? 100 : 0;
|
|
179
|
-
return { cmd, score: 50 + startsWithBonus };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Fuzzy match: check if all query chars appear in order
|
|
183
|
-
let queryIdx = 0;
|
|
184
|
-
let consecutiveBonus = 0;
|
|
185
|
-
let lastMatchIdx = -1;
|
|
186
|
-
|
|
187
|
-
for (let i = 0; i < lowerName.length && queryIdx < lowerQuery.length; i++) {
|
|
188
|
-
if (lowerName[i] === lowerQuery[queryIdx]) {
|
|
189
|
-
if (lastMatchIdx === i - 1) consecutiveBonus += 5;
|
|
190
|
-
lastMatchIdx = i;
|
|
191
|
-
queryIdx++;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (queryIdx === lowerQuery.length) {
|
|
196
|
-
return { cmd, score: 10 + consecutiveBonus };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return { cmd, score: 0 };
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
return scored
|
|
203
|
-
.filter(s => s.score > 0)
|
|
204
|
-
.sort((a, b) => b.score - a.score)
|
|
205
|
-
.map(s => s.cmd);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export function highlightMatch(text: string, query: string): string {
|
|
209
|
-
if (!query) return text;
|
|
210
|
-
|
|
211
|
-
const lowerText = text.toLowerCase();
|
|
212
|
-
const lowerQuery = query.toLowerCase();
|
|
213
|
-
let result = '';
|
|
214
|
-
let queryIdx = 0;
|
|
215
|
-
|
|
216
|
-
for (let i = 0; i < text.length; i++) {
|
|
217
|
-
if (queryIdx < lowerQuery.length && lowerText[i] === lowerQuery[queryIdx]) {
|
|
218
|
-
result += `<mark>${text[i]}</mark>`;
|
|
219
|
-
queryIdx++;
|
|
220
|
-
} else {
|
|
221
|
-
result += text[i];
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return result;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// =============================================================================
|
|
229
|
-
// Category Functions
|
|
230
|
-
// =============================================================================
|
|
231
|
-
|
|
232
|
-
export function groupByCategory(commands: Command[]): Record<string, Command[]> {
|
|
233
|
-
const grouped: Record<string, Command[]> = {};
|
|
234
|
-
for (const cmd of commands) {
|
|
235
|
-
if (!grouped[cmd.category]) {
|
|
236
|
-
grouped[cmd.category] = [];
|
|
237
|
-
}
|
|
238
|
-
grouped[cmd.category].push(cmd);
|
|
239
|
-
}
|
|
240
|
-
return grouped;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function filterByCategory(commands: Command[], category: string): Command[] {
|
|
244
|
-
return commands.filter(cmd => cmd.category === category);
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
// =============================================================================
|
|
248
|
-
// Command Validation
|
|
249
|
-
// =============================================================================
|
|
250
|
-
|
|
251
|
-
export function validateCommand(cmd: unknown): boolean {
|
|
252
|
-
if (!cmd || typeof cmd !== 'object') return false;
|
|
253
|
-
const c = cmd as Record<string, unknown>;
|
|
254
|
-
return (
|
|
255
|
-
typeof c.id === 'string' &&
|
|
256
|
-
typeof c.name === 'string' &&
|
|
257
|
-
typeof c.category === 'string'
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// =============================================================================
|
|
262
|
-
// Command Execution
|
|
263
|
-
// =============================================================================
|
|
264
|
-
|
|
265
|
-
export function executeCommand(command: Command): void {
|
|
266
|
-
addToRecent(command.id);
|
|
267
|
-
if (command.handler) {
|
|
268
|
-
command.handler();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// =============================================================================
|
|
273
|
-
// Recent Commands (localStorage)
|
|
274
|
-
// =============================================================================
|
|
275
|
-
|
|
276
|
-
export function getRecentCommands(): string[] {
|
|
277
|
-
if (typeof localStorage === 'undefined') return [];
|
|
278
|
-
try {
|
|
279
|
-
const stored = localStorage.getItem(RECENT_COMMANDS_KEY);
|
|
280
|
-
return stored ? JSON.parse(stored) : [];
|
|
281
|
-
} catch {
|
|
282
|
-
return [];
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export function addToRecent(commandId: string): void {
|
|
287
|
-
if (typeof localStorage === 'undefined') return;
|
|
288
|
-
const recent = getRecentCommands().filter(id => id !== commandId);
|
|
289
|
-
recent.unshift(commandId);
|
|
290
|
-
const trimmed = recent.slice(0, MAX_RECENT_COMMANDS);
|
|
291
|
-
localStorage.setItem(RECENT_COMMANDS_KEY, JSON.stringify(trimmed));
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function clearRecentCommands(): void {
|
|
295
|
-
if (typeof localStorage === 'undefined') return;
|
|
296
|
-
localStorage.removeItem(RECENT_COMMANDS_KEY);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
export function sortWithRecentFirst(commands: Command[]): Command[] {
|
|
300
|
-
const recent = getRecentCommands();
|
|
301
|
-
const recentSet = new Set(recent);
|
|
302
|
-
|
|
303
|
-
const recentCmds = commands.filter(c => recentSet.has(c.id));
|
|
304
|
-
const otherCmds = commands.filter(c => !recentSet.has(c.id));
|
|
305
|
-
|
|
306
|
-
// Sort recent commands by their order in recent list
|
|
307
|
-
recentCmds.sort((a, b) => recent.indexOf(a.id) - recent.indexOf(b.id));
|
|
308
|
-
|
|
309
|
-
return [...recentCmds, ...otherCmds];
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// =============================================================================
|
|
313
|
-
// Command Registry
|
|
314
|
-
// =============================================================================
|
|
315
|
-
|
|
316
|
-
const commandRegistry: CommandRegistry = new Map();
|
|
317
|
-
|
|
318
|
-
export function registerCommand(command: Command): void {
|
|
319
|
-
commandRegistry.set(command.id, command);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Initialize default commands
|
|
323
|
-
DEFAULT_COMMANDS.forEach(cmd => registerCommand(cmd));
|
|
324
|
-
|
|
325
|
-
// =============================================================================
|
|
326
|
-
// Context and Provider
|
|
327
|
-
// =============================================================================
|
|
328
|
-
|
|
329
|
-
interface CommandPaletteContextValue {
|
|
330
|
-
isOpen: boolean;
|
|
331
|
-
open: () => void;
|
|
332
|
-
close: () => void;
|
|
333
|
-
toggle: () => void;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const CommandPaletteContext = createContext<CommandPaletteContextValue | null>(null);
|
|
337
|
-
|
|
338
|
-
export function useCommandPaletteContext(): CommandPaletteContextValue {
|
|
339
|
-
const ctx = useContext(CommandPaletteContext);
|
|
340
|
-
if (!ctx) {
|
|
341
|
-
throw new Error('useCommandPaletteContext must be used within CommandPaletteProvider');
|
|
342
|
-
}
|
|
343
|
-
return ctx;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export function useCommandPalette() {
|
|
347
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
348
|
-
const [commands] = useState<Command[]>(DEFAULT_COMMANDS);
|
|
349
|
-
|
|
350
|
-
const open = useCallback(() => {
|
|
351
|
-
setIsOpen(true);
|
|
352
|
-
setSelectedIndex(0);
|
|
353
|
-
}, []);
|
|
354
|
-
|
|
355
|
-
const close = useCallback(() => {
|
|
356
|
-
setIsOpen(false);
|
|
357
|
-
}, []);
|
|
358
|
-
|
|
359
|
-
const toggle = useCallback(() => {
|
|
360
|
-
if (isOpen) close();
|
|
361
|
-
else open();
|
|
362
|
-
}, [isOpen, open, close]);
|
|
363
|
-
|
|
364
|
-
return {
|
|
365
|
-
isOpen,
|
|
366
|
-
open,
|
|
367
|
-
close,
|
|
368
|
-
toggle,
|
|
369
|
-
commands,
|
|
370
|
-
selectedIndex: getSelectedIndex(),
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// =============================================================================
|
|
375
|
-
// Global Keyboard Listener
|
|
376
|
-
// =============================================================================
|
|
377
|
-
|
|
378
|
-
let globalListenerSetup = false;
|
|
379
|
-
let globalOpenCallback: (() => void) | null = null;
|
|
380
|
-
|
|
381
|
-
export function setupGlobalKeyboardListener(openCallback: () => void): () => void {
|
|
382
|
-
globalOpenCallback = openCallback;
|
|
383
|
-
|
|
384
|
-
if (!globalListenerSetup) {
|
|
385
|
-
const handler = (event: KeyboardEvent) => {
|
|
386
|
-
if (isCommandPaletteTrigger(event)) {
|
|
387
|
-
event.preventDefault();
|
|
388
|
-
globalOpenCallback?.();
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
document.addEventListener('keydown', handler);
|
|
393
|
-
globalListenerSetup = true;
|
|
394
|
-
|
|
395
|
-
return () => {
|
|
396
|
-
document.removeEventListener('keydown', handler);
|
|
397
|
-
globalListenerSetup = false;
|
|
398
|
-
globalOpenCallback = null;
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return () => {
|
|
403
|
-
globalOpenCallback = null;
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
export function focusSearchInput(): void {
|
|
408
|
-
const input = document.getElementById(SEARCH_INPUT_ID);
|
|
409
|
-
input?.focus();
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
export function closePalette(): void {
|
|
413
|
-
// This is called imperatively; the actual close is handled by the component
|
|
414
|
-
const event = new CustomEvent('cyclist:close-command-palette');
|
|
415
|
-
document.dispatchEvent(event);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// =============================================================================
|
|
419
|
-
// Provider Component
|
|
420
|
-
// =============================================================================
|
|
421
|
-
|
|
422
|
-
export function CommandPaletteProvider({ children }: { children: React.ReactNode }) {
|
|
423
|
-
const palette = useCommandPalette();
|
|
424
|
-
|
|
425
|
-
useEffect(() => {
|
|
426
|
-
const cleanup = setupGlobalKeyboardListener(palette.open);
|
|
427
|
-
return cleanup;
|
|
428
|
-
}, [palette.open]);
|
|
429
|
-
|
|
430
|
-
useEffect(() => {
|
|
431
|
-
const handleClose = () => palette.close();
|
|
432
|
-
document.addEventListener('cyclist:close-command-palette', handleClose);
|
|
433
|
-
return () => document.removeEventListener('cyclist:close-command-palette', handleClose);
|
|
434
|
-
}, [palette.close]);
|
|
435
|
-
|
|
436
|
-
const handleExecute = useCallback((cmd: Command) => {
|
|
437
|
-
executeCommand(cmd);
|
|
438
|
-
palette.close();
|
|
439
|
-
}, [palette.close]);
|
|
440
|
-
|
|
441
|
-
const value: CommandPaletteContextValue = {
|
|
442
|
-
isOpen: palette.isOpen,
|
|
443
|
-
open: palette.open,
|
|
444
|
-
close: palette.close,
|
|
445
|
-
toggle: palette.toggle,
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
return (
|
|
449
|
-
<CommandPaletteContext.Provider value={value}>
|
|
450
|
-
{children}
|
|
451
|
-
<CommandPalette
|
|
452
|
-
commands={palette.commands}
|
|
453
|
-
isOpen={palette.isOpen}
|
|
454
|
-
onClose={palette.close}
|
|
455
|
-
onExecute={handleExecute}
|
|
456
|
-
/>
|
|
457
|
-
</CommandPaletteContext.Provider>
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// =============================================================================
|
|
462
|
-
// CommandPalette Component (shadcn CommandDialog)
|
|
463
|
-
// =============================================================================
|
|
464
|
-
|
|
465
|
-
interface CommandPaletteProps {
|
|
466
|
-
query?: string;
|
|
467
|
-
setQuery?: (q: string) => void;
|
|
468
|
-
commands: Command[];
|
|
469
|
-
onClose: () => void;
|
|
470
|
-
onExecute: (cmd: Command) => void;
|
|
471
|
-
isOpen?: boolean;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function CommandPalette({
|
|
475
|
-
commands,
|
|
476
|
-
onClose,
|
|
477
|
-
onExecute,
|
|
478
|
-
isOpen = true,
|
|
479
|
-
}: CommandPaletteProps): React.ReactElement {
|
|
480
|
-
const platform = detectPlatform();
|
|
481
|
-
const recentIds = getRecentCommands();
|
|
482
|
-
const grouped = groupByCategory(commands);
|
|
483
|
-
const recentCommands = commands.filter(c => recentIds.includes(c.id));
|
|
484
|
-
|
|
485
|
-
return (
|
|
486
|
-
<Dialog open={isOpen} onOpenChange={(open) => { if (!open) onClose(); }}>
|
|
487
|
-
<DialogContent
|
|
488
|
-
className="overflow-hidden p-0"
|
|
489
|
-
aria-label="Command palette"
|
|
490
|
-
>
|
|
491
|
-
<CommandPrimitive className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
|
492
|
-
<CommandInput
|
|
493
|
-
placeholder="Type a command..."
|
|
494
|
-
id={SEARCH_INPUT_ID}
|
|
495
|
-
aria-label="Search commands"
|
|
496
|
-
/>
|
|
497
|
-
<CommandList id={RESULTS_LIST_ID}>
|
|
498
|
-
<CommandEmpty>No matching commands</CommandEmpty>
|
|
499
|
-
|
|
500
|
-
{/* Recent Section */}
|
|
501
|
-
{recentCommands.length > 0 && (
|
|
502
|
-
<CommandGroup heading="Recent" className={RECENT_SECTION_CLASS}>
|
|
503
|
-
{recentCommands.map((cmd) => (
|
|
504
|
-
<ShadcnCommandItem
|
|
505
|
-
key={`recent-${cmd.id}`}
|
|
506
|
-
value={cmd.name}
|
|
507
|
-
onSelect={() => onExecute(cmd)}
|
|
508
|
-
className="command-palette-item"
|
|
509
|
-
>
|
|
510
|
-
<span>{cmd.name}</span>
|
|
511
|
-
{cmd.shortcut && (
|
|
512
|
-
<CommandShortcut className={SHORTCUT_DISPLAY_CLASS}>
|
|
513
|
-
{formatShortcut(cmd.shortcut, platform)}
|
|
514
|
-
</CommandShortcut>
|
|
515
|
-
)}
|
|
516
|
-
</ShadcnCommandItem>
|
|
517
|
-
))}
|
|
518
|
-
</CommandGroup>
|
|
519
|
-
)}
|
|
520
|
-
|
|
521
|
-
{/* Grouped by Category */}
|
|
522
|
-
{COMMAND_CATEGORIES.map(category => {
|
|
523
|
-
const categoryCommands = grouped[category];
|
|
524
|
-
if (!categoryCommands?.length) return null;
|
|
525
|
-
|
|
526
|
-
return (
|
|
527
|
-
<CommandGroup key={category} heading={category}>
|
|
528
|
-
{categoryCommands.map((cmd) => (
|
|
529
|
-
<ShadcnCommandItem
|
|
530
|
-
key={cmd.id}
|
|
531
|
-
value={cmd.name}
|
|
532
|
-
onSelect={() => onExecute(cmd)}
|
|
533
|
-
className="command-palette-item"
|
|
534
|
-
>
|
|
535
|
-
<span>{cmd.name}</span>
|
|
536
|
-
{cmd.shortcut && (
|
|
537
|
-
<CommandShortcut className={SHORTCUT_DISPLAY_CLASS}>
|
|
538
|
-
{formatShortcut(cmd.shortcut, platform)}
|
|
539
|
-
</CommandShortcut>
|
|
540
|
-
)}
|
|
541
|
-
</ShadcnCommandItem>
|
|
542
|
-
))}
|
|
543
|
-
</CommandGroup>
|
|
544
|
-
);
|
|
545
|
-
})}
|
|
546
|
-
</CommandList>
|
|
547
|
-
</CommandPrimitive>
|
|
548
|
-
</DialogContent>
|
|
549
|
-
</Dialog>
|
|
550
|
-
);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Named and default exports
|
|
554
|
-
export { CommandPalette };
|
|
555
|
-
export default CommandPalette;
|