@cat-factory/app 0.53.0 → 0.54.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/bootstrap/BootstrapModal.vue +81 -69
- package/app/components/environments/EnvironmentStatusPanel.vue +37 -14
- package/app/components/fragments/FragmentLibraryManager.vue +104 -69
- package/app/components/fragments/FragmentLibraryPanel.vue +2 -1
- package/app/components/kaizen/KaizenPanel.vue +36 -25
- package/app/components/kaizen/KaizenStepStatus.vue +13 -10
- package/app/components/media/ArtifactLightbox.vue +20 -14
- package/app/components/media/ImageCompare.vue +35 -22
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +27 -14
- package/app/components/recurring/RecurrenceEditor.vue +22 -15
- package/app/components/sandbox/SandboxPanel.vue +126 -59
- package/app/components/settings/InfrastructureBackendPicker.vue +304 -0
- package/app/components/settings/InfrastructureWindow.vue +6 -10
- package/app/components/settings/KubernetesRunnerForm.vue +30 -0
- package/app/components/settings/ProviderConnectionTab.vue +46 -85
- package/app/components/settings/ProviderManifestEditor.vue +19 -3
- package/i18n/locales/en.json +425 -13
- package/i18n/locales/es.json +403 -11
- package/i18n/locales/fr.json +403 -11
- package/i18n/locales/pl.json +403 -11
- package/i18n/locales/uk.json +403 -11
- package/package.json +2 -2
- package/app/components/settings/ExecutionBackendSelector.vue +0 -171
|
@@ -30,6 +30,7 @@ const library =
|
|
|
30
30
|
: useFragmentLibrary(props.kind, props.ownerId)
|
|
31
31
|
const documents = useDocumentsStore()
|
|
32
32
|
const toast = useToast()
|
|
33
|
+
const { t, d } = useI18n()
|
|
33
34
|
|
|
34
35
|
const isWorkspace = props.kind === 'workspace'
|
|
35
36
|
/** Linking a document at the account scope needs a workspace connection to fetch through. */
|
|
@@ -60,13 +61,16 @@ const tabs = computed<Tab[]>(() =>
|
|
|
60
61
|
)
|
|
61
62
|
const tab = ref<Tab>(props.showCatalog ? 'catalog' : 'authored')
|
|
62
63
|
|
|
63
|
-
const ownerLabel =
|
|
64
|
+
const ownerLabel = computed(() =>
|
|
65
|
+
isWorkspace ? t('fragments.owner.workspace') : t('fragments.owner.account'),
|
|
66
|
+
)
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
// Exhaustive tier→label map of literal `t(...)` keys (keeps the typed-key drift guard live).
|
|
69
|
+
const tierLabel = computed<Record<ResolvedFragment['tier'], string>>(() => ({
|
|
70
|
+
builtin: t('fragments.tier.builtin'),
|
|
71
|
+
account: t('fragments.tier.account'),
|
|
72
|
+
workspace: t('fragments.tier.workspace'),
|
|
73
|
+
}))
|
|
70
74
|
// `as const` keeps the literal color names (assignable to UBadge's `color`
|
|
71
75
|
// union) instead of widening to `string`; `satisfies` still checks the shape.
|
|
72
76
|
const tierColor = {
|
|
@@ -75,11 +79,11 @@ const tierColor = {
|
|
|
75
79
|
workspace: 'primary',
|
|
76
80
|
} as const satisfies Record<ResolvedFragment['tier'], string>
|
|
77
81
|
|
|
78
|
-
function tabLabel(
|
|
79
|
-
if (
|
|
80
|
-
if (
|
|
81
|
-
if (
|
|
82
|
-
return '
|
|
82
|
+
function tabLabel(which: Tab): string {
|
|
83
|
+
if (which === 'catalog') return t('fragments.tab.catalog')
|
|
84
|
+
if (which === 'authored') return ownerLabel.value
|
|
85
|
+
if (which === 'documents') return t('fragments.tab.documents')
|
|
86
|
+
return t('fragments.tab.sources')
|
|
83
87
|
}
|
|
84
88
|
|
|
85
89
|
function notifyError(title: string, e: unknown) {
|
|
@@ -110,18 +114,18 @@ async function createFragment() {
|
|
|
110
114
|
.filter(Boolean),
|
|
111
115
|
})
|
|
112
116
|
draft.value = { title: '', summary: '', body: '', tags: '' }
|
|
113
|
-
toast.add({ title: '
|
|
117
|
+
toast.add({ title: t('fragments.toast.added'), icon: 'i-lucide-check' })
|
|
114
118
|
} catch (e) {
|
|
115
|
-
notifyError('
|
|
119
|
+
notifyError(t('fragments.toast.addFailed'), e)
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
async function removeFragment(id: string) {
|
|
120
124
|
try {
|
|
121
125
|
await library.remove(id)
|
|
122
|
-
toast.add({ title: '
|
|
126
|
+
toast.add({ title: t('fragments.toast.removed'), icon: 'i-lucide-trash-2' })
|
|
123
127
|
} catch (e) {
|
|
124
|
-
notifyError('
|
|
128
|
+
notifyError(t('fragments.toast.removeFailed'), e)
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
@@ -148,18 +152,18 @@ async function linkDocumentFragment() {
|
|
|
148
152
|
.filter(Boolean),
|
|
149
153
|
})
|
|
150
154
|
docDraft.value = { source: '', ref: '', tags: '' }
|
|
151
|
-
toast.add({ title: '
|
|
155
|
+
toast.add({ title: t('fragments.toast.documentLinked'), icon: 'i-lucide-link' })
|
|
152
156
|
} catch (e) {
|
|
153
|
-
notifyError('
|
|
157
|
+
notifyError(t('fragments.toast.linkDocumentFailed'), e)
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
|
157
161
|
async function refreshFragment(id: string) {
|
|
158
162
|
try {
|
|
159
163
|
await library.refreshDocumentFragment(id)
|
|
160
|
-
toast.add({ title: '
|
|
164
|
+
toast.add({ title: t('fragments.toast.refreshed'), icon: 'i-lucide-refresh-cw' })
|
|
161
165
|
} catch (e) {
|
|
162
|
-
notifyError('
|
|
166
|
+
notifyError(t('fragments.toast.refreshFailed'), e)
|
|
163
167
|
}
|
|
164
168
|
}
|
|
165
169
|
|
|
@@ -180,9 +184,9 @@ async function linkSource() {
|
|
|
180
184
|
})
|
|
181
185
|
sourceDraft.value = { repoOwner: '', repoName: '', dirPath: '', gitRef: '' }
|
|
182
186
|
await library.syncSource(source.id)
|
|
183
|
-
toast.add({ title: '
|
|
187
|
+
toast.add({ title: t('fragments.toast.sourceLinked'), icon: 'i-lucide-git-branch' })
|
|
184
188
|
} catch (e) {
|
|
185
|
-
notifyError('
|
|
189
|
+
notifyError(t('fragments.toast.linkSourceFailed'), e)
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -190,12 +194,15 @@ async function syncSource(id: string) {
|
|
|
190
194
|
try {
|
|
191
195
|
const result = await library.syncSource(id)
|
|
192
196
|
toast.add({
|
|
193
|
-
title:
|
|
197
|
+
title: t('fragments.toast.synced', {
|
|
198
|
+
updated: result.upserted,
|
|
199
|
+
removed: result.tombstoned,
|
|
200
|
+
}),
|
|
194
201
|
icon: 'i-lucide-refresh-cw',
|
|
195
202
|
color: 'info',
|
|
196
203
|
})
|
|
197
204
|
} catch (e) {
|
|
198
|
-
notifyError('
|
|
205
|
+
notifyError(t('fragments.toast.syncFailed'), e)
|
|
199
206
|
}
|
|
200
207
|
}
|
|
201
208
|
|
|
@@ -203,20 +210,22 @@ async function checkSource(id: string) {
|
|
|
203
210
|
try {
|
|
204
211
|
const status = await library.checkSource(id)
|
|
205
212
|
toast.add({
|
|
206
|
-
title: status.changed
|
|
213
|
+
title: status.changed
|
|
214
|
+
? t('fragments.toast.changesAvailable', { count: status.changedCount }, status.changedCount)
|
|
215
|
+
: t('fragments.toast.upToDate'),
|
|
207
216
|
icon: status.changed ? 'i-lucide-bell-dot' : 'i-lucide-check',
|
|
208
217
|
})
|
|
209
218
|
} catch (e) {
|
|
210
|
-
notifyError('
|
|
219
|
+
notifyError(t('fragments.toast.checkSourceFailed'), e)
|
|
211
220
|
}
|
|
212
221
|
}
|
|
213
222
|
|
|
214
223
|
async function unlinkSource(id: string) {
|
|
215
224
|
try {
|
|
216
225
|
await library.unlinkSource(id)
|
|
217
|
-
toast.add({ title: '
|
|
226
|
+
toast.add({ title: t('fragments.toast.sourceUnlinked'), icon: 'i-lucide-unplug' })
|
|
218
227
|
} catch (e) {
|
|
219
|
-
notifyError('
|
|
228
|
+
notifyError(t('fragments.toast.unlinkSourceFailed'), e)
|
|
220
229
|
}
|
|
221
230
|
}
|
|
222
231
|
</script>
|
|
@@ -225,14 +234,10 @@ async function unlinkSource(id: string) {
|
|
|
225
234
|
<div class="flex flex-col gap-4">
|
|
226
235
|
<p class="text-sm text-slate-400">
|
|
227
236
|
<template v-if="isWorkspace">
|
|
228
|
-
|
|
229
|
-
the built-in catalog, your account, and this board — later tiers override earlier ones —
|
|
230
|
-
then the relevant ones are selected for each agent run.
|
|
237
|
+
{{ t('fragments.intro.workspace') }}
|
|
231
238
|
</template>
|
|
232
239
|
<template v-else>
|
|
233
|
-
|
|
234
|
-
merged below the built-in catalog and above nothing, and a board can override or add its own
|
|
235
|
-
on top. The relevant ones are selected for each agent run.
|
|
240
|
+
{{ t('fragments.intro.account') }}
|
|
236
241
|
</template>
|
|
237
242
|
</p>
|
|
238
243
|
|
|
@@ -252,7 +257,13 @@ async function unlinkSource(id: string) {
|
|
|
252
257
|
<!-- Resolved (merged) catalog — workspace scope only -->
|
|
253
258
|
<div v-if="tab === 'catalog'" class="flex flex-col gap-2">
|
|
254
259
|
<p class="text-xs text-slate-500">
|
|
255
|
-
{{
|
|
260
|
+
{{
|
|
261
|
+
t(
|
|
262
|
+
'fragments.catalog.summary',
|
|
263
|
+
{ count: library.resolved.length, builtin: library.builtinCount },
|
|
264
|
+
library.resolved.length,
|
|
265
|
+
)
|
|
266
|
+
}}
|
|
256
267
|
</p>
|
|
257
268
|
<div
|
|
258
269
|
v-for="f in library.resolved"
|
|
@@ -271,7 +282,7 @@ async function unlinkSource(id: string) {
|
|
|
271
282
|
variant="subtle"
|
|
272
283
|
icon="i-lucide-radio"
|
|
273
284
|
>
|
|
274
|
-
|
|
285
|
+
{{ t('fragments.catalog.live', { source: f.documentRef.source }) }}
|
|
275
286
|
</UBadge>
|
|
276
287
|
<span class="ml-auto font-mono text-[11px] text-slate-500">{{ f.id }}</span>
|
|
277
288
|
</div>
|
|
@@ -294,7 +305,9 @@ async function unlinkSource(id: string) {
|
|
|
294
305
|
<div class="min-w-0">
|
|
295
306
|
<div class="flex items-center gap-2">
|
|
296
307
|
<span class="font-medium text-slate-100">{{ f.title }}</span>
|
|
297
|
-
<UBadge v-if="f.source" size="xs" color="info" variant="subtle">
|
|
308
|
+
<UBadge v-if="f.source" size="xs" color="info" variant="subtle">{{
|
|
309
|
+
t('fragments.authored.fromRepo')
|
|
310
|
+
}}</UBadge>
|
|
298
311
|
</div>
|
|
299
312
|
<p class="text-sm text-slate-400">{{ f.summary }}</p>
|
|
300
313
|
</div>
|
|
@@ -308,21 +321,27 @@ async function unlinkSource(id: string) {
|
|
|
308
321
|
/>
|
|
309
322
|
</div>
|
|
310
323
|
<p v-if="!library.fragments.length" class="text-sm text-slate-500">
|
|
311
|
-
|
|
312
|
-
|
|
324
|
+
{{
|
|
325
|
+
isWorkspace
|
|
326
|
+
? t('fragments.authored.empty.workspace')
|
|
327
|
+
: t('fragments.authored.empty.account')
|
|
328
|
+
}}
|
|
313
329
|
</p>
|
|
314
330
|
|
|
315
331
|
<div class="rounded-md border border-slate-800 p-3">
|
|
316
|
-
<p class="mb-2 text-sm font-medium">
|
|
332
|
+
<p class="mb-2 text-sm font-medium">{{ t('fragments.authored.addTitle') }}</p>
|
|
317
333
|
<div class="flex flex-col gap-2">
|
|
318
|
-
<UInput v-model="draft.title" placeholder="
|
|
319
|
-
<UInput
|
|
334
|
+
<UInput v-model="draft.title" :placeholder="t('fragments.authored.titlePlaceholder')" />
|
|
335
|
+
<UInput
|
|
336
|
+
v-model="draft.summary"
|
|
337
|
+
:placeholder="t('fragments.authored.summaryPlaceholder')"
|
|
338
|
+
/>
|
|
320
339
|
<UTextarea
|
|
321
340
|
v-model="draft.body"
|
|
322
|
-
placeholder="
|
|
341
|
+
:placeholder="t('fragments.authored.bodyPlaceholder')"
|
|
323
342
|
:rows="4"
|
|
324
343
|
/>
|
|
325
|
-
<UInput v-model="draft.tags" placeholder="
|
|
344
|
+
<UInput v-model="draft.tags" :placeholder="t('fragments.authored.tagsPlaceholder')" />
|
|
326
345
|
<UButton
|
|
327
346
|
icon="i-lucide-plus"
|
|
328
347
|
size="sm"
|
|
@@ -331,7 +350,7 @@ async function unlinkSource(id: string) {
|
|
|
331
350
|
class="self-start"
|
|
332
351
|
@click="createFragment"
|
|
333
352
|
>
|
|
334
|
-
|
|
353
|
+
{{ t('fragments.authored.add') }}
|
|
335
354
|
</UButton>
|
|
336
355
|
</div>
|
|
337
356
|
</div>
|
|
@@ -340,9 +359,7 @@ async function unlinkSource(id: string) {
|
|
|
340
359
|
<!-- Document-backed (living) fragments -->
|
|
341
360
|
<div v-else-if="tab === 'documents'" class="flex flex-col gap-3">
|
|
342
361
|
<p class="text-xs text-slate-500">
|
|
343
|
-
|
|
344
|
-
re-resolved from the source at run time — edit the doc and the next agent run follows the
|
|
345
|
-
new version (no re-import).
|
|
362
|
+
{{ t('fragments.documents.intro') }}
|
|
346
363
|
</p>
|
|
347
364
|
|
|
348
365
|
<div
|
|
@@ -360,7 +377,7 @@ async function unlinkSource(id: string) {
|
|
|
360
377
|
</div>
|
|
361
378
|
<p class="text-sm text-slate-400">{{ f.summary }}</p>
|
|
362
379
|
<p v-if="f.resolvedAt" class="text-[11px] text-slate-500">
|
|
363
|
-
|
|
380
|
+
{{ t('fragments.documents.lastResolved', { date: d(new Date(f.resolvedAt), 'long') }) }}
|
|
364
381
|
</p>
|
|
365
382
|
</div>
|
|
366
383
|
<div class="ml-auto flex gap-1">
|
|
@@ -369,7 +386,7 @@ async function unlinkSource(id: string) {
|
|
|
369
386
|
size="xs"
|
|
370
387
|
variant="ghost"
|
|
371
388
|
:loading="library.loading"
|
|
372
|
-
title="
|
|
389
|
+
:title="t('fragments.documents.refreshTitle')"
|
|
373
390
|
@click="refreshFragment(f.id)"
|
|
374
391
|
/>
|
|
375
392
|
<UButton
|
|
@@ -382,17 +399,16 @@ async function unlinkSource(id: string) {
|
|
|
382
399
|
</div>
|
|
383
400
|
</div>
|
|
384
401
|
<p v-if="!documentFragments.length" class="text-sm text-slate-500">
|
|
385
|
-
|
|
402
|
+
{{ t('fragments.documents.empty') }}
|
|
386
403
|
</p>
|
|
387
404
|
|
|
388
405
|
<div class="rounded-md border border-slate-800 p-3">
|
|
389
|
-
<p class="mb-2 text-sm font-medium">
|
|
406
|
+
<p class="mb-2 text-sm font-medium">{{ t('fragments.documents.linkTitle') }}</p>
|
|
390
407
|
<div v-if="docLinkDisabled" class="text-sm text-slate-500">
|
|
391
|
-
|
|
392
|
-
are fetched through one of this account's boards.
|
|
408
|
+
{{ t('fragments.documents.disabledHint') }}
|
|
393
409
|
</div>
|
|
394
410
|
<div v-else-if="!documents.connectedSources.length" class="text-sm text-slate-500">
|
|
395
|
-
|
|
411
|
+
{{ t('fragments.documents.connectFirst') }}
|
|
396
412
|
</div>
|
|
397
413
|
<div v-else class="flex flex-col gap-2">
|
|
398
414
|
<div class="flex flex-wrap gap-2">
|
|
@@ -407,11 +423,8 @@ async function unlinkSource(id: string) {
|
|
|
407
423
|
{{ s.label }}
|
|
408
424
|
</UButton>
|
|
409
425
|
</div>
|
|
410
|
-
<UInput
|
|
411
|
-
|
|
412
|
-
placeholder="Page id or URL (e.g. a Confluence/Notion page or GitHub file URL)"
|
|
413
|
-
/>
|
|
414
|
-
<UInput v-model="docDraft.tags" placeholder="Tags, comma-separated (optional)" />
|
|
426
|
+
<UInput v-model="docDraft.ref" :placeholder="t('fragments.documents.refPlaceholder')" />
|
|
427
|
+
<UInput v-model="docDraft.tags" :placeholder="t('fragments.documents.tagsPlaceholder')" />
|
|
415
428
|
<UButton
|
|
416
429
|
icon="i-lucide-link"
|
|
417
430
|
size="sm"
|
|
@@ -420,7 +433,7 @@ async function unlinkSource(id: string) {
|
|
|
420
433
|
class="self-start"
|
|
421
434
|
@click="linkDocumentFragment"
|
|
422
435
|
>
|
|
423
|
-
|
|
436
|
+
{{ t('fragments.documents.link') }}
|
|
424
437
|
</UButton>
|
|
425
438
|
</div>
|
|
426
439
|
</div>
|
|
@@ -440,7 +453,11 @@ async function unlinkSource(id: string) {
|
|
|
440
453
|
}}<span class="text-slate-500">/{{ s.dirPath || '' }}</span>
|
|
441
454
|
</span>
|
|
442
455
|
<p class="text-xs text-slate-500">
|
|
443
|
-
{{
|
|
456
|
+
{{
|
|
457
|
+
s.lastSyncedAt
|
|
458
|
+
? t('fragments.sources.metaSynced', { ref: s.gitRef })
|
|
459
|
+
: t('fragments.sources.metaNever', { ref: s.gitRef })
|
|
460
|
+
}}
|
|
444
461
|
</p>
|
|
445
462
|
</div>
|
|
446
463
|
<UBadge
|
|
@@ -450,7 +467,13 @@ async function unlinkSource(id: string) {
|
|
|
450
467
|
variant="subtle"
|
|
451
468
|
class="ml-auto"
|
|
452
469
|
>
|
|
453
|
-
{{
|
|
470
|
+
{{
|
|
471
|
+
t(
|
|
472
|
+
'fragments.sources.changes',
|
|
473
|
+
{ count: library.sourceChanges[s.id] },
|
|
474
|
+
library.sourceChanges[s.id] ?? 0,
|
|
475
|
+
)
|
|
476
|
+
}}
|
|
454
477
|
</UBadge>
|
|
455
478
|
<div class="ml-auto flex gap-1">
|
|
456
479
|
<UButton
|
|
@@ -476,23 +499,35 @@ async function unlinkSource(id: string) {
|
|
|
476
499
|
</div>
|
|
477
500
|
</div>
|
|
478
501
|
<p v-if="!library.sources.length" class="text-sm text-slate-500">
|
|
479
|
-
|
|
502
|
+
{{ t('fragments.sources.empty') }}
|
|
480
503
|
</p>
|
|
481
504
|
|
|
482
505
|
<div class="rounded-md border border-slate-800 p-3">
|
|
483
|
-
<p class="mb-2 text-sm font-medium">
|
|
506
|
+
<p class="mb-2 text-sm font-medium">{{ t('fragments.sources.linkTitle') }}</p>
|
|
484
507
|
<div class="flex flex-col gap-2">
|
|
485
508
|
<div class="flex gap-2">
|
|
486
|
-
<UInput
|
|
487
|
-
|
|
509
|
+
<UInput
|
|
510
|
+
v-model="sourceDraft.repoOwner"
|
|
511
|
+
:placeholder="t('fragments.sources.ownerPlaceholder')"
|
|
512
|
+
class="flex-1"
|
|
513
|
+
/>
|
|
514
|
+
<UInput
|
|
515
|
+
v-model="sourceDraft.repoName"
|
|
516
|
+
:placeholder="t('fragments.sources.repoPlaceholder')"
|
|
517
|
+
class="flex-1"
|
|
518
|
+
/>
|
|
488
519
|
</div>
|
|
489
520
|
<div class="flex gap-2">
|
|
490
521
|
<UInput
|
|
491
522
|
v-model="sourceDraft.dirPath"
|
|
492
|
-
placeholder="
|
|
523
|
+
:placeholder="t('fragments.sources.dirPlaceholder')"
|
|
524
|
+
class="flex-1"
|
|
525
|
+
/>
|
|
526
|
+
<UInput
|
|
527
|
+
v-model="sourceDraft.gitRef"
|
|
528
|
+
:placeholder="t('fragments.sources.refPlaceholder')"
|
|
493
529
|
class="flex-1"
|
|
494
530
|
/>
|
|
495
|
-
<UInput v-model="sourceDraft.gitRef" placeholder="ref (default HEAD)" class="flex-1" />
|
|
496
531
|
</div>
|
|
497
532
|
<UButton
|
|
498
533
|
icon="i-lucide-link"
|
|
@@ -502,7 +537,7 @@ async function unlinkSource(id: string) {
|
|
|
502
537
|
class="self-start"
|
|
503
538
|
@click="linkSource"
|
|
504
539
|
>
|
|
505
|
-
|
|
540
|
+
{{ t('fragments.sources.link') }}
|
|
506
541
|
</UButton>
|
|
507
542
|
</div>
|
|
508
543
|
</div>
|
|
@@ -7,6 +7,7 @@ import FragmentLibraryManager from '~/components/fragments/FragmentLibraryManage
|
|
|
7
7
|
|
|
8
8
|
const ui = useUiStore()
|
|
9
9
|
const workspace = useWorkspaceStore()
|
|
10
|
+
const { t } = useI18n()
|
|
10
11
|
|
|
11
12
|
const open = computed({
|
|
12
13
|
get: () => ui.fragmentLibraryOpen,
|
|
@@ -17,7 +18,7 @@ const open = computed({
|
|
|
17
18
|
</script>
|
|
18
19
|
|
|
19
20
|
<template>
|
|
20
|
-
<UModal v-model:open="open" title="
|
|
21
|
+
<UModal v-model:open="open" :title="t('fragments.panel.title')" :ui="{ content: 'max-w-3xl' }">
|
|
21
22
|
<template #body>
|
|
22
23
|
<FragmentLibraryManager
|
|
23
24
|
v-if="workspace.workspaceId"
|
|
@@ -9,6 +9,7 @@ import { agentKindMeta } from '~/utils/catalog'
|
|
|
9
9
|
// grading is scheduled by the engine and run by the background sweep, never from here.
|
|
10
10
|
const ui = useUiStore()
|
|
11
11
|
const kaizen = useKaizenStore()
|
|
12
|
+
const { t, d } = useI18n()
|
|
12
13
|
|
|
13
14
|
const open = computed(() => ui.kaizenScreenOpen)
|
|
14
15
|
|
|
@@ -31,7 +32,7 @@ function meta(kind: string) {
|
|
|
31
32
|
return agentKindMeta(kind)
|
|
32
33
|
}
|
|
33
34
|
function when(ms: number): string {
|
|
34
|
-
return new Date(ms)
|
|
35
|
+
return d(new Date(ms), 'long')
|
|
35
36
|
}
|
|
36
37
|
function gradeTone(g: KaizenGrading): string {
|
|
37
38
|
if (g.status === 'failed') return 'text-slate-500'
|
|
@@ -42,10 +43,10 @@ function gradeTone(g: KaizenGrading): string {
|
|
|
42
43
|
return 'text-rose-400'
|
|
43
44
|
}
|
|
44
45
|
function statusLabel(g: KaizenGrading): string {
|
|
45
|
-
if (g.status === 'scheduled') return '
|
|
46
|
-
if (g.status === 'running') return '
|
|
47
|
-
if (g.status === 'failed') return '
|
|
48
|
-
return g.grade != null ?
|
|
46
|
+
if (g.status === 'scheduled') return t('kaizen.status.scheduled')
|
|
47
|
+
if (g.status === 'running') return t('kaizen.status.grading')
|
|
48
|
+
if (g.status === 'failed') return t('kaizen.status.failed')
|
|
49
|
+
return g.grade != null ? t('kaizen.gradeValue', { grade: g.grade }) : t('kaizen.status.graded')
|
|
49
50
|
}
|
|
50
51
|
</script>
|
|
51
52
|
|
|
@@ -63,9 +64,9 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
63
64
|
<UIcon name="i-lucide-sparkles" class="h-5 w-5 text-teal-400" />
|
|
64
65
|
</div>
|
|
65
66
|
<div class="min-w-0">
|
|
66
|
-
<h1 class="truncate text-base font-semibold text-white">
|
|
67
|
+
<h1 class="truncate text-base font-semibold text-white">{{ t('kaizen.title') }}</h1>
|
|
67
68
|
<p class="truncate text-xs text-slate-500">
|
|
68
|
-
|
|
69
|
+
{{ t('kaizen.subtitle') }}
|
|
69
70
|
</p>
|
|
70
71
|
</div>
|
|
71
72
|
<div class="ml-auto flex items-center gap-2">
|
|
@@ -77,10 +78,10 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
77
78
|
:loading="kaizen.loadingOverview"
|
|
78
79
|
@click="kaizen.loadOverview()"
|
|
79
80
|
>
|
|
80
|
-
|
|
81
|
+
{{ t('kaizen.refresh') }}
|
|
81
82
|
</UButton>
|
|
82
83
|
<UButton icon="i-lucide-x" size="xs" color="neutral" variant="ghost" @click="close">
|
|
83
|
-
|
|
84
|
+
{{ t('common.close') }}
|
|
84
85
|
</UButton>
|
|
85
86
|
</div>
|
|
86
87
|
</header>
|
|
@@ -89,7 +90,7 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
89
90
|
v-if="kaizen.available === false"
|
|
90
91
|
class="flex flex-1 items-center justify-center text-sm text-slate-500"
|
|
91
92
|
>
|
|
92
|
-
|
|
93
|
+
{{ t('kaizen.notConfigured') }}
|
|
93
94
|
</div>
|
|
94
95
|
|
|
95
96
|
<div v-else class="grid flex-1 grid-cols-1 gap-6 overflow-auto p-6 lg:grid-cols-3">
|
|
@@ -97,12 +98,13 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
97
98
|
<section class="lg:col-span-1">
|
|
98
99
|
<h2 class="mb-2 flex items-center gap-2 text-sm font-semibold text-slate-200">
|
|
99
100
|
<UIcon name="i-lucide-badge-check" class="h-4 w-4 text-emerald-400" />
|
|
100
|
-
|
|
101
|
-
<span class="text-xs font-normal text-slate-500">
|
|
101
|
+
{{ t('kaizen.verifiedCombos.title') }}
|
|
102
|
+
<span class="text-xs font-normal text-slate-500">{{
|
|
103
|
+
t('kaizen.verifiedCombos.count', { count: kaizen.verifiedCount })
|
|
104
|
+
}}</span>
|
|
102
105
|
</h2>
|
|
103
106
|
<p class="mb-3 text-[11px] text-slate-500">
|
|
104
|
-
|
|
105
|
-
times in a row. These are no longer graded.
|
|
107
|
+
{{ t('kaizen.verifiedCombos.hint') }}
|
|
106
108
|
</p>
|
|
107
109
|
<ul class="space-y-2">
|
|
108
110
|
<li
|
|
@@ -125,15 +127,20 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
125
127
|
class="ml-auto h-3.5 w-3.5 text-emerald-400"
|
|
126
128
|
/>
|
|
127
129
|
<span v-else class="ml-auto text-[11px] text-slate-500">
|
|
128
|
-
{{ c.consecutiveHighGrades }}
|
|
130
|
+
{{ t('kaizen.verifiedCombos.progress', { count: c.consecutiveHighGrades }) }}
|
|
129
131
|
</span>
|
|
130
132
|
</div>
|
|
131
133
|
<div class="mt-1 truncate text-[11px] text-slate-500" :title="c.model">
|
|
132
|
-
{{
|
|
134
|
+
{{
|
|
135
|
+
t('kaizen.verifiedCombos.modelPrompt', {
|
|
136
|
+
model: c.model,
|
|
137
|
+
version: c.promptVersion,
|
|
138
|
+
})
|
|
139
|
+
}}
|
|
133
140
|
</div>
|
|
134
141
|
</li>
|
|
135
142
|
<li v-if="kaizen.verified.length === 0" class="text-xs text-slate-600">
|
|
136
|
-
|
|
143
|
+
{{ t('kaizen.verifiedCombos.empty') }}
|
|
137
144
|
</li>
|
|
138
145
|
</ul>
|
|
139
146
|
</section>
|
|
@@ -142,17 +149,19 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
142
149
|
<section class="lg:col-span-2">
|
|
143
150
|
<h2 class="mb-2 flex items-center gap-2 text-sm font-semibold text-slate-200">
|
|
144
151
|
<UIcon name="i-lucide-history" class="h-4 w-4 text-teal-400" />
|
|
145
|
-
|
|
152
|
+
{{ t('kaizen.history.title') }}
|
|
146
153
|
</h2>
|
|
147
154
|
<div class="overflow-hidden rounded-lg border border-slate-800">
|
|
148
155
|
<table class="w-full text-left text-xs">
|
|
149
156
|
<thead class="bg-slate-900/60 text-[11px] uppercase tracking-wide text-slate-500">
|
|
150
157
|
<tr>
|
|
151
|
-
<th class="px-3 py-2 font-medium">
|
|
152
|
-
<th class="px-3 py-2 font-medium">
|
|
153
|
-
<th class="px-3 py-2 font-medium">
|
|
154
|
-
<th class="px-3 py-2 font-medium">
|
|
155
|
-
<th class="px-3 py-2 font-medium">
|
|
158
|
+
<th class="px-3 py-2 font-medium">{{ t('kaizen.history.col.when') }}</th>
|
|
159
|
+
<th class="px-3 py-2 font-medium">{{ t('kaizen.history.col.agent') }}</th>
|
|
160
|
+
<th class="px-3 py-2 font-medium">{{ t('kaizen.history.col.model') }}</th>
|
|
161
|
+
<th class="px-3 py-2 font-medium">{{ t('kaizen.history.col.grade') }}</th>
|
|
162
|
+
<th class="px-3 py-2 font-medium">
|
|
163
|
+
{{ t('kaizen.history.col.recommendations') }}
|
|
164
|
+
</th>
|
|
156
165
|
</tr>
|
|
157
166
|
</thead>
|
|
158
167
|
<tbody class="divide-y divide-slate-800/70">
|
|
@@ -168,7 +177,9 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
168
177
|
:style="{ color: meta(g.agentKind).color }"
|
|
169
178
|
/>
|
|
170
179
|
<span class="text-slate-200">{{ meta(g.agentKind).label }}</span>
|
|
171
|
-
<span class="text-slate-600">
|
|
180
|
+
<span class="text-slate-600">{{
|
|
181
|
+
t('kaizen.promptVersion', { version: g.promptVersion })
|
|
182
|
+
}}</span>
|
|
172
183
|
</span>
|
|
173
184
|
</td>
|
|
174
185
|
<td class="max-w-[12rem] truncate px-3 py-2 text-slate-400" :title="g.model">
|
|
@@ -187,7 +198,7 @@ function statusLabel(g: KaizenGrading): string {
|
|
|
187
198
|
</tr>
|
|
188
199
|
<tr v-if="kaizen.history.length === 0">
|
|
189
200
|
<td colspan="5" class="px-3 py-6 text-center text-slate-600">
|
|
190
|
-
|
|
201
|
+
{{ t('kaizen.history.empty') }}
|
|
191
202
|
</td>
|
|
192
203
|
</tr>
|
|
193
204
|
</tbody>
|
|
@@ -13,6 +13,7 @@ const props = defineProps<{
|
|
|
13
13
|
}>()
|
|
14
14
|
|
|
15
15
|
const kaizen = useKaizenStore()
|
|
16
|
+
const { t } = useI18n()
|
|
16
17
|
|
|
17
18
|
const grading = computed(() => {
|
|
18
19
|
if (!props.instanceId || props.stepIndex == null) return null
|
|
@@ -45,50 +46,52 @@ const tone = computed(() => {
|
|
|
45
46
|
<section v-if="grading" class="rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
|
46
47
|
<div class="flex items-center gap-2">
|
|
47
48
|
<UIcon name="i-lucide-sparkles" class="h-4 w-4 text-teal-400" />
|
|
48
|
-
<h3 class="text-sm font-semibold text-slate-200">
|
|
49
|
+
<h3 class="text-sm font-semibold text-slate-200">{{ t('kaizen.grading.title') }}</h3>
|
|
49
50
|
<span class="ml-auto flex items-center gap-1.5 text-xs">
|
|
50
51
|
<template v-if="grading.status === 'scheduled'">
|
|
51
52
|
<UIcon name="i-lucide-clock" class="h-3.5 w-3.5 text-slate-500" />
|
|
52
|
-
<span class="text-slate-400">
|
|
53
|
+
<span class="text-slate-400">{{ t('kaizen.status.scheduled') }}</span>
|
|
53
54
|
</template>
|
|
54
55
|
<template v-else-if="grading.status === 'running'">
|
|
55
56
|
<UIcon name="i-lucide-loader-circle" class="h-3.5 w-3.5 animate-spin text-teal-400" />
|
|
56
|
-
<span class="text-teal-300">
|
|
57
|
+
<span class="text-teal-300">{{ t('kaizen.status.grading') }}</span>
|
|
57
58
|
</template>
|
|
58
59
|
<template v-else-if="grading.status === 'failed'">
|
|
59
60
|
<UIcon name="i-lucide-circle-alert" class="h-3.5 w-3.5 text-rose-400" />
|
|
60
|
-
<span class="text-rose-400">
|
|
61
|
+
<span class="text-rose-400">{{ t('kaizen.status.failed') }}</span>
|
|
61
62
|
</template>
|
|
62
63
|
<template v-else>
|
|
63
|
-
<span class="font-semibold" :class="tone">{{
|
|
64
|
+
<span class="font-semibold" :class="tone">{{
|
|
65
|
+
t('kaizen.gradeValue', { grade: grading.grade })
|
|
66
|
+
}}</span>
|
|
64
67
|
</template>
|
|
65
68
|
</span>
|
|
66
69
|
</div>
|
|
67
70
|
|
|
68
71
|
<p v-if="grading.status === 'scheduled'" class="mt-2 text-[11px] text-slate-500">
|
|
69
|
-
|
|
72
|
+
{{ t('kaizen.scheduledHint') }}
|
|
70
73
|
</p>
|
|
71
74
|
|
|
72
75
|
<template v-else-if="grading.status === 'complete'">
|
|
73
76
|
<p v-if="grading.summary" class="mt-2 text-xs text-slate-300">{{ grading.summary }}</p>
|
|
74
77
|
<div v-if="grading.recommendations.length" class="mt-2">
|
|
75
78
|
<p class="text-[11px] font-medium uppercase tracking-wide text-slate-500">
|
|
76
|
-
|
|
79
|
+
{{ t('kaizen.recommendations') }}
|
|
77
80
|
</p>
|
|
78
81
|
<ul class="mt-1 list-disc space-y-0.5 pl-4 text-xs text-slate-300">
|
|
79
82
|
<li v-for="(r, i) in grading.recommendations" :key="i">{{ r }}</li>
|
|
80
83
|
</ul>
|
|
81
84
|
</div>
|
|
82
85
|
<p v-else class="mt-2 text-[11px] text-emerald-400/80">
|
|
83
|
-
|
|
86
|
+
{{ t('kaizen.noImprovements') }}
|
|
84
87
|
</p>
|
|
85
88
|
<p v-if="grading.graderModel" class="mt-2 text-[10px] text-slate-600">
|
|
86
|
-
|
|
89
|
+
{{ t('kaizen.gradedBy', { model: grading.graderModel }) }}
|
|
87
90
|
</p>
|
|
88
91
|
</template>
|
|
89
92
|
|
|
90
93
|
<p v-else-if="grading.status === 'failed'" class="mt-2 text-[11px] text-rose-400/80">
|
|
91
|
-
{{ grading.error ?? '
|
|
94
|
+
{{ grading.error ?? t('kaizen.failedFallback') }}
|
|
92
95
|
</p>
|
|
93
96
|
</section>
|
|
94
97
|
</template>
|