@cat-factory/app 0.310.1 → 0.310.3

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 (145) hide show
  1. package/README.md +23 -0
  2. package/app/app.config.ts +11 -0
  3. package/app/components/assistant/AssistantModal.vue +0 -1
  4. package/app/components/auth/BackendMisconfiguredScreen.vue +1 -1
  5. package/app/components/auth/LoginScreen.vue +2 -18
  6. package/app/components/auth/ResetPasswordScreen.vue +2 -3
  7. package/app/components/board/AddTaskModal.vue +0 -1
  8. package/app/components/board/BoardCanvas.vue +0 -2
  9. package/app/components/board/CreateInitiativeModal.vue +0 -1
  10. package/app/components/board/RecurringPipelineModal.vue +1 -7
  11. package/app/components/board/ReviewFrictionDialog.vue +1 -7
  12. package/app/components/board/nodes/InitiativeCard.vue +0 -4
  13. package/app/components/board/nodes/TaskCard.vue +1 -8
  14. package/app/components/bootstrap/AdoptionReviewModal.vue +0 -1
  15. package/app/components/bootstrap/BootstrapModal.vue +1 -7
  16. package/app/components/brainstorm/BrainstormWindow.vue +0 -5
  17. package/app/components/bugFishing/BugFishingWindow.vue +0 -2
  18. package/app/components/clarity/ClarityReviewWindow.vue +0 -4
  19. package/app/components/common/AsyncViewError.vue +1 -1
  20. package/app/components/common/ClarificationItem.vue +0 -2
  21. package/app/components/context/ContextAttachmentFields.vue +1 -1
  22. package/app/components/docs/DocInterviewWindow.vue +0 -1
  23. package/app/components/documents/DocumentImportModal.vue +0 -3
  24. package/app/components/documents/DocumentSourceConnectModal.vue +2 -9
  25. package/app/components/documents/DocumentTemplatesModal.vue +1 -7
  26. package/app/components/documents/SpawnPreviewModal.vue +0 -1
  27. package/app/components/documents/StartFromDesignModal.vue +0 -1
  28. package/app/components/environments/steps/EnvPreflightStep.vue +0 -2
  29. package/app/components/environments/steps/EnvReviewStep.vue +1 -4
  30. package/app/components/environments/steps/EnvSaveStep.vue +0 -1
  31. package/app/components/focus/BlockFocusView.vue +0 -2
  32. package/app/components/followUp/FollowUpWindow.vue +0 -2
  33. package/app/components/forkDecision/ForkDecisionWindow.vue +1 -2
  34. package/app/components/foundational/FoundationalServiceCatalogList.vue +2 -0
  35. package/app/components/foundational/FoundationalServiceRegistry.vue +9 -2
  36. package/app/components/foundational/FoundationalServiceSources.vue +2 -0
  37. package/app/components/foundational/FoundationalSuppressions.vue +1 -0
  38. package/app/components/fragments/FragmentLibraryManager.vue +4 -0
  39. package/app/components/fragments/FragmentSelector.vue +0 -1
  40. package/app/components/gates/GateResultView.vue +0 -1
  41. package/app/components/github/AddServiceFromRepoModal.vue +0 -2
  42. package/app/components/github/GitHubConnect.vue +1 -2
  43. package/app/components/github/GitHubPanel.vue +0 -2
  44. package/app/components/github/RepoTreeBrowser.vue +3 -3
  45. package/app/components/guidedReview/GuidedReviewDraftCard.vue +17 -5
  46. package/app/components/guidedReview/GuidedReviewModal.vue +1 -0
  47. package/app/components/guidedReview/GuidedReviewOverview.vue +1 -0
  48. package/app/components/guidedReview/GuidedReviewPicker.vue +0 -1
  49. package/app/components/guidedReview/GuidedReviewThread.vue +0 -2
  50. package/app/components/humanTest/HumanTestWindow.vue +3 -3
  51. package/app/components/initiative/InitiativePlanDecision.vue +0 -1
  52. package/app/components/initiative/InitiativePlanReview.vue +0 -1
  53. package/app/components/initiative/InitiativePlanningWindow.vue +2 -3
  54. package/app/components/initiative/InitiativeTrackerWindow.vue +1 -1
  55. package/app/components/inputGate/InputGateNotice.vue +0 -1
  56. package/app/components/judge/JudgeResultView.vue +1 -1
  57. package/app/components/layout/AccountDeploymentSettings.vue +13 -43
  58. package/app/components/layout/AccountModelPolicySettings.vue +0 -1
  59. package/app/components/layout/AccountPlatformAlertSettings.vue +0 -1
  60. package/app/components/layout/AccountRunCredentialSettings.vue +0 -1
  61. package/app/components/layout/AccountTeamSettings.vue +4 -4
  62. package/app/components/layout/AiProvidersBanner.vue +1 -7
  63. package/app/components/layout/BoardSwitcher.vue +1 -1
  64. package/app/components/layout/DefaultTestEnvBanner.vue +0 -1
  65. package/app/components/layout/GitHubPatBanner.vue +0 -1
  66. package/app/components/layout/GitHubPatPermissionsBanner.vue +0 -1
  67. package/app/components/layout/InfraSetupBanner.vue +0 -1
  68. package/app/components/layout/IntegrationsHub.vue +0 -2
  69. package/app/components/layout/ModelProvidersHub.vue +1 -6
  70. package/app/components/layout/ProviderConfigBanner.vue +0 -1
  71. package/app/components/layout/SideBar.vue +0 -1
  72. package/app/components/layout/SpendWarningBanner.vue +0 -1
  73. package/app/components/layout/WorkspaceMembersSettings.vue +0 -1
  74. package/app/components/notifications/NotificationSettingsPanel.vue +0 -1
  75. package/app/components/outcome/OutcomeSummaryWindow.vue +0 -3
  76. package/app/components/panels/AgentStepDetail.vue +2 -5
  77. package/app/components/panels/DecisionModal.vue +0 -1
  78. package/app/components/panels/InspectorPanel.vue +1 -1
  79. package/app/components/panels/inspector/ContainerSummary.vue +1 -1
  80. package/app/components/panels/inspector/FrontendConfig.vue +0 -4
  81. package/app/components/panels/inspector/InitiativeInspector.vue +1 -6
  82. package/app/components/panels/inspector/RecurringScheduleSettings.vue +1 -3
  83. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -9
  84. package/app/components/panels/inspector/ServiceSelfTests.vue +0 -2
  85. package/app/components/panels/inspector/ServiceTestConfig.vue +1 -4
  86. package/app/components/panels/inspector/ServiceTestSecrets.vue +1 -2
  87. package/app/components/panels/inspector/ServiceTestingContext.vue +0 -1
  88. package/app/components/panels/inspector/ServiceValidationConfig.vue +1 -2
  89. package/app/components/panels/inspector/TaskExecution.vue +1 -5
  90. package/app/components/panels/inspector/TaskReviewTarget.vue +0 -2
  91. package/app/components/panels/inspector/TaskRunSettings.vue +2 -14
  92. package/app/components/panels/inspector/TaskTypeFields.vue +0 -1
  93. package/app/components/pipeline/AgentPromptEditor.vue +0 -2
  94. package/app/components/pipeline/IterationCapPrompt.vue +0 -1
  95. package/app/components/pipeline/PipelineBuilder.vue +2 -11
  96. package/app/components/pipeline/PipelineHealthModal.vue +2 -6
  97. package/app/components/prReview/PrReviewWindow.vue +0 -4
  98. package/app/components/providers/AiPresetMismatchDialog.vue +1 -1
  99. package/app/components/providers/AiProviderOnboardingModal.vue +2 -8
  100. package/app/components/providers/VendorCredentialsModal.vue +1 -7
  101. package/app/components/provisioning/ProvisioningLogsDrawer.vue +1 -0
  102. package/app/components/requirements/RequirementsReviewWindow.vue +0 -7
  103. package/app/components/sandbox/SandboxPanel.vue +1 -3
  104. package/app/components/settings/BudgetSettings.vue +1 -9
  105. package/app/components/settings/ComposeEnvironmentSetupSection.vue +0 -1
  106. package/app/components/settings/ConsensusGroupsSection.vue +0 -1
  107. package/app/components/settings/CustomManifestTypeEditor.vue +1 -1
  108. package/app/components/settings/DefaultProvisionTypeSection.vue +0 -1
  109. package/app/components/settings/InfraHandlersConfigurator.vue +2 -2
  110. package/app/components/settings/IssueTrackerPanel.vue +1 -8
  111. package/app/components/settings/KubernetesEngineForm.vue +1 -7
  112. package/app/components/settings/KubernetesEnvironmentForm.vue +1 -7
  113. package/app/components/settings/LocalContainerPoolSettings.vue +1 -1
  114. package/app/components/settings/LocalModelEndpointsPanel.vue +0 -1
  115. package/app/components/settings/McpAuthorizeScreen.vue +0 -1
  116. package/app/components/settings/McpOAuthCallbackScreen.vue +1 -1
  117. package/app/components/settings/ModelConfigurationPanel.vue +3 -10
  118. package/app/components/settings/ModelPresetHealthModal.vue +0 -3
  119. package/app/components/settings/OpenRouterCatalogPanel.vue +1 -9
  120. package/app/components/settings/ProviderConnectionTab.vue +2 -7
  121. package/app/components/settings/ProviderManifestEditor.vue +0 -1
  122. package/app/components/settings/RiskPolicyCreateForm.vue +0 -1
  123. package/app/components/settings/RiskPolicyEditorRow.vue +1 -2
  124. package/app/components/settings/RiskPolicyHealthModal.vue +0 -3
  125. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +0 -3
  126. package/app/components/settings/ToolServerChecklist.vue +1 -1
  127. package/app/components/settings/UserSecretsSection.vue +1 -7
  128. package/app/components/settings/WorkspaceMetadataSettings.vue +0 -1
  129. package/app/components/settings/WorkspaceSettingsPanel.vue +1 -7
  130. package/app/components/skills/ReviewSkillQueue.vue +0 -1
  131. package/app/components/skills/SkillLibraryManager.vue +2 -0
  132. package/app/components/slack/SlackPanel.vue +1 -5
  133. package/app/components/spec/ServiceSpecWindow.vue +0 -1
  134. package/app/components/tasks/BugHuntModal.vue +0 -3
  135. package/app/components/tasks/TaskImportModal.vue +0 -2
  136. package/app/components/tasks/TaskSourceConnectModal.vue +2 -9
  137. package/app/components/testing/TestReportWindow.vue +2 -0
  138. package/app/components/tutorial/TutorialCatalogue.vue +0 -1
  139. package/app/components/tutorial/TutorialNudge.vue +0 -1
  140. package/app/components/tutorial/TutorialOverlay.vue +0 -2
  141. package/app/components/tutorial/TutorialPrompt.vue +0 -1
  142. package/app/components/vcs/GitLabConnect.vue +0 -1
  143. package/app/components/visualConfirm/VisualConfirmationWindow.vue +1 -1
  144. package/app/pages/index.vue +1 -1
  145. package/package.json +1 -1
