@cat-factory/app 0.302.1 → 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 +10 -9
@@ -58,7 +58,7 @@ const statusColor = (row: TutorialCatalogueRow) =>
58
58
  >
59
59
  <template #body>
60
60
  <div class="space-y-4" data-testid="tutorial-catalogue">
61
- <p v-if="rows.length > 0" class="text-xs text-slate-400" data-testid="tutorial-progress">
61
+ <p v-if="rows.length > 0" class="text-xs text-muted" data-testid="tutorial-progress">
62
62
  {{
63
63
  t('tutorial.catalogue.progress', {
64
64
  completed: progress.completed,
@@ -70,17 +70,17 @@ const statusColor = (row: TutorialCatalogueRow) =>
70
70
  <li
71
71
  v-for="row in rows"
72
72
  :key="row.tour.id"
73
- class="flex items-start gap-3 rounded-lg border border-slate-800 bg-slate-900/60 p-3"
73
+ class="flex items-start gap-3 rounded-lg border border-default bg-default/60 p-3"
74
74
  :class="row.startable ? '' : 'opacity-75'"
75
75
  :data-testid="`tutorial-catalogue-entry-${row.tour.id}`"
76
76
  >
77
77
  <UIcon
78
78
  :name="row.tour.icon ?? 'i-lucide-compass'"
79
- class="mt-0.5 h-5 w-5 shrink-0 text-primary-400"
79
+ class="mt-0.5 h-5 w-5 shrink-0 text-primary"
80
80
  />
81
81
  <div class="min-w-0 flex-1 space-y-1">
82
82
  <div class="flex flex-wrap items-center gap-2">
83
- <span class="text-sm font-medium text-slate-100">{{ t(row.tour.titleKey) }}</span>
83
+ <span class="text-sm font-medium text-app-100">{{ t(row.tour.titleKey) }}</span>
84
84
  <UBadge
85
85
  v-if="showsStatus(row)"
86
86
  :color="statusColor(row)"
@@ -91,15 +91,15 @@ const statusColor = (row: TutorialCatalogueRow) =>
91
91
  {{ t(TUTORIAL_STATUS_KEYS[row.state]) }}
92
92
  </UBadge>
93
93
  </div>
94
- <p class="text-xs text-slate-400">{{ t(row.tour.descriptionKey) }}</p>
95
- <p v-if="row.stepCount !== null" class="text-xs text-slate-500">
94
+ <p class="text-xs text-muted">{{ t(row.tour.descriptionKey) }}</p>
95
+ <p v-if="row.stepCount !== null" class="text-xs text-dimmed">
96
96
  {{ t('tutorial.catalogue.steps', { count: row.stepCount }, row.stepCount) }}
97
97
  </p>
98
98
  <!-- A held-back tour says what would unlock it, rather than vanishing from the
99
99
  list: these are things the reader can go and do. -->
100
100
  <div
101
101
  v-else-if="row.availability === 'blocked'"
102
- class="text-xs text-slate-500"
102
+ class="text-xs text-dimmed"
103
103
  :data-testid="`tutorial-catalogue-requirements-${row.tour.id}`"
104
104
  >
105
105
  <span>{{ t('tutorial.catalogue.blocked') }}</span>
@@ -112,7 +112,7 @@ const statusColor = (row: TutorialCatalogueRow) =>
112
112
  </div>
113
113
  <!-- Requirements met, but every step is about a branch this board isn't on:
114
114
  nothing to go and fix, so it must not read like the case above. -->
115
- <p v-else class="text-xs text-slate-500">
115
+ <p v-else class="text-xs text-dimmed">
116
116
  {{ t('tutorial.catalogue.notApplicable') }}
117
117
  </p>
118
118
  </div>
@@ -130,7 +130,7 @@ const statusColor = (row: TutorialCatalogueRow) =>
130
130
  </ul>
131
131
  <!-- No tours at all is a real state (a deployment may register none of its own and
132
132
  strip the built-ins), and it is not the same as one whose tours are all blocked. -->
133
- <p v-if="rows.length === 0" class="text-sm text-slate-400">
133
+ <p v-if="rows.length === 0" class="text-sm text-muted">
134
134
  {{ t('tutorial.catalogue.empty') }}
135
135
  </p>
136
136
  </div>
@@ -64,7 +64,7 @@ function take(tourId: string) {
64
64
  this far, which is acceptable in a way the standing overlap was not: it clears itself. -->
65
65
  <div
66
66
  v-if="offered && !suppressed"
67
- class="fixed end-4 bottom-20 z-50 w-80 max-w-[calc(100vw-32px)] rounded-xl border border-slate-700 bg-slate-900/95 p-3 shadow-2xl backdrop-blur"
67
+ class="fixed end-4 bottom-20 z-50 w-80 max-w-[calc(100vw-32px)] rounded-xl border border-muted bg-default/95 p-3 shadow-2xl backdrop-blur"
68
68
  role="status"
69
69
  aria-live="polite"
70
70
  data-testid="tutorial-nudge"
@@ -72,14 +72,14 @@ function take(tourId: string) {
72
72
  <div class="flex items-start gap-2">
73
73
  <UIcon
74
74
  :name="offered.icon ?? 'i-lucide-graduation-cap'"
75
- class="text-primary-300 mt-0.5 h-4 w-4 shrink-0"
75
+ class="text-primary mt-0.5 h-4 w-4 shrink-0"
76
76
  />
77
77
  <div class="min-w-0 flex-1">
78
- <p class="text-[11px] tracking-wide text-slate-400 uppercase">
78
+ <p class="text-[11px] tracking-wide text-muted uppercase">
79
79
  {{ t('tutorial.nudge.label') }}
80
80
  </p>
81
- <p class="mt-0.5 text-sm font-medium text-slate-100">{{ t(offered.titleKey) }}</p>
82
- <p class="mt-0.5 text-xs text-slate-400">{{ t(offered.descriptionKey) }}</p>
81
+ <p class="mt-0.5 text-sm font-medium text-app-100">{{ t(offered.titleKey) }}</p>
82
+ <p class="mt-0.5 text-xs text-muted">{{ t(offered.descriptionKey) }}</p>
83
83
  </div>
84
84
  <UButton
85
85
  size="xs"
@@ -541,7 +541,7 @@ onUnmounted(() => {
541
541
  which is exactly the involuntary movement `prefers-reduced-motion` is about. -->
542
542
  <div
543
543
  v-if="targetRect"
544
- class="ring-primary-400 outline-primary-400/25 pointer-events-none fixed z-[70] rounded-lg outline-4 ring-2 motion-safe:transition-all motion-safe:duration-150"
544
+ class="ring-primary outline-primary/25 pointer-events-none fixed z-[70] rounded-lg outline-4 ring-2 motion-safe:transition-all motion-safe:duration-150"
545
545
  :style="{
546
546
  top: `${targetRect.top - 4}px`,
547
547
  left: `${targetRect.left - 4}px`,
@@ -569,27 +569,27 @@ onUnmounted(() => {
569
569
  role="dialog"
570
570
  :tabindex="cardFocusable ? -1 : undefined"
571
571
  :aria-label="t('tutorial.overlay.ariaLabel')"
572
- class="pointer-events-auto fixed z-[70] w-80 max-w-[calc(100vw-16px)] rounded-xl border border-slate-700 bg-slate-900 p-4 shadow-2xl outline-none focus-visible:ring-2 focus-visible:ring-primary-400"
572
+ class="pointer-events-auto fixed z-[70] w-80 max-w-[calc(100vw-16px)] rounded-xl border border-muted bg-default p-4 shadow-2xl outline-none focus-visible:ring-2 focus-visible:ring-primary"
573
573
  :style="{ top: `${layout.top}px`, left: `${layout.left}px` }"
574
574
  data-testid="tutorial-tooltip"
575
575
  @pointerdown.stop
576
576
  @focusout="onCardFocusOut"
577
577
  >
578
578
  <div class="mb-1 flex items-start justify-between gap-3">
579
- <h3 class="text-sm font-semibold text-slate-100">{{ t(step.titleKey) }}</h3>
580
- <span class="shrink-0 text-xs text-slate-500">
579
+ <h3 class="text-sm font-semibold text-app-100">{{ t(step.titleKey) }}</h3>
580
+ <span class="shrink-0 text-xs text-dimmed">
581
581
  {{ t('tutorial.overlay.progress', { current: tutorial.stepIndex + 1, total }) }}
582
582
  </span>
583
583
  </div>
584
584
  <!-- `bodyParams` carries the fixed proper nouns a step names (a repository slug),
585
585
  which live in the catalog's `{named}` placeholders rather than in nine
586
586
  translations of the same literal. Absent for most steps. -->
587
- <p class="text-sm text-slate-300">
587
+ <p class="text-sm text-toned">
588
588
  {{ t(step.bodyKey, step.bodyParams ?? {}) }}
589
589
  </p>
590
590
  <p
591
591
  v-if="searching"
592
- class="mt-2 flex items-center gap-1.5 text-xs text-slate-400"
592
+ class="mt-2 flex items-center gap-1.5 text-xs text-muted"
593
593
  data-testid="tutorial-searching"
594
594
  >
595
595
  <UIcon name="i-lucide-loader" class="h-3.5 w-3.5 motion-safe:animate-spin" />
@@ -597,7 +597,7 @@ onUnmounted(() => {
597
597
  </p>
598
598
  <p
599
599
  v-else-if="step.advanceOn === 'target-click'"
600
- class="text-primary-300 mt-2 text-xs"
600
+ class="text-primary mt-2 text-xs"
601
601
  data-testid="tutorial-click-hint"
602
602
  >
603
603
  {{ t('tutorial.overlay.clickHint') }}
@@ -606,7 +606,7 @@ onUnmounted(() => {
606
606
  whole tour: say so rather than congratulating the user either way. -->
607
607
  <p
608
608
  v-if="isLast && abridged"
609
- class="mt-2 text-xs text-amber-300/90"
609
+ class="mt-2 text-xs text-app-warning-300/90"
610
610
  data-testid="tutorial-abridged"
611
611
  >
612
612
  {{
@@ -625,14 +625,14 @@ onUnmounted(() => {
625
625
  nothing ready, where the plain Done below is the honest ending. -->
626
626
  <div
627
627
  v-if="isLast && nextTour"
628
- class="mt-3 rounded-lg border border-slate-700/70 bg-slate-800/40 p-2.5"
628
+ class="mt-3 rounded-lg border border-muted/70 bg-elevated/40 p-2.5"
629
629
  data-testid="tutorial-next-tour"
630
630
  >
631
- <p class="text-[11px] tracking-wide text-slate-400 uppercase">
631
+ <p class="text-[11px] tracking-wide text-muted uppercase">
632
632
  {{ t('tutorial.overlay.nextUp') }}
633
633
  </p>
634
- <p class="mt-0.5 text-sm font-medium text-slate-100">{{ t(nextTour.titleKey) }}</p>
635
- <p class="mt-0.5 text-xs text-slate-400">{{ t(nextTour.descriptionKey) }}</p>
634
+ <p class="mt-0.5 text-sm font-medium text-app-100">{{ t(nextTour.titleKey) }}</p>
635
+ <p class="mt-0.5 text-xs text-muted">{{ t(nextTour.descriptionKey) }}</p>
636
636
  <UButton
637
637
  size="xs"
638
638
  color="primary"
@@ -40,20 +40,17 @@ const undecided = computed(() => tutorial.decision === null)
40
40
  decline/close buttons are in a SIBLING slot — address them from the page by
41
41
  their own test ids rather than scoping a locator under this one. -->
42
42
  <div class="space-y-4" data-testid="tutorial-prompt">
43
- <p class="text-sm text-slate-300">{{ t('tutorial.prompt.intro') }}</p>
43
+ <p class="text-sm text-toned">{{ t('tutorial.prompt.intro') }}</p>
44
44
  <ul class="space-y-2">
45
45
  <li
46
46
  v-for="tour in offered"
47
47
  :key="tour.id"
48
- class="flex items-center gap-3 rounded-lg border border-slate-800 bg-slate-900/60 p-3"
48
+ class="flex items-center gap-3 rounded-lg border border-default bg-default/60 p-3"
49
49
  >
50
- <UIcon
51
- :name="tour.icon ?? 'i-lucide-compass'"
52
- class="h-5 w-5 shrink-0 text-primary-400"
53
- />
50
+ <UIcon :name="tour.icon ?? 'i-lucide-compass'" class="h-5 w-5 shrink-0 text-primary" />
54
51
  <div class="min-w-0 flex-1">
55
52
  <div class="flex items-center gap-2">
56
- <span class="text-sm font-medium text-slate-100">{{ t(tour.titleKey) }}</span>
53
+ <span class="text-sm font-medium text-app-100">{{ t(tour.titleKey) }}</span>
57
54
  <UBadge
58
55
  v-if="tutorial.isCompleted(tour.id)"
59
56
  color="success"
@@ -64,7 +61,7 @@ const undecided = computed(() => tutorial.decision === null)
64
61
  {{ t('tutorial.status.completed') }}
65
62
  </UBadge>
66
63
  </div>
67
- <p class="text-xs text-slate-400">{{ t(tour.descriptionKey) }}</p>
64
+ <p class="text-xs text-muted">{{ t(tour.descriptionKey) }}</p>
68
65
  </div>
69
66
  <UButton
70
67
  size="sm"
@@ -85,7 +82,7 @@ const undecided = computed(() => tutorial.decision === null)
85
82
  alone, where `board-basics` requires nothing at all — but a consumer's own slot
86
83
  filter can produce it, and "no tours exist" would be the wrong thing to say then.)
87
84
  The footer's browse button is the way on, so it stays. -->
88
- <p v-if="offered.length === 0" class="text-sm text-slate-400">
85
+ <p v-if="offered.length === 0" class="text-sm text-muted">
89
86
  {{ t('tutorial.prompt.empty') }}
90
87
  </p>
91
88
  </div>
@@ -54,7 +54,7 @@ async function connect() {
54
54
 
55
55
  <template>
56
56
  <section class="space-y-3">
57
- <p class="text-sm text-slate-400">{{ t('vcs.connect.gitlab.intro') }}</p>
57
+ <p class="text-sm text-muted">{{ t('vcs.connect.gitlab.intro') }}</p>
58
58
 
59
59
  <UFormField :label="t('vcs.connect.gitlab.tokenLabel')" :hint="t('vcs.connect.gitlab.scope')">
60
60
  <SecretInput
@@ -67,13 +67,13 @@ async function connect() {
67
67
  />
68
68
  </UFormField>
69
69
 
70
- <p class="text-[11px] text-slate-500">
71
- <ULink :to="tokenUrl" target="_blank" class="text-indigo-400 hover:underline">
70
+ <p class="text-[11px] text-dimmed">
71
+ <ULink :to="tokenUrl" target="_blank" class="text-primary hover:underline">
72
72
  {{ t('vcs.connect.gitlab.createToken') }}
73
73
  </ULink>
74
74
  </p>
75
75
 
76
- <p v-if="error" class="text-sm text-rose-400" data-testid="gitlab-connect-error">
76
+ <p v-if="error" class="text-sm text-app-error-400" data-testid="gitlab-connect-error">
77
77
  {{ error }}
78
78
  </p>
79
79
 
@@ -37,7 +37,7 @@ const nothingConfigured = computed(() => !github.canConnectGitHubApp && !github.
37
37
  <template>
38
38
  <div class="space-y-3">
39
39
  <template v-if="github.canConnectGitHubApp">
40
- <p v-if="props.appIntro" class="text-sm text-slate-400">{{ props.appIntro }}</p>
40
+ <p v-if="props.appIntro" class="text-sm text-muted">{{ props.appIntro }}</p>
41
41
  <GitHubConnect />
42
42
  </template>
43
43
 
@@ -50,7 +50,7 @@ const nothingConfigured = computed(() => !github.canConnectGitHubApp && !github.
50
50
 
51
51
  <p
52
52
  v-if="nothingConfigured"
53
- class="rounded-md border border-dashed border-slate-800 px-3 py-3 text-sm text-slate-400"
53
+ class="rounded-md border border-dashed border-default px-3 py-3 text-sm text-muted"
54
54
  >
55
55
  {{ t('vcs.connect.noneConfigured') }}
56
56
  </p>
@@ -224,7 +224,7 @@ async function onFilePicked(e: Event) {
224
224
  <ResultWindowShell
225
225
  :open="open"
226
226
  icon="i-lucide-image-play"
227
- icon-class="bg-amber-500/15 text-amber-300"
227
+ icon-class="bg-app-warning-500/15 text-app-warning-300"
228
228
  :title="headerTitle"
229
229
  :subtitle="phase ? PHASE_LABEL[phase] : t('visualConfirm.subtitle')"
230
230
  width="5xl"
@@ -233,7 +233,7 @@ async function onFilePicked(e: Event) {
233
233
  <div class="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4">
234
234
  <div
235
235
  v-if="!vc"
236
- class="flex flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
236
+ class="flex flex-col items-center justify-center gap-2 py-10 text-center text-muted"
237
237
  >
238
238
  <UIcon name="i-lucide-image-play" class="h-8 w-8 opacity-40" />
239
239
  <p class="text-sm">{{ t('visualConfirm.notStarted') }}</p>
@@ -242,7 +242,7 @@ async function onFilePicked(e: Event) {
242
242
  <template v-else>
243
243
  <p
244
244
  v-if="vc.degradedReason"
245
- class="rounded-lg border border-amber-700/40 bg-amber-500/5 px-3 py-2 text-[12px] text-amber-300/90"
245
+ class="rounded-lg border border-app-warning-700/40 bg-app-warning-500/5 px-3 py-2 text-[12px] text-app-warning-300/90"
246
246
  >
247
247
  {{ vc.degradedReason }}
248
248
  </p>
@@ -251,21 +251,24 @@ async function onFilePicked(e: Event) {
251
251
  reference the reviewer is judging against is never anonymous. -->
252
252
  <section
253
253
  v-if="design"
254
- class="rounded-lg border border-slate-800 bg-slate-900/60 px-3 py-2 text-[12px] text-slate-300"
254
+ class="rounded-lg border border-default bg-default/60 px-3 py-2 text-[12px] text-toned"
255
255
  >
256
256
  <p class="flex items-center gap-1.5">
257
- <UIcon name="i-lucide-figma" class="h-3.5 w-3.5 shrink-0 text-amber-300" />
257
+ <UIcon name="i-lucide-figma" class="h-3.5 w-3.5 shrink-0 text-app-warning-300" />
258
258
  <span>{{
259
259
  t('visualConfirm.design.summary', { count: design.images }, design.images)
260
260
  }}</span>
261
- <span v-if="design.dropped" class="text-slate-500">
261
+ <span v-if="design.dropped" class="text-dimmed">
262
262
  {{ t('visualConfirm.design.dropped', { count: design.dropped }, design.dropped) }}
263
263
  </span>
264
264
  </p>
265
265
  <!-- One line per short design, carrying both ways it can fall short: what its source
266
266
  kept, and what this gallery's shared ceiling cut from it. A design the ceiling shut
267
267
  out entirely reads as one with no frames unless it is named here. -->
268
- <ul v-if="design.gaps?.length" class="mt-1.5 space-y-1 text-[11px] text-amber-300/90">
268
+ <ul
269
+ v-if="design.gaps?.length"
270
+ class="mt-1.5 space-y-1 text-[11px] text-app-warning-300/90"
271
+ >
269
272
  <li v-for="gap in design.gaps" :key="`${gap.title}-${gap.reason ?? 'capped'}`">
270
273
  {{ t('visualConfirm.design.gapLine', { title: gap.title }) }}
271
274
  <template v-if="gap.reason">{{ DESIGN_GAP_LABELS[gap.reason] }}</template>
@@ -278,9 +281,9 @@ async function onFilePicked(e: Event) {
278
281
 
279
282
  <p
280
283
  v-if="working"
281
- class="flex items-center gap-2 rounded-lg border border-slate-800 bg-slate-950/40 px-3 py-2 text-[12px] text-slate-300"
284
+ class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-[12px] text-toned"
282
285
  >
283
- <UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-amber-300" />
286
+ <UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-app-warning-300" />
284
287
  {{ phase ? PHASE_LABEL[phase] : '' }}
285
288
  </p>
286
289
 
@@ -301,7 +304,7 @@ async function onFilePicked(e: Event) {
301
304
  <!-- Per-view note (folded into the fixer findings) -->
302
305
  <div v-if="awaitingHuman" class="px-1">
303
306
  <button
304
- class="flex items-center gap-1.5 text-[11px] text-slate-400 hover:text-slate-200"
307
+ class="flex items-center gap-1.5 text-[11px] text-muted hover:text-default"
305
308
  @click="noteOpen[p.view] = !noteOpen[p.view]"
306
309
  >
307
310
  <UIcon
@@ -311,7 +314,7 @@ async function onFilePicked(e: Event) {
311
314
  {{ t('visualConfirm.noteIssue', { view: p.view }) }}
312
315
  <span
313
316
  v-if="perViewNotes[p.view]?.trim()"
314
- class="rounded-full bg-amber-500/15 px-1.5 text-[9px] text-amber-300"
317
+ class="rounded-full bg-app-warning-500/15 px-1.5 text-[9px] text-app-warning-300"
315
318
  >{{ t('visualConfirm.noted') }}</span
316
319
  >
317
320
  </button>
@@ -320,18 +323,18 @@ async function onFilePicked(e: Event) {
320
323
  v-model="perViewNotes[p.view]"
321
324
  rows="2"
322
325
  :placeholder="t('visualConfirm.notePlaceholder', { view: p.view })"
323
- class="mt-1 w-full rounded-md border border-slate-700 bg-slate-950 px-2 py-1.5 text-[12px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/60"
326
+ class="mt-1 w-full rounded-md border border-muted bg-app-950 px-2 py-1.5 text-[12px] text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
324
327
  />
325
328
  </div>
326
329
  </div>
327
330
  </section>
328
- <p v-else class="text-[12px] italic text-slate-500">
331
+ <p v-else class="text-[12px] italic text-dimmed">
329
332
  {{ t('visualConfirm.noScreenshots') }}
330
333
  </p>
331
334
 
332
335
  <!-- Upload a reference for any view -->
333
- <section class="rounded-lg border border-slate-800 bg-slate-900/60 p-3">
334
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
336
+ <section class="rounded-lg border border-default bg-default/60 p-3">
337
+ <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
335
338
  {{ t('visualConfirm.upload.heading') }}
336
339
  </h3>
337
340
  <div class="flex flex-wrap items-center gap-2">
@@ -339,7 +342,7 @@ async function onFilePicked(e: Event) {
339
342
  v-model="uploadView"
340
343
  list="vc-views"
341
344
  :placeholder="t('visualConfirm.upload.viewPlaceholder')"
342
- class="rounded-md border border-slate-700 bg-slate-950 px-2 py-1 text-[12px] text-slate-200 placeholder:text-slate-600"
345
+ class="rounded-md border border-muted bg-app-950 px-2 py-1 text-[12px] text-default placeholder:text-app-600"
343
346
  />
344
347
  <datalist id="vc-views">
345
348
  <option v-for="p in pairs" :key="p.view" :value="p.view" />
@@ -349,11 +352,11 @@ async function onFilePicked(e: Event) {
349
352
  type="file"
350
353
  accept="image/png,image/jpeg"
351
354
  :disabled="busy || !uploadView.trim()"
352
- class="text-[12px] text-slate-300 file:me-2 file:rounded file:border-0 file:bg-slate-800 file:px-2 file:py-1 file:text-slate-200 disabled:opacity-40"
355
+ class="text-[12px] text-toned file:me-2 file:rounded file:border-0 file:bg-elevated file:px-2 file:py-1 file:text-default disabled:opacity-40"
353
356
  @change="onFilePicked"
354
357
  />
355
358
  </div>
356
- <p class="mt-1.5 text-[10px] text-slate-600">
359
+ <p class="mt-1.5 text-[10px] text-app-600">
357
360
  {{
358
361
  uploadView.trim()
359
362
  ? t('visualConfirm.upload.tipReady')
@@ -363,21 +366,18 @@ async function onFilePicked(e: Event) {
363
366
  </section>
364
367
 
365
368
  <!-- Request fix -->
366
- <section
367
- v-if="awaitingHuman"
368
- class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
369
- >
370
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
369
+ <section v-if="awaitingHuman" class="rounded-lg border border-default bg-default/60 p-3">
370
+ <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
371
371
  {{ t('visualConfirm.requestFix.heading') }}
372
372
  </h3>
373
373
  <textarea
374
374
  v-model="globalFindings"
375
375
  rows="3"
376
376
  :placeholder="t('visualConfirm.requestFix.placeholder')"
377
- class="w-full rounded-md border border-slate-700 bg-slate-950 px-3 py-2 text-[13px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/60"
377
+ class="w-full rounded-md border border-muted bg-app-950 px-3 py-2 text-[13px] text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
378
378
  />
379
379
  <div class="mt-2 flex items-center justify-between">
380
- <span class="text-[11px] text-slate-500">
380
+ <span class="text-[11px] text-dimmed">
381
381
  {{ t('visualConfirm.requestFix.foldedHint') }}
382
382
  </span>
383
383
  <UButton
@@ -397,31 +397,31 @@ async function onFilePicked(e: Event) {
397
397
  <!-- Rounds history -->
398
398
  <section
399
399
  v-if="vc.rounds && vc.rounds.length"
400
- class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
400
+ class="rounded-lg border border-default bg-default/60 p-3"
401
401
  >
402
- <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
402
+ <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
403
403
  {{ t('visualConfirm.history.heading', { count: vc.attempts }, vc.attempts) }}
404
404
  </h3>
405
405
  <ol class="space-y-2">
406
406
  <li v-for="(r, i) in vc.rounds" :key="i" class="flex items-start gap-2 text-[12px]">
407
- <UIcon name="i-lucide-wrench" class="mt-0.5 h-3.5 w-3.5 shrink-0 text-slate-400" />
407
+ <UIcon name="i-lucide-wrench" class="mt-0.5 h-3.5 w-3.5 shrink-0 text-muted" />
408
408
  <div class="min-w-0 flex-1">
409
- <span class="text-slate-200">{{ t('visualConfirm.history.fixRequested') }}</span>
409
+ <span class="text-default">{{ t('visualConfirm.history.fixRequested') }}</span>
410
410
  <span
411
411
  class="ms-1.5 rounded px-1 text-[10px] uppercase"
412
412
  :class="
413
413
  r.outcome === 'completed'
414
- ? 'bg-emerald-500/15 text-emerald-300'
414
+ ? 'bg-app-success-500/15 text-app-success-300'
415
415
  : r.outcome === 'failed'
416
- ? 'bg-rose-500/15 text-rose-300'
417
- : 'bg-slate-500/15 text-slate-300'
416
+ ? 'bg-app-error-500/15 text-app-error-300'
417
+ : 'bg-app-500/15 text-toned'
418
418
  "
419
419
  >
420
420
  {{
421
421
  r.outcome ? OUTCOME_LABELS[r.outcome] : t('visualConfirm.outcome.inProgress')
422
422
  }}
423
423
  </span>
424
- <p v-if="r.findings" class="whitespace-pre-wrap leading-snug text-slate-400">
424
+ <p v-if="r.findings" class="whitespace-pre-wrap leading-snug text-muted">
425
425
  {{ r.findings }}
426
426
  </p>
427
427
  </div>
@@ -433,7 +433,7 @@ async function onFilePicked(e: Event) {
433
433
 
434
434
  <footer
435
435
  v-if="vc"
436
- class="flex items-center justify-between gap-3 border-t border-slate-800 px-5 py-3"
436
+ class="flex items-center justify-between gap-3 border-t border-default px-5 py-3"
437
437
  >
438
438
  <StepRunMeta
439
439
  v-if="step"
@@ -447,9 +447,9 @@ async function onFilePicked(e: Event) {
447
447
  <div class="flex items-center gap-2">
448
448
  <label
449
449
  v-if="awaitingHuman && needsAck"
450
- class="flex items-center gap-1.5 text-[11px] text-amber-300/90"
450
+ class="flex items-center gap-1.5 text-[11px] text-app-warning-300/90"
451
451
  >
452
- <input v-model="ackDegraded" type="checkbox" class="accent-amber-500" />
452
+ <input v-model="ackDegraded" type="checkbox" class="accent-app-warning-500" />
453
453
  {{ t('visualConfirm.reviewedManually') }}
454
454
  </label>
455
455
  <UButton
@@ -118,7 +118,12 @@ export async function sendContract<T extends ApiContract>(
118
118
  const { statusCode, body } = error as { statusCode: number; body: unknown }
119
119
  throw new ApiError(statusCode, body)
120
120
  }
121
- return outcome.result!.body as SuccessBodyOf<T>
121
+ // The success arm is read through the same widening as the error arm above. Since
122
+ // `@toad-contracts/core@1`, a success entry expands to one union member per declared media
123
+ // type, so on an unresolved `T` the member type stays a deferred conditional and even reading
124
+ // `.body` off it is unprovable, not just assigning it to `SuccessBodyOf<T>`.
125
+ const { body } = outcome.result! as { body: unknown }
126
+ return body as SuccessBodyOf<T>
122
127
  }
123
128
 
124
129
  /** Curry {@link sendContract} over a client into the throw-on-error {@link ApiSend}. */
@@ -157,6 +157,20 @@ const CONFLICT_INFO: Record<Exclude<ConflictReason, BespokeConflictReason>, Conf
157
157
  titleKey: 'errors.conflict.title.no_host_frame',
158
158
  descriptionKey: 'errors.conflict.description.no_host_frame',
159
159
  },
160
+ delegated_executor_unwired: {
161
+ titleKey: 'errors.conflict.title.delegated_executor_unwired',
162
+ descriptionKey: 'errors.conflict.description.delegated_executor_unwired',
163
+ },
164
+ // The two conditions that are NOT about a missing registration. They had borrowed the reason
165
+ // above, whose copy sends an operator to register an executor that is registered and fine.
166
+ delegated_step_async_only: {
167
+ titleKey: 'errors.conflict.title.delegated_step_async_only',
168
+ descriptionKey: 'errors.conflict.description.delegated_step_async_only',
169
+ },
170
+ delegated_claim_missing: {
171
+ titleKey: 'errors.conflict.title.delegated_claim_missing',
172
+ descriptionKey: 'errors.conflict.description.delegated_claim_missing',
173
+ },
160
174
  task_limit_reached: {
161
175
  titleKey: 'errors.conflict.title.task_limit_reached',
162
176
  descriptionKey: 'errors.conflict.description.task_limit_reached',
@@ -448,6 +462,9 @@ const REASON_DESCRIPTION_KEYS: Record<UnavailableReason | BootstrapReferenceReas
448
462
  'errors.unavailable.description.service_catalog_response_too_large',
449
463
  assistant_generation_failed: 'errors.unavailable.description.assistant_generation_failed',
450
464
  assistant_reply_unreadable: 'errors.unavailable.description.assistant_reply_unreadable',
465
+ delegated_executor_failed: 'errors.unavailable.description.delegated_executor_failed',
466
+ delegated_work_branch_unprepared:
467
+ 'errors.unavailable.description.delegated_work_branch_unprepared',
451
468
  }
452
469
 
453
470
  /**
@@ -46,5 +46,29 @@ export function customKindToArchetype(kind: CustomAgentKind): AgentArchetype {
46
46
  // the snapshot, and a required step option cannot be gated on something the read model
47
47
  // does not carry.
48
48
  ...(kind.binaryOutput ? { binaryOutput: true } : {}),
49
+ // WHERE the kind's work runs, when it leaves the platform. Lifted from the entry rather than
50
+ // from `presentation` for the reason `binaryOutput` is: it is a fact about how the kind RUNS.
51
+ //
52
+ // A `delegated` kind with no executor resolved still carries its ID, so the card can name what
53
+ // this build cannot: an executor a deployment stopped registering is a step nobody can run,
54
+ // and rendering it as an ordinary one is how that goes unnoticed until a run refuses.
55
+ //
56
+ // Carried only when the entry actually has one. A synthesised `{ id: '' }` was worse than no
57
+ // entry at all: the empty string is not nullish, so it satisfied every `??` fallback downstream
58
+ // and the card rendered a blank name in precisely the case this branch exists to handle.
59
+ ...(kind.executor === 'delegated' && kind.delegatedExecutor
60
+ ? {
61
+ delegatedExecutor: {
62
+ id: kind.delegatedExecutor.id,
63
+ ...(kind.delegatedExecutor.label ? { label: kind.delegatedExecutor.label } : {}),
64
+ ...(kind.delegatedExecutor.description
65
+ ? { description: kind.delegatedExecutor.description }
66
+ : {}),
67
+ ...(kind.delegatedExecutor.telemetry
68
+ ? { telemetry: kind.delegatedExecutor.telemetry }
69
+ : {}),
70
+ },
71
+ }
72
+ : {}),
49
73
  }
50
74
  }
@@ -425,7 +425,7 @@ watch(
425
425
  <!-- A COLUMN, so the translation strip below takes its own height instead of covering the
426
426
  row: page chrome that overlays the app is how the board's top controls came to be
427
427
  buried. Everything else lives in the row beneath it. -->
428
- <div class="flex h-screen w-screen flex-col overflow-hidden bg-slate-950 text-slate-100">
428
+ <div class="flex h-screen w-screen flex-col overflow-hidden bg-app-950 text-app-100">
429
429
  <!-- Non-English locale warning (unofficial translation); slim full-width strip above
430
430
  everything, in flow. -->
431
431
  <TranslationWarningBanner />
@@ -433,7 +433,7 @@ watch(
433
433
  <!-- Resolving whether the GitHub App is installed, before we decide what to show. -->
434
434
  <div
435
435
  v-if="workspace.ready && githubProbePending"
436
- class="m-auto flex flex-col items-center gap-3 text-slate-400"
436
+ class="m-auto flex flex-col items-center gap-3 text-muted"
437
437
  >
438
438
  <UIcon name="i-lucide-loader" class="h-8 w-8 animate-spin" />
439
439
  <span class="text-sm">{{ $t('app.loading') }}</span>
@@ -553,16 +553,16 @@ watch(
553
553
 
554
554
  <!-- Backend unreachable / bootstrap failed -->
555
555
  <div v-else-if="workspace.error" class="m-auto max-w-md p-8 text-center">
556
- <UIcon name="i-lucide-plug-zap" class="mx-auto mb-3 h-10 w-10 text-amber-400" />
556
+ <UIcon name="i-lucide-plug-zap" class="mx-auto mb-3 h-10 w-10 text-app-warning-400" />
557
557
  <h1 class="mb-1 text-lg font-semibold">{{ $t('app.backendUnreachable') }}</h1>
558
- <p class="mb-4 text-sm text-slate-400">{{ workspace.error }}</p>
558
+ <p class="mb-4 text-sm text-muted">{{ workspace.error }}</p>
559
559
  <UButton color="primary" icon="i-lucide-rotate-ccw" @click="workspace.init()">
560
560
  {{ $t('common.retry') }}
561
561
  </UButton>
562
562
  </div>
563
563
 
564
564
  <!-- Initial load -->
565
- <div v-else class="m-auto flex flex-col items-center gap-3 text-slate-400">
565
+ <div v-else class="m-auto flex flex-col items-center gap-3 text-muted">
566
566
  <UIcon name="i-lucide-loader" class="h-8 w-8 animate-spin" />
567
567
  <span class="text-sm">{{ $t('app.loadingBoard') }}</span>
568
568
  </div>