@cat-factory/app 0.54.0 → 0.54.2
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/focus/BlockFocusView.vue +92 -98
- 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/pages/index.vue +20 -8
- package/app/stores/workspace.ts +10 -5
- package/i18n/locales/en.json +409 -0
- package/i18n/locales/es.json +400 -0
- package/i18n/locales/fr.json +400 -0
- package/i18n/locales/pl.json +400 -0
- package/i18n/locales/uk.json +400 -0
- package/package.json +1 -1
|
@@ -8,15 +8,22 @@ import type { Recurrence } from '~/types/recurring'
|
|
|
8
8
|
const props = defineProps<{ modelValue: Recurrence }>()
|
|
9
9
|
const emit = defineEmits<{ 'update:modelValue': [Recurrence] }>()
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
const { t } = useI18n()
|
|
12
|
+
|
|
13
|
+
// Exhaustive day-index→label map of literal `t(...)` keys (keeps the typed-key drift
|
|
14
|
+
// guard live for this short-form weekday list).
|
|
15
|
+
const WEEKDAY_LABELS = computed<Record<number, string>>(() => ({
|
|
16
|
+
0: t('recurring.weekday.sun'),
|
|
17
|
+
1: t('recurring.weekday.mon'),
|
|
18
|
+
2: t('recurring.weekday.tue'),
|
|
19
|
+
3: t('recurring.weekday.wed'),
|
|
20
|
+
4: t('recurring.weekday.thu'),
|
|
21
|
+
5: t('recurring.weekday.fri'),
|
|
22
|
+
6: t('recurring.weekday.sat'),
|
|
23
|
+
}))
|
|
24
|
+
const WEEKDAYS = computed(() =>
|
|
25
|
+
[0, 1, 2, 3, 4, 5, 6].map((value) => ({ value, label: WEEKDAY_LABELS.value[value]! })),
|
|
26
|
+
)
|
|
20
27
|
|
|
21
28
|
function patch(p: Partial<Recurrence>) {
|
|
22
29
|
emit('update:modelValue', { ...props.modelValue, ...p })
|
|
@@ -60,7 +67,7 @@ const timezoneOptions = computed(() =>
|
|
|
60
67
|
|
|
61
68
|
<template>
|
|
62
69
|
<div class="space-y-3">
|
|
63
|
-
<UFormField label="
|
|
70
|
+
<UFormField :label="t('recurring.runEvery')">
|
|
64
71
|
<div class="flex items-center gap-2">
|
|
65
72
|
<UInput
|
|
66
73
|
:model-value="modelValue.intervalHours"
|
|
@@ -69,11 +76,11 @@ const timezoneOptions = computed(() =>
|
|
|
69
76
|
class="w-24"
|
|
70
77
|
@update:model-value="patch({ intervalHours: Math.max(1, Number($event) || 1) })"
|
|
71
78
|
/>
|
|
72
|
-
<span class="text-xs text-slate-400">hours</span>
|
|
79
|
+
<span class="text-xs text-slate-400">{{ t('recurring.hours') }}</span>
|
|
73
80
|
</div>
|
|
74
81
|
</UFormField>
|
|
75
82
|
|
|
76
|
-
<UFormField label="
|
|
83
|
+
<UFormField :label="t('recurring.allowedDays')" :help="t('recurring.allowedDaysHelp')">
|
|
77
84
|
<div class="flex flex-wrap gap-1">
|
|
78
85
|
<UButton
|
|
79
86
|
v-for="d in WEEKDAYS"
|
|
@@ -91,7 +98,7 @@ const timezoneOptions = computed(() =>
|
|
|
91
98
|
<UFormField>
|
|
92
99
|
<UCheckbox
|
|
93
100
|
:model-value="windowEnabled"
|
|
94
|
-
label="
|
|
101
|
+
:label="t('recurring.windowToggle')"
|
|
95
102
|
@update:model-value="toggleWindow(Boolean($event))"
|
|
96
103
|
/>
|
|
97
104
|
</UFormField>
|
|
@@ -103,7 +110,7 @@ const timezoneOptions = computed(() =>
|
|
|
103
110
|
class="w-28"
|
|
104
111
|
@update:model-value="patch({ windowStartHour: Number($event) })"
|
|
105
112
|
/>
|
|
106
|
-
<span class="text-xs text-slate-400">to</span>
|
|
113
|
+
<span class="text-xs text-slate-400">{{ t('recurring.to') }}</span>
|
|
107
114
|
<USelect
|
|
108
115
|
:model-value="modelValue.windowEndHour ?? 24 % 24"
|
|
109
116
|
:items="hours"
|
|
@@ -112,7 +119,7 @@ const timezoneOptions = computed(() =>
|
|
|
112
119
|
/>
|
|
113
120
|
</div>
|
|
114
121
|
|
|
115
|
-
<UFormField label="
|
|
122
|
+
<UFormField :label="t('recurring.timezone')">
|
|
116
123
|
<USelect
|
|
117
124
|
:model-value="modelValue.timezone"
|
|
118
125
|
:items="timezoneOptions"
|
|
@@ -6,11 +6,39 @@
|
|
|
6
6
|
// and Fixtures (the graded inputs each run is scored against). Loaded on demand when the
|
|
7
7
|
// window opens; 503 (the deployment hasn't provisioned the Sandbox DB) shows a notice.
|
|
8
8
|
import { computed, ref, watch } from 'vue'
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
SandboxExperimentStatus,
|
|
11
|
+
SandboxFixtureKind,
|
|
12
|
+
SandboxGrade,
|
|
13
|
+
SandboxPromptVersion,
|
|
14
|
+
SandboxRun,
|
|
15
|
+
} from '~/types/sandbox'
|
|
10
16
|
|
|
11
17
|
const ui = useUiStore()
|
|
12
18
|
const store = useSandboxStore()
|
|
13
19
|
const toast = useToast()
|
|
20
|
+
const { t } = useI18n()
|
|
21
|
+
|
|
22
|
+
// Exhaustive enum→label maps of literal `t(...)` keys (keeps the typed-key drift guard
|
|
23
|
+
// live for these runtime-indexed status/kind/origin lookups).
|
|
24
|
+
const EXPERIMENT_STATUS_LABEL = computed<Record<SandboxExperimentStatus, string>>(() => ({
|
|
25
|
+
draft: t('sandbox.experimentStatus.draft'),
|
|
26
|
+
running: t('sandbox.experimentStatus.running'),
|
|
27
|
+
done: t('sandbox.experimentStatus.done'),
|
|
28
|
+
failed: t('sandbox.experimentStatus.failed'),
|
|
29
|
+
}))
|
|
30
|
+
const FIXTURE_KIND_LABEL = computed<Record<SandboxFixtureKind, string>>(() => ({
|
|
31
|
+
requirements: t('sandbox.fixtureKind.requirements'),
|
|
32
|
+
clarity: t('sandbox.fixtureKind.clarity'),
|
|
33
|
+
architecture: t('sandbox.fixtureKind.architecture'),
|
|
34
|
+
'code-review': t('sandbox.fixtureKind.code-review'),
|
|
35
|
+
'repo-feature': t('sandbox.fixtureKind.repo-feature'),
|
|
36
|
+
'repo-bug': t('sandbox.fixtureKind.repo-bug'),
|
|
37
|
+
}))
|
|
38
|
+
const FIXTURE_ORIGIN_LABEL = computed<Record<'builtin' | 'custom', string>>(() => ({
|
|
39
|
+
builtin: t('sandbox.fixtureOrigin.builtin'),
|
|
40
|
+
custom: t('sandbox.fixtureOrigin.custom'),
|
|
41
|
+
}))
|
|
14
42
|
|
|
15
43
|
const open = computed({
|
|
16
44
|
get: () => ui.sandboxOpen,
|
|
@@ -39,7 +67,7 @@ const selectedFixtureIds = ref<string[]>([])
|
|
|
39
67
|
const selectedJudgeModel = ref<string>('')
|
|
40
68
|
|
|
41
69
|
const judgeModelItems = computed(() => [
|
|
42
|
-
{ label: '
|
|
70
|
+
{ label: t('sandbox.deploymentDefault'), value: '' },
|
|
43
71
|
...store.selectableModels.map((m) => ({ label: m.label, value: m.id })),
|
|
44
72
|
])
|
|
45
73
|
|
|
@@ -82,7 +110,7 @@ async function createAndRun() {
|
|
|
82
110
|
if (!canRun.value) return
|
|
83
111
|
try {
|
|
84
112
|
const created = await store.createExperiment({
|
|
85
|
-
name: name.value.trim() ||
|
|
113
|
+
name: name.value.trim() || t('sandbox.defaultRunName', { kind: agentKind.value }),
|
|
86
114
|
agentKind: agentKind.value,
|
|
87
115
|
judgeModel: selectedJudgeModel.value || undefined,
|
|
88
116
|
matrix: {
|
|
@@ -92,12 +120,12 @@ async function createAndRun() {
|
|
|
92
120
|
},
|
|
93
121
|
})
|
|
94
122
|
name.value = ''
|
|
95
|
-
toast.add({ title: '
|
|
123
|
+
toast.add({ title: t('sandbox.toast.running'), icon: 'i-lucide-flask-conical', color: 'info' })
|
|
96
124
|
await store.launch(created.id)
|
|
97
|
-
toast.add({ title: '
|
|
125
|
+
toast.add({ title: t('sandbox.toast.complete'), icon: 'i-lucide-check', color: 'success' })
|
|
98
126
|
} catch (e) {
|
|
99
127
|
toast.add({
|
|
100
|
-
title: '
|
|
128
|
+
title: t('sandbox.toast.runFailed'),
|
|
101
129
|
description: e instanceof Error ? e.message : String(e),
|
|
102
130
|
icon: 'i-lucide-triangle-alert',
|
|
103
131
|
color: 'error',
|
|
@@ -149,11 +177,11 @@ async function saveVersion() {
|
|
|
149
177
|
savingPrompt.value = true
|
|
150
178
|
try {
|
|
151
179
|
await store.saveVersion(editing.value.id, editText.value)
|
|
152
|
-
toast.add({ title: '
|
|
180
|
+
toast.add({ title: t('sandbox.toast.versionSaved'), icon: 'i-lucide-check', color: 'success' })
|
|
153
181
|
editing.value = null
|
|
154
182
|
} catch (e) {
|
|
155
183
|
toast.add({
|
|
156
|
-
title: '
|
|
184
|
+
title: t('sandbox.toast.saveFailed'),
|
|
157
185
|
description: e instanceof Error ? e.message : String(e),
|
|
158
186
|
icon: 'i-lucide-triangle-alert',
|
|
159
187
|
color: 'error',
|
|
@@ -169,7 +197,7 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
169
197
|
if (editing.value?.id === prompt.id) editing.value = null
|
|
170
198
|
} catch (e) {
|
|
171
199
|
toast.add({
|
|
172
|
-
title: '
|
|
200
|
+
title: t('sandbox.toast.archiveFailed'),
|
|
173
201
|
description: e instanceof Error ? e.message : String(e),
|
|
174
202
|
icon: 'i-lucide-triangle-alert',
|
|
175
203
|
color: 'error',
|
|
@@ -181,8 +209,8 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
181
209
|
<template>
|
|
182
210
|
<UModal
|
|
183
211
|
v-model:open="open"
|
|
184
|
-
title="
|
|
185
|
-
description="
|
|
212
|
+
:title="t('sandbox.title')"
|
|
213
|
+
:description="t('sandbox.description')"
|
|
186
214
|
:ui="{ content: 'max-w-5xl' }"
|
|
187
215
|
>
|
|
188
216
|
<template #body>
|
|
@@ -194,21 +222,21 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
194
222
|
v-else-if="!store.available"
|
|
195
223
|
class="rounded-lg border border-slate-700 bg-slate-900/50 p-6 text-sm text-slate-300"
|
|
196
224
|
>
|
|
197
|
-
<p class="font-medium text-slate-200">
|
|
198
|
-
<p class="mt-1 text-slate-400">
|
|
199
|
-
|
|
200
|
-
<code>sandbox</code>
|
|
201
|
-
</
|
|
225
|
+
<p class="font-medium text-slate-200">{{ t('sandbox.unavailable.title') }}</p>
|
|
226
|
+
<i18n-t keypath="sandbox.unavailable.body" tag="p" class="mt-1 text-slate-400">
|
|
227
|
+
<template #db><code>SANDBOX_DB</code></template>
|
|
228
|
+
<template #schema><code>sandbox</code></template>
|
|
229
|
+
</i18n-t>
|
|
202
230
|
</div>
|
|
203
231
|
|
|
204
232
|
<div
|
|
205
233
|
v-else-if="store.error"
|
|
206
234
|
class="rounded-lg border border-rose-800 bg-rose-950/40 p-6 text-sm text-rose-200"
|
|
207
235
|
>
|
|
208
|
-
<p class="font-medium text-rose-100">
|
|
236
|
+
<p class="font-medium text-rose-100">{{ t('sandbox.error.title') }}</p>
|
|
209
237
|
<p class="mt-1 text-rose-300">{{ store.error }}</p>
|
|
210
238
|
<UButton class="mt-3" size="xs" color="neutral" variant="subtle" @click="store.load()">
|
|
211
|
-
|
|
239
|
+
{{ t('common.retry') }}
|
|
212
240
|
</UButton>
|
|
213
241
|
</div>
|
|
214
242
|
|
|
@@ -216,9 +244,17 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
216
244
|
<UTabs
|
|
217
245
|
v-model="tab"
|
|
218
246
|
:items="[
|
|
219
|
-
{
|
|
220
|
-
|
|
221
|
-
|
|
247
|
+
{
|
|
248
|
+
label: t('sandbox.tab.experiments'),
|
|
249
|
+
value: 'experiments',
|
|
250
|
+
icon: 'i-lucide-flask-conical',
|
|
251
|
+
},
|
|
252
|
+
{ label: t('sandbox.tab.prompts'), value: 'prompts', icon: 'i-lucide-file-text' },
|
|
253
|
+
{
|
|
254
|
+
label: t('sandbox.tab.fixtures'),
|
|
255
|
+
value: 'fixtures',
|
|
256
|
+
icon: 'i-lucide-clipboard-list',
|
|
257
|
+
},
|
|
222
258
|
]"
|
|
223
259
|
/>
|
|
224
260
|
|
|
@@ -227,10 +263,10 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
227
263
|
<!-- builder -->
|
|
228
264
|
<div class="space-y-3 rounded-lg border border-slate-700 bg-slate-900/40 p-3">
|
|
229
265
|
<p class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
230
|
-
|
|
266
|
+
{{ t('sandbox.builder.title') }}
|
|
231
267
|
</p>
|
|
232
268
|
|
|
233
|
-
<UFormField label="
|
|
269
|
+
<UFormField :label="t('sandbox.builder.agent')">
|
|
234
270
|
<USelect
|
|
235
271
|
v-model="agentKind"
|
|
236
272
|
:items="store.agentKinds.map((k) => ({ label: k.label, value: k.agentKind }))"
|
|
@@ -241,7 +277,7 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
241
277
|
|
|
242
278
|
<div>
|
|
243
279
|
<span class="mb-1 block text-[10px] uppercase tracking-wide text-slate-500">
|
|
244
|
-
|
|
280
|
+
{{ t('sandbox.builder.promptVersions') }}
|
|
245
281
|
</span>
|
|
246
282
|
<div class="max-h-28 space-y-1 overflow-auto pr-1">
|
|
247
283
|
<label
|
|
@@ -261,7 +297,11 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
261
297
|
variant="soft"
|
|
262
298
|
size="xs"
|
|
263
299
|
>
|
|
264
|
-
{{
|
|
300
|
+
{{
|
|
301
|
+
p.origin === 'baseline'
|
|
302
|
+
? t('sandbox.baseline')
|
|
303
|
+
: t('sandbox.versionLabel', { version: p.version })
|
|
304
|
+
}}
|
|
265
305
|
</UBadge>
|
|
266
306
|
</label>
|
|
267
307
|
</div>
|
|
@@ -269,7 +309,7 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
269
309
|
|
|
270
310
|
<div>
|
|
271
311
|
<span class="mb-1 block text-[10px] uppercase tracking-wide text-slate-500">
|
|
272
|
-
|
|
312
|
+
{{ t('sandbox.builder.models') }}
|
|
273
313
|
</span>
|
|
274
314
|
<div class="max-h-28 space-y-1 overflow-auto pr-1">
|
|
275
315
|
<label
|
|
@@ -286,14 +326,14 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
286
326
|
<span class="truncate">{{ m.label }}</span>
|
|
287
327
|
</label>
|
|
288
328
|
<p v-if="!store.selectableModels.length" class="text-xs text-slate-500">
|
|
289
|
-
|
|
329
|
+
{{ t('sandbox.builder.noModels') }}
|
|
290
330
|
</p>
|
|
291
331
|
</div>
|
|
292
332
|
</div>
|
|
293
333
|
|
|
294
334
|
<div>
|
|
295
335
|
<span class="mb-1 block text-[10px] uppercase tracking-wide text-slate-500">
|
|
296
|
-
|
|
336
|
+
{{ t('sandbox.builder.fixtures') }}
|
|
297
337
|
</span>
|
|
298
338
|
<div class="max-h-28 space-y-1 overflow-auto pr-1">
|
|
299
339
|
<label
|
|
@@ -310,24 +350,30 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
310
350
|
<span class="truncate">{{ f.name }}</span>
|
|
311
351
|
</label>
|
|
312
352
|
<p v-if="!kindFixtures.length" class="text-xs text-slate-500">
|
|
313
|
-
|
|
353
|
+
{{ t('sandbox.builder.noFixtures') }}
|
|
314
354
|
</p>
|
|
315
355
|
</div>
|
|
316
356
|
</div>
|
|
317
357
|
|
|
318
|
-
<UFormField
|
|
358
|
+
<UFormField
|
|
359
|
+
:label="t('sandbox.builder.judgeModel')"
|
|
360
|
+
:hint="t('sandbox.builder.judgeModelHint')"
|
|
361
|
+
>
|
|
319
362
|
<USelect v-model="selectedJudgeModel" :items="judgeModelItems" />
|
|
320
363
|
</UFormField>
|
|
321
364
|
|
|
322
|
-
<UFormField label="
|
|
323
|
-
<UInput
|
|
365
|
+
<UFormField :label="t('sandbox.builder.nameLabel')">
|
|
366
|
+
<UInput
|
|
367
|
+
v-model="name"
|
|
368
|
+
:placeholder="t('sandbox.defaultRunName', { kind: agentKind })"
|
|
369
|
+
/>
|
|
324
370
|
</UFormField>
|
|
325
371
|
|
|
326
372
|
<div class="flex items-center justify-between">
|
|
327
373
|
<span class="text-xs text-slate-500">
|
|
328
|
-
{{ cellCount
|
|
374
|
+
{{ t('sandbox.builder.cellCount', { count: cellCount }, cellCount) }}
|
|
329
375
|
<span v-if="cellCount > store.maxCells" class="text-rose-400">
|
|
330
|
-
(
|
|
376
|
+
{{ t('sandbox.builder.maxCells', { max: store.maxCells }) }}
|
|
331
377
|
</span>
|
|
332
378
|
</span>
|
|
333
379
|
<UButton
|
|
@@ -338,7 +384,7 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
338
384
|
:disabled="!canRun"
|
|
339
385
|
@click="createAndRun()"
|
|
340
386
|
>
|
|
341
|
-
|
|
387
|
+
{{ t('sandbox.builder.run') }}
|
|
342
388
|
</UButton>
|
|
343
389
|
</div>
|
|
344
390
|
</div>
|
|
@@ -350,17 +396,19 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
350
396
|
<p class="text-sm font-medium text-slate-200">
|
|
351
397
|
{{ store.detail.experiment.name }}
|
|
352
398
|
</p>
|
|
353
|
-
<UBadge variant="soft" size="xs">{{
|
|
399
|
+
<UBadge variant="soft" size="xs">{{
|
|
400
|
+
EXPERIMENT_STATUS_LABEL[store.detail.experiment.status]
|
|
401
|
+
}}</UBadge>
|
|
354
402
|
</div>
|
|
355
403
|
<div class="overflow-auto">
|
|
356
404
|
<table class="w-full text-left text-xs">
|
|
357
405
|
<thead class="text-slate-500">
|
|
358
406
|
<tr>
|
|
359
|
-
<th class="py-1 pr-2 font-medium">
|
|
360
|
-
<th class="py-1 pr-2 font-medium">
|
|
361
|
-
<th class="py-1 pr-2 font-medium">
|
|
362
|
-
<th class="py-1 pr-2 font-medium">
|
|
363
|
-
<th class="py-1 font-medium">
|
|
407
|
+
<th class="py-1 pr-2 font-medium">{{ t('sandbox.results.col.prompt') }}</th>
|
|
408
|
+
<th class="py-1 pr-2 font-medium">{{ t('sandbox.results.col.model') }}</th>
|
|
409
|
+
<th class="py-1 pr-2 font-medium">{{ t('sandbox.results.col.fixture') }}</th>
|
|
410
|
+
<th class="py-1 pr-2 font-medium">{{ t('sandbox.results.col.score') }}</th>
|
|
411
|
+
<th class="py-1 font-medium">{{ t('sandbox.results.col.objective') }}</th>
|
|
364
412
|
</tr>
|
|
365
413
|
</thead>
|
|
366
414
|
<tbody>
|
|
@@ -383,9 +431,9 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
383
431
|
>
|
|
384
432
|
{{ grade.weightedTotal.toFixed(2) }}
|
|
385
433
|
</span>
|
|
386
|
-
<span v-else-if="run.status === 'failed'" class="text-rose-400"
|
|
387
|
-
|
|
388
|
-
>
|
|
434
|
+
<span v-else-if="run.status === 'failed'" class="text-rose-400">{{
|
|
435
|
+
t('sandbox.results.failed')
|
|
436
|
+
}}</span>
|
|
389
437
|
<span v-else class="text-slate-600">—</span>
|
|
390
438
|
</td>
|
|
391
439
|
<td class="py-1">
|
|
@@ -429,7 +477,9 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
429
477
|
</div>
|
|
430
478
|
</div>
|
|
431
479
|
|
|
432
|
-
<p class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
480
|
+
<p class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
481
|
+
{{ t('sandbox.results.past') }}
|
|
482
|
+
</p>
|
|
433
483
|
<div class="max-h-56 space-y-1 overflow-auto">
|
|
434
484
|
<button
|
|
435
485
|
v-for="x in store.experiments"
|
|
@@ -438,10 +488,10 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
438
488
|
@click="store.openExperiment(x.id)"
|
|
439
489
|
>
|
|
440
490
|
<span class="truncate text-slate-300">{{ x.name }}</span>
|
|
441
|
-
<UBadge variant="soft" size="xs">{{ x.status }}</UBadge>
|
|
491
|
+
<UBadge variant="soft" size="xs">{{ EXPERIMENT_STATUS_LABEL[x.status] }}</UBadge>
|
|
442
492
|
</button>
|
|
443
493
|
<p v-if="!store.experiments.length" class="text-xs text-slate-500">
|
|
444
|
-
|
|
494
|
+
{{ t('sandbox.results.empty') }}
|
|
445
495
|
</p>
|
|
446
496
|
</div>
|
|
447
497
|
</div>
|
|
@@ -463,7 +513,11 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
463
513
|
variant="soft"
|
|
464
514
|
size="xs"
|
|
465
515
|
>
|
|
466
|
-
{{
|
|
516
|
+
{{
|
|
517
|
+
p.origin === 'baseline'
|
|
518
|
+
? t('sandbox.baseline')
|
|
519
|
+
: t('sandbox.versionLabel', { version: p.version })
|
|
520
|
+
}}
|
|
467
521
|
</UBadge>
|
|
468
522
|
</div>
|
|
469
523
|
<span class="text-[11px] text-slate-500">{{ p.agentKind }}</span>
|
|
@@ -474,7 +528,11 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
474
528
|
color="neutral"
|
|
475
529
|
variant="ghost"
|
|
476
530
|
size="xs"
|
|
477
|
-
:title="
|
|
531
|
+
:title="
|
|
532
|
+
p.origin === 'baseline'
|
|
533
|
+
? t('sandbox.prompts.forkTitle')
|
|
534
|
+
: t('sandbox.prompts.editTitle')
|
|
535
|
+
"
|
|
478
536
|
@click="edit(p)"
|
|
479
537
|
/>
|
|
480
538
|
<UButton
|
|
@@ -494,12 +552,16 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
494
552
|
class="space-y-2 rounded-lg border border-slate-700 bg-slate-900/40 p-3"
|
|
495
553
|
>
|
|
496
554
|
<p class="text-[11px] uppercase tracking-wide text-slate-500">
|
|
497
|
-
{{
|
|
555
|
+
{{
|
|
556
|
+
editing.origin === 'baseline'
|
|
557
|
+
? t('sandbox.prompts.forkOf', { name: editing.name })
|
|
558
|
+
: t('sandbox.prompts.newVersionOf', { name: editing.name })
|
|
559
|
+
}}
|
|
498
560
|
</p>
|
|
499
561
|
<UTextarea v-model="editText" :rows="16" class="w-full font-mono text-xs" autoresize />
|
|
500
562
|
<div class="flex justify-end gap-2">
|
|
501
563
|
<UButton color="neutral" variant="ghost" size="sm" @click="editing = null">
|
|
502
|
-
|
|
564
|
+
{{ t('common.cancel') }}
|
|
503
565
|
</UButton>
|
|
504
566
|
<UButton
|
|
505
567
|
color="primary"
|
|
@@ -509,13 +571,12 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
509
571
|
:disabled="!editText.trim()"
|
|
510
572
|
@click="saveVersion()"
|
|
511
573
|
>
|
|
512
|
-
|
|
574
|
+
{{ t('sandbox.prompts.saveVersion') }}
|
|
513
575
|
</UButton>
|
|
514
576
|
</div>
|
|
515
577
|
</div>
|
|
516
578
|
<p v-else class="self-start text-xs text-slate-500">
|
|
517
|
-
|
|
518
|
-
immutable version you can put under test.
|
|
579
|
+
{{ t('sandbox.prompts.hint') }}
|
|
519
580
|
</p>
|
|
520
581
|
</div>
|
|
521
582
|
|
|
@@ -529,23 +590,29 @@ async function archive(prompt: SandboxPromptVersion) {
|
|
|
529
590
|
<div class="flex items-center justify-between">
|
|
530
591
|
<span class="text-slate-200">{{ f.name }}</span>
|
|
531
592
|
<div class="flex items-center gap-1.5">
|
|
532
|
-
<UBadge variant="soft" size="xs">{{ f.kind }}</UBadge>
|
|
593
|
+
<UBadge variant="soft" size="xs">{{ FIXTURE_KIND_LABEL[f.kind] }}</UBadge>
|
|
533
594
|
<UBadge
|
|
534
595
|
:color="f.origin === 'builtin' ? 'neutral' : 'primary'"
|
|
535
596
|
variant="soft"
|
|
536
597
|
size="xs"
|
|
537
598
|
>
|
|
538
|
-
{{ f.origin }}
|
|
599
|
+
{{ FIXTURE_ORIGIN_LABEL[f.origin] }}
|
|
539
600
|
</UBadge>
|
|
540
601
|
</div>
|
|
541
602
|
</div>
|
|
542
603
|
<p v-if="f.objective?.kind === 'findings'" class="mt-0.5 text-[11px] text-slate-500">
|
|
543
|
-
{{
|
|
544
|
-
|
|
604
|
+
{{
|
|
605
|
+
t(
|
|
606
|
+
'sandbox.fixtures.expectations',
|
|
607
|
+
{ count: f.objective.expectations.length },
|
|
608
|
+
f.objective.expectations.length,
|
|
609
|
+
)
|
|
545
610
|
}}
|
|
546
611
|
</p>
|
|
547
612
|
</div>
|
|
548
|
-
<p v-if="!store.fixtures.length" class="text-xs text-slate-500">
|
|
613
|
+
<p v-if="!store.fixtures.length" class="text-xs text-slate-500">
|
|
614
|
+
{{ t('sandbox.fixtures.empty') }}
|
|
615
|
+
</p>
|
|
549
616
|
</div>
|
|
550
617
|
</div>
|
|
551
618
|
</template>
|
package/app/pages/index.vue
CHANGED
|
@@ -14,11 +14,7 @@ import InspectorPanel from '~/components/panels/InspectorPanel.vue'
|
|
|
14
14
|
import DecisionModal from '~/components/panels/DecisionModal.vue'
|
|
15
15
|
import AgentStepDetail from '~/components/panels/AgentStepDetail.vue'
|
|
16
16
|
import StepResultViewHost from '~/components/panels/StepResultViewHost.vue'
|
|
17
|
-
import BlockFocusView from '~/components/focus/BlockFocusView.vue'
|
|
18
|
-
import TaskSourceConnectModal from '~/components/tasks/TaskSourceConnectModal.vue'
|
|
19
|
-
import TaskImportModal from '~/components/tasks/TaskImportModal.vue'
|
|
20
17
|
import AddTaskModal from '~/components/board/AddTaskModal.vue'
|
|
21
|
-
import RecurringPipelineModal from '~/components/board/RecurringPipelineModal.vue'
|
|
22
18
|
import GitHubOnboarding from '~/components/github/GitHubOnboarding.vue'
|
|
23
19
|
import CommandBar from '~/components/layout/CommandBar.vue'
|
|
24
20
|
import PersonalCredentialModal from '~/components/providers/PersonalCredentialModal.vue'
|
|
@@ -30,6 +26,17 @@ const ObservabilityPanel = defineAsyncComponent(
|
|
|
30
26
|
() => import('~/components/panels/ObservabilityPanel.vue'),
|
|
31
27
|
)
|
|
32
28
|
const KaizenPanel = defineAsyncComponent(() => import('~/components/kaizen/KaizenPanel.vue'))
|
|
29
|
+
// Occasional, externally store-gated surfaces — deferred to their own chunks like the
|
|
30
|
+
// sibling document modals above. Each mounts only while its ui open-flag is set, so it
|
|
31
|
+
// loads on first open instead of bloating the initial bundle.
|
|
32
|
+
const BlockFocusView = defineAsyncComponent(() => import('~/components/focus/BlockFocusView.vue'))
|
|
33
|
+
const TaskSourceConnectModal = defineAsyncComponent(
|
|
34
|
+
() => import('~/components/tasks/TaskSourceConnectModal.vue'),
|
|
35
|
+
)
|
|
36
|
+
const TaskImportModal = defineAsyncComponent(() => import('~/components/tasks/TaskImportModal.vue'))
|
|
37
|
+
const RecurringPipelineModal = defineAsyncComponent(
|
|
38
|
+
() => import('~/components/board/RecurringPipelineModal.vue'),
|
|
39
|
+
)
|
|
33
40
|
const DocumentSourceConnectModal = defineAsyncComponent(
|
|
34
41
|
() => import('~/components/documents/DocumentSourceConnectModal.vue'),
|
|
35
42
|
)
|
|
@@ -248,7 +255,12 @@ watch(
|
|
|
248
255
|
<BoardToolbar />
|
|
249
256
|
<SpendWarningBanner />
|
|
250
257
|
<InspectorPanel />
|
|
251
|
-
|
|
258
|
+
<!-- Code-split focus view. The fade lives here (not inside the component) so the
|
|
259
|
+
leave animation still plays when `focusBlockId` clears and the v-if unmounts
|
|
260
|
+
the chunk — an inner Transition would be torn down before it could run. -->
|
|
261
|
+
<Transition name="focus-fade">
|
|
262
|
+
<BlockFocusView v-if="ui.focusBlockId" />
|
|
263
|
+
</Transition>
|
|
252
264
|
</main>
|
|
253
265
|
|
|
254
266
|
<!-- Always-mounted, fast-path surfaces. -->
|
|
@@ -256,15 +268,15 @@ watch(
|
|
|
256
268
|
<DecisionModal />
|
|
257
269
|
<AgentStepDetail />
|
|
258
270
|
<StepResultViewHost />
|
|
259
|
-
<TaskSourceConnectModal />
|
|
260
|
-
<TaskImportModal />
|
|
261
271
|
<AddTaskModal />
|
|
262
|
-
<RecurringPipelineModal />
|
|
263
272
|
<CommandBar />
|
|
264
273
|
<PersonalCredentialModal />
|
|
265
274
|
|
|
266
275
|
<!-- Lazy panels: mounted only while their ui open-flag is set, so each loads on
|
|
267
276
|
first open (its own chunk) rather than bloating the initial bundle. -->
|
|
277
|
+
<TaskSourceConnectModal v-if="ui.taskConnect" />
|
|
278
|
+
<TaskImportModal v-if="ui.taskImport" />
|
|
279
|
+
<RecurringPipelineModal v-if="ui.addRecurringFrameId" />
|
|
268
280
|
<ObservabilityPanel v-if="ui.observabilityInstanceId" />
|
|
269
281
|
<KaizenPanel v-if="ui.kaizenScreenOpen" />
|
|
270
282
|
<DocumentSourceConnectModal v-if="ui.documentConnect" />
|
package/app/stores/workspace.ts
CHANGED
|
@@ -113,11 +113,16 @@ export const useWorkspaceStore = defineStore(
|
|
|
113
113
|
ready.value = false
|
|
114
114
|
error.value = null
|
|
115
115
|
try {
|
|
116
|
-
// Accounts
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
// Accounts (an auth concept — empty in dev, which leaves boards unscoped) and the
|
|
117
|
+
// workspace list are independent, so fetch them concurrently. resolveActiveBoard
|
|
118
|
+
// needs both, so it still runs after.
|
|
119
|
+
const [, workspaceList] = await Promise.all([
|
|
120
|
+
useAccountsStore()
|
|
121
|
+
.load()
|
|
122
|
+
.catch(() => {}),
|
|
123
|
+
api.listWorkspaces(),
|
|
124
|
+
])
|
|
125
|
+
workspaces.value = workspaceList
|
|
121
126
|
await resolveActiveBoard()
|
|
122
127
|
ready.value = true
|
|
123
128
|
} catch (e) {
|