@cat-factory/app 0.80.0 → 0.81.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/requirements/RequirementsReviewWindow.vue +126 -42
- package/i18n/locales/en.json +3 -0
- package/i18n/locales/es.json +3 -0
- package/i18n/locales/fr.json +3 -0
- package/i18n/locales/he.json +3 -0
- package/i18n/locales/ja.json +3 -0
- package/i18n/locales/pl.json +3 -0
- package/i18n/locales/tr.json +3 -0
- package/i18n/locales/uk.json +3 -0
- package/package.json +1 -1
|
@@ -38,6 +38,9 @@ const reRequestNotes = ref<Record<string, string>>({})
|
|
|
38
38
|
// Freeform "do it differently" comment when redoing a merge the human was unhappy with.
|
|
39
39
|
const redoComment = ref('')
|
|
40
40
|
const showRedo = ref(false)
|
|
41
|
+
// Human's explicit collapse choice for the whole incorporated-requirements section; null = follow
|
|
42
|
+
// the default (collapse it while there's still work to do so it doesn't dominate the window).
|
|
43
|
+
const docCollapsedOverride = ref<boolean | null>(null)
|
|
41
44
|
|
|
42
45
|
// The seam contract (open/blockId/close + Escape handling + load-on-open) lives in
|
|
43
46
|
// `useResultView`, so this window can't drift from the others. Declaring `onOpen` makes the
|
|
@@ -52,6 +55,7 @@ const { open, blockId, instanceId, stepIndex, close } = useResultView('requireme
|
|
|
52
55
|
reRequestNotes.value = {}
|
|
53
56
|
redoComment.value = ''
|
|
54
57
|
showRedo.value = false
|
|
58
|
+
docCollapsedOverride.value = null
|
|
55
59
|
void requirements.load(id)
|
|
56
60
|
},
|
|
57
61
|
})
|
|
@@ -256,6 +260,41 @@ const recommendationProgress = computed(() => {
|
|
|
256
260
|
const ready = readyRecommendations.value.filter((r) => batchTimes.has(r.createdAt)).length
|
|
257
261
|
return { ready, total: ready + generating.length }
|
|
258
262
|
})
|
|
263
|
+
// Whether the human still has something to act on (findings to answer/dismiss or recommendations
|
|
264
|
+
// to decide). Drives the incorporated-document default collapse so the reference doc doesn't push
|
|
265
|
+
// the actionable findings/recommendations off-screen while there's still work.
|
|
266
|
+
const hasActionableWork = computed(() => {
|
|
267
|
+
if (!review.value) return false
|
|
268
|
+
const findingWork = review.value.items.some(
|
|
269
|
+
(i) => i.status === 'open' || i.status === 'answered' || i.status === 'recommend_requested',
|
|
270
|
+
)
|
|
271
|
+
return (
|
|
272
|
+
findingWork ||
|
|
273
|
+
readyRecommendations.value.length > 0 ||
|
|
274
|
+
generatingRecommendations.value.length > 0
|
|
275
|
+
)
|
|
276
|
+
})
|
|
277
|
+
// The whole incorporated-requirements section collapses as a unit (independent of the per-heading
|
|
278
|
+
// collapse below). Default: collapsed only in the pre-incorporation `ready`-style phase while
|
|
279
|
+
// there's still actionable work — so the (potentially long) reference doc stays out of the way of
|
|
280
|
+
// the findings the human is working through. In `merged` (inspect the draft to decide re-review vs
|
|
281
|
+
// redo) and `incorporated` (the settled deliverable) the document IS the thing to read, so it
|
|
282
|
+
// defaults expanded. The human's explicit toggle wins within a phase; a status change (below)
|
|
283
|
+
// clears it so a collapse from one phase doesn't leak into the next.
|
|
284
|
+
const docCollapsed = computed(
|
|
285
|
+
() =>
|
|
286
|
+
docCollapsedOverride.value ?? (!incorporated.value && !merged.value && hasActionableWork.value),
|
|
287
|
+
)
|
|
288
|
+
function toggleDoc() {
|
|
289
|
+
docCollapsedOverride.value = !docCollapsed.value
|
|
290
|
+
}
|
|
291
|
+
// Reset the manual collapse on every status transition so a collapse chosen in one phase doesn't
|
|
292
|
+
// persist into the next (e.g. a `ready` collapse leaking into `merged`, or surviving convergence to
|
|
293
|
+
// `incorporated` and hiding the final requirements) — each phase then falls back to its own default.
|
|
294
|
+
watch(status, () => {
|
|
295
|
+
docCollapsedOverride.value = null
|
|
296
|
+
})
|
|
297
|
+
|
|
259
298
|
function isMarkedForRecommend(item: RequirementReviewItem): boolean {
|
|
260
299
|
return markedForRecommend.value.has(item.id)
|
|
261
300
|
}
|
|
@@ -751,9 +790,19 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
751
790
|
</div>
|
|
752
791
|
</section>
|
|
753
792
|
|
|
754
|
-
<!-- incorporated document: the standard-format requirements
|
|
793
|
+
<!-- incorporated document: the standard-format requirements. The whole section
|
|
794
|
+
collapses as a unit (a long doc otherwise pushes the findings/recommendations
|
|
795
|
+
off-screen); the per-heading toggles below still work when it's expanded. -->
|
|
755
796
|
<section v-if="outline" class="mt-6 border-t border-slate-800 pt-5">
|
|
756
|
-
<
|
|
797
|
+
<button
|
|
798
|
+
class="mb-3 flex w-full items-center gap-1.5 text-[11px] text-emerald-400"
|
|
799
|
+
@click="toggleDoc"
|
|
800
|
+
>
|
|
801
|
+
<UIcon
|
|
802
|
+
name="i-lucide-chevron-right"
|
|
803
|
+
class="h-3.5 w-3.5 shrink-0 transition-transform"
|
|
804
|
+
:class="docCollapsed ? '' : 'rotate-90'"
|
|
805
|
+
/>
|
|
757
806
|
<UIcon name="i-lucide-file-check-2" class="h-3.5 w-3.5" />
|
|
758
807
|
<span class="font-semibold uppercase tracking-wide">
|
|
759
808
|
{{
|
|
@@ -762,29 +811,31 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
762
811
|
: t('requirements.incorporatedDraft')
|
|
763
812
|
}}
|
|
764
813
|
</span>
|
|
765
|
-
</
|
|
766
|
-
<div v-
|
|
767
|
-
<
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
814
|
+
</button>
|
|
815
|
+
<div v-show="!docCollapsed">
|
|
816
|
+
<div v-for="s in outline.sections" :key="s.id" class="mb-2">
|
|
817
|
+
<button
|
|
818
|
+
v-if="s.title"
|
|
819
|
+
class="group flex w-full items-center gap-2 text-start"
|
|
820
|
+
@click="toggle(s.id)"
|
|
821
|
+
>
|
|
822
|
+
<UIcon
|
|
823
|
+
name="i-lucide-chevron-right"
|
|
824
|
+
class="h-3.5 w-3.5 shrink-0 text-slate-500 transition-transform"
|
|
825
|
+
:class="collapsed[s.id] ? '' : 'rotate-90'"
|
|
826
|
+
/>
|
|
827
|
+
<span
|
|
828
|
+
class="font-semibold text-white"
|
|
829
|
+
:class="s.depth <= 1 ? 'text-base' : s.depth === 2 ? 'text-sm' : 'text-xs'"
|
|
830
|
+
v-html="s.titleHtml"
|
|
831
|
+
/>
|
|
832
|
+
</button>
|
|
833
|
+
<div
|
|
834
|
+
v-show="!s.title || !collapsed[s.id]"
|
|
835
|
+
class="reader-prose mt-1 ps-5.5 text-[13px] leading-relaxed text-slate-300"
|
|
836
|
+
v-html="s.bodyHtml"
|
|
781
837
|
/>
|
|
782
|
-
</
|
|
783
|
-
<div
|
|
784
|
-
v-show="!s.title || !collapsed[s.id]"
|
|
785
|
-
class="reader-prose mt-1 ps-5.5 text-[13px] leading-relaxed text-slate-300"
|
|
786
|
-
v-html="s.bodyHtml"
|
|
787
|
-
/>
|
|
838
|
+
</div>
|
|
788
839
|
</div>
|
|
789
840
|
</section>
|
|
790
841
|
</template>
|
|
@@ -806,12 +857,63 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
806
857
|
<span>{{ t('requirements.stats.answered') }}</span>
|
|
807
858
|
<span class="text-slate-300">{{ answeredCount }}</span>
|
|
808
859
|
</div>
|
|
860
|
+
<!-- awaited recommendations — kept here (always visible) so the human can see what
|
|
861
|
+
the Writer is still producing / what's waiting on them even while reading the
|
|
862
|
+
incorporated document or acting elsewhere in the window. -->
|
|
863
|
+
<template v-if="generatingRecommendations.length || readyRecommendations.length">
|
|
864
|
+
<div
|
|
865
|
+
class="flex items-center gap-1.5 border-t border-slate-800/60 pt-2 text-indigo-300"
|
|
866
|
+
>
|
|
867
|
+
<UIcon name="i-lucide-wand-2" class="h-3 w-3" />
|
|
868
|
+
<span class="font-medium">{{ t('requirements.stats.recommendations') }}</span>
|
|
869
|
+
</div>
|
|
870
|
+
<div
|
|
871
|
+
v-if="generatingRecommendations.length"
|
|
872
|
+
class="flex items-center justify-between"
|
|
873
|
+
>
|
|
874
|
+
<span>{{ t('requirements.stats.recsGenerating') }}</span>
|
|
875
|
+
<span class="text-indigo-300">{{ generatingRecommendations.length }}</span>
|
|
876
|
+
</div>
|
|
877
|
+
<div v-if="readyRecommendations.length" class="flex items-center justify-between">
|
|
878
|
+
<span>{{ t('requirements.stats.recsToReview') }}</span>
|
|
879
|
+
<span class="text-indigo-300">{{ readyRecommendations.length }}</span>
|
|
880
|
+
</div>
|
|
881
|
+
</template>
|
|
809
882
|
<div v-if="review.model" class="flex items-center justify-between">
|
|
810
883
|
<span>{{ t('requirements.stats.model') }}</span>
|
|
811
884
|
<span class="truncate ps-2 text-slate-500">{{ review.model }}</span>
|
|
812
885
|
</div>
|
|
813
886
|
</div>
|
|
814
887
|
|
|
888
|
+
<!-- Request the Requirement Writer for the marked findings. Kept OUT of the
|
|
889
|
+
status-scoped blocks below so it's available whenever the review is still
|
|
890
|
+
editable — the `ready` first pass AND a `merged` review being reworked — not
|
|
891
|
+
only when status is exactly `ready`. Scoped to exactly those two states (NOT a
|
|
892
|
+
bare `!frozen`, which would also expose it in `exceeded`, where the run is parked
|
|
893
|
+
on the cap decision and a fresh recommendation batch has no path to settle). -->
|
|
894
|
+
<div
|
|
895
|
+
v-if="review && markedForRecommend.size > 0 && (status === 'ready' || merged)"
|
|
896
|
+
class="border-t border-slate-800 pt-4"
|
|
897
|
+
>
|
|
898
|
+
<UButton
|
|
899
|
+
color="primary"
|
|
900
|
+
variant="soft"
|
|
901
|
+
size="sm"
|
|
902
|
+
block
|
|
903
|
+
icon="i-lucide-wand-2"
|
|
904
|
+
:loading="recommending"
|
|
905
|
+
@click="requestRecommendations"
|
|
906
|
+
>
|
|
907
|
+
{{
|
|
908
|
+
t(
|
|
909
|
+
'requirements.actions.requestRecommendations',
|
|
910
|
+
{ count: markedForRecommend.size },
|
|
911
|
+
markedForRecommend.size,
|
|
912
|
+
)
|
|
913
|
+
}}
|
|
914
|
+
</UButton>
|
|
915
|
+
</div>
|
|
916
|
+
|
|
815
917
|
<!-- action: ready (answer → incorporate / proceed) -->
|
|
816
918
|
<div
|
|
817
919
|
v-if="review && status === 'ready'"
|
|
@@ -841,24 +943,6 @@ async function resolveExceeded(choice: 'extra-round' | 'proceed' | 'stop-reset')
|
|
|
841
943
|
>
|
|
842
944
|
{{ t('requirements.actions.incorporateAnswers') }}
|
|
843
945
|
</UButton>
|
|
844
|
-
<UButton
|
|
845
|
-
v-if="markedForRecommend.size > 0"
|
|
846
|
-
color="primary"
|
|
847
|
-
variant="soft"
|
|
848
|
-
size="sm"
|
|
849
|
-
block
|
|
850
|
-
icon="i-lucide-wand-2"
|
|
851
|
-
:loading="recommending"
|
|
852
|
-
@click="requestRecommendations"
|
|
853
|
-
>
|
|
854
|
-
{{
|
|
855
|
-
t(
|
|
856
|
-
'requirements.actions.requestRecommendations',
|
|
857
|
-
{ count: markedForRecommend.size },
|
|
858
|
-
markedForRecommend.size,
|
|
859
|
-
)
|
|
860
|
-
}}
|
|
861
|
-
</UButton>
|
|
862
946
|
<p class="text-[11px] leading-relaxed text-slate-500">
|
|
863
947
|
<template v-if="canProceed">
|
|
864
948
|
{{ t('requirements.help.canProceed') }}
|
package/i18n/locales/en.json
CHANGED
package/i18n/locales/es.json
CHANGED
|
@@ -2982,6 +2982,9 @@
|
|
|
2982
2982
|
"findings": "Hallazgos",
|
|
2983
2983
|
"open": "Abiertos",
|
|
2984
2984
|
"answered": "Respondidos",
|
|
2985
|
+
"recommendations": "Recomendaciones",
|
|
2986
|
+
"recsGenerating": "Generando",
|
|
2987
|
+
"recsToReview": "Por revisar",
|
|
2985
2988
|
"model": "Modelo"
|
|
2986
2989
|
},
|
|
2987
2990
|
"actions": {
|
package/i18n/locales/fr.json
CHANGED
|
@@ -2982,6 +2982,9 @@
|
|
|
2982
2982
|
"findings": "Observations",
|
|
2983
2983
|
"open": "Ouvertes",
|
|
2984
2984
|
"answered": "Répondues",
|
|
2985
|
+
"recommendations": "Recommandations",
|
|
2986
|
+
"recsGenerating": "En cours",
|
|
2987
|
+
"recsToReview": "À examiner",
|
|
2985
2988
|
"model": "Modèle"
|
|
2986
2989
|
},
|
|
2987
2990
|
"actions": {
|
package/i18n/locales/he.json
CHANGED
package/i18n/locales/ja.json
CHANGED
package/i18n/locales/pl.json
CHANGED
|
@@ -2982,6 +2982,9 @@
|
|
|
2982
2982
|
"findings": "Uwagi",
|
|
2983
2983
|
"open": "Otwarte",
|
|
2984
2984
|
"answered": "Odpowiedziane",
|
|
2985
|
+
"recommendations": "Rekomendacje",
|
|
2986
|
+
"recsGenerating": "Generowanie",
|
|
2987
|
+
"recsToReview": "Do przejrzenia",
|
|
2985
2988
|
"model": "Model"
|
|
2986
2989
|
},
|
|
2987
2990
|
"actions": {
|
package/i18n/locales/tr.json
CHANGED
package/i18n/locales/uk.json
CHANGED
|
@@ -2982,6 +2982,9 @@
|
|
|
2982
2982
|
"findings": "Зауваження",
|
|
2983
2983
|
"open": "Відкриті",
|
|
2984
2984
|
"answered": "З відповіддю",
|
|
2985
|
+
"recommendations": "Рекомендації",
|
|
2986
|
+
"recsGenerating": "Створення",
|
|
2987
|
+
"recsToReview": "На перегляд",
|
|
2985
2988
|
"model": "Модель"
|
|
2986
2989
|
},
|
|
2987
2990
|
"actions": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
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",
|