@cat-factory/app 0.302.2 → 0.303.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.
Files changed (328) hide show
  1. package/README.md +16 -0
  2. package/app/app.config.ts +19 -10
  3. package/app/assets/css/main.css +13 -13
  4. package/app/assets/css/prose.css +22 -22
  5. package/app/assets/css/tokens.css +298 -0
  6. package/app/components/assistant/AssistantModal.vue +22 -22
  7. package/app/components/auth/AuthGate.vue +1 -1
  8. package/app/components/auth/BackendMisconfiguredScreen.vue +10 -10
  9. package/app/components/auth/LoginScreen.vue +34 -36
  10. package/app/components/auth/ResetPasswordScreen.vue +12 -12
  11. package/app/components/auth/UserMenu.vue +4 -4
  12. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +27 -25
  13. package/app/components/binaryOutput/BinaryOutputReport.vue +15 -15
  14. package/app/components/binaryOutput/StoredAssetView.vue +5 -5
  15. package/app/components/board/AddTaskModal.vue +17 -20
  16. package/app/components/board/AgentFailureCard.vue +11 -11
  17. package/app/components/board/AgentFailureHistory.vue +2 -2
  18. package/app/components/board/BoardCanvas.vue +6 -5
  19. package/app/components/board/CreateInitiativeModal.vue +8 -10
  20. package/app/components/board/DependencyConnectOverlay.vue +1 -1
  21. package/app/components/board/FailureHistoryList.vue +7 -7
  22. package/app/components/board/RecurringPipelineModal.vue +17 -17
  23. package/app/components/board/ReviewFrictionDialog.vue +6 -6
  24. package/app/components/board/StepExecutionHistory.vue +13 -13
  25. package/app/components/board/TaskDependencyEdges.vue +8 -8
  26. package/app/components/board/nodes/AgentChip.vue +10 -10
  27. package/app/components/board/nodes/BlockNode.vue +29 -29
  28. package/app/components/board/nodes/DecisionBadge.vue +2 -2
  29. package/app/components/board/nodes/EpicNode.vue +14 -10
  30. package/app/components/board/nodes/FrameSwimlanes.vue +7 -9
  31. package/app/components/board/nodes/InitiativeCard.vue +8 -8
  32. package/app/components/board/nodes/LaneGroup.vue +1 -1
  33. package/app/components/board/nodes/LaneTask.vue +2 -2
  34. package/app/components/board/nodes/ResizeGrips.vue +4 -4
  35. package/app/components/board/nodes/TaskCard.vue +18 -18
  36. package/app/components/board/nodes/TaskLane.vue +5 -5
  37. package/app/components/board/nodes/TaskPipelineMini.vue +18 -18
  38. package/app/components/bootstrap/AdoptionReviewModal.vue +29 -26
  39. package/app/components/bootstrap/BootstrapModal.vue +21 -23
  40. package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
  41. package/app/components/brainstorm/BrainstormWindow.vue +30 -33
  42. package/app/components/bugFishing/BugFishingWindow.vue +49 -49
  43. package/app/components/clarity/ClarityReviewWindow.vue +30 -30
  44. package/app/components/common/AsyncViewError.vue +5 -5
  45. package/app/components/common/ClarificationItem.vue +8 -13
  46. package/app/components/common/ConfirmDialog.vue +4 -2
  47. package/app/components/common/DescriptorFields.vue +1 -1
  48. package/app/components/common/EmptyState.vue +3 -3
  49. package/app/components/common/InterviewGateNotice.vue +7 -7
  50. package/app/components/common/KeyboardShortcutsHelp.vue +1 -1
  51. package/app/components/common/MarkdownProse.vue +14 -13
  52. package/app/components/consensus/ConsensusSessionWindow.vue +26 -26
  53. package/app/components/context/ContextAttachmentFields.vue +17 -17
  54. package/app/components/docs/DocInterviewWindow.vue +14 -14
  55. package/app/components/documents/ContextDocumentPicker.vue +17 -17
  56. package/app/components/documents/DocumentImportModal.vue +7 -7
  57. package/app/components/documents/DocumentSourceConnectModal.vue +3 -3
  58. package/app/components/documents/DocumentSyncState.vue +5 -5
  59. package/app/components/documents/DocumentTemplatesModal.vue +15 -15
  60. package/app/components/documents/RepoContextDocPicker.vue +5 -5
  61. package/app/components/documents/SpawnPreviewModal.vue +12 -12
  62. package/app/components/documents/StartFromDesignModal.vue +11 -11
  63. package/app/components/documents/TaskContextDocs.vue +5 -5
  64. package/app/components/environments/EnvSetupStepper.vue +6 -6
  65. package/app/components/environments/EnvironmentStatusPanel.vue +17 -17
  66. package/app/components/environments/steps/EnvPickStep.vue +3 -3
  67. package/app/components/environments/steps/EnvPreflightStep.vue +9 -9
  68. package/app/components/environments/steps/EnvReviewStep.vue +25 -22
  69. package/app/components/environments/steps/EnvSaveStep.vue +7 -5
  70. package/app/components/environments/steps/JourneyStepNav.vue +1 -1
  71. package/app/components/focus/BlockFocusView.vue +17 -19
  72. package/app/components/followUp/FollowUpWindow.vue +23 -23
  73. package/app/components/forkDecision/ForkDecisionWindow.vue +37 -39
  74. package/app/components/foundational/FoundationalContractSummary.vue +7 -7
  75. package/app/components/foundational/FoundationalServiceCatalogList.vue +11 -11
  76. package/app/components/foundational/FoundationalServiceManager.vue +1 -1
  77. package/app/components/foundational/FoundationalServiceRegistry.vue +13 -13
  78. package/app/components/foundational/FoundationalServiceSources.vue +13 -13
  79. package/app/components/foundational/FoundationalSuppressions.vue +7 -7
  80. package/app/components/foundational/ServiceCatalogConnection.vue +10 -10
  81. package/app/components/fragments/FragmentLibraryManager.vue +41 -44
  82. package/app/components/fragments/FragmentSelector.vue +13 -13
  83. package/app/components/fragments/GitHubDocUrlImport.vue +1 -1
  84. package/app/components/gates/GateFailingCheckList.vue +5 -5
  85. package/app/components/gates/GateResultView.vue +50 -47
  86. package/app/components/github/AddServiceFromRepoModal.vue +14 -14
  87. package/app/components/github/BranchProtectionPreflight.vue +13 -13
  88. package/app/components/github/GitHubConnect.vue +7 -7
  89. package/app/components/github/GitHubOnboarding.vue +7 -7
  90. package/app/components/github/GitHubPanel.vue +37 -40
  91. package/app/components/github/RepoTreeBrowser.vue +18 -18
  92. package/app/components/humanTest/HumanTestWindow.vue +30 -33
  93. package/app/components/initiative/InitiativePlanNotice.vue +5 -5
  94. package/app/components/initiative/InitiativePlanReview.vue +27 -29
  95. package/app/components/initiative/InitiativePlanningWindow.vue +10 -10
  96. package/app/components/initiative/InitiativeTrackerWindow.vue +71 -65
  97. package/app/components/inputGate/InputGateNotice.vue +2 -4
  98. package/app/components/judge/JudgeResultView.vue +30 -32
  99. package/app/components/kaizen/KaizenPanel.vue +40 -36
  100. package/app/components/kaizen/KaizenStepStatus.vue +21 -21
  101. package/app/components/layout/AccountAuditLog.vue +10 -10
  102. package/app/components/layout/AccountDeploymentSettings.vue +20 -20
  103. package/app/components/layout/AccountFailureKindRules.vue +13 -13
  104. package/app/components/layout/AccountFoundationalSettings.vue +1 -1
  105. package/app/components/layout/AccountFragmentSettings.vue +1 -1
  106. package/app/components/layout/AccountModelPolicySettings.vue +9 -9
  107. package/app/components/layout/AccountPlatformAlertSettings.vue +11 -11
  108. package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
  109. package/app/components/layout/AccountRunCredentialSettings.vue +5 -5
  110. package/app/components/layout/AccountSkillSettings.vue +1 -1
  111. package/app/components/layout/AccountTeamSettings.vue +23 -15
  112. package/app/components/layout/AiProvidersBanner.vue +11 -8
  113. package/app/components/layout/AppearanceSwitcher.vue +108 -0
  114. package/app/components/layout/BoardSwitcher.vue +8 -11
  115. package/app/components/layout/BoardToolbar.vue +3 -3
  116. package/app/components/layout/CommandBar.vue +7 -9
  117. package/app/components/layout/ConnectionStatusBanner.vue +2 -2
  118. package/app/components/layout/DefaultTestEnvBanner.vue +5 -5
  119. package/app/components/layout/GitHubPatBanner.vue +5 -5
  120. package/app/components/layout/GitHubPatPermissionsBanner.vue +16 -10
  121. package/app/components/layout/InfraSetupBanner.vue +6 -6
  122. package/app/components/layout/IntegrationsHub.vue +15 -15
  123. package/app/components/layout/LanguageSwitcher.vue +5 -5
  124. package/app/components/layout/ModelProvidersHub.vue +13 -16
  125. package/app/components/layout/NotificationsInbox.vue +10 -10
  126. package/app/components/layout/PersonalSetupModal.vue +8 -8
  127. package/app/components/layout/ProviderConfigBanner.vue +4 -4
  128. package/app/components/layout/RolePrompt.vue +7 -7
  129. package/app/components/layout/SideBar.vue +6 -4
  130. package/app/components/layout/SpendWarningBanner.vue +17 -14
  131. package/app/components/layout/TranslationWarningBanner.vue +4 -4
  132. package/app/components/layout/UiModeSwitcher.vue +13 -13
  133. package/app/components/layout/UiRoleSwitcher.vue +8 -8
  134. package/app/components/layout/WorkspaceMembersSettings.vue +15 -11
  135. package/app/components/media/ArtifactLightbox.vue +13 -13
  136. package/app/components/media/ImageCompare.vue +31 -24
  137. package/app/components/merge/MergeEffortChips.vue +2 -2
  138. package/app/components/notifications/NotificationSettingsPanel.vue +14 -12
  139. package/app/components/observability/OutcomeFilterChips.vue +6 -6
  140. package/app/components/observability/RunFailureSummary.vue +28 -25
  141. package/app/components/observability/StepMetricsBar.vue +26 -28
  142. package/app/components/observability/StepModelActivity.vue +2 -2
  143. package/app/components/observability/ToolCallList.vue +24 -24
  144. package/app/components/outcome/OutcomeSummaryWindow.vue +47 -47
  145. package/app/components/palettes/AgentPalette.vue +8 -8
  146. package/app/components/palettes/AgentTierSelect.vue +1 -1
  147. package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
  148. package/app/components/panels/AgentStepDetail.vue +52 -51
  149. package/app/components/panels/AttemptEntryHeader.vue +4 -6
  150. package/app/components/panels/DecisionModal.vue +7 -7
  151. package/app/components/panels/GenericStructuredResultView.vue +7 -7
  152. package/app/components/panels/InspectorPanel.vue +17 -17
  153. package/app/components/panels/MergerResultView.vue +19 -19
  154. package/app/components/panels/ObservabilityPanel.vue +115 -123
  155. package/app/components/panels/OperatorDashboardPanel.vue +89 -97
  156. package/app/components/panels/ReportsPanel.vue +69 -66
  157. package/app/components/panels/ReportsSpendBreakdown.vue +9 -9
  158. package/app/components/panels/ResultWindowShell.vue +38 -38
  159. package/app/components/panels/RunDetailLoadState.vue +2 -2
  160. package/app/components/panels/StepContainerStatus.vue +9 -9
  161. package/app/components/panels/StepDelegatedStatus.logic.spec.ts +135 -0
  162. package/app/components/panels/StepDelegatedStatus.logic.ts +160 -0
  163. package/app/components/panels/StepDelegatedStatus.vue +181 -0
  164. package/app/components/panels/StepEffortReport.vue +16 -15
  165. package/app/components/panels/StepFragmentAdherence.vue +14 -10
  166. package/app/components/panels/StepMetadataCard.vue +74 -45
  167. package/app/components/panels/StepReproductionReport.vue +25 -21
  168. package/app/components/panels/StepRunMeta.vue +20 -20
  169. package/app/components/panels/StepTestReport.logic.spec.ts +8 -3
  170. package/app/components/panels/StepTestReport.logic.ts +4 -4
  171. package/app/components/panels/StepTestReport.vue +28 -28
  172. package/app/components/panels/StepToolServers.vue +14 -11
  173. package/app/components/panels/StepValidationReport.vue +7 -7
  174. package/app/components/panels/inspector/ContainerSummary.vue +9 -9
  175. package/app/components/panels/inspector/DocReferenceRepos.vue +3 -3
  176. package/app/components/panels/inspector/EnvProbeReport.vue +24 -27
  177. package/app/components/panels/inspector/EpicChildren.vue +8 -12
  178. package/app/components/panels/inspector/FrontendBindingsResolved.vue +11 -11
  179. package/app/components/panels/inspector/FrontendConfig.vue +37 -41
  180. package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
  181. package/app/components/panels/inspector/InspectorSection.vue +6 -6
  182. package/app/components/panels/inspector/RecurringScheduleSettings.vue +16 -19
  183. package/app/components/panels/inspector/ServiceConnections.vue +2 -2
  184. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
  185. package/app/components/panels/inspector/ServiceSelfTests.vue +21 -17
  186. package/app/components/panels/inspector/ServiceTestConfig.vue +51 -59
  187. package/app/components/panels/inspector/ServiceTestSecrets.vue +5 -5
  188. package/app/components/panels/inspector/ServiceTestingContext.vue +3 -3
  189. package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
  190. package/app/components/panels/inspector/TaskAgentConfig.vue +3 -3
  191. package/app/components/panels/inspector/TaskAprioriBranches.vue +8 -5
  192. package/app/components/panels/inspector/TaskDependencies.vue +1 -1
  193. package/app/components/panels/inspector/TaskEstimateBadge.vue +11 -11
  194. package/app/components/panels/inspector/TaskExecution.vue +17 -17
  195. package/app/components/panels/inspector/TaskReviewTarget.vue +2 -2
  196. package/app/components/panels/inspector/TaskRunSettings.vue +35 -37
  197. package/app/components/panels/inspector/TaskStructure.vue +3 -3
  198. package/app/components/pipeline/AgentKindIcon.vue +1 -1
  199. package/app/components/pipeline/AgentPromptEditor.vue +17 -17
  200. package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -56
  201. package/app/components/pipeline/EstimateThresholdFields.vue +4 -4
  202. package/app/components/pipeline/GateConfigFields.vue +13 -10
  203. package/app/components/pipeline/IterationCapPrompt.vue +4 -2
  204. package/app/components/pipeline/PipelineBuilder.vue +47 -55
  205. package/app/components/pipeline/PipelineHealthModal.vue +27 -29
  206. package/app/components/pipeline/PipelinePicker.vue +10 -10
  207. package/app/components/pipeline/PipelinePreview.vue +10 -10
  208. package/app/components/pipeline/PipelineProgress.vue +64 -67
  209. package/app/components/prReview/PrReviewPhaseBadge.vue +7 -7
  210. package/app/components/prReview/PrReviewWindow.vue +75 -77
  211. package/app/components/providers/AiPresetMismatchDialog.vue +5 -5
  212. package/app/components/providers/AiProviderOnboardingModal.vue +9 -9
  213. package/app/components/providers/ApiKeysSection.vue +13 -13
  214. package/app/components/providers/PersonalCredentialModal.vue +2 -2
  215. package/app/components/providers/PersonalSubscriptionSection.vue +10 -10
  216. package/app/components/providers/SignInRequiredNotice.vue +1 -1
  217. package/app/components/providers/VendorCredentialsModal.vue +10 -10
  218. package/app/components/provisioning/ProvisioningLogsDrawer.vue +13 -13
  219. package/app/components/ralph/RalphLoopResultView.vue +28 -30
  220. package/app/components/recurring/RecurrenceEditor.vue +2 -2
  221. package/app/components/requirements/RequirementsReviewWindow.vue +51 -58
  222. package/app/components/riskPolicy/RiskPolicyPicker.vue +12 -15
  223. package/app/components/riskPolicy/RiskPolicyPreview.vue +14 -14
  224. package/app/components/sandbox/SandboxPanel.vue +58 -59
  225. package/app/components/settings/AccountSettingsPanel.vue +1 -1
  226. package/app/components/settings/ApiTokensPanel.vue +10 -10
  227. package/app/components/settings/BudgetSettings.vue +18 -22
  228. package/app/components/settings/CapabilityCredentialsPanel.vue +15 -15
  229. package/app/components/settings/CloudflareHandlerSection.vue +20 -20
  230. package/app/components/settings/ComposeEnvironmentSetupSection.vue +13 -13
  231. package/app/components/settings/ConnectionTestVerdict.vue +3 -3
  232. package/app/components/settings/ConnectionWarnings.vue +1 -1
  233. package/app/components/settings/ConsensusGroupsSection.vue +27 -35
  234. package/app/components/settings/CustomManifestTypeEditor.vue +10 -10
  235. package/app/components/settings/DefaultProvisionTypeSection.vue +6 -6
  236. package/app/components/settings/InfraHandlersConfigurator.vue +24 -22
  237. package/app/components/settings/InfrastructureBackendPicker.vue +6 -6
  238. package/app/components/settings/InfrastructureWindow.vue +5 -5
  239. package/app/components/settings/IssueTrackerPanel.vue +29 -23
  240. package/app/components/settings/KubernetesEngineForm.vue +11 -11
  241. package/app/components/settings/KubernetesEnvironmentForm.vue +4 -4
  242. package/app/components/settings/LocalContainerPoolSettings.vue +6 -11
  243. package/app/components/settings/LocalModelEndpointsPanel.vue +20 -18
  244. package/app/components/settings/McpAuthorizeScreen.vue +13 -15
  245. package/app/components/settings/McpOAuthCallbackScreen.vue +16 -10
  246. package/app/components/settings/MergeClassRulesEditor.vue +6 -9
  247. package/app/components/settings/MergeRolePolicyEditor.vue +16 -16
  248. package/app/components/settings/ModelConfigurationPanel.vue +23 -30
  249. package/app/components/settings/ModelPresetHealthModal.vue +15 -13
  250. package/app/components/settings/ObservabilityConnectionPanel.vue +4 -4
  251. package/app/components/settings/OpenRouterCatalogPanel.vue +23 -19
  252. package/app/components/settings/PackageRegistriesPanel.vue +8 -8
  253. package/app/components/settings/ProviderConnectionTab.vue +8 -11
  254. package/app/components/settings/ProviderManifestEditor.vue +9 -9
  255. package/app/components/settings/ProviderPreferenceEditor.vue +8 -8
  256. package/app/components/settings/RiskPolicyCreateForm.vue +10 -10
  257. package/app/components/settings/RiskPolicyEditorRow.vue +14 -14
  258. package/app/components/settings/RiskPolicyHealthModal.vue +15 -13
  259. package/app/components/settings/RiskPolicyInheritedRow.vue +1 -1
  260. package/app/components/settings/RiskPolicyPanel.vue +10 -15
  261. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +7 -7
  262. package/app/components/settings/SharedStacksPanel.vue +9 -12
  263. package/app/components/settings/TaskSourceCard.vue +4 -4
  264. package/app/components/settings/TaskTypeSuppressionsPanel.vue +6 -6
  265. package/app/components/settings/ToolServerChecklist.vue +36 -29
  266. package/app/components/settings/UsageSettings.vue +15 -15
  267. package/app/components/settings/UserSecretsSection.vue +9 -12
  268. package/app/components/settings/WorkspaceMetadataSettings.vue +5 -5
  269. package/app/components/settings/WorkspaceSettingsPanel.vue +47 -47
  270. package/app/components/skills/ReviewSkillQueue.vue +10 -10
  271. package/app/components/skills/SkillLibraryManager.vue +23 -20
  272. package/app/components/slack/SlackPanel.vue +13 -17
  273. package/app/components/spec/ServiceSpecWindow.vue +45 -42
  274. package/app/components/tasks/BugHuntModal.vue +24 -27
  275. package/app/components/tasks/ContextIssuePicker.vue +11 -11
  276. package/app/components/tasks/TaskContextIssues.vue +3 -3
  277. package/app/components/tasks/TaskImportModal.vue +7 -7
  278. package/app/components/tasks/TaskSourceConnectModal.vue +10 -8
  279. package/app/components/testing/TestReportWindow.vue +99 -82
  280. package/app/components/theme/ThemeImportModal.vue +115 -0
  281. package/app/components/tutorial/TutorialCatalogue.vue +9 -9
  282. package/app/components/tutorial/TutorialNudge.vue +5 -5
  283. package/app/components/tutorial/TutorialOverlay.vue +12 -12
  284. package/app/components/tutorial/TutorialPrompt.vue +6 -9
  285. package/app/components/vcs/GitLabConnect.vue +4 -4
  286. package/app/components/vcs/VcsConnectSurfaces.vue +2 -2
  287. package/app/components/visualConfirm/VisualConfirmationWindow.vue +36 -36
  288. package/app/composables/api/client.ts +6 -1
  289. package/app/composables/usePipelineErrorToast.ts +17 -0
  290. package/app/modular/agent-kinds.ts +24 -0
  291. package/app/pages/index.vue +5 -5
  292. package/app/plugins/theme.client.ts +125 -0
  293. package/app/spa-loading-template.html +52 -14
  294. package/app/stores/agents.ts +1 -1
  295. package/app/stores/theme.spec.ts +56 -0
  296. package/app/stores/theme.ts +87 -0
  297. package/app/types/domain.ts +19 -0
  298. package/app/types/execution.ts +4 -0
  299. package/app/utils/agentOutput.ts +1 -1
  300. package/app/utils/binaryOutput.ts +6 -6
  301. package/app/utils/bootstrapSteps.ts +14 -14
  302. package/app/utils/catalog.ts +87 -83
  303. package/app/utils/colorTint.ts +10 -0
  304. package/app/utils/failureKinds.ts +1 -0
  305. package/app/utils/observability.ts +3 -3
  306. package/app/utils/pipelineRender.ts +13 -17
  307. package/app/utils/swimlanes.ts +4 -4
  308. package/app/utils/theme/bootPalette.spec.ts +54 -0
  309. package/app/utils/theme/bootPalette.ts +108 -0
  310. package/app/utils/theme/builtins.spec.ts +38 -0
  311. package/app/utils/theme/builtins.ts +55 -0
  312. package/app/utils/theme/css.ts +162 -0
  313. package/app/utils/theme/doc.ts +353 -0
  314. package/app/utils/theme/link.ts +93 -0
  315. package/app/utils/theme/presets.ts +509 -0
  316. package/app/utils/theme/theme.spec.ts +224 -0
  317. package/i18n/locales/de.json +61 -4
  318. package/i18n/locales/en.json +70 -4
  319. package/i18n/locales/es.json +61 -4
  320. package/i18n/locales/fr.json +61 -4
  321. package/i18n/locales/he.json +61 -4
  322. package/i18n/locales/it.json +61 -4
  323. package/i18n/locales/ja.json +61 -4
  324. package/i18n/locales/pl.json +61 -4
  325. package/i18n/locales/tr.json +61 -4
  326. package/i18n/locales/uk.json +61 -4
  327. package/nuxt.config.ts +37 -11
  328. package/package.json +9 -8
