@cat-factory/app 0.92.2 → 0.93.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 (50) hide show
  1. package/app/components/auth/LoginScreen.vue +5 -1
  2. package/app/components/board/AddTaskModal.vue +25 -5
  3. package/app/components/board/CreateInitiativeModal.vue +9 -1
  4. package/app/components/board/RecurringPipelineModal.vue +20 -4
  5. package/app/components/bootstrap/BootstrapModal.vue +9 -1
  6. package/app/components/brainstorm/BrainstormWindow.vue +5 -1
  7. package/app/components/clarity/ClarityReviewWindow.vue +5 -1
  8. package/app/components/docs/DocInterviewWindow.vue +209 -0
  9. package/app/components/documents/DocumentTemplatesModal.vue +5 -1
  10. package/app/components/fragments/FragmentLibraryManager.vue +10 -2
  11. package/app/components/github/GitHubPanel.vue +25 -4
  12. package/app/components/layout/BoardSwitcher.vue +10 -1
  13. package/app/components/layout/BoardToolbar.vue +15 -3
  14. package/app/components/layout/GitHubPatBanner.vue +5 -1
  15. package/app/components/layout/ProviderConfigBanner.vue +5 -1
  16. package/app/components/panels/AgentStepDetail.vue +5 -1
  17. package/app/components/panels/InspectorPanel.vue +5 -1
  18. package/app/components/panels/StepRestartControl.vue +10 -2
  19. package/app/components/panels/StepResultViewHost.vue +4 -0
  20. package/app/components/panels/inspector/FrontendConfig.vue +10 -1
  21. package/app/components/panels/inspector/RecurringScheduleSettings.vue +11 -3
  22. package/app/components/pipeline/PipelineBuilder.vue +15 -2
  23. package/app/components/pipeline/PipelineProgress.vue +10 -2
  24. package/app/components/providers/AiPresetMismatchDialog.vue +10 -1
  25. package/app/components/providers/PersonalCredentialModal.vue +18 -2
  26. package/app/components/requirements/RequirementsReviewWindow.vue +5 -1
  27. package/app/components/sandbox/SandboxPanel.vue +10 -1
  28. package/app/components/settings/InfraHandlersConfigurator.vue +5 -1
  29. package/app/components/settings/IssueTrackerPanel.vue +20 -4
  30. package/app/components/settings/LocalModelEndpointsPanel.vue +5 -1
  31. package/app/components/settings/ModelConfigurationPanel.vue +20 -3
  32. package/app/components/settings/ProviderConnectionTab.vue +5 -1
  33. package/app/components/spec/ServiceSpecWindow.vue +15 -3
  34. package/app/components/testing/TestReportWindow.vue +10 -2
  35. package/app/composables/api/docInterview.ts +36 -0
  36. package/app/composables/useApi.ts +2 -0
  37. package/app/composables/useWorkspaceStream.ts +5 -0
  38. package/app/stores/docInterview.ts +89 -0
  39. package/app/stores/workspace.ts +1 -0
  40. package/app/types/doc-interview.ts +9 -0
  41. package/app/types/domain.ts +1 -0
  42. package/i18n/locales/en.json +16 -0
  43. package/i18n/locales/es.json +16 -0
  44. package/i18n/locales/fr.json +16 -0
  45. package/i18n/locales/he.json +16 -0
  46. package/i18n/locales/ja.json +16 -0
  47. package/i18n/locales/pl.json +16 -0
  48. package/i18n/locales/tr.json +16 -0
  49. package/i18n/locales/uk.json +16 -0
  50. package/package.json +5 -5
