@pennyfarthing/core 11.3.7 → 11.4.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 +1 -1
- package/package.json +17 -16
- package/packages/core/dist/public/css/react.css +1 -1
- package/packages/core/dist/public/js/react/react.js +24 -24
- package/packages/core/src/public/App.tsx +356 -0
- package/packages/core/src/public/components/AgentLoadDialog.tsx +202 -0
- package/packages/core/src/public/components/AgentPopup.tsx +308 -0
- package/packages/core/src/public/components/ApprovalModal/ApprovalModal.css +35 -0
- package/packages/core/src/public/components/ApprovalModal/index.tsx +632 -0
- package/packages/core/src/public/components/BikeRackIndex.tsx +53 -0
- package/packages/core/src/public/components/BikeRackWorkspace.tsx +217 -0
- package/packages/core/src/public/components/CommandPalette.tsx +554 -0
- package/packages/core/src/public/components/ConfirmDialog.tsx +168 -0
- package/packages/core/src/public/components/ContextIndicator/ContextIndicator.css +85 -0
- package/packages/core/src/public/components/ContextIndicator/index.tsx +330 -0
- package/packages/core/src/public/components/ContextSparkline.tsx +56 -0
- package/packages/core/src/public/components/ControlBar.tsx +636 -0
- package/packages/core/src/public/components/DeadCodeDialog.tsx +169 -0
- package/packages/core/src/public/components/DiffViewer.tsx +585 -0
- package/packages/core/src/public/components/DockviewWorkspace.tsx +749 -0
- package/packages/core/src/public/components/Editor.tsx +630 -0
- package/packages/core/src/public/components/ErrorBoundary.tsx +67 -0
- package/packages/core/src/public/components/FileTree.tsx +379 -0
- package/packages/core/src/public/components/FontPicker/FontPicker.css +276 -0
- package/packages/core/src/public/components/FontPicker/index.tsx +430 -0
- package/packages/core/src/public/components/FullFileTree.tsx +237 -0
- package/packages/core/src/public/components/HealthGauge.tsx +181 -0
- package/packages/core/src/public/components/Message.tsx +225 -0
- package/packages/core/src/public/components/MessageList.tsx +98 -0
- package/packages/core/src/public/components/MessageView.tsx +400 -0
- package/packages/core/src/public/components/ModeSwitch/ModeSwitch.css +165 -0
- package/packages/core/src/public/components/ModeSwitch/index.tsx +372 -0
- package/packages/core/src/public/components/PersonaHeader.tsx +242 -0
- package/packages/core/src/public/components/ProjectInfoBar.tsx +45 -0
- package/packages/core/src/public/components/QuickActions.tsx +267 -0
- package/packages/core/src/public/components/SpanTimeline.tsx +352 -0
- package/packages/core/src/public/components/StandalonePanel.tsx +82 -0
- package/packages/core/src/public/components/StatsStrip.tsx +162 -0
- package/packages/core/src/public/components/StreamingContent.tsx +77 -0
- package/packages/core/src/public/components/SubagentSpan.tsx +180 -0
- package/packages/core/src/public/components/TandemPortrait.tsx +72 -0
- package/packages/core/src/public/components/ThemePalette/ThemePalette.css +179 -0
- package/packages/core/src/public/components/ThemePalette/index.tsx +326 -0
- package/packages/core/src/public/components/ToolCallBlock.tsx +252 -0
- package/packages/core/src/public/components/ToolStack.tsx +209 -0
- package/packages/core/src/public/components/ToolStatus.tsx +57 -0
- package/packages/core/src/public/components/dialogs/CodeMarkersDialog.tsx +169 -0
- package/packages/core/src/public/components/dialogs/ComplexityDialog.tsx +163 -0
- package/packages/core/src/public/components/dialogs/DependenciesDialog.tsx +120 -0
- package/packages/core/src/public/components/dialogs/HotspotsDialog.tsx +451 -0
- package/packages/core/src/public/components/dialogs/ToolDialog.tsx +43 -0
- package/packages/core/src/public/components/panel-registry.ts +13 -0
- package/packages/core/src/public/components/panels/ACPanel.tsx +93 -0
- package/packages/core/src/public/components/panels/AcceptanceCriteriaPanel.tsx +104 -0
- package/packages/core/src/public/components/panels/AuditLogPanel.tsx +489 -0
- package/packages/core/src/public/components/panels/BackgroundPanel.tsx +115 -0
- package/packages/core/src/public/components/panels/BikeLanePanel.tsx +214 -0
- package/packages/core/src/public/components/panels/DebugPanel.tsx +344 -0
- package/packages/core/src/public/components/panels/DiffView.tsx +109 -0
- package/packages/core/src/public/components/panels/DiffsPanel.tsx +56 -0
- package/packages/core/src/public/components/panels/GitPanel.tsx +260 -0
- package/packages/core/src/public/components/panels/HotspotsPanel.tsx +365 -0
- package/packages/core/src/public/components/panels/MessageFeed.tsx +39 -0
- package/packages/core/src/public/components/panels/MessagePanel.tsx +497 -0
- package/packages/core/src/public/components/panels/ProgressPanel.tsx +189 -0
- package/packages/core/src/public/components/panels/SettingsPanel.tsx +361 -0
- package/packages/core/src/public/components/panels/SprintPanel.tsx +723 -0
- package/packages/core/src/public/components/panels/TandemPanel.tsx +104 -0
- package/packages/core/src/public/components/panels/TaskTracker.tsx +48 -0
- package/packages/core/src/public/components/panels/TeamPanel.tsx +64 -0
- package/packages/core/src/public/components/panels/TeamRoster.tsx +67 -0
- package/packages/core/src/public/components/panels/TodoPanel.tsx +142 -0
- package/packages/core/src/public/components/panels/WorkflowPanel.tsx +224 -0
- package/packages/core/src/public/components/panels/index.ts +24 -0
- package/packages/core/src/public/components/ui/alert-dialog.tsx +139 -0
- package/packages/core/src/public/components/ui/badge.tsx +36 -0
- package/packages/core/src/public/components/ui/button.tsx +57 -0
- package/packages/core/src/public/components/ui/checkbox.tsx +28 -0
- package/packages/core/src/public/components/ui/collapsible.tsx +9 -0
- package/packages/core/src/public/components/ui/command.tsx +151 -0
- package/packages/core/src/public/components/ui/dialog.tsx +120 -0
- package/packages/core/src/public/components/ui/popover.tsx +31 -0
- package/packages/core/src/public/components/ui/progress.tsx +28 -0
- package/packages/core/src/public/components/ui/scroll-area.tsx +46 -0
- package/packages/core/src/public/components/ui/select.tsx +157 -0
- package/packages/core/src/public/components/ui/separator.tsx +29 -0
- package/packages/core/src/public/components/ui/skeleton.tsx +15 -0
- package/packages/core/src/public/components/ui/switch.tsx +27 -0
- package/packages/core/src/public/components/ui/toggle-group.tsx +59 -0
- package/packages/core/src/public/components/ui/toggle.tsx +43 -0
- package/packages/core/src/public/components/ui/tooltip.tsx +30 -0
- package/packages/core/src/public/contexts/ClaudeContext.tsx +311 -0
- package/packages/core/src/public/contexts/MessageQueueContext.tsx +143 -0
- package/packages/core/src/public/css/theme-browser.css +550 -0
- package/packages/core/src/public/css/theme-system.css +630 -0
- package/packages/core/src/public/hooks/index.ts +49 -0
- package/packages/core/src/public/hooks/useAgentLoad.ts +105 -0
- package/packages/core/src/public/hooks/useBackgroundTasks.ts +131 -0
- package/packages/core/src/public/hooks/useClaude.ts +234 -0
- package/packages/core/src/public/hooks/useCodeMarkers.ts +101 -0
- package/packages/core/src/public/hooks/useColorScheme.ts +42 -0
- package/packages/core/src/public/hooks/useCommandHistory.ts +99 -0
- package/packages/core/src/public/hooks/useComplexity.ts +80 -0
- package/packages/core/src/public/hooks/useDeadCode.ts +99 -0
- package/packages/core/src/public/hooks/useDependencies.ts +82 -0
- package/packages/core/src/public/hooks/useDiffs.ts +143 -0
- package/packages/core/src/public/hooks/useFileBrowser.ts +73 -0
- package/packages/core/src/public/hooks/useFocusPanel.ts +137 -0
- package/packages/core/src/public/hooks/useGitStatus.ts +233 -0
- package/packages/core/src/public/hooks/useHealthScore.ts +71 -0
- package/packages/core/src/public/hooks/useHotspots.ts +123 -0
- package/packages/core/src/public/hooks/useLayoutPersistence.ts +141 -0
- package/packages/core/src/public/hooks/useMarkdownParser.ts +36 -0
- package/packages/core/src/public/hooks/useMarkerActions.ts +234 -0
- package/packages/core/src/public/hooks/useMessageQueue.ts +380 -0
- package/packages/core/src/public/hooks/useMessageStream.ts +131 -0
- package/packages/core/src/public/hooks/usePersona.ts +112 -0
- package/packages/core/src/public/hooks/usePlanModeExit.ts +105 -0
- package/packages/core/src/public/hooks/useResponsiveLayout.ts +173 -0
- package/packages/core/src/public/hooks/useSprint.ts +157 -0
- package/packages/core/src/public/hooks/useStatsStrip.ts +204 -0
- package/packages/core/src/public/hooks/useStory.ts +135 -0
- package/packages/core/src/public/hooks/useSubagentHelper.ts +64 -0
- package/packages/core/src/public/hooks/useSyntaxHighlighter.ts +52 -0
- package/packages/core/src/public/hooks/useTabCompletion.ts +124 -0
- package/packages/core/src/public/hooks/useTandemObservations.ts +165 -0
- package/packages/core/src/public/hooks/useTeamMembers.ts +273 -0
- package/packages/core/src/public/hooks/useTodos.ts +93 -0
- package/packages/core/src/public/hooks/useUserAvatar.ts +54 -0
- package/packages/core/src/public/images/cyclist-dark.png +0 -0
- package/packages/core/src/public/images/cyclist-light.png +0 -0
- package/packages/core/src/public/index.html +14 -0
- package/packages/core/src/public/index.tsx +10 -0
- package/packages/core/src/public/lib/utils.ts +6 -0
- package/packages/core/src/public/styles/dockview-theme.css +376 -0
- package/packages/core/src/public/styles/tailwind.css +4454 -0
- package/packages/core/src/public/types/message.ts +51 -0
- package/packages/core/src/public/utils/avatar-service.ts +73 -0
- package/packages/core/src/public/utils/color-presets.ts +940 -0
- package/packages/core/src/public/utils/font-presets.ts +362 -0
- package/packages/core/src/public/utils/formatDuration.ts +14 -0
- package/packages/core/src/public/utils/markdown.ts +249 -0
- package/packages/core/src/public/utils/messageFilters.ts +128 -0
- package/packages/core/src/public/utils/slash-commands.ts +341 -0
- package/packages/core/src/public/utils/subagent-display.ts +146 -0
- package/packages/core/src/public/utils/syntax.ts +219 -0
- package/packages/core/src/public/utils/toolIntentSummarizer.ts +199 -0
- package/packages/core/src/public/utils/toolStackGrouper.ts +106 -0
- package/packages/core/src/public/utils/toolTypeColors.ts +45 -0
- package/pennyfarthing-dist/pf/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/__pycache__/split.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bc/cli.py +0 -1
- package/pennyfarthing-dist/pf/bc/focus.py +0 -1
- package/pennyfarthing-dist/pf/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bikerack/base_panel.py +0 -1
- package/pennyfarthing-dist/pf/bikerack/events.py +1 -7
- package/pennyfarthing-dist/pf/bikerack/git_panel.py +273 -10
- package/pennyfarthing-dist/pf/bikerack/portrait_resolver.py +21 -0
- package/pennyfarthing-dist/pf/bikerack/sprint_panel.py +58 -1
- package/pennyfarthing-dist/pf/bikerack/tui.py +5 -20
- package/pennyfarthing-dist/pf/bmad/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bmad/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bmad/__pycache__/parser.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/bmad/parser.py +15 -9
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/output.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/pr_config.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/__pycache__/themes.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/common/pr_config.py +27 -2
- package/pennyfarthing-dist/pf/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/complexity/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/deadcode/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/dependencies/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/epic/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/epic/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/git_group/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/healthscore/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hooks/cyclist-pretooluse-hook.sh +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/hotspots/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/claim.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/client.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/create.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/epic.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/operations.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/story.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/jira/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/launch/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/launch/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/models.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/persona.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/session.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/tiers.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/session/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/session/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/__pycache__/settings.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/settings/settings.py +44 -8
- package/pennyfarthing-dist/pf/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/archive.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/status.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/validator.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/work.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/sprint/story_finish.py +14 -2
- package/pennyfarthing-dist/pf/sprint/validator.py +7 -7
- package/pennyfarthing-dist/pf/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing-dist/pf/tests/test_sprint_validator.py +44 -0
- package/pennyfarthing-dist/pf/theme/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/theme/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/validate/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/scale.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/pf/workflow/__pycache__/state.cpython-314.pyc +0 -0
- package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
- package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
- package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +0 -0
- package/pennyfarthing-dist/scripts/core/pf.sh +0 -0
- package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
- package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
- package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
- package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
- package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
- package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +0 -0
- package/pennyfarthing-dist/scripts/git/release.sh +0 -0
- package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
- package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
- package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
- package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
- package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/env.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/find-root.sh +1 -1
- package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
- package/pennyfarthing-dist/scripts/lib/run-pf.sh +0 -0
- package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
- package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
- package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -1
- package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
- package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +13 -13
- package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
- package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +0 -0
- package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
- package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
- package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
- package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
- package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
- package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/check.py +4 -6
- package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/complete-step.py +2 -2
- package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
- package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
- package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
- package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
- package/pennyfarthing-dist/skills/skill-registry.yaml +19 -0
- package/pennyfarthing-dist/workflows/release/steps/step-10-publish.md +41 -9
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +15 -2
- package/packages/core/dist/workflow/__test_context_watch__/.session/.tandem-turn-counter +0 -1
- package/packages/core/dist/workflow/__test_context_watch__/.session/95-6-session.md +0 -3
- package/packages/core/dist/workflow/__test_context_watch__/.session/95-6-tandem-architect.md +0 -6
- package/packages/core/dist/workflow/__test_file_watch__/.session/95-4-tandem-architect.md +0 -6
- package/packages/core/dist/workflow/__test_file_watch__/workdir/trigger.ts +0 -1
- package/packages/core/dist/workflow/__test_tool_watch__/.session/95-5-tandem-architect.md +0 -6
- package/packages/core/dist/workflow/__test_tool_watch__/.session/95-5-tandem-toolcalls.jsonl +0 -1
- package/pennyfarthing-dist/pf/bikerack/changed_panel.py +0 -201
- package/scripts/README.md +0 -41
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font Presets - MSSCI-12769
|
|
3
|
+
*
|
|
4
|
+
* Font customization system for Cyclist with UI and code font presets,
|
|
5
|
+
* Tailwind-like size scale, and global persistence.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// =============================================================================
|
|
9
|
+
// Types
|
|
10
|
+
// =============================================================================
|
|
11
|
+
|
|
12
|
+
export interface FontPreset {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
fontFamily: string;
|
|
16
|
+
isCustom?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type FontSize = 'xs' | 'sm' | 'base' | 'lg' | 'xl';
|
|
20
|
+
|
|
21
|
+
export interface FontSettings {
|
|
22
|
+
uiFont: string;
|
|
23
|
+
codeFont: string;
|
|
24
|
+
uiFontSize: FontSize;
|
|
25
|
+
codeFontSize: FontSize;
|
|
26
|
+
customUiFont?: string;
|
|
27
|
+
customCodeFont?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface FontSizeScale {
|
|
31
|
+
xs: string;
|
|
32
|
+
sm: string;
|
|
33
|
+
base: string;
|
|
34
|
+
lg: string;
|
|
35
|
+
xl: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Font Presets
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
export const UI_FONT_PRESETS: FontPreset[] = [
|
|
43
|
+
{
|
|
44
|
+
id: 'system',
|
|
45
|
+
name: 'System',
|
|
46
|
+
fontFamily: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'inter',
|
|
50
|
+
name: 'Inter',
|
|
51
|
+
fontFamily: "'Inter', system-ui, -apple-system, sans-serif",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'custom',
|
|
55
|
+
name: 'Custom',
|
|
56
|
+
fontFamily: '',
|
|
57
|
+
isCustom: true,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
export const CODE_FONT_PRESETS: FontPreset[] = [
|
|
62
|
+
{
|
|
63
|
+
id: 'system-mono',
|
|
64
|
+
name: 'System Mono',
|
|
65
|
+
fontFamily: "ui-monospace, 'SF Mono', Monaco, Consolas, 'Liberation Mono', monospace",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'jetbrains-mono',
|
|
69
|
+
name: 'JetBrains Mono',
|
|
70
|
+
fontFamily: "'JetBrains Mono', ui-monospace, 'SF Mono', Monaco, monospace",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'fira-code',
|
|
74
|
+
name: 'Fira Code',
|
|
75
|
+
fontFamily: "'Fira Code', ui-monospace, 'SF Mono', Monaco, monospace",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'custom',
|
|
79
|
+
name: 'Custom',
|
|
80
|
+
fontFamily: '',
|
|
81
|
+
isCustom: true,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// Font Size Scale (Tailwind-like)
|
|
87
|
+
// =============================================================================
|
|
88
|
+
|
|
89
|
+
export const FONT_SIZE_SCALE: FontSizeScale = {
|
|
90
|
+
xs: '0.75rem',
|
|
91
|
+
sm: '0.875rem',
|
|
92
|
+
base: '1rem',
|
|
93
|
+
lg: '1.125rem',
|
|
94
|
+
xl: '1.25rem',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const VALID_SIZES: FontSize[] = ['xs', 'sm', 'base', 'lg', 'xl'];
|
|
98
|
+
|
|
99
|
+
// =============================================================================
|
|
100
|
+
// Sanitization
|
|
101
|
+
// =============================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Sanitize custom font family input to prevent CSS injection.
|
|
105
|
+
* Removes characters that could break out of font-family context.
|
|
106
|
+
*/
|
|
107
|
+
export function sanitizeFontFamily(input: string): string {
|
|
108
|
+
if (!input || typeof input !== 'string') {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
// Remove dangerous characters that could break CSS context
|
|
112
|
+
// Allow: letters, numbers, spaces, quotes, commas, hyphens, underscores
|
|
113
|
+
return input
|
|
114
|
+
.replace(/[;{}()<>\\]/g, '') // Remove CSS-breaking chars
|
|
115
|
+
.replace(/javascript:/gi, '') // Remove script protocol
|
|
116
|
+
.replace(/expression\s*\(/gi, '') // Remove IE expression()
|
|
117
|
+
.trim()
|
|
118
|
+
.slice(0, 500); // Limit length
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// Default Settings
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
export const DEFAULT_FONT_SETTINGS: FontSettings = {
|
|
126
|
+
uiFont: 'system',
|
|
127
|
+
codeFont: 'system-mono',
|
|
128
|
+
uiFontSize: 'base',
|
|
129
|
+
codeFontSize: 'base',
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// In-Memory State
|
|
134
|
+
// =============================================================================
|
|
135
|
+
|
|
136
|
+
let currentSettings: FontSettings = { ...DEFAULT_FONT_SETTINGS };
|
|
137
|
+
|
|
138
|
+
// =============================================================================
|
|
139
|
+
// UI Font Functions
|
|
140
|
+
// =============================================================================
|
|
141
|
+
|
|
142
|
+
export function getUIFont(): string {
|
|
143
|
+
return currentSettings.uiFont;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function setUIFont(presetId: string, customFamily?: string): void {
|
|
147
|
+
currentSettings.uiFont = presetId;
|
|
148
|
+
if (presetId === 'custom' && customFamily) {
|
|
149
|
+
currentSettings.customUiFont = customFamily;
|
|
150
|
+
}
|
|
151
|
+
applyUIFont(presetId, customFamily);
|
|
152
|
+
persistSettings();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function getCustomUIFont(): string | undefined {
|
|
156
|
+
return currentSettings.customUiFont;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// =============================================================================
|
|
160
|
+
// Code Font Functions
|
|
161
|
+
// =============================================================================
|
|
162
|
+
|
|
163
|
+
export function getCodeFont(): string {
|
|
164
|
+
return currentSettings.codeFont;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function setCodeFont(presetId: string, customFamily?: string): void {
|
|
168
|
+
currentSettings.codeFont = presetId;
|
|
169
|
+
if (presetId === 'custom' && customFamily) {
|
|
170
|
+
currentSettings.customCodeFont = customFamily;
|
|
171
|
+
}
|
|
172
|
+
applyCodeFont(presetId, customFamily);
|
|
173
|
+
persistSettings();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function getCustomCodeFont(): string | undefined {
|
|
177
|
+
return currentSettings.customCodeFont;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// =============================================================================
|
|
181
|
+
// Font Size Functions
|
|
182
|
+
// =============================================================================
|
|
183
|
+
|
|
184
|
+
export function getUIFontSize(): FontSize {
|
|
185
|
+
return currentSettings.uiFontSize;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function setUIFontSize(size: FontSize): void {
|
|
189
|
+
if (!VALID_SIZES.includes(size)) {
|
|
190
|
+
throw new Error(`Invalid font size: ${size}. Must be one of: ${VALID_SIZES.join(', ')}`);
|
|
191
|
+
}
|
|
192
|
+
currentSettings.uiFontSize = size;
|
|
193
|
+
applyFontSizes(currentSettings.uiFontSize, currentSettings.codeFontSize);
|
|
194
|
+
persistSettings();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function getCodeFontSize(): FontSize {
|
|
198
|
+
return currentSettings.codeFontSize;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function setCodeFontSize(size: FontSize): void {
|
|
202
|
+
if (!VALID_SIZES.includes(size)) {
|
|
203
|
+
throw new Error(`Invalid font size: ${size}. Must be one of: ${VALID_SIZES.join(', ')}`);
|
|
204
|
+
}
|
|
205
|
+
currentSettings.codeFontSize = size;
|
|
206
|
+
applyFontSizes(currentSettings.uiFontSize, currentSettings.codeFontSize);
|
|
207
|
+
persistSettings();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// =============================================================================
|
|
211
|
+
// Apply Functions (CSS Variables)
|
|
212
|
+
// =============================================================================
|
|
213
|
+
|
|
214
|
+
export function applyUIFont(presetId: string, customFamily?: string): void {
|
|
215
|
+
const preset = UI_FONT_PRESETS.find(p => p.id === presetId);
|
|
216
|
+
if (!preset) {
|
|
217
|
+
console.error(`[font-presets] Unknown UI font preset: ${presetId}`);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const fontFamily = preset.isCustom && customFamily
|
|
222
|
+
? sanitizeFontFamily(customFamily)
|
|
223
|
+
: preset.fontFamily;
|
|
224
|
+
document.documentElement.style.setProperty('--font-ui', fontFamily);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function applyCodeFont(presetId: string, customFamily?: string): void {
|
|
228
|
+
const preset = CODE_FONT_PRESETS.find(p => p.id === presetId);
|
|
229
|
+
if (!preset) {
|
|
230
|
+
console.error(`[font-presets] Unknown code font preset: ${presetId}`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const fontFamily = preset.isCustom && customFamily
|
|
235
|
+
? sanitizeFontFamily(customFamily)
|
|
236
|
+
: preset.fontFamily;
|
|
237
|
+
document.documentElement.style.setProperty('--font-mono', fontFamily);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function applyFontSizes(uiSize: FontSize, codeSize: FontSize): void {
|
|
241
|
+
document.documentElement.style.setProperty('--font-size-ui', FONT_SIZE_SCALE[uiSize]);
|
|
242
|
+
document.documentElement.style.setProperty('--font-size-code', FONT_SIZE_SCALE[codeSize]);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function applyFontSettings(settings: FontSettings): void {
|
|
246
|
+
// Apply UI font
|
|
247
|
+
const uiPreset = UI_FONT_PRESETS.find(p => p.id === settings.uiFont);
|
|
248
|
+
if (uiPreset) {
|
|
249
|
+
const uiFontFamily = uiPreset.isCustom && settings.customUiFont
|
|
250
|
+
? sanitizeFontFamily(settings.customUiFont)
|
|
251
|
+
: uiPreset.fontFamily;
|
|
252
|
+
document.documentElement.style.setProperty('--font-ui', uiFontFamily);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Apply code font
|
|
256
|
+
const codePreset = CODE_FONT_PRESETS.find(p => p.id === settings.codeFont);
|
|
257
|
+
if (codePreset) {
|
|
258
|
+
const codeFontFamily = codePreset.isCustom && settings.customCodeFont
|
|
259
|
+
? sanitizeFontFamily(settings.customCodeFont)
|
|
260
|
+
: codePreset.fontFamily;
|
|
261
|
+
document.documentElement.style.setProperty('--font-mono', codeFontFamily);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Apply sizes
|
|
265
|
+
applyFontSizes(settings.uiFontSize, settings.codeFontSize);
|
|
266
|
+
|
|
267
|
+
// Update in-memory state
|
|
268
|
+
currentSettings = { ...settings };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// =============================================================================
|
|
272
|
+
// IPC Integration
|
|
273
|
+
// =============================================================================
|
|
274
|
+
|
|
275
|
+
declare global {
|
|
276
|
+
interface Window {
|
|
277
|
+
electronAPI?: {
|
|
278
|
+
send?: (channel: string, data: unknown) => void;
|
|
279
|
+
invoke?: (channel: string, data: unknown) => Promise<unknown>;
|
|
280
|
+
font?: {
|
|
281
|
+
save: (settings: FontSettings) => Promise<boolean>;
|
|
282
|
+
load: () => Promise<FontSettings | null>;
|
|
283
|
+
getSettingsPath: () => Promise<string>;
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export function notifyFontChange(type: 'ui' | 'code', presetId: string): void {
|
|
290
|
+
// Font change is broadcast via settings WebSocket
|
|
291
|
+
console.log('[font-presets] Font changed:', type, presetId);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// =============================================================================
|
|
295
|
+
// Persistence Functions
|
|
296
|
+
// =============================================================================
|
|
297
|
+
|
|
298
|
+
export async function saveFontSettings(settings: FontSettings): Promise<void> {
|
|
299
|
+
currentSettings = { ...settings };
|
|
300
|
+
try {
|
|
301
|
+
// Use REST API to save font settings
|
|
302
|
+
await fetch('/api/settings', {
|
|
303
|
+
method: 'PATCH',
|
|
304
|
+
headers: { 'Content-Type': 'application/json' },
|
|
305
|
+
body: JSON.stringify({ display: { fonts: settings } }),
|
|
306
|
+
});
|
|
307
|
+
} catch (err) {
|
|
308
|
+
console.error('[font-presets] Failed to save settings:', err);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Helper to persist current settings (async but not awaited for better UX)
|
|
314
|
+
*/
|
|
315
|
+
function persistSettings(): void {
|
|
316
|
+
saveFontSettings(currentSettings).catch(() => {
|
|
317
|
+
// Error already logged in saveFontSettings
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export async function loadFontSettings(): Promise<FontSettings> {
|
|
322
|
+
try {
|
|
323
|
+
// Use REST API to load font settings
|
|
324
|
+
const response = await fetch('/api/settings');
|
|
325
|
+
if (response.ok) {
|
|
326
|
+
const settings = await response.json();
|
|
327
|
+
if (settings?.display?.fonts) {
|
|
328
|
+
currentSettings = { ...settings.display.fonts };
|
|
329
|
+
return currentSettings;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
} catch (err) {
|
|
333
|
+
console.error('[font-presets] Failed to load settings:', err);
|
|
334
|
+
}
|
|
335
|
+
return { ...DEFAULT_FONT_SETTINGS };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function getFontSettingsPath(): string {
|
|
339
|
+
// Global settings path (in user's home directory)
|
|
340
|
+
const home = typeof process !== 'undefined' ? process.env.HOME : '~';
|
|
341
|
+
return `${home}/.config/cyclist/font-settings.yaml`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// =============================================================================
|
|
345
|
+
// Preset Lookup Helpers
|
|
346
|
+
// =============================================================================
|
|
347
|
+
|
|
348
|
+
export function getUIFontPreset(id: string): FontPreset | undefined {
|
|
349
|
+
return UI_FONT_PRESETS.find(p => p.id === id);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export function getCodeFontPreset(id: string): FontPreset | undefined {
|
|
353
|
+
return CODE_FONT_PRESETS.find(p => p.id === id);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function getUIFontPresetIds(): string[] {
|
|
357
|
+
return UI_FONT_PRESETS.map(p => p.id);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function getCodeFontPresetIds(): string[] {
|
|
361
|
+
return CODE_FONT_PRESETS.map(p => p.id);
|
|
362
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format duration in milliseconds to human-readable string
|
|
3
|
+
*
|
|
4
|
+
* Story MSSCI-13402 - Tool use visual design polish
|
|
5
|
+
*
|
|
6
|
+
* @param ms - Duration in milliseconds
|
|
7
|
+
* @returns Formatted string like "245ms" or "2.3s"
|
|
8
|
+
*/
|
|
9
|
+
export function formatDuration(ms: number | undefined): string {
|
|
10
|
+
if (ms === undefined || ms === null) return '—';
|
|
11
|
+
if (ms < 0) return '0ms';
|
|
12
|
+
if (ms < 1000) return `${Math.round(ms)}ms`;
|
|
13
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Utilities
|
|
3
|
+
*
|
|
4
|
+
* Story MSSCI-13969: TypeScript markdown parsing utilities extracted from
|
|
5
|
+
* js/components/message-view/markdown-parser.js
|
|
6
|
+
*
|
|
7
|
+
* Security: XSS prevention via HTML escaping BEFORE markdown processing.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { highlightCode } from './syntax';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Strip CYCLIST structured markers from text before rendering.
|
|
14
|
+
* These markers are used for machine parsing (quick actions) but should
|
|
15
|
+
* be invisible to users.
|
|
16
|
+
* @param text - Text that may contain CYCLIST markers
|
|
17
|
+
* @returns Text with markers removed
|
|
18
|
+
*/
|
|
19
|
+
export function stripMarkers(text: string | null | undefined): string {
|
|
20
|
+
if (!text) return text as string;
|
|
21
|
+
// Remove CYCLIST markers: <!-- CYCLIST:TYPE --> or <!-- CYCLIST:TYPE:value -->
|
|
22
|
+
return text.replace(/<!--\s*CYCLIST:[^>]+?\s*-->/gi, '').trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Escape HTML special characters to prevent XSS.
|
|
27
|
+
* @param text - Raw text that may contain HTML
|
|
28
|
+
* @returns Escaped text safe for HTML rendering
|
|
29
|
+
*/
|
|
30
|
+
export function escapeHtml(text: string): string {
|
|
31
|
+
const map: Record<string, string> = {
|
|
32
|
+
'&': '&',
|
|
33
|
+
'<': '<',
|
|
34
|
+
'>': '>',
|
|
35
|
+
'"': '"',
|
|
36
|
+
"'": ''',
|
|
37
|
+
};
|
|
38
|
+
return text.replace(/[&<>"']/g, (c) => map[c]);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse cells from a table line: | cell1 | cell2 | cell3 |
|
|
43
|
+
* @param line - Table row line
|
|
44
|
+
* @returns Array of cell contents
|
|
45
|
+
*/
|
|
46
|
+
function parseCells(line: string): string[] {
|
|
47
|
+
return line
|
|
48
|
+
.split('|')
|
|
49
|
+
.map((cell) => cell.trim())
|
|
50
|
+
.filter((cell, index, arr) => {
|
|
51
|
+
// Filter out empty first/last cells from leading/trailing |
|
|
52
|
+
if (index === 0 && cell === '') return false;
|
|
53
|
+
if (index === arr.length - 1 && cell === '') return false;
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if a line is a separator row (|---|---|)
|
|
60
|
+
* @param line - Table row line
|
|
61
|
+
* @returns boolean
|
|
62
|
+
*/
|
|
63
|
+
function isSeparator(line: string): boolean {
|
|
64
|
+
const cells = parseCells(line);
|
|
65
|
+
return cells.every((cell) => /^[-:]+$/.test(cell));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Convert array of table lines to HTML table
|
|
70
|
+
* @param lines - Array of | delimited lines
|
|
71
|
+
* @returns HTML table string
|
|
72
|
+
*/
|
|
73
|
+
function convertTableLinesToHtml(lines: string[]): string {
|
|
74
|
+
if (lines.length === 0) return '';
|
|
75
|
+
|
|
76
|
+
// Filter out separator rows and identify header
|
|
77
|
+
const dataLines: string[] = [];
|
|
78
|
+
let headerLine: string | null = null;
|
|
79
|
+
let foundSeparator = false;
|
|
80
|
+
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
if (isSeparator(line)) {
|
|
83
|
+
foundSeparator = true;
|
|
84
|
+
continue; // Skip separator rows
|
|
85
|
+
}
|
|
86
|
+
if (!foundSeparator && headerLine === null) {
|
|
87
|
+
headerLine = line; // First non-separator row is header
|
|
88
|
+
} else {
|
|
89
|
+
dataLines.push(line);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Build HTML table
|
|
94
|
+
let html = '<div class="table-wrapper"><table>';
|
|
95
|
+
|
|
96
|
+
// Header row
|
|
97
|
+
if (headerLine) {
|
|
98
|
+
const headerCells = parseCells(headerLine);
|
|
99
|
+
html += '<thead><tr>';
|
|
100
|
+
for (let i = 0; i < headerCells.length; i++) {
|
|
101
|
+
html += `<th data-col="${i}" class="sortable-th">${headerCells[i]} <span class="sort-indicator"></span></th>`;
|
|
102
|
+
}
|
|
103
|
+
html += '</tr></thead>';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Body rows
|
|
107
|
+
if (dataLines.length > 0) {
|
|
108
|
+
html += '<tbody>';
|
|
109
|
+
for (const line of dataLines) {
|
|
110
|
+
const cells = parseCells(line);
|
|
111
|
+
html += '<tr>';
|
|
112
|
+
for (const cell of cells) {
|
|
113
|
+
html += `<td>${cell}</td>`;
|
|
114
|
+
}
|
|
115
|
+
html += '</tr>';
|
|
116
|
+
}
|
|
117
|
+
html += '</tbody>';
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
html += '</table></div>';
|
|
121
|
+
return html;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Parse markdown tables to HTML
|
|
126
|
+
* Detects consecutive lines starting with | and converts to <table>
|
|
127
|
+
* @param text - Text with escaped HTML
|
|
128
|
+
* @returns Text with tables converted to HTML
|
|
129
|
+
*/
|
|
130
|
+
function parseMarkdownTables(text: string): string {
|
|
131
|
+
const lines = text.split('\n');
|
|
132
|
+
const result: string[] = [];
|
|
133
|
+
let tableLines: string[] = [];
|
|
134
|
+
let inTable = false;
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < lines.length; i++) {
|
|
137
|
+
const line = lines[i].trim();
|
|
138
|
+
|
|
139
|
+
// Check if line is a table row (starts and ends with |, or starts with |)
|
|
140
|
+
const isTableRow = line.startsWith('|') && line.includes('|', 1);
|
|
141
|
+
|
|
142
|
+
if (isTableRow) {
|
|
143
|
+
if (!inTable) {
|
|
144
|
+
inTable = true;
|
|
145
|
+
tableLines = [];
|
|
146
|
+
}
|
|
147
|
+
tableLines.push(line);
|
|
148
|
+
} else {
|
|
149
|
+
// End of table or not a table line
|
|
150
|
+
if (inTable && tableLines.length > 0) {
|
|
151
|
+
result.push(convertTableLinesToHtml(tableLines));
|
|
152
|
+
tableLines = [];
|
|
153
|
+
inTable = false;
|
|
154
|
+
}
|
|
155
|
+
result.push(lines[i]); // Preserve original line (not trimmed)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Handle table at end of text
|
|
160
|
+
if (inTable && tableLines.length > 0) {
|
|
161
|
+
result.push(convertTableLinesToHtml(tableLines));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return result.join('\n');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Parse markdown text to HTML with XSS protection.
|
|
169
|
+
* @param markdown - Raw markdown text
|
|
170
|
+
* @returns HTML string
|
|
171
|
+
*/
|
|
172
|
+
export function parseMarkdown(markdown: string | null | undefined): string {
|
|
173
|
+
if (!markdown) return '';
|
|
174
|
+
|
|
175
|
+
// Strip CYCLIST structured markers BEFORE escaping HTML
|
|
176
|
+
// These are for quick-actions parsing, not display
|
|
177
|
+
const cleaned = stripMarkers(markdown);
|
|
178
|
+
|
|
179
|
+
// SECURITY: Escape HTML special characters FIRST to prevent XSS
|
|
180
|
+
// This ensures any <script>, <img onerror>, etc. are neutralized before processing
|
|
181
|
+
// Markdown syntax chars (*, #, `, -) are NOT escaped, so regexes still work
|
|
182
|
+
let html = escapeHtml(cleaned);
|
|
183
|
+
|
|
184
|
+
// Code blocks with syntax highlighting (must be first to avoid conflicts)
|
|
185
|
+
// Content is already escaped, apply highlighting then wrap
|
|
186
|
+
html = html.replace(/```(\w+)?\n?([\s\S]*?)```/g, (_, lang, code) => {
|
|
187
|
+
const langClass = lang ? ` class="language-${lang}"` : '';
|
|
188
|
+
const highlighted = lang ? highlightCode(code.trim(), lang) : code.trim();
|
|
189
|
+
return `<pre><code${langClass}>${highlighted}</code></pre>`;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// B-15: Join multi-line list items BEFORE other processing
|
|
193
|
+
// Indented continuation lines (2+ spaces) are joined to previous list item
|
|
194
|
+
// Stop at blank lines or new list items
|
|
195
|
+
html = html.replace(/^(\d+\.\s+.+)\n((?: +[^\n]+\n)+)/gm, (_, firstLine, continuation) => {
|
|
196
|
+
// Join continuation lines with spaces, removing leading whitespace
|
|
197
|
+
// Preserve trailing newline for next list item
|
|
198
|
+
const joined = continuation.trim().replace(/\n\s*/g, ' ');
|
|
199
|
+
return `${firstLine} ${joined}\n`;
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Inline code - content already escaped
|
|
203
|
+
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
|
204
|
+
|
|
205
|
+
// Tables - convert | delimited rows to HTML tables
|
|
206
|
+
// Must be after code blocks to avoid parsing tables in code
|
|
207
|
+
html = parseMarkdownTables(html);
|
|
208
|
+
|
|
209
|
+
// Headers (h1-h6) - content already escaped
|
|
210
|
+
html = html.replace(/^######\s+(.+)$/gm, '<h6>$1</h6>');
|
|
211
|
+
html = html.replace(/^#####\s+(.+)$/gm, '<h5>$1</h5>');
|
|
212
|
+
html = html.replace(/^####\s+(.+)$/gm, '<h4>$1</h4>');
|
|
213
|
+
html = html.replace(/^###\s+(.+)$/gm, '<h3>$1</h3>');
|
|
214
|
+
html = html.replace(/^##\s+(.+)$/gm, '<h2>$1</h2>');
|
|
215
|
+
html = html.replace(/^#\s+(.+)$/gm, '<h1>$1</h1>');
|
|
216
|
+
|
|
217
|
+
// Bold and italic - content already escaped
|
|
218
|
+
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
219
|
+
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
|
|
220
|
+
|
|
221
|
+
// Unordered lists - mark with data attribute to distinguish from ordered
|
|
222
|
+
html = html.replace(/^- (.+)$/gm, '<li data-ul>$1</li>');
|
|
223
|
+
|
|
224
|
+
// Ordered lists - mark with data attribute
|
|
225
|
+
// B-15: Match ordered list items that may contain inline formatting
|
|
226
|
+
html = html.replace(/^\d+\.\s+(.+)$/gm, '<li data-ol>$1</li>');
|
|
227
|
+
|
|
228
|
+
// Wrap consecutive unordered list items
|
|
229
|
+
html = html.replace(/(<li data-ul>.*?<\/li>\n?)+/g, (match) => {
|
|
230
|
+
return `<ul>${match.replace(/ data-ul/g, '')}</ul>`;
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Wrap consecutive ordered list items
|
|
234
|
+
html = html.replace(/(<li data-ol>.*?<\/li>\n?)+/g, (match) => {
|
|
235
|
+
return `<ol>${match.replace(/ data-ol/g, '')}</ol>`;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Paragraphs (lines not already wrapped) - content already escaped
|
|
239
|
+
// Excludes: h1-6, ul, ol, li, pre, div (table-wrapper), table elements
|
|
240
|
+
html = html.replace(/^(?!<[hulodtp]|<pre|<li)(.+)$/gm, '<p>$1</p>');
|
|
241
|
+
|
|
242
|
+
return html;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export default {
|
|
246
|
+
parseMarkdown,
|
|
247
|
+
escapeHtml,
|
|
248
|
+
stripMarkers,
|
|
249
|
+
};
|