@@ -0,0 +1,125 @@
1
+ import { computed, nextTick, ref, toRaw, watch } from 'vue'
2
+ import { useThemeStore } from '~/stores/theme'
3
+ import { captureBootPalette, writeBootPalette } from '~/utils/theme/bootPalette'
4
+ import { isSafePaletteName, themeDocToCss } from '~/utils/theme/css'
5
+ import {
6
+ COLOR_ALIASES,
7
+ DEFAULT_COLORS,
8
+ docColors,
9
+ docComponentOverrides,
10
+ mergeUi,
11
+ type ComponentOverrides,
12
+ type ThemeDoc,
13
+ } from '~/utils/theme/doc'
14
+
15
+ /**
16
+ * Applies the active theme document to the running app, the runtime twin of pasting the editor's
17
+ * `main.css` + `app.config.ts` export into the source tree:
18
+ *
19
+ * - the alias → palette map lands on `appConfig.ui.colors`, which Nuxt UI's own colours plugin
20
+ * watches and re-emits as the `--ui-color-<alias>-<shade>` ramps (so every role token, every
21
+ * bare `text-primary`, and the app's `--app-*` tokens follow with no work here);
22
+ * - the default-variant expansion and explicit component overrides land on `appConfig.ui.<comp>`,
23
+ * merged INTO the layer's own overrides from `app.config.ts` (the modal / slideover / toaster
24
+ * slots) rather than over them, and reset when the next theme no longer touches a component;
25
+ * - everything that is a CSS variable rides one `<style id="app-theme">` in the head plus a
26
+ * `data-theme` attribute on `<html>` the stylesheet's selectors anchor on (see `css.ts`).
27
+ *
28
+ * Runs client-only because the SPA is `ssr: false` and the document lives in browser storage.
29
+ */
30
+
31
+ type UiConfig = Record<string, unknown> & { colors: Record<string, string> }
32
+
33
+ export default defineNuxtPlugin(() => {
34
+ const store = useThemeStore()
35
+ const appConfig = useAppConfig()
36
+ const ui = appConfig.ui as unknown as UiConfig
37
+
38
+ // The layer's own overrides, snapshotted ONCE before any theme touches them: they are the base
39
+ // every theme merges into, and the value a component returns to when a theme stops naming it.
40
+ const baseUi: ComponentOverrides = {}
41
+ for (const [key, value] of Object.entries(toRaw(ui))) {
42
+ if (key !== 'colors' && typeof value === 'object' && value !== null) {
43
+ baseUi[key] = structuredClone(toRaw(value)) as Record<string, unknown>
44
+ }
45
+ }
46
+ let themedKeys = new Set<string>()
47
+
48
+ function apply(doc: ThemeDoc) {
49
+ // `docColors` fills defaults but passes a document's own alias values through untouched, and
50
+ // Nuxt UI interpolates each into `--ui-color-<alias>-<shade>: var(--color-<value>-<shade>)`, so
51
+ // an imported document's value has to clear the same grammar as any token that reaches CSS.
52
+ const colors = docColors(doc)
53
+ for (const alias of COLOR_ALIASES) {
54
+ const value = colors[alias]
55
+ ui.colors[alias] = isSafePaletteName(value) ? value : DEFAULT_COLORS[alias]
56
+ }
57
+
58
+ const overrides = docComponentOverrides(doc)
59
+ const next = new Set(Object.keys(overrides))
60
+ for (const key of themedKeys) {
61
+ if (next.has(key)) continue
62
+ if (key in baseUi) ui[key] = structuredClone(baseUi[key])
63
+ else delete ui[key]
64
+ }
65
+ const merged = mergeUi(
66
+ Object.fromEntries([...next].map((key) => [key, baseUi[key]]).filter(([, v]) => v)),
67
+ overrides,
68
+ )
69
+ for (const key of next) ui[key] = merged[key]
70
+ themedKeys = next
71
+ }
72
+
73
+ const css = computed(() => themeDocToCss(store.active.doc))
74
+ useHead({
75
+ htmlAttrs: { 'data-theme': computed(() => store.active.id) },
76
+ style: [{ id: 'app-theme', innerHTML: css }],
77
+ })
78
+
79
+ watch(() => store.active.doc, apply, { immediate: true })
80
+
81
+ // The browser chrome and the loading shell's cache both want the COMPUTED canvas colour, which
82
+ // exists only after unhead has written the theme style and Nuxt UI's colour ramps into the DOM.
83
+ // That moment is unhead's own `dom:rendered` hook (the same one Nuxt UI's colours plugin uses),
84
+ // so the capture runs there rather than after a tick or a frame: a tick is too early, and a
85
+ // hidden tab never gets a frame. `getComputedStyle` flushes style itself.
86
+ //
87
+ // The chrome rides `useHead` too: unhead dedupes a `theme-color` meta by name AND media, so this
88
+ // pair (same media as the first-paint pair in `nuxt.config.ts`, no key) overrides those two tags
89
+ // with the live colour, whichever the OS scheme selects, and unhead never re-renders a stale
90
+ // value over a hand-edited tag.
91
+ const colorMode = useColorMode()
92
+ const chrome = ref<string | null>(null)
93
+ useHead({
94
+ meta: [
95
+ {
96
+ name: 'theme-color',
97
+ media: '(prefers-color-scheme: dark)',
98
+ // The first-paint fallbacks, the same two values `nuxt.config.ts` ships.
99
+ content: computed(() => chrome.value ?? '#020618'), // colour-literal-ok: first paint
100
+ },
101
+ {
102
+ name: 'theme-color',
103
+ media: '(prefers-color-scheme: light)',
104
+ content: computed(() => chrome.value ?? '#e2e8f0'), // colour-literal-ok: first paint
105
+ },
106
+ ],
107
+ })
108
+ function recordBootColours() {
109
+ const mode = colorMode.value
110
+ if (mode !== 'light' && mode !== 'dark') return
111
+ const entry = captureBootPalette()
112
+ if (!entry) return
113
+ writeBootPalette(store.active.id, mode, entry)
114
+ chrome.value = entry.bg
115
+ }
116
+ injectHead()?.hooks?.hook('dom:rendered', recordBootColours)
117
+ watch(
118
+ () => [store.active.id, colorMode.value] as const,
119
+ async () => {
120
+ await nextTick()
121
+ recordBootColours()
122
+ },
123
+ { immediate: true },
124
+ )
125
+ })
@@ -4,6 +4,29 @@
4
4
  brand name (a proper noun, identical in every locale) so it stays locale-neutral.
