@cat-factory/app 0.138.1 → 0.139.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.
- package/app/components/brainstorm/BrainstormWindow.vue +338 -374
- package/app/components/clarity/ClarityReviewWindow.vue +323 -353
- package/app/components/consensus/ConsensusSessionWindow.vue +138 -150
- package/app/components/docs/DocInterviewWindow.vue +108 -136
- package/app/components/followUp/FollowUpWindow.vue +3 -5
- package/app/components/forkDecision/ForkDecisionWindow.vue +1 -3
- package/app/components/gates/GateResultView.vue +3 -5
- package/app/components/humanTest/HumanTestWindow.vue +3 -5
- package/app/components/initiative/InitiativePlanningWindow.vue +89 -112
- package/app/components/initiative/InitiativeTrackerWindow.vue +389 -424
- package/app/components/panels/GenericStructuredResultView.vue +3 -5
- package/app/components/panels/MergerResultView.vue +3 -5
- package/app/components/panels/ResultWindowShell.vue +1 -1
- package/app/components/prReview/PrReviewWindow.vue +181 -205
- package/app/components/ralph/RalphLoopResultView.vue +3 -5
- package/app/components/requirements/RequirementsReviewWindow.vue +512 -557
- package/app/components/spec/ServiceSpecWindow.vue +235 -266
- package/app/components/testing/TestReportWindow.vue +4 -6
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +3 -5
- package/app/composables/useResultView.ts +10 -21
- package/package.json +1 -1
|
@@ -18,11 +18,9 @@ const agents = useAgentsStore()
|
|
|
18
18
|
const { t } = useI18n()
|
|
19
19
|
|
|
20
20
|
// Shared seam contract (open/blockId/close). No `onOpen` loader: this window reads its data
|
|
21
|
-
// straight off the execution step, so there's nothing to fetch on open. `
|
|
22
|
-
//
|
|
23
|
-
const { open, blockId, instanceId, stepIndex, close } = useResultView('generic-structured'
|
|
24
|
-
manageEscape: false,
|
|
25
|
-
})
|
|
21
|
+
// straight off the execution step, so there's nothing to fetch on open. `ResultWindowShell`
|
|
22
|
+
// owns Escape (and focus trap + scroll lock + stacking).
|
|
23
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('generic-structured')
|
|
26
24
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
27
25
|
|
|
28
26
|
const instance = computed(() =>
|
|
@@ -18,11 +18,9 @@ const agents = useAgentsStore()
|
|
|
18
18
|
const { t, n } = useI18n()
|
|
19
19
|
|
|
20
20
|
// Shared seam contract (open/blockId/close). No loader: the verdict is read straight off
|
|
21
|
-
// the execution step. `
|
|
22
|
-
//
|
|
23
|
-
const { open, blockId, instanceId, stepIndex, close } = useResultView('merger'
|
|
24
|
-
manageEscape: false,
|
|
25
|
-
})
|
|
21
|
+
// the execution step. `ResultWindowShell` owns Escape (and focus trap + scroll lock +
|
|
22
|
+
// stacking) via the shared overlay behaviour.
|
|
23
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('merger')
|
|
26
24
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
27
25
|
|
|
28
26
|
const instance = computed(() =>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// `useModalBehavior` (`@modular-vue/core`, the slice-5 overlay-host release): focus-trap
|
|
13
13
|
// + focus-return, body-scroll lock, and a shared overlay STACK so the top overlay closes
|
|
14
14
|
// first on Escape. A window becomes body-only markup wrapped in `<ResultWindowShell>`; it
|
|
15
|
-
// keeps its `useResultView` seam
|
|
15
|
+
// keeps its `useResultView` seam (Escape lives in this shell now, not in `useResultView`).
|
|
16
16
|
//
|
|
17
17
|
// The pick-one SELECTION of which window is active stays exactly the slice-2
|
|
18
18
|
// `resolveComponentRegistry` in `StepResultViewHost.vue` — this shell only owns the
|
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
PrReviewSeverity,
|
|
19
19
|
PrReviewStepState,
|
|
20
20
|
} from '~/types/execution'
|
|
21
|
+
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
21
22
|
|
|
22
23
|
const execution = useExecutionStore()
|
|
23
24
|
const board = useBoardStore()
|
|
@@ -122,219 +123,194 @@ async function onResolve(action: PrReviewResolution): Promise<void> {
|
|
|
122
123
|
</script>
|
|
123
124
|
|
|
124
125
|
<template>
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
126
|
+
<ResultWindowShell
|
|
127
|
+
:open="open"
|
|
128
|
+
icon="i-lucide-clipboard-check"
|
|
129
|
+
icon-class="bg-indigo-500/15 text-indigo-300"
|
|
130
|
+
:title="block ? t('prReview.titleWithBlock', { title: block.title }) : t('prReview.title')"
|
|
131
|
+
:subtitle="t('prReview.subtitle')"
|
|
132
|
+
width="3xl"
|
|
133
|
+
testid="pr-review-window"
|
|
134
|
+
@close="close"
|
|
135
|
+
>
|
|
136
|
+
<template v-if="state?.prUrl" #header-extras>
|
|
137
|
+
<a
|
|
138
|
+
:href="state.prUrl"
|
|
139
|
+
target="_blank"
|
|
140
|
+
rel="noopener"
|
|
141
|
+
class="rounded-md px-2 py-1 text-[11px] text-indigo-300 hover:bg-slate-800"
|
|
136
142
|
>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
class="flex h-8 w-8 items-center justify-center rounded-lg bg-indigo-500/15 text-indigo-300"
|
|
141
|
-
>
|
|
142
|
-
<UIcon name="i-lucide-clipboard-check" class="h-4 w-4" />
|
|
143
|
-
</span>
|
|
144
|
-
<div class="min-w-0 flex-1">
|
|
145
|
-
<h2 class="truncate text-sm font-semibold text-slate-100">
|
|
146
|
-
{{
|
|
147
|
-
block ? t('prReview.titleWithBlock', { title: block.title }) : t('prReview.title')
|
|
148
|
-
}}
|
|
149
|
-
</h2>
|
|
150
|
-
<p class="truncate text-[11px] text-slate-400">{{ t('prReview.subtitle') }}</p>
|
|
151
|
-
</div>
|
|
152
|
-
<a
|
|
153
|
-
v-if="state?.prUrl"
|
|
154
|
-
:href="state.prUrl"
|
|
155
|
-
target="_blank"
|
|
156
|
-
rel="noopener"
|
|
157
|
-
class="rounded-md px-2 py-1 text-[11px] text-indigo-300 hover:bg-slate-800"
|
|
158
|
-
>
|
|
159
|
-
{{ t('prReview.openPr') }}
|
|
160
|
-
</a>
|
|
161
|
-
<button
|
|
162
|
-
class="rounded-md p-1.5 text-slate-400 hover:bg-slate-800 hover:text-slate-200"
|
|
163
|
-
@click="close"
|
|
164
|
-
>
|
|
165
|
-
<UIcon name="i-lucide-x" class="h-4 w-4" />
|
|
166
|
-
</button>
|
|
167
|
-
</header>
|
|
143
|
+
{{ t('prReview.openPr') }}
|
|
144
|
+
</a>
|
|
145
|
+
</template>
|
|
168
146
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
147
|
+
<div class="min-h-0 flex-1 overflow-y-auto px-5 py-4">
|
|
148
|
+
<!-- Reviewing: the read-only reviewer is still working. -->
|
|
149
|
+
<div
|
|
150
|
+
v-if="status === 'reviewing'"
|
|
151
|
+
class="flex h-full flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
|
|
152
|
+
>
|
|
153
|
+
<UIcon name="i-lucide-loader-circle" class="h-8 w-8 animate-spin opacity-60" />
|
|
154
|
+
<p class="text-sm">{{ t('prReview.reviewing.title') }}</p>
|
|
155
|
+
<p class="max-w-sm text-[11px] text-slate-500">{{ t('prReview.reviewing.hint') }}</p>
|
|
156
|
+
</div>
|
|
179
157
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
158
|
+
<!-- A resolution is executing: the Fixer is committing / comments are being posted. -->
|
|
159
|
+
<div
|
|
160
|
+
v-else-if="working"
|
|
161
|
+
data-testid="pr-review-working"
|
|
162
|
+
class="flex h-full flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
|
|
163
|
+
>
|
|
164
|
+
<UIcon name="i-lucide-loader-circle" class="h-8 w-8 animate-spin opacity-60" />
|
|
165
|
+
<p class="text-sm">
|
|
166
|
+
{{ status === 'fixing' ? t('prReview.fixing.title') : t('prReview.posting.title') }}
|
|
167
|
+
</p>
|
|
168
|
+
<p class="max-w-sm text-[11px] text-slate-500">
|
|
169
|
+
{{ status === 'fixing' ? t('prReview.fixing.hint') : t('prReview.posting.hint') }}
|
|
170
|
+
</p>
|
|
171
|
+
</div>
|
|
194
172
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
173
|
+
<template v-else>
|
|
174
|
+
<p
|
|
175
|
+
v-if="prReview.error"
|
|
176
|
+
class="mb-3 rounded-md bg-rose-500/10 px-3 py-2 text-[12px] text-rose-300"
|
|
177
|
+
>
|
|
178
|
+
{{ prReview.error }}
|
|
179
|
+
</p>
|
|
202
180
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
181
|
+
<!-- The reviewer's overall assessment. -->
|
|
182
|
+
<p
|
|
183
|
+
v-if="state?.summary"
|
|
184
|
+
class="mb-3 rounded-md bg-slate-800/50 px-3 py-2 text-[12px] text-slate-300"
|
|
185
|
+
>
|
|
186
|
+
<span class="text-slate-500">{{ t('prReview.summaryLabel') }}</span>
|
|
187
|
+
{{ state.summary }}
|
|
188
|
+
</p>
|
|
211
189
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
190
|
+
<!-- A clean PR / resolved review with no findings. -->
|
|
191
|
+
<div
|
|
192
|
+
v-if="findings.length === 0"
|
|
193
|
+
class="rounded-xl border border-slate-800 bg-slate-900/60 px-4 py-6 text-center text-[13px] text-slate-300"
|
|
194
|
+
>
|
|
195
|
+
{{ t('prReview.noFindings') }}
|
|
196
|
+
</div>
|
|
219
197
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
198
|
+
<template v-else>
|
|
199
|
+
<!-- Selection toolbar -->
|
|
200
|
+
<div v-if="awaiting" class="mb-2 flex items-center gap-3 text-[11px] text-slate-400">
|
|
201
|
+
<span data-testid="pr-review-selected-count">
|
|
202
|
+
{{ t('prReview.selectedCount', { count: selected.size }) }}
|
|
203
|
+
</span>
|
|
204
|
+
<button class="text-indigo-300 hover:underline" @click="selectAll">
|
|
205
|
+
{{ t('prReview.selectAll') }}
|
|
206
|
+
</button>
|
|
207
|
+
<button class="text-indigo-300 hover:underline" @click="clearAll">
|
|
208
|
+
{{ t('prReview.clear') }}
|
|
209
|
+
</button>
|
|
210
|
+
</div>
|
|
233
211
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
</div>
|
|
277
|
-
<p class="mt-0.5 text-[11px] text-slate-500">
|
|
278
|
-
{{ f.path
|
|
279
|
-
}}<template v-if="f.line != null">
|
|
280
|
-
· {{ t('prReview.line', { line: f.line }) }}</template
|
|
281
|
-
>
|
|
282
|
-
</p>
|
|
283
|
-
<p class="mt-1 whitespace-pre-wrap text-[12px] text-slate-300">
|
|
284
|
-
{{ f.detail }}
|
|
285
|
-
</p>
|
|
286
|
-
<p
|
|
287
|
-
v-if="f.suggestedFix"
|
|
288
|
-
class="mt-1 whitespace-pre-wrap rounded-md bg-slate-800/50 px-2 py-1 text-[11px] text-slate-300"
|
|
289
|
-
>
|
|
290
|
-
<span class="text-slate-500">{{ t('prReview.suggestedFix') }}</span>
|
|
291
|
-
{{ f.suggestedFix }}
|
|
292
|
-
</p>
|
|
293
|
-
</div>
|
|
212
|
+
<!-- Findings grouped by slice -->
|
|
213
|
+
<section v-for="g in groups" :key="g.id" class="mb-4">
|
|
214
|
+
<h3 class="mb-1.5 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
215
|
+
{{ g.title }}
|
|
216
|
+
</h3>
|
|
217
|
+
<p v-if="g.rationale" class="mb-1.5 text-[11px] text-slate-500">
|
|
218
|
+
{{ g.rationale }}
|
|
219
|
+
</p>
|
|
220
|
+
<article
|
|
221
|
+
v-for="f in g.items"
|
|
222
|
+
:key="f.id"
|
|
223
|
+
data-testid="pr-review-finding"
|
|
224
|
+
class="mb-1.5 rounded-xl border px-3 py-2 transition"
|
|
225
|
+
:class="
|
|
226
|
+
awaiting && selected.has(f.id)
|
|
227
|
+
? 'border-indigo-500/60 bg-indigo-500/5'
|
|
228
|
+
: 'border-slate-800 bg-slate-900/60'
|
|
229
|
+
"
|
|
230
|
+
>
|
|
231
|
+
<div class="flex items-start gap-2">
|
|
232
|
+
<input
|
|
233
|
+
v-if="awaiting"
|
|
234
|
+
type="checkbox"
|
|
235
|
+
class="mt-1 accent-indigo-500"
|
|
236
|
+
data-testid="pr-review-finding-toggle"
|
|
237
|
+
:checked="selected.has(f.id)"
|
|
238
|
+
@change="toggle(f.id)"
|
|
239
|
+
/>
|
|
240
|
+
<div class="min-w-0 flex-1">
|
|
241
|
+
<div class="flex flex-wrap items-center gap-1.5">
|
|
242
|
+
<span
|
|
243
|
+
class="rounded px-1.5 py-0.5 text-[10px] font-semibold uppercase ring-1"
|
|
244
|
+
:class="SEVERITY_CLASS[f.severity]"
|
|
245
|
+
>
|
|
246
|
+
{{ t(`prReview.severity.${f.severity}`) }}
|
|
247
|
+
</span>
|
|
248
|
+
<span class="rounded bg-slate-800 px-1.5 py-0.5 text-[10px] text-slate-300">
|
|
249
|
+
{{ t(`prReview.category.${f.category}`) }}
|
|
250
|
+
</span>
|
|
251
|
+
<h4 class="min-w-0 flex-1 text-[13px] font-medium text-slate-100">
|
|
252
|
+
{{ f.title }}
|
|
253
|
+
</h4>
|
|
294
254
|
</div>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
color="neutral"
|
|
318
|
-
variant="soft"
|
|
319
|
-
:disabled="!canResolve || !hasSelection || !access.canExecuteRuns.value"
|
|
320
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
321
|
-
data-testid="pr-review-post"
|
|
322
|
-
@click="onResolve('post')"
|
|
323
|
-
>
|
|
324
|
-
{{ t('prReview.post') }}
|
|
325
|
-
</UButton>
|
|
326
|
-
<UButton
|
|
327
|
-
color="primary"
|
|
328
|
-
:loading="prReview.resolving"
|
|
329
|
-
:disabled="!canResolve || !hasSelection || !access.canExecuteRuns.value"
|
|
330
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
331
|
-
data-testid="pr-review-fix"
|
|
332
|
-
@click="onResolve('fix')"
|
|
333
|
-
>
|
|
334
|
-
{{ t('prReview.fix') }}
|
|
335
|
-
</UButton>
|
|
336
|
-
</footer>
|
|
337
|
-
</div>
|
|
255
|
+
<p class="mt-0.5 text-[11px] text-slate-500">
|
|
256
|
+
{{ f.path
|
|
257
|
+
}}<template v-if="f.line != null">
|
|
258
|
+
· {{ t('prReview.line', { line: f.line }) }}</template
|
|
259
|
+
>
|
|
260
|
+
</p>
|
|
261
|
+
<p class="mt-1 whitespace-pre-wrap text-[12px] text-slate-300">
|
|
262
|
+
{{ f.detail }}
|
|
263
|
+
</p>
|
|
264
|
+
<p
|
|
265
|
+
v-if="f.suggestedFix"
|
|
266
|
+
class="mt-1 whitespace-pre-wrap rounded-md bg-slate-800/50 px-2 py-1 text-[11px] text-slate-300"
|
|
267
|
+
>
|
|
268
|
+
<span class="text-slate-500">{{ t('prReview.suggestedFix') }}</span>
|
|
269
|
+
{{ f.suggestedFix }}
|
|
270
|
+
</p>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</article>
|
|
274
|
+
</section>
|
|
275
|
+
</template>
|
|
276
|
+
</template>
|
|
338
277
|
</div>
|
|
339
|
-
|
|
278
|
+
|
|
279
|
+
<!-- Footer -->
|
|
280
|
+
<footer
|
|
281
|
+
v-if="awaiting"
|
|
282
|
+
class="flex items-center justify-end gap-2 border-t border-slate-800 px-5 py-3"
|
|
283
|
+
>
|
|
284
|
+
<UButton
|
|
285
|
+
color="neutral"
|
|
286
|
+
variant="ghost"
|
|
287
|
+
:disabled="!canResolve || !access.canExecuteRuns.value"
|
|
288
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
289
|
+
data-testid="pr-review-finish"
|
|
290
|
+
@click="onResolve('finish')"
|
|
291
|
+
>
|
|
292
|
+
{{ t('prReview.finish') }}
|
|
293
|
+
</UButton>
|
|
294
|
+
<UButton
|
|
295
|
+
color="neutral"
|
|
296
|
+
variant="soft"
|
|
297
|
+
:disabled="!canResolve || !hasSelection || !access.canExecuteRuns.value"
|
|
298
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
299
|
+
data-testid="pr-review-post"
|
|
300
|
+
@click="onResolve('post')"
|
|
301
|
+
>
|
|
302
|
+
{{ t('prReview.post') }}
|
|
303
|
+
</UButton>
|
|
304
|
+
<UButton
|
|
305
|
+
color="primary"
|
|
306
|
+
:loading="prReview.resolving"
|
|
307
|
+
:disabled="!canResolve || !hasSelection || !access.canExecuteRuns.value"
|
|
308
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
309
|
+
data-testid="pr-review-fix"
|
|
310
|
+
@click="onResolve('fix')"
|
|
311
|
+
>
|
|
312
|
+
{{ t('prReview.fix') }}
|
|
313
|
+
</UButton>
|
|
314
|
+
</footer>
|
|
315
|
+
</ResultWindowShell>
|
|
340
316
|
</template>
|
|
@@ -15,11 +15,9 @@ const board = useBoardStore()
|
|
|
15
15
|
const execution = useExecutionStore()
|
|
16
16
|
const { t, d } = useI18n()
|
|
17
17
|
|
|
18
|
-
// `
|
|
19
|
-
//
|
|
20
|
-
const { open, blockId, instanceId, stepIndex, close } = useResultView('ralph-loop'
|
|
21
|
-
manageEscape: false,
|
|
22
|
-
})
|
|
18
|
+
// `ResultWindowShell` owns Escape (and focus trap + scroll lock + stacking) via the shared
|
|
19
|
+
// overlay behaviour.
|
|
20
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('ralph-loop')
|
|
23
21
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
24
22
|
const headerTitle = computed(
|
|
25
23
|
() => `${meta.value.label}${block.value ? ` — ${block.value.title}` : ''}`,
|