@cat-factory/app 0.309.0 → 0.310.1

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 (133) hide show
  1. package/README.md +11 -2
  2. package/app/components/assistant/AssistantModal.vue +5 -4
  3. package/app/components/auth/BackendMisconfiguredScreen.vue +4 -3
  4. package/app/components/auth/LoginScreen.vue +40 -26
  5. package/app/components/auth/ResetPasswordScreen.vue +9 -4
  6. package/app/components/auth/UserMenu.vue +4 -3
  7. package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +34 -23
  8. package/app/components/binaryOutput/StoredAssetView.vue +8 -6
  9. package/app/components/board/AddTaskModal.vue +16 -15
  10. package/app/components/board/AgentFailureCard.vue +12 -9
  11. package/app/components/board/AgentFailureHistory.vue +18 -9
  12. package/app/components/board/CreateInitiativeModal.vue +5 -4
  13. package/app/components/board/FailureDetail.vue +22 -12
  14. package/app/components/board/ReviewFrictionDialog.vue +4 -3
  15. package/app/components/board/nodes/BlockNode.vue +17 -15
  16. package/app/components/board/nodes/DecisionBadge.vue +4 -3
  17. package/app/components/board/nodes/FrameSwimlanes.vue +4 -3
  18. package/app/components/board/nodes/TaskCard.vue +12 -7
  19. package/app/components/board/nodes/TaskPipelineMini.vue +9 -7
  20. package/app/components/bootstrap/AdoptionReviewModal.vue +30 -21
  21. package/app/components/bootstrap/BootstrapModal.vue +3 -3
  22. package/app/components/brainstorm/BrainstormWindow.vue +5 -3
  23. package/app/components/bugFishing/BugFishingWindow.vue +49 -27
  24. package/app/components/clarity/ClarityReviewWindow.vue +5 -3
  25. package/app/components/common/CopyButton.vue +12 -11
  26. package/app/components/common/IconButton.vue +34 -9
  27. package/app/components/context/ContextAttachmentFields.vue +23 -12
  28. package/app/components/documents/ContextDocumentPicker.vue +12 -9
  29. package/app/components/documents/DocumentImportModal.vue +4 -3
  30. package/app/components/documents/DocumentOriginLink.vue +12 -9
  31. package/app/components/documents/DocumentSyncState.vue +3 -3
  32. package/app/components/documents/RepoContextDocPicker.vue +4 -3
  33. package/app/components/environments/EnvironmentStatusPanel.vue +4 -3
  34. package/app/components/followUp/FollowUpWindow.vue +9 -6
  35. package/app/components/forkDecision/ForkDecisionWindow.vue +71 -80
  36. package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -2
  37. package/app/components/foundational/FoundationalServiceRegistry.vue +7 -6
  38. package/app/components/foundational/FoundationalServiceSources.vue +7 -6
  39. package/app/components/fragments/FragmentLibraryManager.vue +5 -4
  40. package/app/components/fragments/FragmentSelector.vue +12 -9
  41. package/app/components/gates/GateFailingCheckList.vue +4 -3
  42. package/app/components/gates/GateResultView.vue +17 -12
  43. package/app/components/github/AddServiceFromRepoModal.vue +11 -7
  44. package/app/components/github/GitHubOnboarding.vue +7 -2
  45. package/app/components/github/GitHubPanel.vue +9 -6
  46. package/app/components/github/RepoTreeBrowser.vue +15 -12
  47. package/app/components/humanTest/HumanTestWindow.vue +13 -9
  48. package/app/components/initiative/InitiativePlanReview.vue +28 -18
  49. package/app/components/initiative/InitiativeTrackerWindow.vue +171 -124
  50. package/app/components/judge/JudgeResultView.vue +5 -3
  51. package/app/components/kaizen/KaizenPanel.vue +67 -53
  52. package/app/components/layout/AccountTeamSettings.vue +9 -9
  53. package/app/components/layout/AppearanceSwitcher.vue +4 -3
  54. package/app/components/layout/BoardSwitcher.vue +10 -8
  55. package/app/components/layout/BoardToolbar.vue +5 -4
  56. package/app/components/layout/CommandBar.vue +4 -3
  57. package/app/components/layout/DefaultTestEnvBanner.vue +4 -3
  58. package/app/components/layout/IntegrationsHub.vue +8 -6
  59. package/app/components/layout/LanguageSwitcher.vue +4 -3
  60. package/app/components/layout/ModelProvidersHub.vue +4 -3
  61. package/app/components/layout/NotificationsInbox.vue +40 -30
  62. package/app/components/layout/PersonalSetupModal.vue +4 -3
  63. package/app/components/layout/RolePrompt.vue +4 -3
  64. package/app/components/layout/SideBar.vue +4 -3
  65. package/app/components/layout/TranslationWarningBanner.vue +4 -3
  66. package/app/components/layout/UiModeSwitcher.vue +8 -6
  67. package/app/components/layout/UiRoleSwitcher.vue +8 -6
  68. package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
  69. package/app/components/media/ArtifactLightbox.vue +69 -39
  70. package/app/components/media/ImageCompare.vue +69 -74
  71. package/app/components/observability/OutcomeFilterChips.vue +5 -4
  72. package/app/components/observability/RunFailureSummary.vue +8 -6
  73. package/app/components/observability/StepModelActivity.vue +5 -3
  74. package/app/components/observability/ToolCallList.vue +4 -2
  75. package/app/components/outcome/OutcomeSummaryWindow.vue +5 -4
  76. package/app/components/palettes/AgentPalette.vue +18 -14
  77. package/app/components/panels/AgentStepDetail.vue +30 -19
  78. package/app/components/panels/ObservabilityPanel.vue +149 -114
  79. package/app/components/panels/OperatorDashboardPanel.vue +99 -91
  80. package/app/components/panels/ReportsPanel.vue +64 -61
  81. package/app/components/panels/ResultWindowShell.vue +148 -134
  82. package/app/components/panels/StepContainerStatus.vue +9 -9
  83. package/app/components/panels/StepDelegatedStatus.vue +11 -9
  84. package/app/components/panels/StepRestartControl.vue +3 -2
  85. package/app/components/panels/inspector/EpicChildren.vue +4 -3
  86. package/app/components/panels/inspector/FrontendConfig.vue +3 -2
  87. package/app/components/panels/inspector/InspectorSection.vue +22 -17
  88. package/app/components/panels/inspector/ServiceConnections.vue +3 -2
  89. package/app/components/panels/inspector/ServiceTestConfig.vue +5 -4
  90. package/app/components/panels/inspector/TaskExecution.vue +5 -4
  91. package/app/components/panels/inspector/TaskRunSettings.vue +10 -6
  92. package/app/components/pipeline/EstimateThresholdFields.vue +13 -10
  93. package/app/components/pipeline/GateConfigFields.vue +14 -18
  94. package/app/components/pipeline/PipelineBuilder.vue +90 -77
  95. package/app/components/pipeline/PipelinePicker.vue +8 -6
  96. package/app/components/pipeline/PipelineProgress.vue +26 -21
  97. package/app/components/prReview/PrReviewWindow.vue +46 -25
  98. package/app/components/providers/ApiKeysSection.vue +4 -3
  99. package/app/components/ralph/RalphLoopResultView.vue +4 -3
  100. package/app/components/requirements/RequirementsReviewWindow.vue +10 -6
  101. package/app/components/riskPolicy/RiskPolicyPicker.vue +8 -6
  102. package/app/components/sandbox/SandboxPanel.vue +73 -54
  103. package/app/components/settings/CloudflareHandlerSection.vue +33 -30
  104. package/app/components/settings/ConsensusGroupsSection.vue +40 -30
  105. package/app/components/settings/InfraHandlersConfigurator.vue +16 -13
  106. package/app/components/settings/InfrastructureBackendPicker.vue +17 -17
  107. package/app/components/settings/IssueTrackerPanel.vue +21 -6
  108. package/app/components/settings/LocalModelEndpointsPanel.vue +3 -2
  109. package/app/components/settings/ModelConfigurationPanel.vue +9 -8
  110. package/app/components/settings/OpenRouterCatalogPanel.vue +4 -3
  111. package/app/components/settings/ProviderPreferenceEditor.vue +5 -6
  112. package/app/components/settings/RiskPolicyEditorRow.vue +3 -2
  113. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +10 -8
  114. package/app/components/skills/ReviewSkillQueue.vue +4 -3
  115. package/app/components/skills/SkillLibraryManager.vue +7 -6
  116. package/app/components/slack/SlackPanel.vue +9 -5
  117. package/app/components/spec/ServiceSpecWindow.vue +4 -3
  118. package/app/components/tasks/ContextIssuePicker.vue +12 -9
  119. package/app/components/tasks/TaskContextIssues.vue +4 -3
  120. package/app/components/testing/TestReportWindow.vue +14 -8
  121. package/app/components/theme/ThemeImportModal.vue +2 -2
  122. package/app/components/visualConfirm/VisualConfirmationWindow.vue +62 -35
  123. package/i18n/locales/de.json +7 -3
  124. package/i18n/locales/en.json +7 -3
  125. package/i18n/locales/es.json +7 -3
  126. package/i18n/locales/fr.json +7 -3
  127. package/i18n/locales/he.json +7 -3
  128. package/i18n/locales/it.json +7 -3
  129. package/i18n/locales/ja.json +7 -3
  130. package/i18n/locales/pl.json +7 -3
  131. package/i18n/locales/tr.json +7 -3
  132. package/i18n/locales/uk.json +7 -3
  133. package/package.json +2 -2