5
5
  Wired via app.spaLoadingTemplate in nuxt.config.ts. -->
6
6
  <div class="cf-spa-loader" role="status" aria-label="Cat Factory is starting">
7
+ <!-- Wear the ACTIVE theme, not the default: the theme plugin caches the computed colours of the
8
+ few tokens this shell paints with, per colour mode, under `cf-boot-palette` (see
9
+ utils/theme/bootPalette.ts). The colour-mode script in <head> has already classed <html>, so
10
+ pick that mode's entry; a mode never seen since the theme was picked keeps the defaults below.
11
+ Values are computed colours applied through setProperty, so nothing here can be injected. -->
12
+ <script>
13
+ ;(function () {
14
+ try {
15
+ var mode = document.documentElement.classList.contains('light') ? 'light' : 'dark'
16
+ var palette = JSON.parse(localStorage.getItem('cf-boot-palette') || '{}')[mode]
17
+ if (!palette) return
18
+ var el = document.currentScript.parentElement
19
+ var slots = ['bg', 'track', 'accent', 'status']
20
+ for (var i = 0; i < slots.length; i++) {
21
+ if (typeof palette[slots[i]] === 'string') {
22
+ el.style.setProperty('--cf-' + slots[i], palette[slots[i]])
23
+ }
24
+ }
25
+ } catch (e) {
26
+ /* storage unavailable: the shell keeps its default colours */
27
+ }
28
+ })()
29
+ </script>
7
30
  <div class="cf-spa-loader__badge">