@@ -386,7 +386,11 @@ const noSignInMethod = computed(
386
386
  :variant="p === remotePatProvider ? 'solid' : 'subtle'"
387
387
  :icon="PROVIDER_ICONS[p]"
388
388
  size="sm"
389
- @click="remotePatProvider = p"
389
+ @click="
390
+ () => {
391
+ remotePatProvider = p
392
+ }
393
+ "
390
394
  >
391
395
  {{ PROVIDER_LABELS[p] }}
392
396
  </UButton>
@@ -489,7 +489,11 @@ async function add() {
489
489
  :variant="taskType === ty.value ? 'soft' : 'ghost'"
490
490
  :icon="ty.icon"
491
491
  size="xs"
492
- @click="taskType = ty.value"
492
+ @click="
493
+ () => {
494
+ taskType = ty.value
495
+ }
496
+ "
493
497
  >
494
498
  {{ ty.label }}
495
499
  </UButton>
@@ -584,7 +588,11 @@ async function add() {
584
588
  :variant="severity === s ? 'soft' : 'ghost'"
585
589
  size="xs"
586
590
  class="capitalize"
587
- @click="severity = severity === s ? '' : s"
591
+ @click="
592
+ () => {
593
+ severity = severity === s ? '' : s
594
+ }
595
+ "
588
596
  >
589
597
  {{ s }}
590
598
  </UButton>
@@ -621,7 +629,11 @@ async function add() {
621
629
  :variant="docKind === k ? 'soft' : 'ghost'"
622
630
  size="xs"
623
631
  class="uppercase"
624
- @click="docKind = docKind === k ? '' : k"
632
+ @click="
633
+ () => {
634
+ docKind = docKind === k ? '' : k
635
+ }
636
+ "
625
637
  >
626
638
  {{ k }}
627
639
  </UButton>
@@ -761,7 +773,11 @@ async function add() {
761
773
  variant="soft"
762
774
  size="xs"
763
775
  :icon="showDocPicker ? 'i-lucide-x' : 'i-lucide-plus'"
764
- @click="showDocPicker = !showDocPicker"
776
+ @click="
777
+ () => {
778
+ showDocPicker = !showDocPicker
779
+ }
780
+ "
765
781
  >
766
782
  {{ showDocPicker ? t('board.addTask.done') : t('board.addTask.attach') }}
767
783
  </UButton>
@@ -832,7 +848,11 @@ async function add() {
832
848
  variant="soft"
833
849
  size="xs"
834
850
  :icon="showIssuePicker ? 'i-lucide-x' : 'i-lucide-plus'"
835
- @click="showIssuePicker = !showIssuePicker"
851
+ @click="
852
+ () => {
853
+ showIssuePicker = !showIssuePicker
854
+ }
855
+ "
836
856
  >
837
857
  {{ showIssuePicker ? t('board.addTask.done') : t('board.addTask.attach') }}
838
858
  </UButton>
@@ -95,7 +95,15 @@ async function create() {
95
95
  </template>
96
96
  <template #footer>
97
97
  <div class="flex w-full justify-end gap-2">
98
- <UButton color="neutral" variant="ghost" @click="open = false">
98
+ <UButton
99
+ color="neutral"
100
+ variant="ghost"
101
+ @click="
102
+ () => {
103
+ open = false
104
+ }
105
+ "
106
+ >
99
107
  {{ t('common.cancel') }}
100
108
  </UButton>
101
109
  <UButton
@@ -284,7 +284,11 @@ async function add() {
284
284
  :color="trackerKind === 'github' ? 'primary' : 'neutral'"
285
285
  :variant="trackerKind === 'github' ? 'solid' : 'subtle'"
286
286
  icon="i-lucide-github"
287
- @click="trackerKind = 'github'"
287
+ @click="
288
+ () => {
289
+ trackerKind = 'github'
290
+ }
291
+ "
288
292
  >
289
293
  {{ t('board.recurring.githubIssues') }}
290
294
  </UButton>
@@ -293,7 +297,11 @@ async function add() {
293
297
  :color="trackerKind === 'jira' ? 'primary' : 'neutral'"
294
298
  :variant="trackerKind === 'jira' ? 'solid' : 'subtle'"
295
299
  icon="i-lucide-square-check"
296
- @click="trackerKind = 'jira'"
300
+ @click="
301
+ () => {
302
+ trackerKind = 'jira'
303
+ }
304
+ "
297
305
  >
298
306
  {{ t('board.recurring.jira') }}
299
307
  </UButton>
@@ -302,7 +310,11 @@ async function add() {
302
310
  :color="trackerKind === 'linear' ? 'primary' : 'neutral'"
303
311
  :variant="trackerKind === 'linear' ? 'solid' : 'subtle'"
304
312
  icon="i-lucide-square-kanban"
305
- @click="trackerKind = 'linear'"
313
+ @click="
314
+ () => {
315
+ trackerKind = 'linear'
316
+ }
317
+ "
306
318
  >
307
319
  {{ t('board.recurring.linear') }}
308
320
  </UButton>
@@ -337,7 +349,11 @@ async function add() {
337
349
  :color="intakeSource === s.source ? 'primary' : 'neutral'"
338
350
  :variant="intakeSource === s.source ? 'solid' : 'subtle'"
339
351
  :icon="s.icon"
340
- @click="intakeSource = s.source"
352
+ @click="
353
+ () => {
354
+ intakeSource = s.source
355
+ }
356
+ "
341
357
  >
342
358
  {{ s.label }}
343
359
  </UButton>
@@ -713,7 +713,15 @@ const statusLabel = computed<Record<BootstrapStatus, string>>(() => ({
713
713
  />
714
714
  </UFormField>
715
715
  <div class="flex justify-end gap-2">
716
- <UButton color="neutral" variant="ghost" @click="showArchForm = false">
716
+ <UButton
717
+ color="neutral"
718
+ variant="ghost"
719
+ @click="
720
+ () => {
721
+ showArchForm = false
722
+ }
723
+ "
724
+ >
717
725
  {{ t('common.cancel') }}
718
726
  </UButton>
719
727
  <UButton
@@ -570,7 +570,11 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
570
570
  size="sm"
571
571
  block
572
572
  icon="i-lucide-pencil"
573
- @click="showRedo = !showRedo"
573
+ @click="
574
+ () => {
575
+ showRedo = !showRedo
576
+ }
577
+ "
574
578
  >
575
579
  {{ t('brainstorm.redoIncorporation') }}
576
580
  </UButton>
@@ -558,7 +558,11 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
558
558
  size="sm"
559
559
  block
560
560
  icon="i-lucide-pencil"
561
- @click="showRedo = !showRedo"
561
+ @click="
562
+ () => {
563
+ showRedo = !showRedo
564
+ }
565
+ "
562
566
  >
563
567
  {{ t('clarity.redoIncorporation') }}
564
568
  </UButton>
@@ -0,0 +1,209 @@
1
+ <script setup lang="ts">
2
+ // The interactive document-interview window (WS5) — the dedicated view of the doc-authoring
3
+ // INTERVIEWER gate. While the document run is parked between the outline and the draft, the
4
+ // interviewer's clarifying questions (pending `qa` entries with an empty answer) are shown here;
5
+ // the human answers them, then either CONTINUES (the interviewer re-runs and may ask follow-ups)
6
+ // or PROCEEDS (skip remaining questions — the interviewer converges into an authoring brief and
7
+ // the run advances to the writer). Opened via the universal result-view host as the
8
+ // `doc-interviewer` step's result view. Live `docInterview` stream events patch the store, so an
9
+ // open window follows the interview as it progresses. Mirrors InitiativePlanningWindow.vue.
10
+ import { computed, reactive, watch } from 'vue'
11
+
12
+ const board = useBoardStore()
13
+ const docInterview = useDocInterviewStore()
14
+ const { t } = useI18n()
15
+
16
+ const { open, blockId, close } = useResultView('doc-interview', {
17
+ onOpen: (id) => void docInterview.load(id),
18
+ })
19
+
20
+ const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
21
+ const session = computed(() => (blockId.value ? docInterview.forBlock(blockId.value) : null))
22
+
23
+ /** Every interview exchange, with a stable key for the list + draft map. */
24
+ const questions = computed(() =>
25
+ (session.value?.qa ?? []).map((q, i) => ({ ...q, key: q.id ?? `q-${i}` })),
26
+ )
27
+ const pending = computed(() => questions.value.filter((q) => !(q.answer ?? '').trim()))
28
+ /** The interview converged (or never started with a model): nothing left to answer. */
29
+ const converged = computed(() => session.value?.status === 'done')
30
+
31
+ // Per-question answer drafts, seeded from the entity and refreshed as new rounds arrive without
32
+ // clobbering an answer the human is mid-edit on.
33
+ const drafts = reactive<Record<string, string>>({})
34
+ watch(
35
+ questions,
36
+ (list) => {
37
+ for (const q of list) {
38
+ if (!(q.key in drafts)) drafts[q.key] = q.answer ?? ''
39
+ }
40
+ },
41
+ { immediate: true },
42
+ )
43
+
44
+ const resuming = computed(() => docInterview.resuming)
45
+ /** Continue is meaningful once every pending question has a drafted answer. */
46
+ const allAnswered = computed(() => pending.value.every((q) => drafts[q.key]?.trim()))
47
+
48
+ /** Persist one answer if its draft differs from what's recorded. */
49
+ async function persist(q: { id?: string; key: string; answer?: string }) {
50
+ const id = q.id
51
+ if (!id || !blockId.value) return
52
+ const next = (drafts[q.key] ?? '').trim()
53
+ if (!next || next === (q.answer ?? '').trim()) return
54
+ await docInterview.answerQuestion(blockId.value, id, next)
55
+ }
56
+
57
+ /** Flush all dirty drafts, then run a window action (continue / proceed). */
58
+ async function flushThen(action: (id: string) => Promise<unknown>) {
59
+ if (!blockId.value) return
60
+ for (const q of questions.value) await persist(q)
61
+ await action(blockId.value)
62
+ }
63
+
64
+ const onContinue = () => flushThen((id) => docInterview.continueInterview(id))
65
+ const onProceed = () => flushThen((id) => docInterview.proceedInterview(id))
66
+ </script>
67
+
68
+ <template>
69
+ <Teleport to="body">
70
+ <div
71
+ v-if="open"
72
+ class="fixed inset-0 z-50 flex max-h-[100dvh] items-stretch justify-center bg-slate-950/70 backdrop-blur-sm"
73
+ @click.self="close"
74
+ >
75
+ <div
76
+ class="m-4 flex w-full max-w-3xl flex-col overflow-hidden rounded-2xl border border-slate-800 bg-slate-900 shadow-2xl"
77
+ role="dialog"
78
+ aria-modal="true"
79
+ data-testid="doc-interview-window"
80
+ >
81
+ <!-- Header -->
82
+ <header class="flex items-center gap-3 border-b border-slate-800 px-5 py-3">
83
+ <span
84
+ class="flex h-8 w-8 items-center justify-center rounded-lg bg-indigo-500/15 text-indigo-300"
85
+ >
86
+ <UIcon name="i-lucide-messages-square" class="h-4 w-4" />
87
+ </span>
88
+ <div class="min-w-0 flex-1">
89
+ <h2 class="truncate text-sm font-semibold text-slate-100">
90
+ {{ block?.title ?? t('docInterview.title') }}
91
+ </h2>
92
+ <p class="truncate text-[11px] text-slate-400">
93
+ {{ t('docInterview.subtitle') }}
94
+ </p>
95
+ </div>
96
+ <UBadge
97
+ v-if="session"
98
+ :color="converged ? 'success' : 'primary'"
99
+ variant="subtle"
100
+ size="sm"
101
+ >
102
+ {{ t(converged ? 'docInterview.status.done' : 'docInterview.status.awaiting') }}
103
+ </UBadge>
104
+ <button
105
+ class="rounded-md p-1.5 text-slate-400 hover:bg-slate-800 hover:text-slate-200"
106
+ @click="close"
107
+ >
108
+ <UIcon name="i-lucide-x" class="h-4 w-4" />
109
+ </button>
110
+ </header>
111
+
112
+ <div class="min-h-0 flex-1 overflow-y-auto px-5 py-4">
113
+ <!-- No session yet -->
114
+ <div
115
+ v-if="!session"
116
+ class="flex h-full flex-col items-center justify-center gap-2 text-center text-slate-400"
117
+ >
118
+ <UIcon name="i-lucide-messages-square" class="h-8 w-8 opacity-40" />
119
+ <p class="text-sm">{{ t('docInterview.empty') }}</p>
120
+ </div>
121
+
122
+ <template v-else>
123
+ <p class="mb-4 text-[13px] leading-relaxed text-slate-300">
124
+ {{ t('docInterview.intro') }}
125
+ </p>
126
+
127
+ <!-- Converged: show the synthesized authoring brief -->
128
+ <div
129
+ v-if="converged"
130
+ class="rounded-lg border border-slate-800 bg-slate-950/40 p-4"
131
+ data-testid="doc-interview-converged"
132
+ >
133
+ <p class="mb-2 text-[11px] font-medium uppercase tracking-wide text-slate-500">
134
+ {{ t('docInterview.brief') }}
135
+ </p>
136
+ <pre
137
+ v-if="session.brief"
138
+ class="whitespace-pre-wrap break-words text-[13px] leading-relaxed text-slate-300"
139
+ >{{ session.brief }}</pre
140
+ >
141
+ <p v-else class="text-[13px] text-slate-400">{{ t('docInterview.converged') }}</p>
142
+ </div>
143
+
144
+ <!-- No pending questions but not yet converged -->
145
+ <div
146
+ v-else-if="questions.length === 0"
147
+ class="rounded-lg border border-slate-800 bg-slate-950/40 p-4 text-center text-[13px] text-slate-400"
148
+ >
149
+ {{ t('docInterview.converged') }}
150
+ </div>
151
+
152
+ <!-- Interview questions -->
153
+ <ul v-else class="space-y-4">
154
+ <li
155
+ v-for="q in questions"
156
+ :key="q.key"
157
+ class="rounded-lg border border-slate-800 bg-slate-950/40 p-3"
158
+ data-testid="doc-interview-question"
159
+ >
160
+ <p class="mb-2 text-[13px] font-medium text-slate-200">{{ q.question }}</p>
161
+ <UTextarea
162
+ v-model="drafts[q.key]"
163
+ :rows="2"
164
+ autoresize
165
+ :placeholder="t('docInterview.answerPlaceholder')"
166
+ class="w-full"
167
+ data-testid="doc-interview-answer"
168
+ @blur="persist(q)"
169
+ />
170
+ </li>
171
+ </ul>
172
+ </template>
173
+ </div>
174
+
175
+ <!-- Action rail -->
176
+ <footer
177
+ v-if="session && !converged && questions.length > 0"
178
+ class="flex items-center justify-between gap-3 border-t border-slate-800 px-5 py-3"
179
+ >
180
+ <p class="text-[11px] text-slate-500">
181
+ {{ t('docInterview.hint') }}
182
+ </p>
183
+ <div class="flex items-center gap-2">
184
+ <UButton
185
+ color="neutral"
186
+ variant="ghost"
187
+ size="sm"
188
+ :loading="resuming"
189
+ data-testid="doc-interview-proceed"
190
+ @click="onProceed"
191
+ >
192
+ {{ t('docInterview.proceed') }}
193
+ </UButton>
194
+ <UButton
195
+ color="primary"
196
+ size="sm"
197
+ :loading="resuming"
198
+ :disabled="!allAnswered"
199
+ data-testid="doc-interview-continue"
200
+ @click="onContinue"
201
+ >
202
+ {{ t('docInterview.continue') }}
203
+ </UButton>
204
+ </div>
205
+ </footer>
206
+ </div>
207
+ </div>
208
+ </Teleport>
209
+ </template>
@@ -133,7 +133,11 @@ async function unlink(doc: SourceDocument) {
133
133
  :variant="kind === k ? 'soft' : 'ghost'"
134
134
  size="xs"
135
135
  class="uppercase"
136
- @click="kind = k"
136
+ @click="
137
+ () => {
138
+ kind = k
139
+ }
140
+ "
137
141
  >
138
142
  {{ k }}
139
143
  </UButton>
@@ -344,7 +344,11 @@ async function unlinkSource(id: string) {
344
344
  :color="tab === t ? 'primary' : 'neutral'"
345
345
  :variant="tab === t ? 'solid' : 'ghost'"
346
346
  size="sm"
347
- @click="tab = t"
347
+ @click="
348
+ () => {
349
+ tab = t
350
+ }
351
+ "
348
352
  >
349
353
  {{ tabLabel(t) }}
350
354
  </UButton>
@@ -516,7 +520,11 @@ async function unlinkSource(id: string) {
516
520
  size="xs"
517
521
  :color="docDraft.source === s.source ? 'primary' : 'neutral'"
518
522
  :variant="docDraft.source === s.source ? 'solid' : 'outline'"
519
- @click="docDraft.source = s.source"
523
+ @click="
524
+ () => {
525
+ docDraft.source = s.source
526
+ }
527
+ "
520
528
  >
521
529
  {{ s.label }}
522
530
  </UButton>
@@ -319,7 +319,11 @@ async function merge(pr: GitHubPullRequest) {
319
319
  :color="tab === tabItem.id ? 'primary' : 'neutral'"
320
320
  :variant="tab === tabItem.id ? 'soft' : 'ghost'"
321
321
  :icon="tabItem.icon"
322
- @click="tab = tabItem.id"
322
+ @click="
323
+ () => {
324
+ tab = tabItem.id
325
+ }
326
+ "
323
327
  >
324
328
  {{ tabItem.label }}
325
329
  </UButton>
@@ -342,7 +346,11 @@ async function merge(pr: GitHubPullRequest) {
342
346
  color="neutral"
343
347
  variant="soft"
344
348
  icon="i-lucide-list-checks"
345
- @click="managing ? (managing = false) : openManage()"
349
+ @click="
350
+ () => {
351
+ managing ? (managing = false) : openManage()
352
+ }
353
+ "
346
354
  >
347
355
  {{ managing ? t('common.close') : t('github.panel.manageRepos') }}
348
356
  </UButton>
@@ -385,7 +393,16 @@ async function merge(pr: GitHubPullRequest) {
385
393
  </button>
386
394
  </div>
387
395
  <div class="flex items-center justify-end gap-2 pt-1">
388
- <UButton color="neutral" variant="ghost" size="sm" @click="managing = false">
396
+ <UButton
397
+ color="neutral"
398
+ variant="ghost"
399
+ size="sm"
400
+ @click="
401
+ () => {
402
+ managing = false
403
+ }
404
+ "
405
+ >
389
406
  {{ t('common.cancel') }}
390
407
  </UButton>
391
408
  <UButton
@@ -502,7 +519,11 @@ async function merge(pr: GitHubPullRequest) {
502
519
  color="neutral"
503
520
  variant="soft"
504
521
  icon="i-lucide-plus"
505
- @click="showPrForm = !showPrForm"
522
+ @click="
523
+ () => {
524
+ showPrForm = !showPrForm
525
+ }
526
+ "
506
527
  >
507
528
  {{ t('github.panel.openPr') }}
508
529
  </UButton>
@@ -300,7 +300,16 @@ async function submitPrompt() {
300
300
  />
301
301
  </UFormField>
302
302
  <div class="flex justify-end gap-2">
303
- <UButton color="neutral" variant="ghost" :disabled="busy" @click="prompt = null">
303
+ <UButton
304
+ color="neutral"
305
+ variant="ghost"
306
+ :disabled="busy"
307
+ @click="
308
+ () => {
309
+ prompt = null
310
+ }
311
+ "
312
+ >
304
313
  {{ t('common.cancel') }}
305
314
  </UButton>
306
315
  <UButton type="submit" color="primary" :loading="busy">
@@ -105,7 +105,11 @@ const decisionItems = computed(() =>
105
105
  variant="ghost"
106
106
  size="sm"
107
107
  data-testid="board-zoom-out"
108
- @click="zoomOut()"
108
+ @click="
109
+ () => {
110
+ zoomOut()
111
+ }
112
+ "
109
113
  />
110
114
  <!-- The zoom %/LOD readout is the first thing to drop on narrow viewports. -->
111
115
  <div class="hidden w-20 text-center text-xs tabular-nums text-slate-300 sm:block">
@@ -118,7 +122,11 @@ const decisionItems = computed(() =>
118
122
  variant="ghost"
119
123
  size="sm"
120
124
  data-testid="board-zoom-in"
121
- @click="zoomIn()"
125
+ @click="
126
+ () => {
127
+ zoomIn()
128
+ }
129
+ "
122
130
  />
123
131
  <UButton
124
132
  icon="i-lucide-maximize"
@@ -126,7 +134,11 @@ const decisionItems = computed(() =>
126
134
  variant="ghost"
127
135
  size="sm"
128
136
  data-testid="board-fit-view"
129
- @click="fitView({ padding: 0.2 })"
137
+ @click="
138
+ () => {
139
+ fitView({ padding: 0.2 })
140
+ }
141
+ "
130
142
  />
131
143
 
132
144
  <USeparator orientation="vertical" class="mx-1 h-6" />
@@ -34,7 +34,11 @@ const show = computed(() => !!setupUrl.value && !dismissed.value)
34
34
  size="xs"
35
35
  icon="i-lucide-x"
36
36
  :aria-label="t('common.close')"
37
- @click="dismissed = true"
37
+ @click="
38
+ () => {
39
+ dismissed = true
40
+ }
41
+ "
38
42
  />
39
43
  </div>
40
44
  <p class="mt-1 text-sm text-amber-200/90">
@@ -62,7 +62,11 @@ const show = computed(() => pending.value.length > 0 && !dismissed.value)
62
62
  size="xs"
63
63
  icon="i-lucide-x"
64
64
  :aria-label="t('common.close')"
65
- @click="dismissed = true"
65
+ @click="
66
+ () => {
67
+ dismissed = true
68
+ }
69
+ "
66
70
  />
67
71
  </div>
68
72
  <p class="mt-1 text-sm text-amber-200/90">
@@ -403,7 +403,11 @@ async function copyOutput() {
403
403
  :icon="showProvisioning ? 'i-lucide-chevron-up' : 'i-lucide-scroll-text'"
404
404
  variant="ghost"
405
405
  size="xs"
406
- @click="showProvisioning = !showProvisioning"
406
+ @click="
407
+ () => {
408
+ showProvisioning = !showProvisioning
409
+ }
410
+ "
407
411
  >
408
412
  {{
409
413
  showProvisioning
@@ -323,7 +323,11 @@ const showOriginalDescription = ref(false)
323
323
  variant="ghost"
324
324
  size="xs"
325
325
  :icon="showOriginalDescription ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
326
- @click="showOriginalDescription = !showOriginalDescription"
326
+ @click="
327
+ () => {
328
+ showOriginalDescription = !showOriginalDescription
329
+ }
330
+ "
327
331
  >
328
332
  {{ t('panels.inspector.originalDescriptionFrozen') }}
329
333
  </UButton>
@@ -64,7 +64,11 @@ async function restart() {
64
64
  variant="ghost"
65
65
  size="sm"
66
66
  :title="t('panels.stepRestart.restartFromStep')"
67
- @click="armed = true"
67
+ @click="
68
+ () => {
69
+ armed = true
70
+ }
71
+ "
68
72
  />
69
73
  <template v-else>
70
74
  <UButton
@@ -82,7 +86,11 @@ async function restart() {
82
86
  variant="ghost"
83
87
  size="sm"
84
88
  :disabled="restarting"
85
- @click="armed = false"
89
+ @click="
90
+ () => {
91
+ armed = false
92
+ }
93
+ "
86
94
  >
87
95
  {{ t('common.cancel') }}
88
96
  </UButton>
@@ -25,6 +25,7 @@ import FollowUpWindow from '~/components/followUp/FollowUpWindow.vue'
25
25
  import MergerResultView from '~/components/panels/MergerResultView.vue'
26
26
  import InitiativeTrackerWindow from '~/components/initiative/InitiativeTrackerWindow.vue'
27
27
  import InitiativePlanningWindow from '~/components/initiative/InitiativePlanningWindow.vue'
28
+ import DocInterviewWindow from '~/components/docs/DocInterviewWindow.vue'
28
29
 
29
30
  const ui = useUiStore()
30
31
 
@@ -59,6 +60,9 @@ const STEP_RESULT_VIEWS: Record<string, Component> = {
59
60
  // as the planner step's result view.
60
61
  'initiative-tracker': InitiativeTrackerWindow,
61
62
  'initiative-planning': InitiativePlanningWindow,
63
+ // The interactive document-interview gate (WS5): the interviewer's clarifying questions +
64
+ // answer / continue / proceed, opened as the `doc-interviewer` step's result view.
65
+ 'doc-interview': DocInterviewWindow,
62
66
  }
63
67
 
64
68
  const active = computed<Component | null>(() => {