@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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// description — is what every downstream agent step (the bug investigator, the coder)
|
|
10
10
|
// consumes.
|
|
11
11
|
import IterationCapPrompt from '~/components/pipeline/IterationCapPrompt.vue'
|
|
12
|
-
import
|
|
12
|
+
import ResultWindowShell from '~/components/panels/ResultWindowShell.vue'
|
|
13
13
|
import { parseOutputOutline } from '~/utils/agentOutput'
|
|
14
14
|
import type {
|
|
15
15
|
ClarityItemStatus,
|
|
@@ -303,385 +303,355 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
303
303
|
</script>
|
|
304
304
|
|
|
305
305
|
<template>
|
|
306
|
-
<
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
306
|
+
<ResultWindowShell
|
|
307
|
+
:open="open"
|
|
308
|
+
icon="i-lucide-bug"
|
|
309
|
+
icon-class="bg-indigo-500/15 text-indigo-300"
|
|
310
|
+
:title="t('clarity.title')"
|
|
311
|
+
:subtitle="block?.title"
|
|
312
|
+
variant="centered"
|
|
313
|
+
width="5xl"
|
|
314
|
+
@close="close"
|
|
315
|
+
>
|
|
316
|
+
<template v-if="review" #header-extras>
|
|
317
|
+
<UBadge color="neutral" variant="subtle" size="sm">
|
|
318
|
+
{{ t('clarity.iteration', { current: iteration, max: maxIterations }) }}
|
|
319
|
+
</UBadge>
|
|
320
|
+
</template>
|
|
321
|
+
|
|
322
|
+
<div class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
|
323
|
+
<!-- main column -->
|
|
324
|
+
<div class="min-w-0 flex-1 overflow-y-auto px-6 py-5">
|
|
325
|
+
<p class="mb-4 text-sm text-slate-400">
|
|
326
|
+
<i18n-t keypath="clarity.intro" tag="span" scope="global">
|
|
327
|
+
<template #level>{{ block?.level ?? t('clarity.itemFallback') }}</template>
|
|
328
|
+
<template #answer
|
|
329
|
+
><span class="text-slate-300">{{ t('clarity.introAnswer') }}</span></template
|
|
330
|
+
>
|
|
331
|
+
<template #dismiss
|
|
332
|
+
><span class="text-slate-300">{{ t('clarity.introDismiss') }}</span></template
|
|
333
|
+
>
|
|
334
|
+
</i18n-t>
|
|
335
|
+
</p>
|
|
336
|
+
|
|
337
|
+
<!-- empty state — the reviewer runs automatically as the first pipeline
|
|
338
|
+
gate step, so there's nothing to do here until then -->
|
|
339
|
+
<div
|
|
340
|
+
v-if="!review && !busy && !loading"
|
|
341
|
+
class="rounded-lg border border-dashed border-slate-700 p-8 text-center text-sm text-slate-500"
|
|
342
|
+
>
|
|
343
|
+
{{ t('clarity.empty') }}
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<!-- working state (initial fetch on open, or a reviewer pass running) -->
|
|
347
|
+
<div
|
|
348
|
+
v-else-if="(busy || loading) && !review"
|
|
349
|
+
class="flex items-center justify-center gap-2 p-8 text-sm text-slate-400"
|
|
350
|
+
>
|
|
351
|
+
<UIcon name="i-lucide-loader-circle" class="h-4 w-4 animate-spin" />
|
|
352
|
+
{{ loading && !busy ? t('clarity.loadingReview') : t('clarity.triaging') }}
|
|
353
|
+
</div>
|
|
354
|
+
|
|
355
|
+
<template v-else-if="review">
|
|
356
|
+
<!-- converged: reviewer satisfied -->
|
|
319
357
|
<div
|
|
320
|
-
|
|
358
|
+
v-if="incorporated"
|
|
359
|
+
class="mb-4 flex items-center gap-2 rounded-lg border border-emerald-900/60 bg-emerald-950/30 p-4 text-sm text-emerald-300"
|
|
321
360
|
>
|
|
322
|
-
<UIcon name="i-lucide-
|
|
323
|
-
|
|
324
|
-
<div class="min-w-0">
|
|
325
|
-
<h1 class="truncate text-base font-semibold text-white">{{ t('clarity.title') }}</h1>
|
|
326
|
-
<p v-if="block" class="truncate text-xs text-slate-500">{{ block.title }}</p>
|
|
327
|
-
</div>
|
|
328
|
-
<div class="ms-auto flex items-center gap-1.5">
|
|
329
|
-
<UBadge v-if="review" color="neutral" variant="subtle" size="sm">
|
|
330
|
-
{{ t('clarity.iteration', { current: iteration, max: maxIterations }) }}
|
|
331
|
-
</UBadge>
|
|
332
|
-
<IconButton
|
|
333
|
-
icon="i-lucide-x"
|
|
334
|
-
color="neutral"
|
|
335
|
-
variant="ghost"
|
|
336
|
-
size="sm"
|
|
337
|
-
:label="t('common.close')"
|
|
338
|
-
@click="close"
|
|
339
|
-
/>
|
|
361
|
+
<UIcon name="i-lucide-circle-check" class="h-5 w-5 shrink-0" />
|
|
362
|
+
{{ t('clarity.converged') }}
|
|
340
363
|
</div>
|
|
341
|
-
</header>
|
|
342
|
-
|
|
343
|
-
<div class="flex min-h-0 flex-1 flex-col lg:flex-row">
|
|
344
|
-
<!-- main column -->
|
|
345
|
-
<div class="min-w-0 flex-1 overflow-y-auto px-6 py-5">
|
|
346
|
-
<p class="mb-4 text-sm text-slate-400">
|
|
347
|
-
<i18n-t keypath="clarity.intro" tag="span" scope="global">
|
|
348
|
-
<template #level>{{ block?.level ?? t('clarity.itemFallback') }}</template>
|
|
349
|
-
<template #answer
|
|
350
|
-
><span class="text-slate-300">{{ t('clarity.introAnswer') }}</span></template
|
|
351
|
-
>
|
|
352
|
-
<template #dismiss
|
|
353
|
-
><span class="text-slate-300">{{ t('clarity.introDismiss') }}</span></template
|
|
354
|
-
>
|
|
355
|
-
</i18n-t>
|
|
356
|
-
</p>
|
|
357
364
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
365
|
+
<!-- iteration cap hit -->
|
|
366
|
+
<IterationCapPrompt
|
|
367
|
+
v-else-if="exceeded"
|
|
368
|
+
class="mb-4"
|
|
369
|
+
:heading="t('clarity.capHeading', { max: maxIterations })"
|
|
370
|
+
:detail="t('clarity.capDetail')"
|
|
371
|
+
:loading="acting"
|
|
372
|
+
@resolve="resolveExceeded"
|
|
373
|
+
/>
|
|
374
|
+
|
|
375
|
+
<!-- working: the async cycle is running in the driver. Two distinct stages so
|
|
376
|
+
the human can see which of the two LLM calls is currently in progress. -->
|
|
377
|
+
<div
|
|
378
|
+
v-else-if="working"
|
|
379
|
+
class="mb-4 flex items-center gap-2 rounded-lg border border-indigo-900/60 bg-indigo-950/30 p-4 text-sm text-indigo-200"
|
|
380
|
+
>
|
|
381
|
+
<UIcon name="i-lucide-loader-circle" class="h-5 w-5 shrink-0 animate-spin" />
|
|
382
|
+
<span v-if="incorporating">
|
|
383
|
+
{{ t('clarity.incorporatingStage') }}
|
|
384
|
+
</span>
|
|
385
|
+
<span v-else>
|
|
386
|
+
{{ t('clarity.reReviewingStage') }}
|
|
387
|
+
</span>
|
|
388
|
+
</div>
|
|
366
389
|
|
|
367
|
-
|
|
390
|
+
<!-- findings to react to -->
|
|
391
|
+
<div v-if="review.items.length" class="flex flex-col gap-3">
|
|
368
392
|
<div
|
|
369
|
-
v-
|
|
370
|
-
|
|
393
|
+
v-for="item in sortedItems"
|
|
394
|
+
:key="item.id"
|
|
395
|
+
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
396
|
+
:class="{ 'opacity-60': item.status === 'dismissed' }"
|
|
371
397
|
>
|
|
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
|
-
|
|
398
|
-
<div
|
|
399
|
-
v-else-if="working"
|
|
400
|
-
class="mb-4 flex items-center gap-2 rounded-lg border border-indigo-900/60 bg-indigo-950/30 p-4 text-sm text-indigo-200"
|
|
401
|
-
>
|
|
402
|
-
<UIcon name="i-lucide-loader-circle" class="h-5 w-5 shrink-0 animate-spin" />
|
|
403
|
-
<span v-if="incorporating">
|
|
404
|
-
{{ t('clarity.incorporatingStage') }}
|
|
405
|
-
</span>
|
|
406
|
-
<span v-else>
|
|
407
|
-
{{ t('clarity.reReviewingStage') }}
|
|
408
|
-
</span>
|
|
409
|
-
</div>
|
|
398
|
+
<div class="flex items-start gap-2">
|
|
399
|
+
<UIcon
|
|
400
|
+
:name="CATEGORY_ICON[item.category]"
|
|
401
|
+
class="mt-0.5 h-4 w-4 shrink-0 text-slate-400"
|
|
402
|
+
/>
|
|
403
|
+
<div class="min-w-0 flex-1">
|
|
404
|
+
<div class="flex flex-wrap items-center gap-1.5">
|
|
405
|
+
<span class="text-sm font-medium text-white">{{ item.title }}</span>
|
|
406
|
+
<UBadge size="xs" variant="subtle" :color="SEVERITY_COLOR[item.severity]">
|
|
407
|
+
{{ t(SEVERITY_LABELS[item.severity]) }}
|
|
408
|
+
</UBadge>
|
|
409
|
+
<UBadge size="xs" variant="outline" color="neutral">
|
|
410
|
+
{{ t(CATEGORY_LABELS[item.category]) }}
|
|
411
|
+
</UBadge>
|
|
412
|
+
<UBadge
|
|
413
|
+
size="xs"
|
|
414
|
+
variant="soft"
|
|
415
|
+
:color="STATUS_COLOR[item.status]"
|
|
416
|
+
class="ms-auto"
|
|
417
|
+
>
|
|
418
|
+
{{ t(STATUS_LABELS[item.status]) }}
|
|
419
|
+
</UBadge>
|
|
420
|
+
</div>
|
|
421
|
+
<p class="mt-1 whitespace-pre-line text-sm text-slate-400">
|
|
422
|
+
{{ item.detail }}
|
|
423
|
+
</p>
|
|
410
424
|
|
|
411
|
-
|
|
412
|
-
<div v-if="review.items.length" class="flex flex-col gap-3">
|
|
413
|
-
<div
|
|
414
|
-
v-for="item in sortedItems"
|
|
415
|
-
:key="item.id"
|
|
416
|
-
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
417
|
-
:class="{ 'opacity-60': item.status === 'dismissed' }"
|
|
418
|
-
>
|
|
419
|
-
<div class="flex items-start gap-2">
|
|
420
|
-
<UIcon
|
|
421
|
-
:name="CATEGORY_ICON[item.category]"
|
|
422
|
-
class="mt-0.5 h-4 w-4 shrink-0 text-slate-400"
|
|
423
|
-
/>
|
|
424
|
-
<div class="min-w-0 flex-1">
|
|
425
|
-
<div class="flex flex-wrap items-center gap-1.5">
|
|
426
|
-
<span class="text-sm font-medium text-white">{{ item.title }}</span>
|
|
427
|
-
<UBadge size="xs" variant="subtle" :color="SEVERITY_COLOR[item.severity]">
|
|
428
|
-
{{ t(SEVERITY_LABELS[item.severity]) }}
|
|
429
|
-
</UBadge>
|
|
430
|
-
<UBadge size="xs" variant="outline" color="neutral">
|
|
431
|
-
{{ t(CATEGORY_LABELS[item.category]) }}
|
|
432
|
-
</UBadge>
|
|
433
|
-
<UBadge
|
|
434
|
-
size="xs"
|
|
435
|
-
variant="soft"
|
|
436
|
-
:color="STATUS_COLOR[item.status]"
|
|
437
|
-
class="ms-auto"
|
|
438
|
-
>
|
|
439
|
-
{{ t(STATUS_LABELS[item.status]) }}
|
|
440
|
-
</UBadge>
|
|
441
|
-
</div>
|
|
442
|
-
<p class="mt-1 whitespace-pre-line text-sm text-slate-400">
|
|
443
|
-
{{ item.detail }}
|
|
444
|
-
</p>
|
|
445
|
-
|
|
446
|
-
<!-- recorded answer (only for non-editable findings — for editable ones
|
|
425
|
+
<!-- recorded answer (only for non-editable findings — for editable ones
|
|
447
426
|
the answer lives in the textarea below, seeded from the reply) -->
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
427
|
+
<div
|
|
428
|
+
v-if="item.reply && item.status !== 'open' && item.status !== 'answered'"
|
|
429
|
+
class="mt-2 rounded-md border-s-2 border-slate-700 bg-slate-950/40 px-3 py-1.5 text-sm text-slate-300"
|
|
430
|
+
>
|
|
431
|
+
<span class="text-[10px] uppercase tracking-wide text-slate-500">
|
|
432
|
+
{{ t('clarity.answerLabel') }}
|
|
433
|
+
</span>
|
|
434
|
+
<p class="whitespace-pre-line">{{ item.reply }}</p>
|
|
435
|
+
</div>
|
|
457
436
|
|
|
458
|
-
|
|
437
|
+
<!-- react: answer (relevant) or dismiss (irrelevant). The answer
|
|
459
438
|
auto-saves on blur — no explicit save button. Disabled once the
|
|
460
439
|
bug report is clarified / awaiting a higher-level decision. -->
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
{{ t('clarity.dismissIrrelevant') }}
|
|
485
|
-
</UButton>
|
|
486
|
-
</div>
|
|
487
|
-
</template>
|
|
488
|
-
|
|
489
|
-
<!-- reopen a dismissed finding -->
|
|
490
|
-
<div v-else-if="item.status === 'dismissed'" class="mt-2">
|
|
491
|
-
<UButton
|
|
492
|
-
color="neutral"
|
|
493
|
-
variant="ghost"
|
|
494
|
-
size="xs"
|
|
495
|
-
icon="i-lucide-rotate-ccw"
|
|
496
|
-
:disabled="frozen || !access.canExecuteRuns.value"
|
|
497
|
-
:title="
|
|
498
|
-
access.canExecuteRuns.value ? undefined : t('access.noRunExecute')
|
|
499
|
-
"
|
|
500
|
-
@click="setStatus(item, 'open')"
|
|
501
|
-
>
|
|
502
|
-
{{ t('clarity.reopen') }}
|
|
503
|
-
</UButton>
|
|
504
|
-
</div>
|
|
440
|
+
<template v-if="item.status === 'open' || item.status === 'answered'">
|
|
441
|
+
<UTextarea
|
|
442
|
+
v-model="drafts[item.id]"
|
|
443
|
+
:rows="2"
|
|
444
|
+
autoresize
|
|
445
|
+
size="sm"
|
|
446
|
+
class="mt-2 w-full"
|
|
447
|
+
:placeholder="t('clarity.answerPlaceholder')"
|
|
448
|
+
:disabled="frozen"
|
|
449
|
+
@blur="persistDraft(item)"
|
|
450
|
+
/>
|
|
451
|
+
<div class="mt-2 flex flex-wrap items-center gap-2">
|
|
452
|
+
<UButton
|
|
453
|
+
color="neutral"
|
|
454
|
+
variant="ghost"
|
|
455
|
+
size="xs"
|
|
456
|
+
icon="i-lucide-x"
|
|
457
|
+
:disabled="frozen || !access.canExecuteRuns.value"
|
|
458
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
459
|
+
@click="setStatus(item, 'dismissed')"
|
|
460
|
+
>
|
|
461
|
+
{{ t('clarity.dismissIrrelevant') }}
|
|
462
|
+
</UButton>
|
|
505
463
|
</div>
|
|
464
|
+
</template>
|
|
465
|
+
|
|
466
|
+
<!-- reopen a dismissed finding -->
|
|
467
|
+
<div v-else-if="item.status === 'dismissed'" class="mt-2">
|
|
468
|
+
<UButton
|
|
469
|
+
color="neutral"
|
|
470
|
+
variant="ghost"
|
|
471
|
+
size="xs"
|
|
472
|
+
icon="i-lucide-rotate-ccw"
|
|
473
|
+
:disabled="frozen || !access.canExecuteRuns.value"
|
|
474
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
475
|
+
@click="setStatus(item, 'open')"
|
|
476
|
+
>
|
|
477
|
+
{{ t('clarity.reopen') }}
|
|
478
|
+
</UButton>
|
|
506
479
|
</div>
|
|
507
480
|
</div>
|
|
508
481
|
</div>
|
|
509
|
-
|
|
510
|
-
<!-- clarified document: the standard-format bug report -->
|
|
511
|
-
<section v-if="outline" class="mt-6 border-t border-slate-800 pt-5">
|
|
512
|
-
<div class="mb-3 flex items-center gap-1.5 text-[11px] text-emerald-400">
|
|
513
|
-
<UIcon name="i-lucide-file-check-2" class="h-3.5 w-3.5" />
|
|
514
|
-
<span class="font-semibold uppercase tracking-wide">
|
|
515
|
-
{{ incorporated ? t('clarity.docHeading') : t('clarity.docHeadingDraft') }}
|
|
516
|
-
</span>
|
|
517
|
-
</div>
|
|
518
|
-
<div v-for="s in outline.sections" :key="s.id" class="mb-2">
|
|
519
|
-
<button
|
|
520
|
-
v-if="s.title"
|
|
521
|
-
class="group flex w-full items-center gap-2 text-start"
|
|
522
|
-
@click="toggle(s.id)"
|
|
523
|
-
>
|
|
524
|
-
<UIcon
|
|
525
|
-
name="i-lucide-chevron-right"
|
|
526
|
-
class="h-3.5 w-3.5 shrink-0 text-slate-500 transition-transform"
|
|
527
|
-
:class="collapsed[s.id] ? '' : 'rotate-90'"
|
|
528
|
-
/>
|
|
529
|
-
<span
|
|
530
|
-
class="font-semibold text-white"
|
|
531
|
-
:class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
|
|
532
|
-
v-html="s.titleHtml"
|
|
533
|
-
/>
|
|
534
|
-
</button>
|
|
535
|
-
<div
|
|
536
|
-
v-show="!s.title || !collapsed[s.id]"
|
|
537
|
-
class="reader-prose mt-1 ps-5.5 text-[13px] leading-relaxed text-slate-300"
|
|
538
|
-
v-html="s.bodyHtml"
|
|
539
|
-
/>
|
|
540
|
-
</div>
|
|
541
|
-
</section>
|
|
542
|
-
</template>
|
|
482
|
+
</div>
|
|
543
483
|
</div>
|
|
544
484
|
|
|
545
|
-
<!--
|
|
485
|
+
<!-- clarified document: the standard-format bug report -->
|
|
486
|
+
<section v-if="outline" class="mt-6 border-t border-slate-800 pt-5">
|
|
487
|
+
<div class="mb-3 flex items-center gap-1.5 text-[11px] text-emerald-400">
|
|
488
|
+
<UIcon name="i-lucide-file-check-2" class="h-3.5 w-3.5" />
|
|
489
|
+
<span class="font-semibold uppercase tracking-wide">
|
|
490
|
+
{{ incorporated ? t('clarity.docHeading') : t('clarity.docHeadingDraft') }}
|
|
491
|
+
</span>
|
|
492
|
+
</div>
|
|
493
|
+
<div v-for="s in outline.sections" :key="s.id" class="mb-2">
|
|
494
|
+
<button
|
|
495
|
+
v-if="s.title"
|
|
496
|
+
class="group flex w-full items-center gap-2 text-start"
|
|
497
|
+
@click="toggle(s.id)"
|
|
498
|
+
>
|
|
499
|
+
<UIcon
|
|
500
|
+
name="i-lucide-chevron-right"
|
|
501
|
+
class="h-3.5 w-3.5 shrink-0 text-slate-500 transition-transform"
|
|
502
|
+
:class="collapsed[s.id] ? '' : 'rotate-90'"
|
|
503
|
+
/>
|
|
504
|
+
<span
|
|
505
|
+
class="font-semibold text-white"
|
|
506
|
+
:class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
|
|
507
|
+
v-html="s.titleHtml"
|
|
508
|
+
/>
|
|
509
|
+
</button>
|
|
510
|
+
<div
|
|
511
|
+
v-show="!s.title || !collapsed[s.id]"
|
|
512
|
+
class="reader-prose mt-1 ps-5.5 text-[13px] leading-relaxed text-slate-300"
|
|
513
|
+
v-html="s.bodyHtml"
|
|
514
|
+
/>
|
|
515
|
+
</div>
|
|
516
|
+
</section>
|
|
517
|
+
</template>
|
|
518
|
+
</div>
|
|
519
|
+
|
|
520
|
+
<!-- action rail: a right-hand column on wide screens, a bottom action bar below `lg`
|
|
546
521
|
(never hidden — the gate is otherwise unadvanceable on a laptop split-screen /
|
|
547
522
|
tablet, UX-32). The informational stats collapse away below `lg` to keep the
|
|
548
523
|
bottom bar compact; the actions themselves always show. -->
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
<!-- action: ready (answer → incorporate / proceed) -->
|
|
575
|
-
<div
|
|
576
|
-
v-if="review && status === 'ready'"
|
|
577
|
-
class="space-y-2 border-t border-slate-800 pt-4"
|
|
578
|
-
>
|
|
579
|
-
<UButton
|
|
580
|
-
v-if="canProceed"
|
|
581
|
-
color="primary"
|
|
582
|
-
size="sm"
|
|
583
|
-
block
|
|
584
|
-
icon="i-lucide-arrow-right"
|
|
585
|
-
:ui="{ leadingIcon: 'rtl:-scale-x-100', trailingIcon: 'rtl:-scale-x-100' }"
|
|
586
|
-
:loading="acting"
|
|
587
|
-
:disabled="!access.canExecuteRuns.value"
|
|
588
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
589
|
-
@click="proceed"
|
|
590
|
-
>
|
|
591
|
-
{{ t('clarity.proceedNothing') }}
|
|
592
|
-
</UButton>
|
|
593
|
-
<UButton
|
|
594
|
-
v-else
|
|
595
|
-
color="primary"
|
|
596
|
-
size="sm"
|
|
597
|
-
block
|
|
598
|
-
icon="i-lucide-wand-sparkles"
|
|
599
|
-
:loading="reworking"
|
|
600
|
-
:disabled="!canIncorporate || !access.canExecuteRuns.value"
|
|
601
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
602
|
-
@click="incorporate()"
|
|
603
|
-
>
|
|
604
|
-
{{ t('clarity.incorporateAnswers') }}
|
|
605
|
-
</UButton>
|
|
606
|
-
<p class="text-[11px] leading-relaxed text-slate-500">
|
|
607
|
-
<template v-if="canProceed">
|
|
608
|
-
{{ t('clarity.hint.proceed') }}
|
|
609
|
-
</template>
|
|
610
|
-
<template v-else-if="canIncorporate">
|
|
611
|
-
{{ t('clarity.hint.incorporate') }}
|
|
612
|
-
</template>
|
|
613
|
-
<template v-else> {{ t('clarity.hint.answerAll') }} </template>
|
|
614
|
-
</p>
|
|
615
|
-
</div>
|
|
524
|
+
<aside
|
|
525
|
+
class="flex w-full shrink-0 flex-col border-t border-slate-800 lg:w-72 lg:border-s lg:border-t-0"
|
|
526
|
+
>
|
|
527
|
+
<div class="flex flex-col gap-4 px-4 py-5">
|
|
528
|
+
<div v-if="review" class="hidden space-y-2 text-xs text-slate-400 lg:block">
|
|
529
|
+
<div class="flex items-center justify-between">
|
|
530
|
+
<span>{{ t('clarity.rail.findings') }}</span>
|
|
531
|
+
<span class="text-slate-300">{{ review.items.length }}</span>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="flex items-center justify-between">
|
|
534
|
+
<span>{{ t('clarity.rail.open') }}</span>
|
|
535
|
+
<span class="text-slate-300">{{ openCount }}</span>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="flex items-center justify-between">
|
|
538
|
+
<span>{{ t('clarity.rail.answered') }}</span>
|
|
539
|
+
<span class="text-slate-300">{{ answeredCount }}</span>
|
|
540
|
+
</div>
|
|
541
|
+
<div v-if="review.model" class="flex items-center justify-between">
|
|
542
|
+
<span>{{ t('clarity.rail.model') }}</span>
|
|
543
|
+
<span class="truncate ps-2 text-slate-500">{{
|
|
544
|
+
models.labelForRef(review.model) ?? review.model
|
|
545
|
+
}}</span>
|
|
546
|
+
</div>
|
|
547
|
+
</div>
|
|
616
548
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
variant="soft"
|
|
657
|
-
size="xs"
|
|
658
|
-
block
|
|
659
|
-
icon="i-lucide-wand-sparkles"
|
|
660
|
-
:loading="reworking"
|
|
661
|
-
:disabled="!redoComment.trim() || !access.canExecuteRuns.value"
|
|
662
|
-
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
663
|
-
@click="incorporate(redoComment.trim())"
|
|
664
|
-
>
|
|
665
|
-
{{ t('clarity.redoWithDirection') }}
|
|
666
|
-
</UButton>
|
|
667
|
-
</div>
|
|
668
|
-
<p class="text-[11px] leading-relaxed text-slate-500">
|
|
669
|
-
{{ t('clarity.redoHint') }}
|
|
670
|
-
</p>
|
|
671
|
-
</div>
|
|
549
|
+
<!-- action: ready (answer → incorporate / proceed) -->
|
|
550
|
+
<div v-if="review && status === 'ready'" class="space-y-2 border-t border-slate-800 pt-4">
|
|
551
|
+
<UButton
|
|
552
|
+
v-if="canProceed"
|
|
553
|
+
color="primary"
|
|
554
|
+
size="sm"
|
|
555
|
+
block
|
|
556
|
+
icon="i-lucide-arrow-right"
|
|
557
|
+
:ui="{ leadingIcon: 'rtl:-scale-x-100', trailingIcon: 'rtl:-scale-x-100' }"
|
|
558
|
+
:loading="acting"
|
|
559
|
+
:disabled="!access.canExecuteRuns.value"
|
|
560
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
561
|
+
@click="proceed"
|
|
562
|
+
>
|
|
563
|
+
{{ t('clarity.proceedNothing') }}
|
|
564
|
+
</UButton>
|
|
565
|
+
<UButton
|
|
566
|
+
v-else
|
|
567
|
+
color="primary"
|
|
568
|
+
size="sm"
|
|
569
|
+
block
|
|
570
|
+
icon="i-lucide-wand-sparkles"
|
|
571
|
+
:loading="reworking"
|
|
572
|
+
:disabled="!canIncorporate || !access.canExecuteRuns.value"
|
|
573
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
574
|
+
@click="incorporate()"
|
|
575
|
+
>
|
|
576
|
+
{{ t('clarity.incorporateAnswers') }}
|
|
577
|
+
</UButton>
|
|
578
|
+
<p class="text-[11px] leading-relaxed text-slate-500">
|
|
579
|
+
<template v-if="canProceed">
|
|
580
|
+
{{ t('clarity.hint.proceed') }}
|
|
581
|
+
</template>
|
|
582
|
+
<template v-else-if="canIncorporate">
|
|
583
|
+
{{ t('clarity.hint.incorporate') }}
|
|
584
|
+
</template>
|
|
585
|
+
<template v-else> {{ t('clarity.hint.answerAll') }} </template>
|
|
586
|
+
</p>
|
|
587
|
+
</div>
|
|
672
588
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
589
|
+
<!-- action: merged (inspect → re-review / redo) -->
|
|
590
|
+
<div v-if="review && merged" class="space-y-2 border-t border-slate-800 pt-4">
|
|
591
|
+
<UButton
|
|
592
|
+
color="primary"
|
|
593
|
+
size="sm"
|
|
594
|
+
block
|
|
595
|
+
icon="i-lucide-sparkles"
|
|
596
|
+
:loading="busy"
|
|
597
|
+
:disabled="!access.canExecuteRuns.value"
|
|
598
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
599
|
+
@click="reReview"
|
|
600
|
+
>
|
|
601
|
+
{{ busy ? t('clarity.reReviewing') : t('clarity.looksGoodReReview') }}
|
|
602
|
+
</UButton>
|
|
603
|
+
<UButton
|
|
604
|
+
color="neutral"
|
|
605
|
+
variant="soft"
|
|
606
|
+
size="sm"
|
|
607
|
+
block
|
|
608
|
+
icon="i-lucide-pencil"
|
|
609
|
+
@click="
|
|
610
|
+
() => {
|
|
611
|
+
showRedo = !showRedo
|
|
612
|
+
}
|
|
613
|
+
"
|
|
614
|
+
>
|
|
615
|
+
{{ t('clarity.redoIncorporation') }}
|
|
616
|
+
</UButton>
|
|
617
|
+
<div v-if="showRedo" class="space-y-2">
|
|
618
|
+
<UTextarea
|
|
619
|
+
v-model="redoComment"
|
|
620
|
+
:rows="3"
|
|
621
|
+
autoresize
|
|
622
|
+
size="sm"
|
|
623
|
+
class="w-full"
|
|
624
|
+
:placeholder="t('clarity.redoPlaceholder')"
|
|
625
|
+
/>
|
|
626
|
+
<UButton
|
|
627
|
+
color="primary"
|
|
628
|
+
variant="soft"
|
|
629
|
+
size="xs"
|
|
630
|
+
block
|
|
631
|
+
icon="i-lucide-wand-sparkles"
|
|
632
|
+
:loading="reworking"
|
|
633
|
+
:disabled="!redoComment.trim() || !access.canExecuteRuns.value"
|
|
634
|
+
:title="access.canExecuteRuns.value ? undefined : t('access.noRunExecute')"
|
|
635
|
+
@click="incorporate(redoComment.trim())"
|
|
676
636
|
>
|
|
677
|
-
{{ t('clarity.
|
|
678
|
-
</
|
|
637
|
+
{{ t('clarity.redoWithDirection') }}
|
|
638
|
+
</UButton>
|
|
679
639
|
</div>
|
|
680
|
-
|
|
640
|
+
<p class="text-[11px] leading-relaxed text-slate-500">
|
|
641
|
+
{{ t('clarity.redoHint') }}
|
|
642
|
+
</p>
|
|
643
|
+
</div>
|
|
644
|
+
|
|
645
|
+
<div
|
|
646
|
+
v-if="review && incorporated"
|
|
647
|
+
class="border-t border-slate-800 pt-4 text-[11px] leading-relaxed text-slate-500"
|
|
648
|
+
>
|
|
649
|
+
{{ t('clarity.incorporatedFooter') }}
|
|
650
|
+
</div>
|
|
681
651
|
</div>
|
|
682
|
-
</
|
|
652
|
+
</aside>
|
|
683
653
|
</div>
|
|
684
|
-
</
|
|
654
|
+
</ResultWindowShell>
|
|
685
655
|
</template>
|
|
686
656
|
|
|
687
657
|
<style scoped>
|