package/README.md CHANGED
@@ -330,6 +330,29 @@ Every CONTROL in the SPA is the Nuxt UI component for it, never the raw HTML ele
330
330
  - **Prose and layout markup is not in scope.** `<p>`, `<h1>`-`<h6>`, `<ul>`, `<li>`, `<dl>`, `<pre>`, `<code>`, `<img>`, `<nav>`, `<div>` and `<span>` are not controls and have no Nuxt UI component; heading typography is the type scale's rule above.
331
331
  - `scripts/check-frontend-primitives.mjs` (CI) holds the count at zero. There is deliberately **no allow-list**: the migration expected two exceptions (a Vue Flow gesture handle, a full-bleed media tile) and both converted cleanly, so the exception table would have had more surface than the thing it governed. A site that genuinely cannot convert says why with a `raw-control-ok:` comment, on that line or the one above; nothing in the tree needs one, and a second one appearing is the signal that the rule needs revisiting rather than the site.
332
332
 
333
+ ### Buttons and badges follow one variant policy
334
+
335
+ What a primary, a secondary or a destructive action looks like is decided once, here, and not per call site. The weights are Nuxt UI's own [variant table](https://github.com/nuxt/ui/blob/v4/skills/nuxt-ui/references/design-system.md) (`solid` highest, then `outline`, `soft`, `subtle`, `ghost`, and `link` for a text link inside content), mapped onto the app's kinds of action:
336
+
337
+ | Kind | Props |
338
+ | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
339
+ | Primary action, ONE per view | none (the default, `primary solid`) |
340
+ | Dismissal in a dialog or form (Cancel, Close) | `color="neutral" variant="ghost"` |
341
+ | Real secondary action (Save draft, Back) | `color="neutral" variant="outline"` |
342
+ | Bulk action in a dialog footer, beside Close (Reseed all, Update all) | `variant="ghost"` (primary colour): the dialog's main action, kept light because each row above it has its own button |
343
+ | Send-back on a gate (bounce, request a fix) | `color="warning" variant="soft"`, so the approve beside it stays the one primary |
344
+ | Inline, navigation and chrome actions | `color="neutral" variant="ghost"`, usually with `size="xs"` |
345
+ | Icon-only action | `IconButton` with `color="neutral" variant="ghost"` |
346
+ | Destructive action, labelled | `color="error" variant="soft"`; `color="error"` alone (solid) only as the confirm step of a destructive action (a dialog's confirm, or an armed inline confirm such as a step's reject) |
347
+ | Destructive action, icon-only (a row's trash icon) | `IconButton` with `color="error" variant="ghost"` |
348
+ | A banner's call to action | solid, in the banner's colour (`color="warning"`, `color="error"`) |
349
+ | Status chip | `UBadge` with `variant="subtle" size="sm"`, and a `BadgeColor` map for the colour |
350
+
351
+ - **The defaults are Nuxt UI's own**: `primary solid md` for both `UButton` and `UBadge`, stated in `app.config.ts` (`ui.button.defaultVariants`, `ui.badge.defaultVariants`) so the value has one visible home and the guard can read it. A call site states only what DIFFERS.
352
+ - **One solid button per view.** Two solid buttons side by side compete for the same attention, so the second action takes a lighter variant from the table. The guard refuses two solid buttons under one parent element; it counts a `v-if` chain as one button and a `v-for` list as one, looks through a plain `<template>` wrapper, and treats a slot `<template #name>` as its own region. A file holding several forms that each end in a Save is several views and passes. A site that genuinely needs two says why with a `solid-ok:` comment on its own line, directly above the second button.
353
+ - **The unstyled group is the one a theme restyles, so it is the primary tier.** A theme document's `style.defaults` (a size, a button variant, a button colour) REPLACES the default for every control that does not state that prop; it does not scale or shift the others. So whichever group is unstyled is the group a theme moves. With the primary actions unstyled, a theme with `size: 'lg'` or `variants: { buttons: 'outline' }` restyles the actions and leaves the chrome alone. With the quiet chrome unstyled, the same theme would grow or fill every toolbar and icon button past the actions and invert the hierarchy. This is why the dense, quiet chrome carries its props and the primary action carries none, and why Nuxt UI's own internal buttons (a modal's close button, a toast's actions) agree with the app's.
354
+ - **A restated default is a bug, not noise.** It pins the value against a theme that changes the default, and it hides which props are the reason a control looks the way it does. `scripts/check-frontend-variants.mjs` (CI) refuses a value equal to the default on `UButton` and `UBadge` (either casing, either quote style, or a key of a `v-bind="{ … }"` object) and on every component that spreads its attributes into one (`IconButton`; the list is derived, so a new wrapper is covered), reading the defaults from `app.config.ts` itself. Inside a `UFieldGroup` or `UButtonGroup` it does not check `size`: the group sets the size there, so an explicit `size` overrides the group rather than restating the default. It reads template tags only, so a `ButtonProps` object (a toast's `actions`, a modal's `:close`) is not checked. A site that must keep a literal says why with a `variant-default-ok:` comment on its own line, directly above the tag; it waives that tag only.
355
+
333
356
  ### Themes and colour mode
334
357
 
335
358
  A **theme** is a Nuxt UI theme editor document (https://ui.nuxt.com/theme): the sparse `ThemeDoc` the editor edits and shares as a `?doc=` link, decoded by `utils/theme/link.ts` and typed in `utils/theme/doc.ts` (a port of the editor engine's tables). The app speaks THAT format rather than one of its own, so a theme built in the editor imports as-is and a built-in theme is a document a user could have exported. `utils/theme/builtins.ts` ships `Cat Factory` (the default; its `colors` MUST equal `app.config.ts`'s `ui.colors`, which paints the first frame) and the editor's own `Mono` preset, referenced from the preset table rather than copied. The `theme` store persists the pick and the user's imported documents in this browser; `plugins/theme.client.ts` applies the active document at runtime: aliases onto `appConfig.ui.colors` (Nuxt UI's own colours plugin re-emits the ramps), default variants and component overrides onto `appConfig.ui.<component>` merged INTO the layer's own overrides, and tokens / radius / fonts onto one `<style id="app-theme">` whose selectors anchor on the `data-theme` attribute on `<html>` (`utils/theme/css.ts`, which is also the write boundary for untrusted values). The faces the built-in themes name (Geist, Geist Mono) are self-hosted through `@nuxt/fonts` (`fonts.families` in `nuxt.config.ts`, pinned by `builtins.spec.ts`); a font an IMPORTED theme names is applied as a family name only, so it renders when the device or the deployment provides it. A editor link whose document equals one of the editor's presets collapses to `{ preset: 'mono' }`; `utils/theme/presets.ts` is a copy of the editor's preset table so such a link rebuilds (a decode table, not menu entries).
package/app/app.config.ts CHANGED
@@ -12,6 +12,17 @@ export default defineAppConfig({
12
12
  error: 'rose',
13
13
  neutral: 'slate',
14
14
  },
15
+ // The default button and badge are Nuxt UI's own, stated so they have one visible home and
16
+ // `scripts/check-frontend-variants.mjs` can refuse a literal equal to them (the variant policy
17
+ // in `frontend/app/README.md`). They must stay the PRIMARY tier: a theme's `style.defaults`
18
+ // replaces these for every control that does not state the prop, so the unstyled group is the
19
+ // one a theme restyles, and the quiet chrome states `color="neutral" variant="ghost"` itself.
20
+ button: {
21
+ defaultVariants: { color: 'primary', variant: 'solid', size: 'md' },
22
+ },
23
+ badge: {
24
+ defaultVariants: { color: 'primary', variant: 'solid', size: 'md' },
25
+ },
15
26
  // Give every overlay the same layered surface the agent-run-details reader uses: the deep
16
27
  // `app-950` surface (below `bg-default`) so the `bg-default` panels/cards inside pop, with
17
28
  // `border-default` chrome. All tokens are theme role tokens (`app-950` is the hand-defined
@@ -220,7 +220,6 @@ function reveal(blockId: string): void {
220
220
 
221
221
  <div class="flex flex-wrap items-center gap-2">
222
222
  <UButton
223
- color="primary"
224
223
  icon="i-lucide-sparkles"
225
224
  :loading="assistant.running"
226
225
  :disabled="gate.state !== 'ready'"
@@ -56,7 +56,7 @@ function reload() {
56
56
  </ul>
57
57
 
58
58
  <div class="mt-6 flex items-center justify-center gap-3">
59
- <UButton color="primary" icon="i-lucide-rotate-ccw" @click="reload">
59
+ <UButton icon="i-lucide-rotate-ccw" @click="reload">
60
60
  {{ t('app.misconfigured.reload') }}
61
61
  </UButton>
62
62
  </div>
@@ -234,7 +234,6 @@ const noSignInMethod = computed(
234
234
  <UButton
235
235
  block
236
236
  size="lg"
237
- color="primary"
238
237
  icon="i-lucide-cloud"
239
238
  data-testid="mothership-signin"
240
239
  @click="auth.signInViaMothership()"
@@ -260,7 +259,6 @@ const noSignInMethod = computed(
260
259
  :key="p"
261
260
  block
262
261
  size="lg"
263
- color="primary"
264
262
  :icon="PROVIDER_ICONS[p]"
265
263
  :loading="patBusy"
266
264
  @click="submitPat(p)"
@@ -329,7 +327,6 @@ const noSignInMethod = computed(
329
327
  <UButton
330
328
  block
331
329
  size="lg"
332
- color="primary"
333
330
  icon="i-lucide-building-2"
334
331
  data-testid="sso-signin"
335
332
  @click="auth.loginWithSso(invite)"
@@ -352,7 +349,6 @@ const noSignInMethod = computed(
352
349
  v-if="auth.providers.github"
353
350
  block
354
351
  size="lg"
355
- color="primary"
356
352
  icon="i-lucide-github"
357
353
  @click="auth.login(invite)"
358
354
  >
@@ -413,14 +409,7 @@ const noSignInMethod = computed(
413
409
  data-testid="login-password"
414
410
  />
415
411
  <p v-if="error" class="text-sm text-app-error-400" data-testid="login-error">{{ error }}</p>
416
- <UButton
417
- block
418
- size="lg"
419
- color="primary"
420
- type="submit"
421
- :loading="busy"
422
- data-testid="login-submit"
423
- >
412
+ <UButton block size="lg" type="submit" :loading="busy" data-testid="login-submit">
424
413
  {{ mode === 'signup' ? t('auth.login.createAccount') : t('auth.login.signIn') }}
425
414
  </UButton>
426
415
  <p class="text-center text-xs text-muted">
@@ -428,7 +417,6 @@ const noSignInMethod = computed(
428
417
  <i18n-t keypath="auth.login.needAccount" tag="span" scope="global">
429
418
  <template #signUp>
430
419
  <UButton
431
- color="primary"
432
420
  variant="link"
433
421
  class="p-0 text-xs text-primary hover:underline"
434
422
  @click="setMode('signup')"
@@ -442,7 +430,6 @@ const noSignInMethod = computed(
442
430
  <i18n-t keypath="auth.login.haveAccount" tag="span" scope="global">
443
431
  <template #signIn>
444
432
  <UButton
445
- color="primary"
446
433
  variant="link"
447
434
  class="p-0 text-xs text-primary hover:underline"
448
435
  @click="setMode('login')"
@@ -455,7 +442,6 @@ const noSignInMethod = computed(
455
442
  </p>
456
443
  <p v-if="mode === 'login'" class="text-center text-xs text-muted">
457
444
  <UButton
458
- color="primary"
459
445
  variant="link"
460
446
  class="p-0 text-xs text-primary hover:underline"
461
447
  @click="setMode('forgot')"
@@ -512,7 +498,6 @@ const noSignInMethod = computed(
512
498
  <UButton
513
499
  block
514
500
  size="lg"
515
- color="primary"
516
501
  type="submit"
517
502
  :icon="PROVIDER_ICONS[remotePatProvider]"
518
503
  :loading="remotePatBusy"
@@ -572,13 +557,12 @@ const noSignInMethod = computed(
572
557
  class="w-full"
573
558
  />
574
559
  <p v-if="error" class="text-sm text-app-error-400">{{ error }}</p>
575
- <UButton block size="lg" color="primary" type="submit" :loading="busy">
560
+ <UButton block size="lg" type="submit" :loading="busy">
576
561
  {{ t('auth.login.sendResetLink') }}
577
562
  </UButton>
578
563
  </template>
579
564
  <p class="text-center text-xs text-muted">
580
565
  <UButton
581
- color="primary"
582
566
  variant="link"
583
567
  class="p-0 text-xs text-primary hover:underline"
584
568
  @click="setMode('login')"
@@ -62,7 +62,7 @@ function goToLogin() {
62
62
  <p class="mb-4 text-sm text-toned">
63
63
  {{ t('auth.resetPassword.doneBody') }}
64
64
  </p>
65
- <UButton block size="lg" color="primary" @click="goToLogin">{{
65
+ <UButton block size="lg" @click="goToLogin">{{
66
66
  t('auth.resetPassword.goToSignIn')
67
67
  }}</UButton>
68
68
  </template>
@@ -94,12 +94,11 @@ function goToLogin() {
94
94
  class="w-full"
95
95
  />
96
96
  <p v-if="error" class="text-sm text-app-error-400">{{ error }}</p>
97
- <UButton block size="lg" color="primary" type="submit" :loading="busy">
97
+ <UButton block size="lg" type="submit" :loading="busy">
98
98
  {{ t('auth.resetPassword.submit') }}
99
99
  </UButton>
100
100
  <p class="text-center text-xs text-muted">
101
101
  <UButton
102
- color="primary"
103
102
  variant="link"
104
103
  class="p-0 text-xs text-primary hover:underline"
105
104
  @click="goToLogin"
@@ -1456,7 +1456,6 @@ function openReviewFrictionDialog(conflict: NonNullable<ReturnType<typeof parseC
1456
1456
  t('common.cancel')
1457
1457
  }}</UButton>
1458
1458
  <UButton
1459
- color="primary"
1460
1459
  data-testid="add-task-submit"
1461
1460
  :icon="isRecurring ? 'i-lucide-arrow-right' : 'i-lucide-plus'"
1462
1461
  :ui="{ leadingIcon: 'rtl:-scale-x-100', trailingIcon: 'rtl:-scale-x-100' }"
@@ -179,7 +179,6 @@ function onPaneClick() {
179
179
  >
180
180
  <UButton
181
181
  v-if="access.canManageIntegrations.value"
182
- color="primary"
183
182
  icon="i-lucide-git-branch-plus"
184
183
  @click="ui.openBootstrap()"
185
184
  >
@@ -187,7 +186,6 @@ function onPaneClick() {
187
186
  </UButton>
188
187
  <UButton
189
188
  v-if="github.available && access.canWriteBoard.value"
190
- color="primary"
191
189
  variant="soft"
192
190
  icon="i-lucide-folder-git-2"
193
191
  @click="ui.openAddService()"
@@ -285,7 +285,6 @@ async function create() {
285
285
  </UButton>
286
286
  <UButton
287
287
  data-testid="create-initiative-submit"
288
- color="primary"
289
288
  :loading="submitting || initiatives.creating"
290
289
  :disabled="!canSubmit"
291
290
  @click="create"
@@ -689,13 +689,7 @@ async function add() {
689
689
  <UButton color="neutral" variant="ghost" @click="requestClose()">{{
690
690
  t('common.cancel')
691
691
  }}</UButton>
692
- <UButton
693
- color="primary"
694
- icon="i-lucide-repeat"
695
- :loading="saving"
696
- :disabled="!canAdd"
697
- @click="add"
698
- >
692
+ <UButton icon="i-lucide-repeat" :loading="saving" :disabled="!canAdd" @click="add">
699
693
  {{ t('board.recurring.submit') }}
700
694
  </UButton>
701
695
  </div>
@@ -135,13 +135,7 @@ function createAnyway() {
135
135
  >
136
136
  {{ t('errors.reviewFriction.createAnyway') }}
137
137
  </UButton>
138
- <UButton
139
- color="primary"
140
- size="sm"
141
- icon="i-lucide-list-checks"
142
- :disabled="pending"
143
- @click="goReview"
144
- >
138
+ <UButton size="sm" icon="i-lucide-list-checks" :disabled="pending" @click="goReview">
145
139
  {{ t('errors.reviewFriction.goReview') }}
146
140
  </UButton>
147
141
  </div>
@@ -105,7 +105,6 @@ function select() {
105
105
  data-testid="initiative-card-review"
106
106
  :data-attention="attention.kind"
107
107
  size="xs"
108
- variant="solid"
109
108
  color="warning"
110
109
  :icon="INITIATIVE_ATTENTION_ICONS[attention.kind]"
111
110
  @click.stop="attention.open()"
@@ -116,8 +115,6 @@ function select() {
116
115
  v-else-if="awaitingAnswers"
117
116
  data-testid="initiative-card-answer-planning"
118
117
  size="xs"
119
- variant="solid"
120
- color="primary"
121
118
  icon="i-lucide-messages-square"
122
119
  @click.stop="openPlanning"
123
120
  >
@@ -141,7 +138,6 @@ function select() {
141
138
  data-testid="initiative-card-run-planning"
142
139
  size="xs"
143
140
  variant="soft"
144
- color="primary"
145
141
  icon="i-lucide-play"
146
142
  :loading="starting || running"
147
143
  :disabled="!planningPipeline || running || starting"
@@ -495,7 +495,6 @@ function selectTask() {
495
495
  <template v-if="task.status === 'pr_ready'">
496
496
  <UButton
497
497
  v-if="outcomeReadable"
498
- color="primary"
499
498
  variant="soft"
500
499
  size="xs"
501
500
  icon="i-lucide-clipboard-check"
@@ -530,13 +529,7 @@ function selectTask() {
530
529
  >
531
530
  {{ t('board.task.review') }}
532
531
  </UButton>
533
- <UButton
534
- color="success"
535
- variant="solid"
536
- size="xs"
537
- icon="i-lucide-git-merge"
538
- @click.stop="merge"
539
- >
532
+ <UButton color="success" size="xs" icon="i-lucide-git-merge" @click.stop="merge">
540
533
  {{ t('board.task.merge') }}
541
534
  </UButton>
542
535
  </template>
@@ -387,7 +387,6 @@ watch(open, (isOpen) => {
387
387
  {{ t('common.cancel') }}
388
388
  </UButton>
389
389
  <UButton
390
- color="primary"
391
390
  icon="i-lucide-play"
392
391
  :loading="submitting"
393
392
  :disabled="!canSubmit"
@@ -900,7 +900,6 @@ const statusLabel = computed<Record<BootstrapStatus, string>>(() => ({
900
900
 
901
901
  <div class="flex justify-end">
902
902
  <UButton
903
- color="primary"
904
903
  icon="i-lucide-rocket"
905
904
  :loading="launching"
906
905
  :disabled="!canLaunch"
@@ -1082,12 +1081,7 @@ const statusLabel = computed<Record<BootstrapStatus, string>>(() => ({
1082
1081
  >
1083
1082
  {{ t('common.cancel') }}
1084
1083
  </UButton>
1085
- <UButton
1086
- color="primary"
1087
- :loading="savingArch"
1088
- :disabled="!canSaveArch"
1089
- @click="saveArch"
1090
- >
1084
+ <UButton :loading="savingArch" :disabled="!canSaveArch" @click="saveArch">
1091
1085
  {{ archForm.id ? t('common.save') : t('bootstrap.arch.add') }}
1092
1086
  </UButton>
1093
1087
  </div>
@@ -408,7 +408,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
408
408
  />
409
409
  <div class="mt-2 flex flex-wrap items-center gap-2">
410
410
  <UButton
411
- color="primary"
412
411
  variant="soft"
413
412
  size="xs"
414
413
  icon="i-lucide-corner-down-left"
@@ -519,7 +518,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
519
518
  <div v-if="session && status === 'ready'" class="space-y-2 border-t border-default pt-4">
520
519
  <UButton
521
520
  v-if="canProceed"
522
- color="primary"
523
521
  size="sm"
524
522
  block
525
523
  icon="i-lucide-arrow-right"
@@ -533,7 +531,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
533
531
  </UButton>
534
532
  <UButton
535
533
  v-else
536
- color="primary"
537
534
  size="sm"
538
535
  block
539
536
  icon="i-lucide-wand-sparkles"
@@ -558,7 +555,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
558
555
  <!-- action: merged (inspect → re-run / redo) -->
559
556
  <div v-if="session && merged" class="space-y-2 border-t border-default pt-4">
560
557
  <UButton
561
- color="primary"
562
558
  size="sm"
563
559
  block
564
560
  icon="i-lucide-sparkles"
@@ -593,7 +589,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
593
589
  :placeholder="t('brainstorm.redoPlaceholder')"
594
590
  />
595
591
  <UButton
596
- color="primary"
597
592
  variant="soft"
598
593
  size="xs"
599
594
  block
@@ -612,7 +612,6 @@ const PHASE_ICON: Record<string, string> = {
612
612
  <div v-if="isOpen(finding) && canAct" class="mt-2 flex items-center gap-2">
613
613
  <UButton
614
614
  size="xs"
615
- color="primary"
616
615
  icon="i-lucide-wrench"
617
616
  :loading="bugFishing.spawning.has(finding.id)"
618
617
  :disabled="finding.dismissed || bugFishing.spawning.has(finding.id)"
@@ -654,7 +653,6 @@ const PHASE_ICON: Record<string, string> = {
654
653
  }}
655
654
  </p>
656
655
  <UButton
657
- color="primary"
658
656
  icon="i-lucide-check"
659
657
  :loading="bugFishing.resolving"
660
658
  :disabled="!canAct"
@@ -552,7 +552,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
552
552
  <div v-if="review && status === 'ready'" class="space-y-2 border-t border-default pt-4">
553
553
  <UButton
554
554
  v-if="canProceed"
555
- color="primary"
556
555
  size="sm"
557
556
  block
558
557
  icon="i-lucide-arrow-right"
@@ -566,7 +565,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
566
565
  </UButton>
567
566
  <UButton
568
567
  v-else
569
- color="primary"
570
568
  size="sm"
571
569
  block
572
570
  icon="i-lucide-wand-sparkles"
@@ -591,7 +589,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
591
589
  <!-- action: merged (inspect → re-review / redo) -->
592
590
  <div v-if="review && merged" class="space-y-2 border-t border-default pt-4">
593
591
  <UButton
594
- color="primary"
595
592
  size="sm"
596
593
  block
597
594
  icon="i-lucide-sparkles"
@@ -626,7 +623,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
626
623
  :placeholder="t('clarity.redoPlaceholder')"
627
624
  />
628
625
  <UButton
629
- color="primary"
630
626
  variant="soft"
631
627
  size="xs"
632
628
  block
@@ -22,7 +22,7 @@ function reload() {
22
22
  <p class="mt-2 text-xs leading-relaxed text-muted">
23
23
  {{ t('errors.asyncView.body') }}
24
24
  </p>
25
- <UButton class="mt-4" color="primary" variant="soft" size="xs" @click="reload">
25
+ <UButton class="mt-4" variant="soft" size="xs" @click="reload">
26
26
  {{ t('errors.asyncView.reload') }}
27
27
  </UButton>
28
28
  </div>
@@ -113,7 +113,6 @@ const draft = computed({
113
113
  v-if="canRecommend !== false"
114
114
  size="xs"
115
115
  variant="soft"
116
- color="primary"
117
116
  icon="i-lucide-wand-2"
118
117
  data-testid="clarification-recommend"
119
118
  :loading="recommending"
@@ -139,7 +138,6 @@ const draft = computed({
139
138
  class="mt-1.5"
140
139
  size="xs"
141
140
  variant="soft"
142
- color="primary"
143
141
  icon="i-lucide-check"
144
142
  data-testid="clarification-use-recommendation"
145
143
  :disabled="disabled"
@@ -259,7 +259,7 @@ function removePending(item: PendingContext) {
259
259
  })
260
260
  }}
261
261
  </span>
262
- <UButton color="primary" variant="soft" size="xs" @click="acceptOffer">
262
+ <UButton variant="soft" size="xs" @click="acceptOffer">
263
263
  {{ t('contextAttachments.pastedLink.attach') }}
264
264
  </UButton>
265
265
  </div>
@@ -255,7 +255,6 @@ const onProceed = () =>
255
255
  {{ t('docInterview.proceed') }}
256
256
  </UButton>
257
257
  <UButton
258
- color="primary"
259
258
  size="sm"
260
259
  :loading="resuming"
261
260
  :disabled="!!continueBlockedReason"
@@ -87,7 +87,6 @@ function preview(externalId: string) {
87
87
  <UButton
88
88
  v-for="s in documents.sources"
89
89
  :key="s.source"
90
- color="primary"
91
90
  variant="soft"
92
91
  :icon="s.icon"
93
92
  @click="ui.openDocumentConnect(s.source)"
@@ -115,7 +114,6 @@ function preview(externalId: string) {
115
114
  />
116
115
  </UFormField>
117
116
  <UButton
118
- color="primary"
119
117
  icon="i-lucide-file-down"
120
118
  :loading="importing"
121
119
  :disabled="!ref_.trim()"
@@ -149,7 +147,6 @@ function preview(externalId: string) {
149
147
  <DocumentSyncState :doc="doc" class="mt-1" />
150
148
  </div>
151
149
  <UButton
152
- color="primary"
153
150
  variant="soft"
154
151
  size="xs"
155
152
  icon="i-lucide-wand-sparkles"
@@ -124,7 +124,6 @@ async function disconnect() {
124
124
 
125
125
  <div v-if="oauth" class="space-y-2">
126
126
  <UButton
127
- color="primary"
128
127
  icon="i-lucide-shield-check"
129
128
  :loading="startingOAuth"
130
129
  data-testid="document-connect-oauth"
@@ -158,20 +157,14 @@ async function disconnect() {
158
157
  <UButton
159
158
  v-if="connected"
160
159
  color="error"
161
- variant="ghost"
160
+ variant="soft"
162
161
  icon="i-lucide-unplug"
163
162
  @click="disconnect"
164
163
  >
165
164
  {{ t('documents.connect.disconnect') }}
166
165
  </UButton>
167
166
  <div v-else />
168
- <UButton
169
- color="primary"
170
- icon="i-lucide-plug"
171
- :loading="saving"
172
- :disabled="!canSubmit"
173
- @click="submit"
174
- >
167
+ <UButton icon="i-lucide-plug" :loading="saving" :disabled="!canSubmit" @click="submit">
175
168
  {{ connected ? t('documents.connect.update') : t('documents.connect.connect') }}
176
169
  </UButton>
177
170
  </div>
@@ -100,12 +100,7 @@ async function unlink(doc: SourceDocument) {
100
100
  <div v-if="!documents.documents.length" class="space-y-3 text-center">
101
101
  <UIcon name="i-lucide-file-plus" class="mx-auto h-8 w-8 text-dimmed" />
102
102
  <p class="text-sm text-muted">{{ t('documents.templates.importFirst') }}</p>
103
- <UButton
104
- color="primary"
105
- variant="soft"
106
- icon="i-lucide-file-down"
107
- @click="ui.openDocumentImport()"
108
- >
103
+ <UButton variant="soft" icon="i-lucide-file-down" @click="ui.openDocumentImport()">
109
104
  {{ t('documents.templates.importButton') }}
110
105
  </UButton>
111
106
  </div>
@@ -215,7 +210,6 @@ async function unlink(doc: SourceDocument) {
215
210
  />
216
211
  </UFormField>
217
212
  <UButton
218
- color="primary"
219
213
  variant="soft"
220
214
  icon="i-lucide-file-badge"
221
215
  :loading="busy"
@@ -219,7 +219,6 @@ async function spawn() {
219
219
  t('common.cancel')
220
220
  }}</UButton>
221
221
  <UButton
222
- color="primary"
223
222
  icon="i-lucide-wand-sparkles"
224
223
  :loading="spawning"
225
224
  :disabled="!plan || loadingPlan"
@@ -221,7 +221,6 @@ function stage() {
221
221
  {{ t('common.cancel') }}
222
222
  </UButton>
223
223
  <UButton
224
- color="primary"
225
224
  icon="i-lucide-arrow-right"
226
225
  :disabled="!row || !target"
227
226
  data-testid="start-from-design-continue"
@@ -39,7 +39,6 @@ const PREFLIGHT_COLOR: Record<'pass' | 'warn' | 'fail', 'success' | 'warning' |
39
39
  <UButton
40
40
  size="xs"
41
41
  variant="soft"
42
- color="primary"
43
42
  icon="i-lucide-list-checks"
44
43
  :loading="store.preflightRunning"
45
44
  data-testid="env-setup-preflight-run"
@@ -101,7 +100,6 @@ const PREFLIGHT_COLOR: Record<'pass' | 'warn' | 'fail', 'success' | 'warning' |
101
100
  <JourneyStepNav :go-back="goBack">
102
101
  <template #primary>
103
102
  <UButton
104
- color="primary"
105
103
  trailing-icon="i-lucide-arrow-right"
106
104
  data-testid="env-setup-next"
107
105
  @click="exit('advance')"
@@ -120,7 +120,6 @@ const canLeaveReview = computed(
120
120
  <UButton
121
121
  size="xs"
122
122
  variant="soft"
123
- color="primary"
124
123
  icon="i-lucide-wand-sparkles"
125
124
  :loading="store.detecting"
126
125
  :disabled="!store.hasRepo"
@@ -200,7 +199,6 @@ const canLeaveReview = computed(
200
199
  <UButton
201
200
  size="xs"
202
201
  variant="soft"
203
- color="primary"
204
202
  icon="i-lucide-git-merge"
205
203
  data-testid="env-setup-apply-analysis"
206
204
  @click="store.applyAnalystDraft()"
@@ -360,7 +358,7 @@ const canLeaveReview = computed(
360
358
  {{ rawError }}
361
359
  </p>
362
360
  <div class="flex justify-end">
363
- <UButton size="xs" color="primary" data-testid="env-setup-raw-apply" @click="applyRaw">
361
+ <UButton size="xs" data-testid="env-setup-raw-apply" @click="applyRaw">
364
362
  {{ t('environmentWizard.review.rawApply') }}
365
363
  </UButton>
366
364
  </div>
@@ -371,7 +369,6 @@ const canLeaveReview = computed(
371
369
  <JourneyStepNav :go-back="goBack">
372
370
  <template #primary>
373
371
  <UButton
374
- color="primary"
375
372
  trailing-icon="i-lucide-arrow-right"
376
373
  :disabled="!canLeaveReview"
377
374
  data-testid="env-setup-next"
@@ -52,7 +52,6 @@ useEnvironmentWizardTarget(() => props.input.frameId)
52
52
 
53
53
  <div v-if="!store.saved" class="flex justify-end">
54
54
  <UButton
55
- color="primary"
56
55
  icon="i-lucide-save"
57
56
  :loading="store.saving"
58
57
  :disabled="!store.composeService.trim()"