@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,69 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
export interface HealthScoreDimension {
|
|
4
|
-
name: string;
|
|
5
|
-
score: number | null;
|
|
6
|
-
weight: number;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface HealthScoreData {
|
|
10
|
-
success: boolean;
|
|
11
|
-
composite_score: number;
|
|
12
|
-
dimensions: HealthScoreDimension[];
|
|
13
|
-
cached: boolean;
|
|
14
|
-
error?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface UseHealthScoreReturn {
|
|
18
|
-
data: HealthScoreData | null;
|
|
19
|
-
isLoading: boolean;
|
|
20
|
-
error: Error | null;
|
|
21
|
-
lastFetchedAt: number | null;
|
|
22
|
-
refresh: () => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function useHealthScore(): UseHealthScoreReturn {
|
|
26
|
-
const [data, setData] = useState<HealthScoreData | null>(null);
|
|
27
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
28
|
-
const [error, setError] = useState<Error | null>(null);
|
|
29
|
-
const [lastFetchedAt, setLastFetchedAt] = useState<number | null>(null);
|
|
30
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
31
|
-
|
|
32
|
-
const refresh = useCallback(() => {
|
|
33
|
-
if (abortRef.current) {
|
|
34
|
-
abortRef.current.abort();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const controller = new AbortController();
|
|
38
|
-
abortRef.current = controller;
|
|
39
|
-
|
|
40
|
-
setIsLoading(true);
|
|
41
|
-
setError(null);
|
|
42
|
-
|
|
43
|
-
fetch('/api/health-score', { signal: controller.signal })
|
|
44
|
-
.then((res) => {
|
|
45
|
-
if (!res.ok) {
|
|
46
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
47
|
-
}
|
|
48
|
-
return res.json();
|
|
49
|
-
})
|
|
50
|
-
.then((json: HealthScoreData) => {
|
|
51
|
-
setData(json);
|
|
52
|
-
setLastFetchedAt(Date.now());
|
|
53
|
-
setIsLoading(false);
|
|
54
|
-
})
|
|
55
|
-
.catch((err) => {
|
|
56
|
-
if (err.name === 'AbortError') return;
|
|
57
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
58
|
-
setIsLoading(false);
|
|
59
|
-
});
|
|
60
|
-
}, []);
|
|
61
|
-
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
return () => {
|
|
64
|
-
abortRef.current?.abort();
|
|
65
|
-
};
|
|
66
|
-
}, []);
|
|
67
|
-
|
|
68
|
-
return { data, isLoading, error, lastFetchedAt, refresh };
|
|
69
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
// Types matching Python HotspotResult / MultiRepoHotspotResult
|
|
4
|
-
export interface FileHotspot {
|
|
5
|
-
path: string;
|
|
6
|
-
change_count: number;
|
|
7
|
-
bug_fix_count: number;
|
|
8
|
-
author_count: number;
|
|
9
|
-
lines_added: number;
|
|
10
|
-
lines_deleted: number;
|
|
11
|
-
churn: number;
|
|
12
|
-
last_changed: string;
|
|
13
|
-
hotspot_score: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface DirectoryHotspot {
|
|
17
|
-
path: string;
|
|
18
|
-
file_count: number;
|
|
19
|
-
total_changes: number;
|
|
20
|
-
total_bug_fixes: number;
|
|
21
|
-
avg_author_count: number;
|
|
22
|
-
hotspot_score: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface HotspotRepoResult {
|
|
26
|
-
success: boolean;
|
|
27
|
-
repo_name: string;
|
|
28
|
-
repo_path: string;
|
|
29
|
-
time_window_days: number;
|
|
30
|
-
commit_count: number;
|
|
31
|
-
file_hotspots: FileHotspot[];
|
|
32
|
-
directory_hotspots: DirectoryHotspot[];
|
|
33
|
-
error?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface HotspotData {
|
|
37
|
-
success: boolean;
|
|
38
|
-
// Single-repo result fields (when --path is used)
|
|
39
|
-
repo_name?: string;
|
|
40
|
-
repo_path?: string;
|
|
41
|
-
time_window_days?: number;
|
|
42
|
-
commit_count?: number;
|
|
43
|
-
file_hotspots?: FileHotspot[];
|
|
44
|
-
directory_hotspots?: DirectoryHotspot[];
|
|
45
|
-
// Multi-repo result fields
|
|
46
|
-
repo_results?: HotspotRepoResult[];
|
|
47
|
-
error?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface UseHotspotsOptions {
|
|
51
|
-
days: number;
|
|
52
|
-
repo?: string;
|
|
53
|
-
skipTypes?: string[];
|
|
54
|
-
includeOrchestrator?: boolean;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface UseHotspotsReturn {
|
|
58
|
-
data: HotspotData | null;
|
|
59
|
-
isLoading: boolean;
|
|
60
|
-
error: Error | null;
|
|
61
|
-
refresh: () => void;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function useHotspots(options: UseHotspotsOptions): UseHotspotsReturn {
|
|
65
|
-
const [data, setData] = useState<HotspotData | null>(null);
|
|
66
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
67
|
-
const [error, setError] = useState<Error | null>(null);
|
|
68
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
69
|
-
|
|
70
|
-
const fetchHotspots = useCallback(() => {
|
|
71
|
-
// Cancel any in-flight request
|
|
72
|
-
if (abortRef.current) {
|
|
73
|
-
abortRef.current.abort();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const controller = new AbortController();
|
|
77
|
-
abortRef.current = controller;
|
|
78
|
-
|
|
79
|
-
setIsLoading(true);
|
|
80
|
-
setError(null);
|
|
81
|
-
|
|
82
|
-
const params = new URLSearchParams({ days: String(options.days) });
|
|
83
|
-
if (options.repo) {
|
|
84
|
-
params.set('repo', options.repo);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Determine skip_type values: use explicit skipTypes, or default to ['orchestrator']
|
|
88
|
-
// unless includeOrchestrator is true
|
|
89
|
-
const skipTypes = options.skipTypes ??
|
|
90
|
-
(options.includeOrchestrator ? [] : ['orchestrator']);
|
|
91
|
-
for (const st of skipTypes) {
|
|
92
|
-
params.append('skip_type', st);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
fetch(`/api/hotspots?${params}`, { signal: controller.signal })
|
|
96
|
-
.then((res) => {
|
|
97
|
-
if (!res.ok) {
|
|
98
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
99
|
-
}
|
|
100
|
-
return res.json();
|
|
101
|
-
})
|
|
102
|
-
.then((json: HotspotData) => {
|
|
103
|
-
setData(json);
|
|
104
|
-
setIsLoading(false);
|
|
105
|
-
})
|
|
106
|
-
.catch((err) => {
|
|
107
|
-
if (err.name === 'AbortError') return;
|
|
108
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
109
|
-
setIsLoading(false);
|
|
110
|
-
});
|
|
111
|
-
}, [options.days, options.repo, options.skipTypes, options.includeOrchestrator]);
|
|
112
|
-
|
|
113
|
-
// Cleanup abort controller on unmount
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
return () => {
|
|
116
|
-
if (abortRef.current) {
|
|
117
|
-
abortRef.current.abort();
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
}, []);
|
|
121
|
-
|
|
122
|
-
return { data, isLoading, error, refresh: fetchHotspots };
|
|
123
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useLayoutPersistence Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for saving and restoring layout state to config.local.yaml.
|
|
5
|
-
* Story MSSCI-12706 - Layout Persistence
|
|
6
|
-
*
|
|
7
|
-
* Uses native Dockview SerializedDockview format for complete layout fidelity.
|
|
8
|
-
*
|
|
9
|
-
* REST API:
|
|
10
|
-
* - GET /api/settings/layout - Load layout
|
|
11
|
-
* - PATCH /api/settings/layout - Save layout
|
|
12
|
-
*
|
|
13
|
-
* Features:
|
|
14
|
-
* - Load layout from config on mount
|
|
15
|
-
* - Save layout on changes (debounced)
|
|
16
|
-
* - Per-project independent layouts
|
|
17
|
-
* - Graceful handling of corrupted/missing config
|
|
18
|
-
* - Native Dockview serialization for perfect restore
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
22
|
-
import type { SerializedDockview } from 'dockview-react';
|
|
23
|
-
|
|
24
|
-
const DEBOUNCE_DELAY = 300;
|
|
25
|
-
|
|
26
|
-
interface UseLayoutPersistenceResult {
|
|
27
|
-
layout: SerializedDockview | null;
|
|
28
|
-
isLoading: boolean;
|
|
29
|
-
isSaving: boolean;
|
|
30
|
-
error: Error | null;
|
|
31
|
-
saveLayout: (layout: SerializedDockview) => void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Validate that the layout has the native Dockview structure
|
|
36
|
-
*/
|
|
37
|
-
function isValidDockviewLayout(layout: unknown): layout is SerializedDockview {
|
|
38
|
-
if (!layout || typeof layout !== 'object') return false;
|
|
39
|
-
const layoutObj = layout as Record<string, unknown>;
|
|
40
|
-
|
|
41
|
-
// Check for native Dockview structure: grid and panels are required
|
|
42
|
-
if (!layoutObj.grid || typeof layoutObj.grid !== 'object') return false;
|
|
43
|
-
if (!layoutObj.panels || typeof layoutObj.panels !== 'object') return false;
|
|
44
|
-
|
|
45
|
-
const grid = layoutObj.grid as Record<string, unknown>;
|
|
46
|
-
// Grid should have root, width, height, orientation
|
|
47
|
-
if (!grid.root || typeof grid.width !== 'number' || typeof grid.height !== 'number') return false;
|
|
48
|
-
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function useLayoutPersistence(): UseLayoutPersistenceResult {
|
|
53
|
-
const [layout, setLayout] = useState<SerializedDockview | null>(null);
|
|
54
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
55
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
56
|
-
const [error, setError] = useState<Error | null>(null);
|
|
57
|
-
|
|
58
|
-
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
59
|
-
const pendingLayoutRef = useRef<SerializedDockview | null>(null);
|
|
60
|
-
|
|
61
|
-
// Load layout on mount via REST API
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
const loadLayout = async () => {
|
|
64
|
-
try {
|
|
65
|
-
const response = await fetch('/api/settings/layout');
|
|
66
|
-
if (response.ok) {
|
|
67
|
-
const data = await response.json();
|
|
68
|
-
if (data.layout && isValidDockviewLayout(data.layout)) {
|
|
69
|
-
// Use native Dockview layout directly
|
|
70
|
-
setLayout(data.layout as SerializedDockview);
|
|
71
|
-
} else {
|
|
72
|
-
// No saved layout or invalid format - let DockviewWorkspace build default
|
|
73
|
-
setLayout(null);
|
|
74
|
-
}
|
|
75
|
-
} else {
|
|
76
|
-
// API error - let DockviewWorkspace build default
|
|
77
|
-
setLayout(null);
|
|
78
|
-
}
|
|
79
|
-
} catch (err) {
|
|
80
|
-
// On error, let DockviewWorkspace build default
|
|
81
|
-
console.error('[useLayoutPersistence] Failed to load layout:', err);
|
|
82
|
-
setLayout(null);
|
|
83
|
-
setError(err instanceof Error ? err : new Error('Failed to load layout'));
|
|
84
|
-
} finally {
|
|
85
|
-
setIsLoading(false);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
loadLayout();
|
|
90
|
-
}, []);
|
|
91
|
-
|
|
92
|
-
// Debounced save function via REST API - saves native Dockview format directly
|
|
93
|
-
const saveLayout = useCallback((newLayout: SerializedDockview) => {
|
|
94
|
-
pendingLayoutRef.current = newLayout;
|
|
95
|
-
|
|
96
|
-
// Clear existing debounce timer
|
|
97
|
-
if (debounceRef.current) {
|
|
98
|
-
clearTimeout(debounceRef.current);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Set new debounce timer
|
|
102
|
-
debounceRef.current = setTimeout(async () => {
|
|
103
|
-
const layoutToSave = pendingLayoutRef.current;
|
|
104
|
-
if (!layoutToSave) return;
|
|
105
|
-
|
|
106
|
-
setIsSaving(true);
|
|
107
|
-
try {
|
|
108
|
-
// Save native Dockview format directly - no conversion needed
|
|
109
|
-
const response = await fetch('/api/settings/layout', {
|
|
110
|
-
method: 'PATCH',
|
|
111
|
-
headers: { 'Content-Type': 'application/json' },
|
|
112
|
-
body: JSON.stringify(layoutToSave),
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
if (!response.ok) {
|
|
116
|
-
throw new Error('Failed to save layout');
|
|
117
|
-
}
|
|
118
|
-
setError(null);
|
|
119
|
-
} catch (err) {
|
|
120
|
-
console.error('[useLayoutPersistence] Failed to save layout:', err);
|
|
121
|
-
setError(err instanceof Error ? err : new Error('Failed to save layout'));
|
|
122
|
-
} finally {
|
|
123
|
-
setIsSaving(false);
|
|
124
|
-
}
|
|
125
|
-
}, DEBOUNCE_DELAY);
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
// Cleanup debounce timer on unmount
|
|
129
|
-
useEffect(() => {
|
|
130
|
-
return () => {
|
|
131
|
-
if (debounceRef.current) {
|
|
132
|
-
clearTimeout(debounceRef.current);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
}, []);
|
|
136
|
-
|
|
137
|
-
return { layout, isLoading, isSaving, error, saveLayout };
|
|
138
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useMarkdownParser Hook
|
|
3
|
-
*
|
|
4
|
-
* Story MSSCI-13969: React hook for parsing markdown to HTML with security.
|
|
5
|
-
* Extracted from js/components/message-view/markdown-parser.js
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - XSS prevention via HTML escaping
|
|
9
|
-
* - CYCLIST marker stripping
|
|
10
|
-
* - Memoization for performance
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { useMemo } from 'react';
|
|
14
|
-
import { parseMarkdown } from '../utils/markdown.js';
|
|
15
|
-
|
|
16
|
-
export interface UseMarkdownParserResult {
|
|
17
|
-
html: string;
|
|
18
|
-
isLoading: boolean;
|
|
19
|
-
error: Error | null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* React hook for parsing markdown to HTML with security and memoization.
|
|
24
|
-
* @param markdown - Raw markdown text (or null)
|
|
25
|
-
* @returns Object with html string, loading state, and error
|
|
26
|
-
*/
|
|
27
|
-
export function useMarkdownParser(
|
|
28
|
-
markdown: string | null
|
|
29
|
-
): UseMarkdownParserResult {
|
|
30
|
-
const html = useMemo(() => {
|
|
31
|
-
if (!markdown) return '';
|
|
32
|
-
return parseMarkdown(markdown);
|
|
33
|
-
}, [markdown]);
|
|
34
|
-
|
|
35
|
-
return { html, isLoading: false, error: null };
|
|
36
|
-
}
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useMarkerActions Hook
|
|
3
|
-
*
|
|
4
|
-
* Detects CYCLIST markers in message content and returns action metadata
|
|
5
|
-
* for rendering QuickActions buttons.
|
|
6
|
-
*
|
|
7
|
-
* Story: MSSCI-12787 - Implement CYCLIST Marker Parsing and Action Buttons
|
|
8
|
-
*
|
|
9
|
-
* @see sprint/context/MSSCI-12787-reference/quick-actions.js.deleted
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { useMemo } from 'react';
|
|
13
|
-
import { detectMarkers, stripMarkers, MARKER_TYPES } from '@pennyfarthing/shared/browser';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Action types that map to different UI presentations
|
|
17
|
-
*/
|
|
18
|
-
export type ActionType =
|
|
19
|
-
| 'handoff' // Show agent button + "Not yet" option
|
|
20
|
-
| 'yesno' // Show Yes/No buttons
|
|
21
|
-
| 'open' // Show text input
|
|
22
|
-
| 'choices' // Show choice buttons
|
|
23
|
-
| 'continue' // Show Continue button
|
|
24
|
-
| 'invoke' // Auto-execute (no buttons)
|
|
25
|
-
| 'context_clear'; // Clear context and reload
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Result from marker detection
|
|
29
|
-
*/
|
|
30
|
-
export interface MarkerAction {
|
|
31
|
-
type: ActionType;
|
|
32
|
-
value?: string;
|
|
33
|
-
responses?: string[];
|
|
34
|
-
choices?: Array<{ number: number; text: string }>;
|
|
35
|
-
autoExecute?: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Extract option text from message content for numbered choices.
|
|
40
|
-
* Looks for patterns like "1. Option text" or "1) Option text".
|
|
41
|
-
*/
|
|
42
|
-
function extractChoiceTexts(
|
|
43
|
-
text: string,
|
|
44
|
-
choiceNumbers: number[]
|
|
45
|
-
): Array<{ number: number; text: string }> {
|
|
46
|
-
if (!text) {
|
|
47
|
-
return choiceNumbers.map(num => ({ number: num, text: `Option ${num}` }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Remove code blocks
|
|
51
|
-
const withoutCode = text.replace(/```[\s\S]*?```/g, '');
|
|
52
|
-
|
|
53
|
-
// Patterns for numbered lists
|
|
54
|
-
const patterns = [
|
|
55
|
-
/^\s*(\d+)\.\s+(.+)$/gm, // "1. Option text"
|
|
56
|
-
/^\s*(\d+)\)\s+(.+)$/gm, // "1) Option text"
|
|
57
|
-
/\*\*(\d+)[.)]\*\*\s*(.+)/gm, // "**1.** Option text"
|
|
58
|
-
];
|
|
59
|
-
|
|
60
|
-
const foundChoices = new Map<number, string>();
|
|
61
|
-
|
|
62
|
-
for (const pattern of patterns) {
|
|
63
|
-
pattern.lastIndex = 0;
|
|
64
|
-
let match;
|
|
65
|
-
while ((match = pattern.exec(withoutCode)) !== null) {
|
|
66
|
-
const num = parseInt(match[1], 10);
|
|
67
|
-
if (choiceNumbers.includes(num) && !foundChoices.has(num)) {
|
|
68
|
-
// Clean up the text - remove trailing markdown/formatting
|
|
69
|
-
let optionText = match[2].trim();
|
|
70
|
-
// Remove trailing description after " - " or " — " for cleaner button labels
|
|
71
|
-
const dashIndex = optionText.search(/\s+[-—]\s+/);
|
|
72
|
-
if (dashIndex > 0 && dashIndex < 30) {
|
|
73
|
-
optionText = optionText.substring(0, dashIndex);
|
|
74
|
-
}
|
|
75
|
-
foundChoices.set(num, optionText);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Return choices in order, falling back to "Option N" if not found
|
|
81
|
-
return choiceNumbers.map(num => ({
|
|
82
|
-
number: num,
|
|
83
|
-
text: foundChoices.get(num) || `Option ${num}`,
|
|
84
|
-
}));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Process detected markers into action metadata
|
|
89
|
-
*/
|
|
90
|
-
function processMarkers(
|
|
91
|
-
markers: ReturnType<typeof detectMarkers>,
|
|
92
|
-
fullText: string
|
|
93
|
-
): MarkerAction | null {
|
|
94
|
-
if (!markers || markers.length === 0) return null;
|
|
95
|
-
|
|
96
|
-
const primaryMarker = markers[0];
|
|
97
|
-
|
|
98
|
-
switch (primaryMarker.type) {
|
|
99
|
-
case MARKER_TYPES.HANDOFF:
|
|
100
|
-
return {
|
|
101
|
-
type: 'handoff',
|
|
102
|
-
value: primaryMarker.value,
|
|
103
|
-
responses: [primaryMarker.value, 'Not yet'],
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
case MARKER_TYPES.INVOKE:
|
|
107
|
-
return {
|
|
108
|
-
type: 'invoke',
|
|
109
|
-
value: primaryMarker.value,
|
|
110
|
-
autoExecute: true,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
case MARKER_TYPES.QUESTION: {
|
|
114
|
-
if (primaryMarker.value === 'yesno') {
|
|
115
|
-
return {
|
|
116
|
-
type: 'yesno',
|
|
117
|
-
responses: ['Yes', 'No'],
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
// Handle open questions - may have suggested prompt: "open" or "open:suggested text"
|
|
121
|
-
if (primaryMarker.value?.startsWith('open')) {
|
|
122
|
-
// Check for suggested prompt after "open:"
|
|
123
|
-
const colonIndex = primaryMarker.value.indexOf(':');
|
|
124
|
-
if (colonIndex !== -1) {
|
|
125
|
-
const suggestion = primaryMarker.value.substring(colonIndex + 1).trim();
|
|
126
|
-
if (suggestion) {
|
|
127
|
-
return {
|
|
128
|
-
type: 'open',
|
|
129
|
-
responses: [suggestion],
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
// Plain open question without suggestion
|
|
134
|
-
return {
|
|
135
|
-
type: 'open',
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
// Check for accompanying CHOICES marker
|
|
139
|
-
const choicesMarker = markers.find(m => m.type === MARKER_TYPES.CHOICES);
|
|
140
|
-
if (choicesMarker) {
|
|
141
|
-
return processChoicesMarker(choicesMarker.value, fullText);
|
|
142
|
-
}
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
case MARKER_TYPES.CHOICES:
|
|
147
|
-
return processChoicesMarker(primaryMarker.value, fullText);
|
|
148
|
-
|
|
149
|
-
case MARKER_TYPES.CONTEXT_CLEAR:
|
|
150
|
-
return {
|
|
151
|
-
type: 'context_clear',
|
|
152
|
-
value: primaryMarker.value,
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
case MARKER_TYPES.CONTINUE:
|
|
156
|
-
return {
|
|
157
|
-
type: 'continue',
|
|
158
|
-
responses: ['Continue'],
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
default:
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Process a CHOICES marker value into choice buttons
|
|
168
|
-
*/
|
|
169
|
-
function processChoicesMarker(
|
|
170
|
-
value: string,
|
|
171
|
-
fullText: string
|
|
172
|
-
): MarkerAction {
|
|
173
|
-
const choiceValues = value.split(',').map(v => v.trim());
|
|
174
|
-
const firstValue = choiceValues[0];
|
|
175
|
-
const isNumeric = /^\d+$/.test(firstValue);
|
|
176
|
-
|
|
177
|
-
let choices: Array<{ number: number; text: string }>;
|
|
178
|
-
|
|
179
|
-
if (isNumeric) {
|
|
180
|
-
// Legacy numeric format - extract text from message
|
|
181
|
-
const choiceNumbers = choiceValues.map(n => parseInt(n, 10));
|
|
182
|
-
choices = extractChoiceTexts(fullText, choiceNumbers);
|
|
183
|
-
} else {
|
|
184
|
-
// Text label format - use labels directly
|
|
185
|
-
choices = choiceValues.map((text, index) => ({
|
|
186
|
-
number: index + 1,
|
|
187
|
-
text,
|
|
188
|
-
}));
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
type: 'choices',
|
|
193
|
-
choices,
|
|
194
|
-
responses: choices.map(c => c.text),
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Hook to detect CYCLIST markers in content and return action metadata.
|
|
200
|
-
*
|
|
201
|
-
* @param content - Message content to analyze
|
|
202
|
-
* @returns MarkerAction if marker detected, null otherwise
|
|
203
|
-
*
|
|
204
|
-
* @example
|
|
205
|
-
* ```tsx
|
|
206
|
-
* const actions = useMarkerActions(message.content);
|
|
207
|
-
* if (actions?.type === 'yesno') {
|
|
208
|
-
* // Render Yes/No buttons
|
|
209
|
-
* }
|
|
210
|
-
* ```
|
|
211
|
-
*/
|
|
212
|
-
export function useMarkerActions(content: string | undefined): MarkerAction | null {
|
|
213
|
-
return useMemo(() => {
|
|
214
|
-
if (!content) return null;
|
|
215
|
-
|
|
216
|
-
const markers = detectMarkers(content);
|
|
217
|
-
if (!markers) return null;
|
|
218
|
-
|
|
219
|
-
return processMarkers(markers, content);
|
|
220
|
-
}, [content]);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* Strip CYCLIST markers from content for display.
|
|
225
|
-
*
|
|
226
|
-
* @param content - Content that may contain markers
|
|
227
|
-
* @returns Content with markers removed
|
|
228
|
-
*/
|
|
229
|
-
export function useStrippedContent(content: string | undefined): string {
|
|
230
|
-
return useMemo(() => {
|
|
231
|
-
if (!content) return '';
|
|
232
|
-
return stripMarkers(content);
|
|
233
|
-
}, [content]);
|
|
234
|
-
}
|