8
31
  <div class="cf-spa-loader__ring"></div>
9
32
  <!-- Cat face, drawn inline so it paints without the icon font. -->
@@ -12,11 +35,11 @@
12
35
  d="M4 4l3 3.2A7.5 7.5 0 0 1 12 6c1.9 0 3.6.5 5 1.2L20 4v8a8 8 0 0 1-16 0V4z"
13
36
  fill="currentColor"
14
37
  />
15
- <circle cx="9" cy="11.5" r="1.1" fill="#0b1020" />
16
- <circle cx="15" cy="11.5" r="1.1" fill="#0b1020" />
38
+ <circle cx="9" cy="11.5" r="1.1" />
39
+ <circle cx="15" cy="11.5" r="1.1" />
17
40
  <path
18
41
  d="M10.6 14.4c.4.5 2.4.5 2.8 0"
19
- stroke="#0b1020"
42
+ stroke="currentColor"
20
43
  stroke-width="1"
21
44
  stroke-linecap="round"
22
45
  />
@@ -38,8 +61,14 @@
38
61
  align-items: center;
39
62
  justify-content: center;
40
63
  gap: 1.5rem;
41
- /* --board-bg / slate-950, matching the board surface AuthGate renders on. */
42
- background-color: #0b1020;
64
+ /* Defaults are the default theme's tokens (slate-950 / slate-200 canvas, indigo primary),
65
+ hardcoded because main.css is not loaded yet; the inline script above overrides the
66
+ `--cf-*` slots with the active theme's cached colours when it has them. */
67
+ --cf-bg: #020618;
68
+ --cf-track: #1e293b;
69
+ --cf-accent: #818cf8;
70
+ --cf-status: #94a3b8;
71
+ background-color: var(--cf-bg);
43
72
  font-family:
44
73
  ui-sans-serif,
45
74
  system-ui,
@@ -60,15 +89,15 @@
60
89
  position: absolute;
61
90
  inset: 0;
62
91
  border-radius: 9999px;
63
- /* Track in slate-700, the active arc in indigo-500 (the app's primary). */
64
- border: 3px solid #1e293b;
65
- border-top-color: #6366f1;
92
+ /* Track in the border token, the active arc in the primary. */
93
+ border: 3px solid var(--cf-track);
94
+ border-top-color: var(--cf-accent);
66
95
  animation: cf-spa-spin 1s linear infinite;
67
96
  }
68
97
  .cf-spa-loader__cat {
69
98
  width: 2.5rem;
70
99
  height: 2.5rem;
71
- color: #818cf8;
100
+ color: var(--cf-accent);
72
101
  animation: cf-spa-bob 1.6s ease-in-out infinite;
73
102
  }
74
103
  .cf-spa-loader__text {
@@ -81,14 +110,23 @@
81
110
  font-size: 1.35rem;
82
111
  font-weight: 700;
83
112
  letter-spacing: 0.01em;
84
- background: linear-gradient(90deg, #818cf8, #6366f1);
85
- -webkit-background-clip: text;
86
- background-clip: text;
87
- color: transparent;
113
+ color: var(--cf-accent);
88
114
  }
89
115
  .cf-spa-loader__status {
90
116
  font-size: 0.875rem;
91
- color: #94a3b8;
117
+ color: var(--cf-status);
118
+ }
119
+ /* Light colour mode: the canvas and the two neutral shades flip; the indigo accents stay. */
120
+ html.light .cf-spa-loader {
121
+ --cf-bg: #e2e8f0;
122
+ --cf-track: #cbd5e1;
123
+ --cf-accent: #6366f1;
124
+ --cf-status: #64748b;
125
+ }
126
+ .cf-spa-loader__cat circle,
127
+ .cf-spa-loader__cat path[stroke] {
128
+ fill: var(--cf-bg);
129
+ stroke: var(--cf-bg);
92
130
  }
93
131
  .cf-spa-loader__dots i {
94
132
  font-style: normal;
@@ -151,7 +151,7 @@ export const useAgentsStore = defineStore('agents', () => {
151
151
  label: input.label.trim() || 'Custom Agent',
152
152
  description: input.description?.trim() || 'Custom agent.',
153
153
  icon: input.icon || 'i-lucide-sparkles',
154
- color: input.color || '#22d3ee',
154
+ color: input.color || 'var(--app-hue-cyan)',
155
155
  }
156
156
  runtimeAgents.value = [...runtimeAgents.value, archetype]
157
157
  return archetype
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { useThemeStore } from '~/stores/theme'
3
+ import { BUILTIN_THEMES, DEFAULT_THEME_ID } from '~/utils/theme/builtins'
4
+
5
+ describe('theme store', () => {
6
+ it('boots on the default built-in and lists the built-ins first', () => {
7
+ const store = useThemeStore()
8
+ expect(store.current).toBe(DEFAULT_THEME_ID)
9
+ expect(store.active.id).toBe(DEFAULT_THEME_ID)
10
+ expect(store.themes.slice(0, BUILTIN_THEMES.length)).toEqual(BUILTIN_THEMES)
11
+ expect(store.isCustom).toBe(false)
12
+ })
13
+
14
+ it('selects only a theme it knows', () => {
15
+ const store = useThemeStore()
16
+ store.select('mono')
17
+ expect(store.active.name).toBe('Mono')
18
+ store.select('nope')
19
+ expect(store.current).toBe('mono')
20
+ })
21
+
22
+ it('stores an imported document under a name and switches to it', () => {
23
+ const store = useThemeStore()
24
+ const theme = store.addCustom(' Ocean ', { version: 1, colors: { primary: 'cyan' } })
25
+ expect(theme.name).toBe('Ocean')
26
+ expect(store.current).toBe(theme.id)
27
+ expect(store.isCustom).toBe(true)
28
+ expect(store.active.doc.colors?.primary).toBe('cyan')
29
+ })
30
+
31
+ it('names an unnamed import after its position', () => {
32
+ const store = useThemeStore()
33
+ expect(store.addCustom('', { version: 1 }).name).toBe('Theme 1')
34
+ expect(store.addCustom(' ', { version: 1 }).name).toBe('Theme 2')
35
+ })
36
+
37
+ it('disambiguates a name a built-in or an earlier import already uses', () => {
38
+ const store = useThemeStore()
39
+ expect(store.addCustom('Mono', { version: 1 }).name).toBe('Mono (2)')
40
+ expect(store.addCustom('mono', { version: 1 }).name).toBe('mono (3)')
41
+ expect(store.addCustom('Ocean', { version: 1 }).name).toBe('Ocean')
42
+ expect(store.addCustom('Ocean', { version: 1 }).name).toBe('Ocean (2)')
43
+ })
44
+
45
+ it('falls back to the default when the active custom theme is removed or was never restored', () => {
46
+ const store = useThemeStore()
47
+ const theme = store.addCustom('Gone', { version: 1 })
48
+ store.removeCustom(theme.id)
49
+ expect(store.current).toBe(DEFAULT_THEME_ID)
50
+ expect(store.custom).toEqual([])
51
+
52
+ // A persisted pick naming a theme that no longer exists resolves to a real document.
53
+ store.current = 'custom-stale'
54
+ expect(store.active.id).toBe(DEFAULT_THEME_ID)
55
+ })
56
+ })
@@ -0,0 +1,87 @@
1
+ import { defineStore } from 'pinia'
2
+ import { computed, ref } from 'vue'
3
+ import { BUILTIN_THEMES, DEFAULT_THEME_ID, type NamedTheme } from '~/utils/theme/builtins'
4
+ import type { ThemeDoc } from '~/utils/theme/doc'
5
+
6
+ /**
7
+ * The user's theme choice and their imported themes, persisted in this browser like the language
8
+ * pick (`stores/locale.ts`). Colour MODE (light / dark / system) is NOT here: `@nuxtjs/color-mode`
9
+ * owns that preference and its own storage, and the two are orthogonal (every theme has both
10
+ * modes), so the switcher reads mode from `useColorMode()` and theme from this store.
11
+ *
12
+ * `custom` holds whole documents rather than the editor links they came from, so a theme keeps
13
+ * working when the editor changes its link encoding, and so the persisted blob is exactly what the
14
+ * applier reads (no decode on boot).
15
+ */
16
+ export const useThemeStore = defineStore(
17
+ 'theme',
18
+ () => {
19
+ const current = ref<string>(DEFAULT_THEME_ID)
20
+ const custom = ref<NamedTheme[]>([])
21
+
22
+ const themes = computed<readonly NamedTheme[]>(() => [...BUILTIN_THEMES, ...custom.value])
23
+
24
+ // `?? the default` because the restored id is untrusted input: a persisted pick can name a
25
+ // custom theme that was removed, or an id a later build renamed, and the app must still boot
26
+ // on a real document rather than on `undefined`.
27
+ const active = computed<NamedTheme>(
28
+ () =>
29
+ themes.value.find((theme) => theme.id === current.value) ??
30
+ themes.value.find((theme) => theme.id === DEFAULT_THEME_ID) ??
31
+ BUILTIN_THEMES[0]!,
32
+ )
33
+ const isCustom = computed(() => custom.value.some((theme) => theme.id === current.value))
34
+
35
+ function select(id: string) {
36
+ if (themes.value.some((theme) => theme.id === id)) current.value = id
37
+ }
38
+
39
+ /**
40
+ * A display name no other theme (built-in or imported) already uses. Ids never collide (an
41
+ * import gets a generated one), so the name is the only thing two entries could share, and
42
+ * two menu rows both reading "Mono" tell the user nothing. The first duplicate becomes
43
+ * "Mono (2)", the next "Mono (3)", the file-manager convention. Exposed so the import dialog
44
+ * can show the name that WILL be used while the user is still typing, rather than surprising
45
+ * them after the save.
46
+ */
47
+ function uniqueName(requested: string): string {
48
+ const taken = new Set(themes.value.map((theme) => theme.name.toLowerCase()))
49
+ if (!taken.has(requested.toLowerCase())) return requested
50
+ for (let n = 2; ; n++) {
51
+ const candidate = `${requested} (${n})`
52
+ if (!taken.has(candidate.toLowerCase())) return candidate
53
+ }
54
+ }
55
+
56
+ /** Store an imported document under a name and switch to it. Returns the new theme. */
57
+ function addCustom(name: string, doc: ThemeDoc): NamedTheme {
58
+ const theme: NamedTheme = {
59
+ id: `custom-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
60
+ name: uniqueName(name.trim() || `Theme ${custom.value.length + 1}`),
61
+ doc,
62
+ }
63
+ custom.value = [...custom.value, theme]
64
+ current.value = theme.id
65
+ return theme
66
+ }
67
+
68
+ /** Drop an imported theme; the default takes over if it was the active one. */
69
+ function removeCustom(id: string) {
70
+ custom.value = custom.value.filter((theme) => theme.id !== id)
71
+ if (current.value === id) current.value = DEFAULT_THEME_ID
72
+ }
73
+
74
+ return {
75
+ current,
76
+ custom,
77
+ themes,
78
+ active,
79
+ isCustom,
80
+ uniqueName,
81
+ select,
82
+ addCustom,
83
+ removeCustom,
84
+ }
85
+ },
86
+ { persist: { pick: ['current', 'custom'] } },
87
+ )
@@ -169,6 +169,25 @@ export interface AgentArchetype {
169
169
  * which is every built-in kind.
170
170
  */
171
171
  binaryOutput?: boolean
172
+ /**
173
+ * The registered EXTERNAL executor this kind's work runs on, when it runs on one at all.
174
+ *
175
+ * Carried onto the archetype for the reason {@link binaryOutput} is: the pipeline builder and
176
+ * every run view resolve a step's meta through `agentKindMeta`, not through the snapshot, so a
177
+ * fact a card has to state cannot live only on the wire entry. What it states is the thing a
178
+ * person composing a pipeline most needs to see (which of these steps leaves the platform),
179
+ * and what a run view needs to explain a step with no token usage beside it.
180
+ *
181
+ * `label` is absent when the kind declares a delegated surface whose executor THIS build no
182
+ * longer registers, which is a real state (a deployment can stop registering one while runs that
183
+ * used it are still on the board) and renders as the id rather than as a normal step.
184
+ */
185
+ delegatedExecutor?: {
186
+ id: string
187
+ label?: string
188
+ description?: string
189
+ telemetry?: 'not-reported' | 'self-reported'
190
+ }
172
191
  /**
173
192
  * The platform dispatches this kind for a flow of its own, so the builder palette never offers
174
193
  * it as a placeable block (`narrowAgentPalette` drops it). It still resolves through
@@ -120,6 +120,10 @@ export type {
120
120
  RunEnvironment,
121
121
  RunContainer,
122
122
  RunContainerStatus,
123
+ // The DELEGATED sibling: the external work a step dispatched, and what the platform can see of it.
124
+ RunDelegation,
125
+ RunDelegationAttempt,
126
+ RunDelegationStatus,
123
127
  HumanTestRound,
124
128
  HumanTestStepState,
125
129
  VisualConfirmStepState,
@@ -79,7 +79,7 @@ const md = new MarkdownIt({
79
79
  }).use(sourceLinePlugin)
80
80
 
81
81
  const HEADINGS = new Set(['H1', 'H2', 'H3', 'H4', 'H5', 'H6'])
82
- const LINK_CLASS = 'text-indigo-300 underline decoration-indigo-500/40 hover:text-indigo-200'
82
+ const LINK_CLASS = 'text-primary underline decoration-primary/40 hover:decoration-primary'
83
83
 
84
84
  // A second markdown-it instance for INLINE prose rendering (a rationale, a synthesis,
85
85
  // a summary) — the same secure config as `md` (html: false, so raw HTML is escaped and
@@ -401,37 +401,37 @@ export const BINARY_OUTPUT_STATE_KEYS: Record<
401
401
  > = {
402
402
  'not-started': {
403
403
  icon: 'i-lucide-clock',
404
- tone: 'text-slate-400',
404
+ tone: 'text-muted',
405
405
  summary: 'binaryOutput.state.notStarted.summary',
406
406
  detail: 'binaryOutput.state.notStarted.detail',
407
407
  },
408
408
  configured: {
409
409
  icon: 'i-lucide-hourglass',
410
- tone: 'text-slate-300',
410
+ tone: 'text-toned',
411
411
  summary: 'binaryOutput.state.configured.summary',
412
412
  detail: 'binaryOutput.state.configured.detail',
413
413
  },
414
414
  undeclared: {
415
415
  icon: 'i-lucide-circle-help',
416
- tone: 'text-amber-300',
416
+ tone: 'text-app-warning-300',
417
417
  summary: 'binaryOutput.state.undeclared.summary',
418
418
  detail: 'binaryOutput.state.undeclared.detail',
419
419
  },
420
420
  'parse-failed': {
421
421
  icon: 'i-lucide-file-warning',
422
- tone: 'text-amber-300',
422
+ tone: 'text-app-warning-300',
423
423
  summary: 'binaryOutput.state.parseFailed.summary',
424
424
  detail: 'binaryOutput.state.parseFailed.detail',
425
425
  },
426
426
  'declared-none': {
427
427
  icon: 'i-lucide-circle-slash',
428
- tone: 'text-slate-300',
428
+ tone: 'text-toned',
429
429
  summary: 'binaryOutput.state.declaredNone.summary',
430
430
  detail: 'binaryOutput.state.declaredNone.detail',
431
431
  },
432
432
  stored: {
433
433
  icon: 'i-lucide-package-check',
434
- tone: 'text-emerald-300',
434
+ tone: 'text-app-success-300',
435
435
  summary: 'binaryOutput.state.stored.summary',
436
436
  detail: '',
437
437
  },
@@ -24,37 +24,37 @@ export interface BootstrapStepStyle {
24
24
  export const BOOTSTRAP_STEP_STYLE: Record<BootstrapStepState, BootstrapStepStyle> = {
25
25
  pending: {
26
26
  icon: 'i-lucide-circle',
27
- iconClass: 'text-slate-500',
28
- labelClass: 'text-slate-500',
27
+ iconClass: 'text-dimmed',
28
+ labelClass: 'text-dimmed',
29
29
  },
30
30
  running: {
31
31
  icon: 'i-lucide-loader-circle',
32
- iconClass: 'animate-spin text-amber-400',
33
- labelClass: 'text-amber-100',
32
+ iconClass: 'animate-spin text-app-warning-400',
33
+ labelClass: 'text-app-warning-100',
34
34
  },
35
35
  awaiting_review: {
36
36
  icon: 'i-lucide-user-check',
37
- iconClass: 'text-amber-400',
38
- labelClass: 'text-amber-100',
37
+ iconClass: 'text-app-warning-400',
38
+ labelClass: 'text-app-warning-100',
39
39
  },
40
40
  done: {
41
41
  icon: 'i-lucide-check-circle-2',
42
- iconClass: 'text-emerald-400',
43
- labelClass: 'text-slate-400',
42
+ iconClass: 'text-app-success-400',
43
+ labelClass: 'text-muted',
44
44
  },
45
45
  failed: {
46
46
  icon: 'i-lucide-alert-triangle',
47
- iconClass: 'text-rose-400',
48
- labelClass: 'text-rose-200',
47
+ iconClass: 'text-app-error-400',
48
+ labelClass: 'text-app-error-200',
49
49
  },
50
50
  stopped: {
51
51
  icon: 'i-lucide-circle-stop',
52
- iconClass: 'text-slate-400',
53
- labelClass: 'text-slate-300',
52
+ iconClass: 'text-muted',
53
+ labelClass: 'text-toned',
54
54
  },
55
55
  unknown: {
56
56
  icon: 'i-lucide-help-circle',
57
- iconClass: 'text-slate-400',
58
- labelClass: 'text-slate-400',
57
+ iconClass: 'text-muted',
58
+ labelClass: 'text-muted',
59
59
  },
60
60
  }