@@ -102,10 +102,11 @@ function manageAccount() {
102
102
  <SectionLabel as="p" class="px-2 pb-0.5 pt-1.5">
103
103
  {{ group.category }}
104
104
  </SectionLabel>
105
- <button
105
+ <UButton
106
+ color="neutral"
107
+ variant="ghost"
106
108
  v-for="f in group.fragments"
107
109
  :key="f.id"
108
- type="button"
109
110
  class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm hover:bg-elevated/60"
110
111
  :class="selectedSet.has(f.id) ? 'text-app-100' : 'text-toned'"
111
112
  :title="f.summary"
@@ -119,7 +120,7 @@ function manageAccount() {
119
120
  :class="selectedSet.has(f.id) ? 'text-primary' : 'text-dimmed'"
120
121
  />
121
122
  <span class="flex-1 truncate">{{ f.title }}</span>
122
- </button>
123
+ </UButton>
123
124
  </div>
124
125
  </template>
125
126
  <p v-else class="px-2 py-3 text-xs text-dimmed">
@@ -127,23 +128,25 @@ function manageAccount() {
127
128
  </p>
128
129
 
129
130
  <div class="mt-1 border-t border-default pt-1">
130
- <button
131
- type="button"
131
+ <UButton
132
+ color="neutral"
133
+ variant="ghost"
132
134
  class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm text-toned hover:bg-elevated/60"
133
135
  @click="manageBoard"
134
136
  >
135
137
  <UIcon name="i-lucide-book-marked" class="h-4 w-4 shrink-0 text-muted" />
136
138
  <span class="flex-1 truncate">{{ t('inspector.fragments.manageBoard') }}</span>
137
- </button>
138
- <button
139
+ </UButton>
140
+ <UButton
141
+ color="neutral"
142
+ variant="ghost"
139
143
  v-if="accounts.enabled"
140
- type="button"
141
144
  class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start text-sm text-toned hover:bg-elevated/60"
142
145
  @click="manageAccount"
143
146
  >
144
147
  <UIcon name="i-lucide-users" class="h-4 w-4 shrink-0 text-muted" />
145
148
  <span class="flex-1 truncate">{{ t('inspector.fragments.manageAccount') }}</span>
146
- </button>
149
+ </UButton>
147
150
  </div>
148
151
  </div>
149
152
 
@@ -31,9 +31,10 @@ const { t } = useI18n()
31
31
  class="shrink-0 text-app-error-400"
32
32
  :class="dense ? 'h-3 w-3' : 'h-3.5 w-3.5'"
33
33
  />
34
- <a
34
+ <ULink
35
+ raw
35
36
  v-if="c.url"
36
- :href="c.url"
37
+ :to="c.url"
37
38
  target="_blank"
38
39
  rel="noopener"
39
40
  class="group min-w-0 flex-1 truncate text-app-info-300 hover:text-app-info-200 hover:underline"
@@ -45,7 +46,7 @@ const { t } = useI18n()
45
46
  name="i-lucide-external-link"
46
47
  class="ms-0.5 inline h-3 w-3 opacity-60 group-hover:opacity-100"
47
48
  />
48
- </a>
49
+ </ULink>
49
50
  <span
50
51
  v-else
51
52
  class="min-w-0 flex-1 truncate"
@@ -271,16 +271,17 @@ const conflictVerdict = computed(() => {
271
271
  {{ gate.lastFailureSummary }}
272
272
  </p>
273
273
  </div>
274
- <a
274
+ <ULink
275
+ raw
275
276
  v-if="prUrl"
276
- :href="prUrl"
277
+ :to="prUrl"
277
278
  target="_blank"
278
279
  rel="noopener"
279
280
  class="mt-2 inline-flex items-center gap-1 text-xs text-app-info-300 hover:text-app-info-200 hover:underline"
280
281
  >
281
282
  {{ t('gates.humanReview.reviewPr') }}
282
283
  <UIcon name="i-lucide-external-link" class="h-3 w-3" />
283
- </a>
284
+ </ULink>
284
285
 
285
286
  <!-- Freeform fix request: dispatch the fixer now with these instructions. -->
286
287
  <section v-if="status !== 'gave-up'" class="mt-4">
@@ -290,12 +291,14 @@ const conflictVerdict = computed(() => {
290
291
  <p class="mb-2 text-2xs leading-relaxed text-dimmed">
291
292
  {{ t('gates.humanReview.requestFixDescription') }}
292
293
  </p>
293
- <textarea
294
+ <UTextarea
294
295
  v-model="fixInstructions"
295
- rows="3"
296
+ :rows="3"
296
297
  :disabled="fixBusy"
297
298
  :placeholder="t('gates.humanReview.requestFixPlaceholder')"
298
- class="w-full resize-y rounded-md border border-default bg-app-950/60 px-3 py-2 text-sm text-default placeholder:text-app-600 focus:border-app-secondary-500/60 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-secondary-500/60"
299
+ size="sm"
300
+ class="w-full"
301
+ :ui="{ base: 'resize-y' }"
299
302
  />
300
303
  <div class="mt-2 flex justify-end">
301
304
  <UButton
@@ -343,16 +346,17 @@ const conflictVerdict = computed(() => {
343
346
  <p v-else class="text-sm leading-relaxed text-toned">
344
347
  {{ t('gates.docQuality.findingsFallback') }}
345
348
  </p>
346
- <a
349
+ <ULink
350
+ raw
347
351
  v-if="prUrl"
348
- :href="prUrl"
352
+ :to="prUrl"
349
353
  target="_blank"
350
354
  rel="noopener"
351
355
  class="mt-2 inline-flex items-center gap-1 text-xs text-app-info-300 hover:text-app-info-200 hover:underline"
352
356
  >
353
357
  {{ t('gates.docQuality.viewPr') }}
354
358
  <UIcon name="i-lucide-external-link" class="h-3 w-3" />
355
- </a>
359
+ </ULink>
356
360
  </template>
357
361
 
358
362
  <!-- Conflicts: verdict + the resolver's account of what it left -->
@@ -382,16 +386,17 @@ const conflictVerdict = computed(() => {
382
386
  {{ gate.lastFailureSummary }}
383
387
  </p>
384
388
  </div>
385
- <a
389
+ <ULink
390
+ raw
386
391
  v-if="prUrl"
387
- :href="prUrl"
392
+ :to="prUrl"
388
393
  target="_blank"
389
394
  rel="noopener"
390
395
  class="mt-2 inline-flex items-center gap-1 text-xs text-app-info-300 hover:text-app-info-200 hover:underline"
391
396
  >
392
397
  {{ t('gates.conflicts.viewPr') }}
393
398
  <UIcon name="i-lucide-external-link" class="h-3 w-3" />
394
- </a>
399
+ </ULink>
395
400
  </template>
396
401
 
397
402
  <!-- Attempt history (both gates): what each helper run did and how it ended. -->
@@ -28,6 +28,7 @@ import ServiceTestConfig from '~/components/panels/inspector/ServiceTestConfig.v
28
28
  import ServiceFragments from '~/components/panels/inspector/ServiceFragments.vue'
29
29
  import { appInstallationManageUrl, VCS_PROVIDER_LABELS } from '~/utils/vcs'
30
30
  import SectionLabel from '~/components/common/SectionLabel.vue'
31
+ import IconButton from '~/components/common/IconButton.vue'
31
32
 
32
33
  const { t } = useI18n()
33
34
 
@@ -559,14 +560,17 @@ function done() {
559
560
  class="inline-flex items-center gap-1 rounded-sm bg-elevated px-2 py-0.5 text-xs text-default"
560
561
  >
561
562
  <code class="text-default">{{ dir }}</code>
562
- <button
563
- type="button"
564
- class="text-muted hover:text-app-100"
565
- :aria-label="t('github.addService.removeService', { directory: dir })"
563
+ <IconButton
564
+ color="neutral"
565
+ variant="ghost"
566
+ icon="i-lucide-x"
567
+ :label="t('github.addService.removeService', { directory: dir })"
568
+ :ui="{
569
+ base: 'p-0 text-muted hover:bg-transparent hover:text-app-100',
570
+ leadingIcon: 'h-3 w-3',
571
+ }"
566
572
  @click="removeSelected(dir)"
567
- >
568
- <UIcon name="i-lucide-x" class="h-3 w-3" />
569
- </button>
573
+ />
570
574
  </span>
571
575
  </div>
572
576
  <p v-else class="text-xs text-dimmed">
@@ -44,9 +44,14 @@ const title = computed(() =>
44
44
  class="mt-6 border-t border-default pt-4 text-center text-xs text-dimmed"
45
45
  >
46
46
  {{ t('github.onboarding.signedInAs', { login: auth.user.login }) }} ·
47
- <button class="text-toned underline-offset-2 hover:underline" @click="auth.logout()">
47
+ <UButton
48
+ color="neutral"
49
+ variant="link"
50
+ class="p-0 text-xs text-toned underline-offset-2 hover:underline"
51
+ @click="auth.logout()"
52
+ >
48
53
  {{ t('github.onboarding.signOut') }}
49
- </button>
54
+ </UButton>
50
55
  </p>
51
56
  </div>
52
57
  </div>
@@ -420,10 +420,11 @@ async function merge(pr: GitHubPullRequest) {
420
420
  }}
421
421
  </p>
422
422
  <div v-else class="max-h-64 space-y-1 overflow-y-auto">
423
- <button
423
+ <UButton
424
+ color="neutral"
425
+ variant="ghost"
424
426
  v-for="r in github.availableRepos"
425
427
  :key="r.githubId"
426
- type="button"
427
428
  class="flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-start transition hover:bg-elevated/60"
428
429
  @click="toggleSelected(r.githubId)"
429
430
  >
@@ -436,7 +437,7 @@ async function merge(pr: GitHubPullRequest) {
436
437
  <UBadge v-if="r.private" color="neutral" variant="subtle" size="sm">
437
438
  {{ t('github.panel.private') }}
438
439
  </UBadge>
439
- </button>
440
+ </UButton>
440
441
  </div>
441
442
  <div class="flex items-center justify-end gap-2 pt-1">
442
443
  <UButton
@@ -484,8 +485,10 @@ async function merge(pr: GitHubPullRequest) {
484
485
  class="rounded-md border border-default bg-default/60"
485
486
  >
486
487
  <div class="flex items-center justify-between gap-2 px-3 py-2">
487
- <button
488
- class="flex min-w-0 items-center gap-2 text-start"
488
+ <UButton
489
+ color="neutral"
490
+ variant="ghost"
491
+ class="flex min-w-0 items-center gap-2 p-0 text-start hover:bg-transparent"
489
492
  @click="toggleRepo(repo)"
490
493
  >
491
494
  <UIcon
@@ -502,7 +505,7 @@ async function merge(pr: GitHubPullRequest) {
502
505
  <UBadge v-if="repo.private" color="neutral" variant="subtle" size="sm">
503
506
  {{ t('github.panel.private') }}
504
507
  </UBadge>
505
- </button>
508
+ </UButton>
506
509
  <div class="flex items-center gap-2">
507
510
  <span v-if="repo.defaultBranch" class="text-2xs text-dimmed">
508
511
  {{ repo.defaultBranch }}
@@ -206,14 +206,15 @@ watch(
206
206
  :key="entry.path"
207
207
  class="flex items-center justify-between gap-2 px-3 py-1.5"
208
208
  >
209
- <button
210
- type="button"
211
- class="flex items-center gap-2 truncate text-sm text-default hover:text-primary"
209
+ <UButton
210
+ color="primary"
211
+ variant="ghost"
212
+ class="flex items-center gap-2 truncate p-0 text-sm text-default hover:bg-transparent hover:text-primary"
212
213
  @click="browseTo(entry.path)"
213
214
  >
214
215
  <UIcon name="i-lucide-folder" class="h-4 w-4 shrink-0 text-app-warning-400" />
215
216
  <span class="truncate">{{ entry.name }}</span>
216
- </button>
217
+ </UButton>
217
218
  <span
218
219
  v-if="clashes(entry)"
219
220
  class="flex shrink-0 items-center gap-1 text-xs text-app-warning-400"
@@ -256,9 +257,10 @@ watch(
256
257
  data-testid="repo-tree-select-all"
257
258
  @update:model-value="toggleAllFiles"
258
259
  />
259
- <button
260
- type="button"
261
- class="text-xs text-muted hover:text-primary"
260
+ <UButton
261
+ color="primary"
262
+ variant="ghost"
263
+ class="p-0 text-xs text-muted hover:bg-transparent hover:text-primary"
262
264
  @click="toggleAllFiles"
263
265
  >
264
266
  {{
@@ -268,7 +270,7 @@ watch(
268
270
  selectableFiles.length,
269
271
  )
270
272
  }}
271
- </button>
273
+ </UButton>
272
274
  </li>
273
275
  <li
274
276
  v-for="entry in fileEntries"
@@ -282,16 +284,17 @@ watch(
282
284
  :aria-label="entry.name"
283
285
  @update:model-value="pick(entry.path)"
284
286
  />
285
- <button
286
- type="button"
287
- class="flex items-center gap-2 truncate text-sm hover:text-primary"
287
+ <UButton
288
+ color="primary"
289
+ variant="ghost"
290
+ class="flex items-center gap-2 truncate p-0 text-sm hover:bg-transparent hover:text-primary"
288
291
  :class="isPicked(entry.path) ? 'text-primary' : 'text-toned'"
289
292
  :disabled="isAdded(entry.path)"
290
293
  @click="pick(entry.path)"
291
294
  >
292
295
  <UIcon name="i-lucide-file" class="h-4 w-4 shrink-0 text-muted" />
293
296
  <span class="truncate">{{ entry.name }}</span>
294
- </button>
297
+ </UButton>
295
298
  </div>
296
299
  <span
297
300
  v-if="isAdded(entry.path)"
@@ -183,16 +183,17 @@ const canDestroy = computed(
183
183
  t(ENV_STATUS_LABEL[env.status])
184
184
  }}</span>
185
185
  </div>
186
- <a
186
+ <ULink
187
+ raw
187
188
  v-if="env.url"
188
- :href="env.url"
189
+ :to="env.url"
189
190
  target="_blank"
190
191
  rel="noopener"
191
192
  class="inline-flex items-center gap-1.5 break-all text-sm text-app-info-300 hover:underline"
192
193
  >
193
194
  <UIcon name="i-lucide-external-link" class="h-3.5 w-3.5 shrink-0" />
194
195
  {{ env.url }}
195
- </a>
196
+ </ULink>
196
197
  <p v-else class="text-xs italic text-dimmed">
197
198
  {{ t('humanTest.environment.noUrl') }}
198
199
  </p>
@@ -262,19 +263,22 @@ const canDestroy = computed(
262
263
  <SectionLabel as="h3">
263
264
  {{ t('humanTest.fix.heading') }}
264
265
  </SectionLabel>
265
- <button
266
- class="text-xs text-muted hover:text-default"
266
+ <UButton
267
+ color="neutral"
268
+ variant="ghost"
269
+ class="p-0 text-xs text-muted hover:bg-transparent hover:text-default"
267
270
  @click="showFindings = !showFindings"
268
271
  >
269
272
  {{ showFindings ? t('humanTest.fix.cancel') : t('humanTest.fix.requestFix') }}
270
- </button>
273
+ </UButton>
271
274
  </div>
272
275
  <div v-if="showFindings" class="mt-2 space-y-2">
273
- <textarea
276
+ <UTextarea
274
277
  v-model="findings"
275
- rows="4"
278
+ :rows="4"
276
279
  :placeholder="t('humanTest.fix.placeholder')"
277
- class="w-full rounded-md border border-muted bg-app-950 px-3 py-2 text-sm 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"
280
+ size="sm"
281
+ class="w-full"
278
282
  />
279
283
  <UButton
280
284
  size="sm"
@@ -31,6 +31,7 @@ import { useStepProse } from '~/composables/useStepProse'
31
31
  import { useProseComments } from '~/composables/useProseComments'
32
32
  import InitiativePlanDecision from '~/components/initiative/InitiativePlanDecision.vue'
33
33
  import SectionLabel from '~/components/common/SectionLabel.vue'
34
+ import IconButton from '~/components/common/IconButton.vue'
34
35
 
35
36
  const props = defineProps<{
36
37
  /** The parked gate under review. */
@@ -159,23 +160,23 @@ async function copyPlan() {
159
160
  preamble, which renders no toggle of its own, so collapsing it would hide the whole
160
161
  plan with nothing on screen to bring it back. Copying it does not — that is about the
161
162
  document, which exists either way. -->
162
- <UButton
163
+ <IconButton
163
164
  v-if="outline.hasToc"
164
165
  :icon="allCollapsed ? 'i-lucide-unfold-vertical' : 'i-lucide-fold-vertical'"
165
166
  color="neutral"
166
167
  variant="ghost"
167
168
  size="xs"
168
- :title="
169
+ :label="
169
170
  allCollapsed ? t('panels.stepDetail.expandAll') : t('panels.stepDetail.collapseAll')
170
171
  "
171
172
  @click="setAll(!allCollapsed)"
172
173
  />
173
- <UButton
174
+ <IconButton
174
175
  icon="i-lucide-copy"
175
176
  color="neutral"
176
177
  variant="ghost"
177
178
  size="xs"
178
- :title="t('panels.stepDetail.copyRawOutput')"
179
+ :label="t('panels.stepDetail.copyRawOutput')"
179
180
  @click="copyPlan"
180
181
  />
181
182
  </div>
@@ -185,7 +186,9 @@ async function copyPlan() {
185
186
  :aria-label="t('panels.stepDetail.contents')"
186
187
  class="flex-1 space-y-0.5 overflow-y-auto px-2 py-2"
187
188
  >
188
- <button
189
+ <UButton
190
+ color="neutral"
191
+ variant="ghost"
189
192
  v-for="s in tocSections"
190
193
  :key="s.id"
191
194
  class="block w-full truncate rounded-md px-2 py-1 text-start text-xs transition"
@@ -199,7 +202,7 @@ async function copyPlan() {
199
202
  @click="goTo(s.id)"
200
203
  >
201
204
  {{ s.title }}
202
- </button>
205
+ </UButton>
203
206
  </nav>
204
207
  <!-- Run details (the model, the run id, the token telemetry). Filled by the host, which
205
208
  already resolves the bundle through `useResultViewRunMeta`; it keeps its home in a
@@ -212,8 +215,9 @@ async function copyPlan() {
212
215
  class="flex flex-col"
213
216
  :class="outline.hasToc ? 'max-h-[45%] shrink-0 border-t border-default' : 'min-h-0 flex-1'"
214
217
  >
215
- <button
216
- type="button"
218
+ <UButton
219
+ color="neutral"
220
+ variant="ghost"
217
221
  data-testid="initiative-plan-run-meta-toggle"
218
222
  class="flex shrink-0 items-center gap-1.5 px-3 py-2 text-start transition hover:bg-elevated/40"
219
223
  :aria-expanded="runDetailsOpen"
@@ -226,7 +230,7 @@ async function copyPlan() {
226
230
  :name="runDetailsOpen ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'"
227
231
  class="h-3.5 w-3.5 shrink-0 text-dimmed"
228
232
  />
229
- </button>
233
+ </UButton>
230
234
  <div
231
235
  v-if="runDetailsOpen"
232
236
  data-testid="initiative-plan-run-meta"
@@ -260,9 +264,11 @@ async function copyPlan() {
260
264
  :ref="(el) => (sectionEls[s.id] = el as HTMLElement | null)"
261
265
  class="scroll-mt-2"
262
266
  >
263
- <button
267
+ <UButton
268
+ color="neutral"
269
+ variant="ghost"
264
270
  v-if="s.depth > 0"
265
- class="group flex w-full items-center gap-1.5 rounded-sm py-0.5 text-start transition hover:text-highlighted"
271
+ class="group flex w-full items-center gap-1.5 rounded-sm px-0 py-0.5 text-start transition hover:bg-transparent hover:text-highlighted"
266
272
  :aria-expanded="!collapsed[s.id]"
267
273
  @click="toggle(s.id)"
268
274
  >
@@ -276,7 +282,7 @@ async function copyPlan() {
276
282
  :class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
277
283
  v-html="s.titleHtml"
278
284
  />
279
- </button>
285
+ </UButton>
280
286
  <!-- `review-mode` carries the click-to-comment affordance, so it tracks the same RBAC
281
287
  gate the composer does — a viewer gets the document, not hover targets that lead
282
288
  nowhere. -->
@@ -362,13 +368,17 @@ async function copyPlan() {
362
368
  <SectionLabel>
363
369
  {{ t('panels.stepDetail.commentN', { number: idx + 1 }) }}
364
370
  </SectionLabel>
365
- <button
366
- class="text-dimmed transition hover:text-app-error-400"
367
- :title="t('panels.stepDetail.removeComment')"
371
+ <IconButton
372
+ color="neutral"
373
+ variant="ghost"
374
+ icon="i-lucide-x"
375
+ :label="t('panels.stepDetail.removeComment')"
376
+ :ui="{
377
+ base: 'p-0 text-dimmed transition hover:bg-transparent hover:text-app-error-400',
378
+ leadingIcon: 'h-3.5 w-3.5',
379
+ }"
368
380
  @click="removeComment(idx)"
369
- >
370
- <UIcon name="i-lucide-x" class="h-3.5 w-3.5" />
371
- </button>
381
+ />
372
382
  </div>
373
383
  <pre
374
384
  class="mb-1 max-h-16 overflow-auto whitespace-pre-wrap rounded-sm bg-app-950/50 p-1.5 text-3xs text-muted"