@cat-factory/app 0.137.2 → 0.138.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.
- package/app/components/documents/TaskContextDocs.vue +1 -0
- package/app/components/followUp/FollowUpWindow.vue +174 -200
- package/app/components/forkDecision/ForkDecisionWindow.vue +250 -275
- package/app/components/gates/GateResultView.vue +309 -333
- package/app/components/humanTest/HumanTestWindow.vue +212 -236
- package/app/components/media/ArtifactLightbox.vue +20 -19
- package/app/components/panels/GenericStructuredResultView.vue +60 -88
- package/app/components/ralph/RalphLoopResultView.vue +150 -171
- package/app/components/testing/TestReportWindow.vue +505 -559
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +224 -262
- package/i18n/locales/de.json +0 -1
- package/i18n/locales/en.json +0 -1
- package/i18n/locales/es.json +0 -1
- package/i18n/locales/fr.json +0 -1
- package/i18n/locales/he.json +0 -1
- package/i18n/locales/it.json +0 -1
- package/i18n/locales/ja.json +0 -1
- package/i18n/locales/pl.json +0 -1
- package/i18n/locales/tr.json +0 -1
- package/i18n/locales/uk.json +0 -1
- package/package.json +1 -1
- package/app/composables/useFocusTrap.ts +0 -72
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
import { computed, onUnmounted, reactive, ref, watch } from 'vue'
|
|
11
11
|
import type { VisualConfirmStepState } from '~/types/execution'
|
|
12
12
|
import { useArtifactBlobs } from '~/composables/useArtifactBlobs'
|
|
13
|
-
import { useFocusTrap } from '~/composables/useFocusTrap'
|
|
14
13
|
import ImageCompare from '~/components/media/ImageCompare.vue'
|
|
15
14
|
import ArtifactLightbox from '~/components/media/ArtifactLightbox.vue'
|
|
15
|
+
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
16
16
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
17
17
|
|
|
18
18
|
const board = useBoardStore()
|
|
@@ -26,8 +26,17 @@ const access = useWorkspaceAccess()
|
|
|
26
26
|
const blobs = useArtifactBlobs()
|
|
27
27
|
onUnmounted(() => blobs.revokeAll())
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// `manageEscape: false` — `ResultWindowShell` owns Escape (and the focus trap + scroll lock +
|
|
30
|
+
// stacking); the nested lightbox layers above it on the same shared overlay stack.
|
|
31
|
+
const { open, blockId, instanceId, stepIndex, close } = useResultView('visual-confirm', {
|
|
32
|
+
manageEscape: false,
|
|
33
|
+
})
|
|
30
34
|
const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
|
|
35
|
+
const headerTitle = computed(() =>
|
|
36
|
+
block.value
|
|
37
|
+
? t('visualConfirm.titleWithBlock', { title: block.value.title })
|
|
38
|
+
: t('visualConfirm.title'),
|
|
39
|
+
)
|
|
31
40
|
|
|
32
41
|
const instance = computed(() =>
|
|
33
42
|
instanceId.value === null ? null : (execution.getInstance(instanceId.value) ?? null),
|
|
@@ -95,14 +104,6 @@ function expand(artifactId: string) {
|
|
|
95
104
|
lightboxOpen.value = true
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
// Focus management for the modal panel. While the lightbox is open it owns the trap, so the
|
|
99
|
-
// window hands off (active = open && !lightbox) to avoid two Tab traps fighting.
|
|
100
|
-
const dialogRoot = ref<HTMLElement | null>(null)
|
|
101
|
-
useFocusTrap(
|
|
102
|
-
dialogRoot,
|
|
103
|
-
computed(() => open.value && !lightboxOpen.value),
|
|
104
|
-
)
|
|
105
|
-
|
|
106
107
|
// --- Request a fix: per-view notes + a freeform box, composed into one findings string. ---
|
|
107
108
|
const perViewNotes = reactive<Record<string, string>>({})
|
|
108
109
|
const noteOpen = reactive<Record<string, boolean>>({})
|
|
@@ -187,278 +188,239 @@ const canApprove = computed(
|
|
|
187
188
|
</script>
|
|
188
189
|
|
|
189
190
|
<template>
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
<ResultWindowShell
|
|
192
|
+
:open="open"
|
|
193
|
+
icon="i-lucide-image-play"
|
|
194
|
+
icon-class="bg-amber-500/15 text-amber-300"
|
|
195
|
+
:title="headerTitle"
|
|
196
|
+
:subtitle="phase ? PHASE_LABEL[phase] : t('visualConfirm.subtitle')"
|
|
197
|
+
width="5xl"
|
|
198
|
+
@close="close"
|
|
199
|
+
>
|
|
200
|
+
<div class="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4">
|
|
196
201
|
<div
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
role="dialog"
|
|
200
|
-
aria-modal="true"
|
|
201
|
-
:aria-label="t('visualConfirm.ariaLabel')"
|
|
202
|
-
class="m-4 flex w-full max-w-5xl flex-col overflow-hidden rounded-2xl border border-slate-800 bg-slate-900 shadow-2xl focus:outline-none"
|
|
202
|
+
v-if="!vc"
|
|
203
|
+
class="flex flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
|
|
203
204
|
>
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
>
|
|
208
|
-
<UIcon name="i-lucide-image-play" class="h-4 w-4" />
|
|
209
|
-
</span>
|
|
210
|
-
<div class="min-w-0 flex-1">
|
|
211
|
-
<h2 class="truncate text-sm font-semibold text-slate-100">
|
|
212
|
-
{{
|
|
213
|
-
block
|
|
214
|
-
? t('visualConfirm.titleWithBlock', { title: block.title })
|
|
215
|
-
: t('visualConfirm.title')
|
|
216
|
-
}}
|
|
217
|
-
</h2>
|
|
218
|
-
<p class="truncate text-[11px] text-slate-400">
|
|
219
|
-
{{ phase ? PHASE_LABEL[phase] : t('visualConfirm.subtitle') }}
|
|
220
|
-
</p>
|
|
221
|
-
</div>
|
|
222
|
-
<button
|
|
223
|
-
class="rounded-md p-1.5 text-slate-400 hover:bg-slate-800 hover:text-slate-200"
|
|
224
|
-
@click="close"
|
|
225
|
-
>
|
|
226
|
-
<UIcon name="i-lucide-x" class="h-4 w-4" />
|
|
227
|
-
</button>
|
|
228
|
-
</header>
|
|
229
|
-
|
|
230
|
-
<div class="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4">
|
|
231
|
-
<div
|
|
232
|
-
v-if="!vc"
|
|
233
|
-
class="flex flex-col items-center justify-center gap-2 py-10 text-center text-slate-400"
|
|
234
|
-
>
|
|
235
|
-
<UIcon name="i-lucide-image-play" class="h-8 w-8 opacity-40" />
|
|
236
|
-
<p class="text-sm">{{ t('visualConfirm.notStarted') }}</p>
|
|
237
|
-
</div>
|
|
205
|
+
<UIcon name="i-lucide-image-play" class="h-8 w-8 opacity-40" />
|
|
206
|
+
<p class="text-sm">{{ t('visualConfirm.notStarted') }}</p>
|
|
207
|
+
</div>
|
|
238
208
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
209
|
+
<template v-else>
|
|
210
|
+
<p
|
|
211
|
+
v-if="vc.degradedReason"
|
|
212
|
+
class="rounded-lg border border-amber-700/40 bg-amber-500/5 px-3 py-2 text-[12px] text-amber-300/90"
|
|
213
|
+
>
|
|
214
|
+
{{ vc.degradedReason }}
|
|
215
|
+
</p>
|
|
246
216
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
217
|
+
<p
|
|
218
|
+
v-if="working"
|
|
219
|
+
class="flex items-center gap-2 rounded-lg border border-slate-800 bg-slate-950/40 px-3 py-2 text-[12px] text-slate-300"
|
|
220
|
+
>
|
|
221
|
+
<UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-amber-300" />
|
|
222
|
+
{{ phase ? PHASE_LABEL[phase] : '' }}
|
|
223
|
+
</p>
|
|
254
224
|
|
|
255
|
-
|
|
225
|
+
<!-- Actual-vs-reference gallery. Keyed by `view` (the contract's unique per-pair
|
|
256
226
|
identity) so a pair's note/expand state stays bound to its view across recaptures. -->
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
/>
|
|
278
|
-
{{ t('visualConfirm.noteIssue', { view: p.view }) }}
|
|
279
|
-
<span
|
|
280
|
-
v-if="perViewNotes[p.view]?.trim()"
|
|
281
|
-
class="rounded-full bg-amber-500/15 px-1.5 text-[9px] text-amber-300"
|
|
282
|
-
>{{ t('visualConfirm.noted') }}</span
|
|
283
|
-
>
|
|
284
|
-
</button>
|
|
285
|
-
<textarea
|
|
286
|
-
v-if="noteOpen[p.view]"
|
|
287
|
-
v-model="perViewNotes[p.view]"
|
|
288
|
-
rows="2"
|
|
289
|
-
:placeholder="t('visualConfirm.notePlaceholder', { view: p.view })"
|
|
290
|
-
class="mt-1 w-full rounded-md border border-slate-700 bg-slate-950 px-2 py-1.5 text-[12px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/60"
|
|
291
|
-
/>
|
|
292
|
-
</div>
|
|
293
|
-
</div>
|
|
294
|
-
</section>
|
|
295
|
-
<p v-else class="text-[12px] italic text-slate-500">
|
|
296
|
-
{{ t('visualConfirm.noScreenshots') }}
|
|
297
|
-
</p>
|
|
298
|
-
|
|
299
|
-
<!-- Upload a reference for any view -->
|
|
300
|
-
<section class="rounded-lg border border-slate-800 bg-slate-900/60 p-3">
|
|
301
|
-
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
302
|
-
{{ t('visualConfirm.upload.heading') }}
|
|
303
|
-
</h3>
|
|
304
|
-
<div class="flex flex-wrap items-center gap-2">
|
|
305
|
-
<input
|
|
306
|
-
v-model="uploadView"
|
|
307
|
-
list="vc-views"
|
|
308
|
-
:placeholder="t('visualConfirm.upload.viewPlaceholder')"
|
|
309
|
-
class="rounded-md border border-slate-700 bg-slate-950 px-2 py-1 text-[12px] text-slate-200 placeholder:text-slate-600"
|
|
310
|
-
/>
|
|
311
|
-
<datalist id="vc-views">
|
|
312
|
-
<option v-for="p in pairs" :key="p.view" :value="p.view" />
|
|
313
|
-
</datalist>
|
|
314
|
-
<input
|
|
315
|
-
ref="fileInput"
|
|
316
|
-
type="file"
|
|
317
|
-
accept="image/png,image/jpeg"
|
|
318
|
-
:disabled="busy || !uploadView.trim()"
|
|
319
|
-
class="text-[12px] text-slate-300 file:me-2 file:rounded file:border-0 file:bg-slate-800 file:px-2 file:py-1 file:text-slate-200 disabled:opacity-40"
|
|
320
|
-
@change="onFilePicked"
|
|
227
|
+
<section v-if="pairs.length" class="space-y-4">
|
|
228
|
+
<div v-for="p in pairs" :key="p.view" class="space-y-2">
|
|
229
|
+
<ImageCompare
|
|
230
|
+
:view="p.view"
|
|
231
|
+
:actual-id="p.actualArtifactId"
|
|
232
|
+
:reference-id="p.referenceArtifactId"
|
|
233
|
+
:blobs="blobs"
|
|
234
|
+
:busy="busy"
|
|
235
|
+
@expand="expand"
|
|
236
|
+
@upload-reference="(file: File) => uploadFor(p.view, file)"
|
|
237
|
+
/>
|
|
238
|
+
<!-- Per-view note (folded into the fixer findings) -->
|
|
239
|
+
<div v-if="awaitingHuman" class="px-1">
|
|
240
|
+
<button
|
|
241
|
+
class="flex items-center gap-1.5 text-[11px] text-slate-400 hover:text-slate-200"
|
|
242
|
+
@click="noteOpen[p.view] = !noteOpen[p.view]"
|
|
243
|
+
>
|
|
244
|
+
<UIcon
|
|
245
|
+
:name="noteOpen[p.view] ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
246
|
+
class="h-3 w-3"
|
|
321
247
|
/>
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
</p>
|
|
330
|
-
</section>
|
|
331
|
-
|
|
332
|
-
<!-- Request fix -->
|
|
333
|
-
<section
|
|
334
|
-
v-if="awaitingHuman"
|
|
335
|
-
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
336
|
-
>
|
|
337
|
-
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
338
|
-
{{ t('visualConfirm.requestFix.heading') }}
|
|
339
|
-
</h3>
|
|
248
|
+
{{ t('visualConfirm.noteIssue', { view: p.view }) }}
|
|
249
|
+
<span
|
|
250
|
+
v-if="perViewNotes[p.view]?.trim()"
|
|
251
|
+
class="rounded-full bg-amber-500/15 px-1.5 text-[9px] text-amber-300"
|
|
252
|
+
>{{ t('visualConfirm.noted') }}</span
|
|
253
|
+
>
|
|
254
|
+
</button>
|
|
340
255
|
<textarea
|
|
341
|
-
v-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
256
|
+
v-if="noteOpen[p.view]"
|
|
257
|
+
v-model="perViewNotes[p.view]"
|
|
258
|
+
rows="2"
|
|
259
|
+
:placeholder="t('visualConfirm.notePlaceholder', { view: p.view })"
|
|
260
|
+
class="mt-1 w-full rounded-md border border-slate-700 bg-slate-950 px-2 py-1.5 text-[12px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/60"
|
|
345
261
|
/>
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
color="warning"
|
|
353
|
-
icon="i-lucide-wrench"
|
|
354
|
-
:loading="busy"
|
|
355
|
-
:disabled="busy || !hasFindings || !access.canExecuteRuns.value"
|
|
356
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
357
|
-
@click="submitFix"
|
|
358
|
-
>
|
|
359
|
-
{{ t('visualConfirm.requestFix.send') }}
|
|
360
|
-
</UButton>
|
|
361
|
-
</div>
|
|
362
|
-
</section>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</section>
|
|
265
|
+
<p v-else class="text-[12px] italic text-slate-500">
|
|
266
|
+
{{ t('visualConfirm.noScreenshots') }}
|
|
267
|
+
</p>
|
|
363
268
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}}
|
|
397
|
-
</span>
|
|
398
|
-
<p v-if="r.findings" class="whitespace-pre-wrap leading-snug text-slate-400">
|
|
399
|
-
{{ r.findings }}
|
|
400
|
-
</p>
|
|
401
|
-
</div>
|
|
402
|
-
</li>
|
|
403
|
-
</ol>
|
|
404
|
-
</section>
|
|
405
|
-
</template>
|
|
406
|
-
</div>
|
|
269
|
+
<!-- Upload a reference for any view -->
|
|
270
|
+
<section class="rounded-lg border border-slate-800 bg-slate-900/60 p-3">
|
|
271
|
+
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
272
|
+
{{ t('visualConfirm.upload.heading') }}
|
|
273
|
+
</h3>
|
|
274
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
275
|
+
<input
|
|
276
|
+
v-model="uploadView"
|
|
277
|
+
list="vc-views"
|
|
278
|
+
:placeholder="t('visualConfirm.upload.viewPlaceholder')"
|
|
279
|
+
class="rounded-md border border-slate-700 bg-slate-950 px-2 py-1 text-[12px] text-slate-200 placeholder:text-slate-600"
|
|
280
|
+
/>
|
|
281
|
+
<datalist id="vc-views">
|
|
282
|
+
<option v-for="p in pairs" :key="p.view" :value="p.view" />
|
|
283
|
+
</datalist>
|
|
284
|
+
<input
|
|
285
|
+
ref="fileInput"
|
|
286
|
+
type="file"
|
|
287
|
+
accept="image/png,image/jpeg"
|
|
288
|
+
:disabled="busy || !uploadView.trim()"
|
|
289
|
+
class="text-[12px] text-slate-300 file:me-2 file:rounded file:border-0 file:bg-slate-800 file:px-2 file:py-1 file:text-slate-200 disabled:opacity-40"
|
|
290
|
+
@change="onFilePicked"
|
|
291
|
+
/>
|
|
292
|
+
</div>
|
|
293
|
+
<p class="mt-1.5 text-[10px] text-slate-600">
|
|
294
|
+
{{
|
|
295
|
+
uploadView.trim()
|
|
296
|
+
? t('visualConfirm.upload.tipReady')
|
|
297
|
+
: t('visualConfirm.upload.tipNeedView')
|
|
298
|
+
}}
|
|
299
|
+
</p>
|
|
300
|
+
</section>
|
|
407
301
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
302
|
+
<!-- Request fix -->
|
|
303
|
+
<section
|
|
304
|
+
v-if="awaitingHuman"
|
|
305
|
+
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
411
306
|
>
|
|
412
|
-
<
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
:
|
|
419
|
-
:
|
|
307
|
+
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
308
|
+
{{ t('visualConfirm.requestFix.heading') }}
|
|
309
|
+
</h3>
|
|
310
|
+
<textarea
|
|
311
|
+
v-model="globalFindings"
|
|
312
|
+
rows="3"
|
|
313
|
+
:placeholder="t('visualConfirm.requestFix.placeholder')"
|
|
314
|
+
class="w-full rounded-md border border-slate-700 bg-slate-950 px-3 py-2 text-[13px] text-slate-200 placeholder:text-slate-600 focus:border-amber-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/60"
|
|
420
315
|
/>
|
|
421
|
-
<div class="flex items-center
|
|
422
|
-
<
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
>
|
|
426
|
-
<input v-model="ackDegraded" type="checkbox" class="accent-amber-500" />
|
|
427
|
-
{{ t('visualConfirm.reviewedManually') }}
|
|
428
|
-
</label>
|
|
316
|
+
<div class="mt-2 flex items-center justify-between">
|
|
317
|
+
<span class="text-[11px] text-slate-500">
|
|
318
|
+
{{ t('visualConfirm.requestFix.foldedHint') }}
|
|
319
|
+
</span>
|
|
429
320
|
<UButton
|
|
430
321
|
size="sm"
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
icon="i-lucide-refresh-cw"
|
|
322
|
+
color="warning"
|
|
323
|
+
icon="i-lucide-wrench"
|
|
434
324
|
:loading="busy"
|
|
435
|
-
:disabled="busy || !
|
|
325
|
+
:disabled="busy || !hasFindings || !access.canExecuteRuns.value"
|
|
436
326
|
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
437
|
-
@click="
|
|
327
|
+
@click="submitFix"
|
|
438
328
|
>
|
|
439
|
-
{{ t('visualConfirm.
|
|
440
|
-
</UButton>
|
|
441
|
-
<UButton
|
|
442
|
-
color="primary"
|
|
443
|
-
icon="i-lucide-circle-check"
|
|
444
|
-
:loading="busy"
|
|
445
|
-
:disabled="!canApprove || !access.canExecuteRuns.value"
|
|
446
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
447
|
-
@click="approve"
|
|
448
|
-
>
|
|
449
|
-
{{ t('visualConfirm.approve') }}
|
|
329
|
+
{{ t('visualConfirm.requestFix.send') }}
|
|
450
330
|
</UButton>
|
|
451
331
|
</div>
|
|
452
|
-
</
|
|
453
|
-
|
|
332
|
+
</section>
|
|
333
|
+
|
|
334
|
+
<!-- Rounds history -->
|
|
335
|
+
<section
|
|
336
|
+
v-if="vc.rounds && vc.rounds.length"
|
|
337
|
+
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
338
|
+
>
|
|
339
|
+
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
340
|
+
{{ t('visualConfirm.history.heading', { count: vc.attempts }, vc.attempts) }}
|
|
341
|
+
</h3>
|
|
342
|
+
<ol class="space-y-2">
|
|
343
|
+
<li v-for="(r, i) in vc.rounds" :key="i" class="flex items-start gap-2 text-[12px]">
|
|
344
|
+
<UIcon name="i-lucide-wrench" class="mt-0.5 h-3.5 w-3.5 shrink-0 text-slate-400" />
|
|
345
|
+
<div class="min-w-0 flex-1">
|
|
346
|
+
<span class="text-slate-200">{{ t('visualConfirm.history.fixRequested') }}</span>
|
|
347
|
+
<span
|
|
348
|
+
class="ms-1.5 rounded px-1 text-[10px] uppercase"
|
|
349
|
+
:class="
|
|
350
|
+
r.outcome === 'completed'
|
|
351
|
+
? 'bg-emerald-500/15 text-emerald-300'
|
|
352
|
+
: r.outcome === 'failed'
|
|
353
|
+
? 'bg-rose-500/15 text-rose-300'
|
|
354
|
+
: 'bg-slate-500/15 text-slate-300'
|
|
355
|
+
"
|
|
356
|
+
>
|
|
357
|
+
{{
|
|
358
|
+
r.outcome ? OUTCOME_LABELS[r.outcome] : t('visualConfirm.outcome.inProgress')
|
|
359
|
+
}}
|
|
360
|
+
</span>
|
|
361
|
+
<p v-if="r.findings" class="whitespace-pre-wrap leading-snug text-slate-400">
|
|
362
|
+
{{ r.findings }}
|
|
363
|
+
</p>
|
|
364
|
+
</div>
|
|
365
|
+
</li>
|
|
366
|
+
</ol>
|
|
367
|
+
</section>
|
|
368
|
+
</template>
|
|
454
369
|
</div>
|
|
455
370
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
371
|
+
<footer
|
|
372
|
+
v-if="vc"
|
|
373
|
+
class="flex items-center justify-between gap-3 border-t border-slate-800 px-5 py-3"
|
|
374
|
+
>
|
|
375
|
+
<StepRunMeta
|
|
376
|
+
v-if="step"
|
|
377
|
+
:step="step"
|
|
378
|
+
:instance-id="instanceId ?? undefined"
|
|
379
|
+
:step-number="stepIndex === null ? undefined : stepIndex + 1"
|
|
380
|
+
:total-steps="instance?.steps.length"
|
|
381
|
+
:run-failed="instance?.status === 'failed'"
|
|
382
|
+
:failure-at="instance?.failure?.occurredAt"
|
|
383
|
+
/>
|
|
384
|
+
<div class="flex items-center gap-2">
|
|
385
|
+
<label
|
|
386
|
+
v-if="awaitingHuman && needsAck"
|
|
387
|
+
class="flex items-center gap-1.5 text-[11px] text-amber-300/90"
|
|
388
|
+
>
|
|
389
|
+
<input v-model="ackDegraded" type="checkbox" class="accent-amber-500" />
|
|
390
|
+
{{ t('visualConfirm.reviewedManually') }}
|
|
391
|
+
</label>
|
|
392
|
+
<UButton
|
|
393
|
+
size="sm"
|
|
394
|
+
variant="soft"
|
|
395
|
+
color="neutral"
|
|
396
|
+
icon="i-lucide-refresh-cw"
|
|
397
|
+
:loading="busy"
|
|
398
|
+
:disabled="busy || !awaitingHuman || !access.canExecuteRuns.value"
|
|
399
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
400
|
+
@click="recapture"
|
|
401
|
+
>
|
|
402
|
+
{{ t('visualConfirm.recapture') }}
|
|
403
|
+
</UButton>
|
|
404
|
+
<UButton
|
|
405
|
+
color="primary"
|
|
406
|
+
icon="i-lucide-circle-check"
|
|
407
|
+
:loading="busy"
|
|
408
|
+
:disabled="!canApprove || !access.canExecuteRuns.value"
|
|
409
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
410
|
+
@click="approve"
|
|
411
|
+
>
|
|
412
|
+
{{ t('visualConfirm.approve') }}
|
|
413
|
+
</UButton>
|
|
414
|
+
</div>
|
|
415
|
+
</footer>
|
|
416
|
+
</ResultWindowShell>
|
|
417
|
+
|
|
418
|
+
<!-- Shared zoom/pan viewer for any screenshot in the gallery — a sibling overlay that layers
|
|
419
|
+
above this window on the shared modal stack while open. -->
|
|
420
|
+
<ArtifactLightbox
|
|
421
|
+
v-model:open="lightboxOpen"
|
|
422
|
+
v-model:index="lightboxIndex"
|
|
423
|
+
:items="lightboxItems"
|
|
424
|
+
:blobs="blobs"
|
|
425
|
+
/>
|
|
464
426
|
</template>
|
package/i18n/locales/de.json
CHANGED
|
@@ -4445,7 +4445,6 @@
|
|
|
4445
4445
|
"footer": "Szenarien sind die Bereiche, die der Tester zum Prüfen gewählt hat (seine Akzeptanzszenarien aus der Spezifikation). Ergebnisse und Bedenken werden nach Name darunter gruppiert."
|
|
4446
4446
|
},
|
|
4447
4447
|
"visualConfirm": {
|
|
4448
|
-
"ariaLabel": "Visuelle Bestätigung",
|
|
4449
4448
|
"title": "Visuelle Bestätigung",
|
|
4450
4449
|
"titleWithBlock": "Visuelle Bestätigung — {title}",
|
|
4451
4450
|
"subtitle": "Die UI mit den Referenzdesigns vergleichen",
|
package/i18n/locales/en.json
CHANGED
package/i18n/locales/es.json
CHANGED
|
@@ -4111,7 +4111,6 @@
|
|
|
4111
4111
|
}
|
|
4112
4112
|
},
|
|
4113
4113
|
"visualConfirm": {
|
|
4114
|
-
"ariaLabel": "Confirmación visual",
|
|
4115
4114
|
"title": "Confirmación visual",
|
|
4116
4115
|
"titleWithBlock": "Confirmación visual — {title}",
|
|
4117
4116
|
"subtitle": "Revisa la interfaz frente a los diseños de referencia",
|
package/i18n/locales/fr.json
CHANGED
|
@@ -4111,7 +4111,6 @@
|
|
|
4111
4111
|
}
|
|
4112
4112
|
},
|
|
4113
4113
|
"visualConfirm": {
|
|
4114
|
-
"ariaLabel": "Confirmation visuelle",
|
|
4115
4114
|
"title": "Confirmation visuelle",
|
|
4116
4115
|
"titleWithBlock": "Confirmation visuelle — {title}",
|
|
4117
4116
|
"subtitle": "Comparez l'interface aux maquettes de référence",
|
package/i18n/locales/he.json
CHANGED
|
@@ -4122,7 +4122,6 @@
|
|
|
4122
4122
|
"footer": "תרחישים הם התחומים שהבודק בחר לבחון (תרחישי קבלת המפרט שלו). תוצאות וחששות מקובצים תחתם לפי שם."
|
|
4123
4123
|
},
|
|
4124
4124
|
"visualConfirm": {
|
|
4125
|
-
"ariaLabel": "אישור חזותי",
|
|
4126
4125
|
"title": "אישור חזותי",
|
|
4127
4126
|
"titleWithBlock": "אישור חזותי — {title}",
|
|
4128
4127
|
"subtitle": "סקור את הממשק מול עיצובי הייחוס",
|
package/i18n/locales/it.json
CHANGED
|
@@ -4445,7 +4445,6 @@
|
|
|
4445
4445
|
"footer": "Gli scenari sono le aree che il Tester ha scelto di verificare (i suoi scenari di accettazione della specifica). Esiti e rilievi sono raggruppati sotto di essi per nome."
|
|
4446
4446
|
},
|
|
4447
4447
|
"visualConfirm": {
|
|
4448
|
-
"ariaLabel": "Conferma visiva",
|
|
4449
4448
|
"title": "Conferma visiva",
|
|
4450
4449
|
"titleWithBlock": "Conferma visiva — {title}",
|
|
4451
4450
|
"subtitle": "Confronta l'interfaccia con i design di riferimento",
|
package/i18n/locales/ja.json
CHANGED
|
@@ -4123,7 +4123,6 @@
|
|
|
4123
4123
|
"footer": "シナリオはTesterが検証対象に選んだ領域(スペックの受け入れシナリオ)です。結果と懸念は名前ごとにその下にグループ化されます。"
|
|
4124
4124
|
},
|
|
4125
4125
|
"visualConfirm": {
|
|
4126
|
-
"ariaLabel": "ビジュアル確認",
|
|
4127
4126
|
"title": "ビジュアル確認",
|
|
4128
4127
|
"titleWithBlock": "ビジュアル確認 — {title}",
|
|
4129
4128
|
"subtitle": "リファレンスデザインと照らしてUIをレビュー",
|
package/i18n/locales/pl.json
CHANGED
|
@@ -4111,7 +4111,6 @@
|
|
|
4111
4111
|
}
|
|
4112
4112
|
},
|
|
4113
4113
|
"visualConfirm": {
|
|
4114
|
-
"ariaLabel": "Potwierdzenie wizualne",
|
|
4115
4114
|
"title": "Potwierdzenie wizualne",
|
|
4116
4115
|
"titleWithBlock": "Potwierdzenie wizualne — {title}",
|
|
4117
4116
|
"subtitle": "Porównaj interfejs z projektami referencyjnymi",
|
package/i18n/locales/tr.json
CHANGED
|
@@ -4123,7 +4123,6 @@
|
|
|
4123
4123
|
"footer": "Senaryolar, Tester'ın test etmek için seçtiği alanlardır (spesifikasyon kabul senaryoları). Sonuçlar ve endişeler ada göre bunların altında gruplanır."
|
|
4124
4124
|
},
|
|
4125
4125
|
"visualConfirm": {
|
|
4126
|
-
"ariaLabel": "Görsel doğrulama",
|
|
4127
4126
|
"title": "Görsel doğrulama",
|
|
4128
4127
|
"titleWithBlock": "Görsel doğrulama — {title}",
|
|
4129
4128
|
"subtitle": "Arayüzü referans tasarımlarla karşılaştırarak inceleyin",
|
package/i18n/locales/uk.json
CHANGED
|
@@ -4111,7 +4111,6 @@
|
|
|
4111
4111
|
}
|
|
4112
4112
|
},
|
|
4113
4113
|
"visualConfirm": {
|
|
4114
|
-
"ariaLabel": "Візуальне підтвердження",
|
|
4115
4114
|
"title": "Візуальне підтвердження",
|
|
4116
4115
|
"titleWithBlock": "Візуальне підтвердження — {title}",
|
|
4117
4116
|
"subtitle": "Порівняйте інтерфейс з еталонними дизайнами",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.138.1",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|