@cat-factory/app 0.47.7 → 0.47.8
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/gates/GateResultView.vue +91 -57
- package/app/components/palettes/AgentPalette.vue +4 -2
- package/app/components/pipeline/IterationCapPrompt.vue +13 -10
- package/app/components/pipeline/PipelineBuilder.vue +112 -70
- package/app/components/pipeline/PipelineHealthModal.vue +20 -16
- package/app/components/pipeline/PipelineProgress.vue +79 -39
- package/i18n/locales/en.json +212 -0
- package/i18n/locales/es.json +212 -0
- package/i18n/locales/fr.json +212 -0
- package/i18n/locales/pl.json +212 -0
- package/i18n/locales/uk.json +212 -0
- package/package.json +1 -1
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
// gates; it branches on the step's `agentKind` for the copy and the failure detail.
|
|
8
8
|
import { computed, ref } from 'vue'
|
|
9
9
|
import { agentKindMeta } from '~/utils/catalog'
|
|
10
|
-
import type { GateStepState } from '~/types/execution'
|
|
10
|
+
import type { GateAttempt, GateStepState } from '~/types/execution'
|
|
11
11
|
import StepRestartControl from '~/components/panels/StepRestartControl.vue'
|
|
12
12
|
import StepRunMeta from '~/components/panels/StepRunMeta.vue'
|
|
13
13
|
|
|
14
14
|
const board = useBoardStore()
|
|
15
15
|
const execution = useExecutionStore()
|
|
16
|
+
const { t, d } = useI18n()
|
|
16
17
|
|
|
17
18
|
// Synchronous window: it reads its state straight off the execution step, so there's
|
|
18
19
|
// nothing to fetch on open (no `onOpen` loader).
|
|
@@ -39,10 +40,10 @@ const helperMeta = computed(() => agentKindMeta(helperKind.value))
|
|
|
39
40
|
|
|
40
41
|
const subtitle = computed(() =>
|
|
41
42
|
isHumanReview.value
|
|
42
|
-
? '
|
|
43
|
+
? t('gates.subtitle.humanReview')
|
|
43
44
|
: isCi.value
|
|
44
|
-
? '
|
|
45
|
-
: '
|
|
45
|
+
? t('gates.subtitle.ci')
|
|
46
|
+
: t('gates.subtitle.conflicts'),
|
|
46
47
|
)
|
|
47
48
|
|
|
48
49
|
// Human-review: approval progress + the freeform "request a fix" control.
|
|
@@ -67,8 +68,15 @@ const shortSha = computed(() => (gate.value?.headSha ? gate.value.headSha.slice(
|
|
|
67
68
|
// The helper-agent attempts this gate dispatched, newest first for the timeline.
|
|
68
69
|
const attempts = computed(() => [...(gate.value?.attemptLog ?? [])].reverse())
|
|
69
70
|
|
|
71
|
+
// Exhaustive map of the attempt outcome enum → label (literal keys keep the typed-key
|
|
72
|
+
// drift guard live, vs a runtime-built `gates.outcome.${outcome}`).
|
|
73
|
+
const OUTCOME_LABELS = computed<Record<GateAttempt['outcome'], string>>(() => ({
|
|
74
|
+
completed: t('gates.outcome.completed'),
|
|
75
|
+
failed: t('gates.outcome.failed'),
|
|
76
|
+
}))
|
|
77
|
+
|
|
70
78
|
function formatClock(ms?: number | null): string | null {
|
|
71
|
-
return ms ? new Date(ms)
|
|
79
|
+
return ms ? d(new Date(ms), 'long') : null
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
/**
|
|
@@ -94,45 +102,62 @@ const status = computed<GateDisplayStatus>(() => {
|
|
|
94
102
|
return 'checking'
|
|
95
103
|
})
|
|
96
104
|
|
|
97
|
-
const STATUS_META
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
const STATUS_META = computed<
|
|
106
|
+
Record<
|
|
107
|
+
GateDisplayStatus,
|
|
108
|
+
{
|
|
109
|
+
label: string
|
|
110
|
+
badge: 'success' | 'warning' | 'error' | 'neutral'
|
|
111
|
+
icon: string
|
|
112
|
+
text: string
|
|
113
|
+
}
|
|
114
|
+
>
|
|
115
|
+
>(() => ({
|
|
101
116
|
passed: {
|
|
102
|
-
label: '
|
|
117
|
+
label: t('gates.status.passed'),
|
|
103
118
|
badge: 'success',
|
|
104
119
|
icon: 'i-lucide-circle-check',
|
|
105
120
|
text: 'text-emerald-300',
|
|
106
121
|
},
|
|
107
|
-
'gave-up': {
|
|
108
|
-
|
|
122
|
+
'gave-up': {
|
|
123
|
+
label: t('gates.status.gaveUp'),
|
|
124
|
+
badge: 'error',
|
|
125
|
+
icon: 'i-lucide-circle-x',
|
|
126
|
+
text: 'text-rose-300',
|
|
127
|
+
},
|
|
128
|
+
fixing: {
|
|
129
|
+
label: t('gates.status.fixing'),
|
|
130
|
+
badge: 'warning',
|
|
131
|
+
icon: 'i-lucide-loader',
|
|
132
|
+
text: 'text-amber-300',
|
|
133
|
+
},
|
|
109
134
|
failing: {
|
|
110
|
-
label: '
|
|
135
|
+
label: t('gates.status.failing'),
|
|
111
136
|
badge: 'error',
|
|
112
137
|
icon: 'i-lucide-circle-x',
|
|
113
138
|
text: 'text-rose-300',
|
|
114
139
|
},
|
|
115
140
|
pending: {
|
|
116
|
-
label: '
|
|
141
|
+
label: t('gates.status.pending'),
|
|
117
142
|
badge: 'neutral',
|
|
118
143
|
icon: 'i-lucide-clock',
|
|
119
144
|
text: 'text-slate-300',
|
|
120
145
|
},
|
|
121
146
|
checking: {
|
|
122
|
-
label: '
|
|
147
|
+
label: t('gates.status.checking'),
|
|
123
148
|
badge: 'neutral',
|
|
124
149
|
icon: 'i-lucide-loader',
|
|
125
150
|
text: 'text-slate-300',
|
|
126
151
|
},
|
|
127
|
-
}
|
|
152
|
+
}))
|
|
128
153
|
|
|
129
154
|
// The conflicts gate has no structured detail (GitHub reports mergeability as a single
|
|
130
155
|
// verdict, no file list), so the window shows the verdict + a note rather than a list.
|
|
131
156
|
const conflictVerdict = computed(() => {
|
|
132
|
-
if (status.value === 'passed') return '
|
|
133
|
-
if (gate.value?.lastVerdict === 'pending') return '
|
|
134
|
-
if (gate.value?.lastVerdict === 'fail') return '
|
|
135
|
-
return '
|
|
157
|
+
if (status.value === 'passed') return t('gates.conflict.mergeable')
|
|
158
|
+
if (gate.value?.lastVerdict === 'pending') return t('gates.conflict.computing')
|
|
159
|
+
if (gate.value?.lastVerdict === 'fail') return t('gates.conflict.conflicts')
|
|
160
|
+
return t('gates.conflict.unknown')
|
|
136
161
|
})
|
|
137
162
|
</script>
|
|
138
163
|
|
|
@@ -183,10 +208,9 @@ const conflictVerdict = computed(() => {
|
|
|
183
208
|
class="flex h-full flex-col items-center justify-center gap-2 text-center text-slate-400"
|
|
184
209
|
>
|
|
185
210
|
<UIcon :name="meta.icon" class="h-8 w-8 opacity-40" />
|
|
186
|
-
<p class="text-sm">
|
|
211
|
+
<p class="text-sm">{{ t('gates.noActivity') }}</p>
|
|
187
212
|
<p class="max-w-sm text-[11px] text-slate-500">
|
|
188
|
-
|
|
189
|
-
the board.
|
|
213
|
+
{{ t('gates.noActivityHint') }}
|
|
190
214
|
</p>
|
|
191
215
|
</div>
|
|
192
216
|
|
|
@@ -201,9 +225,7 @@ const conflictVerdict = computed(() => {
|
|
|
201
225
|
class="mt-0.5 h-4 w-4 shrink-0 text-emerald-400"
|
|
202
226
|
/>
|
|
203
227
|
<p class="text-[13px] leading-relaxed text-emerald-200">
|
|
204
|
-
{{
|
|
205
|
-
step?.output || (isCi ? 'CI is green.' : 'The PR merges cleanly with its base.')
|
|
206
|
-
}}
|
|
228
|
+
{{ step?.output || (isCi ? t('gates.passedCi') : t('gates.passedConflicts')) }}
|
|
207
229
|
</p>
|
|
208
230
|
</div>
|
|
209
231
|
|
|
@@ -214,14 +236,20 @@ const conflictVerdict = computed(() => {
|
|
|
214
236
|
>
|
|
215
237
|
<UIcon name="i-lucide-users" class="h-4 w-4 shrink-0 text-violet-300" />
|
|
216
238
|
<span class="text-[13px] text-slate-200">
|
|
217
|
-
{{
|
|
218
|
-
|
|
239
|
+
{{
|
|
240
|
+
t(
|
|
241
|
+
'gates.humanReview.approvals',
|
|
242
|
+
{ approved: gate.lastApprovals ?? 0, required: requiredApprovals },
|
|
243
|
+
requiredApprovals,
|
|
244
|
+
)
|
|
219
245
|
}}
|
|
220
|
-
<template v-if="status === 'fixing'">
|
|
246
|
+
<template v-if="status === 'fixing'">
|
|
247
|
+
{{ t('gates.humanReview.suffixFixing') }}</template
|
|
248
|
+
>
|
|
221
249
|
<template v-else-if="status === 'failing'">
|
|
222
|
-
|
|
250
|
+
{{ t('gates.humanReview.suffixFailing') }}</template
|
|
223
251
|
>
|
|
224
|
-
<template v-else>
|
|
252
|
+
<template v-else> {{ t('gates.humanReview.suffixAwaiting') }}</template>
|
|
225
253
|
</span>
|
|
226
254
|
</div>
|
|
227
255
|
<p
|
|
@@ -237,7 +265,7 @@ const conflictVerdict = computed(() => {
|
|
|
237
265
|
rel="noopener"
|
|
238
266
|
class="mt-2 inline-flex items-center gap-1 text-[12px] text-sky-300 hover:text-sky-200 hover:underline"
|
|
239
267
|
>
|
|
240
|
-
|
|
268
|
+
{{ t('gates.humanReview.reviewPr') }}
|
|
241
269
|
<UIcon name="i-lucide-external-link" class="h-3 w-3" />
|
|
242
270
|
</a>
|
|
243
271
|
|
|
@@ -246,17 +274,16 @@ const conflictVerdict = computed(() => {
|
|
|
246
274
|
<h3
|
|
247
275
|
class="mb-1.5 text-[11px] font-semibold uppercase tracking-wide text-slate-500"
|
|
248
276
|
>
|
|
249
|
-
|
|
277
|
+
{{ t('gates.humanReview.requestFixHeading') }}
|
|
250
278
|
</h3>
|
|
251
279
|
<p class="mb-2 text-[11px] leading-relaxed text-slate-500">
|
|
252
|
-
|
|
253
|
-
review comments, which it addresses automatically).
|
|
280
|
+
{{ t('gates.humanReview.requestFixDescription') }}
|
|
254
281
|
</p>
|
|
255
282
|
<textarea
|
|
256
283
|
v-model="fixInstructions"
|
|
257
284
|
rows="3"
|
|
258
285
|
:disabled="fixBusy"
|
|
259
|
-
placeholder="
|
|
286
|
+
:placeholder="t('gates.humanReview.requestFixPlaceholder')"
|
|
260
287
|
class="w-full resize-y rounded-md border border-slate-800 bg-slate-950/60 px-3 py-2 text-[13px] text-slate-200 placeholder:text-slate-600 focus:border-violet-500/60 focus:outline-none"
|
|
261
288
|
/>
|
|
262
289
|
<div class="mt-2 flex justify-end">
|
|
@@ -268,7 +295,7 @@ const conflictVerdict = computed(() => {
|
|
|
268
295
|
:disabled="fixBusy || fixInstructions.trim().length === 0"
|
|
269
296
|
@click="submitFix"
|
|
270
297
|
>
|
|
271
|
-
|
|
298
|
+
{{ t('gates.humanReview.requestFix') }}
|
|
272
299
|
</UButton>
|
|
273
300
|
</div>
|
|
274
301
|
</section>
|
|
@@ -277,7 +304,7 @@ const conflictVerdict = computed(() => {
|
|
|
277
304
|
<!-- CI: failing checks -->
|
|
278
305
|
<template v-else-if="isCi">
|
|
279
306
|
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
280
|
-
|
|
307
|
+
{{ t('gates.ci.failingChecks') }}
|
|
281
308
|
</h3>
|
|
282
309
|
<ul v-if="failingChecks.length" class="space-y-1">
|
|
283
310
|
<li
|
|
@@ -292,7 +319,7 @@ const conflictVerdict = computed(() => {
|
|
|
292
319
|
target="_blank"
|
|
293
320
|
rel="noopener"
|
|
294
321
|
class="group min-w-0 flex-1 truncate text-[13px] text-sky-300 hover:text-sky-200 hover:underline"
|
|
295
|
-
:title="
|
|
322
|
+
:title="t('gates.ci.openOnGithub', { name: c.name })"
|
|
296
323
|
>
|
|
297
324
|
{{ c.name }}
|
|
298
325
|
<UIcon
|
|
@@ -304,19 +331,19 @@ const conflictVerdict = computed(() => {
|
|
|
304
331
|
c.name
|
|
305
332
|
}}</span>
|
|
306
333
|
<span class="shrink-0 text-[11px] uppercase text-rose-300">
|
|
307
|
-
{{ c.conclusion ?? '
|
|
334
|
+
{{ c.conclusion ?? t('gates.ci.conclusionFallback') }}
|
|
308
335
|
</span>
|
|
309
336
|
</li>
|
|
310
337
|
</ul>
|
|
311
338
|
<p v-else class="text-[13px] leading-relaxed text-slate-300">
|
|
312
|
-
{{ gate.lastFailureSummary || '
|
|
339
|
+
{{ gate.lastFailureSummary || t('gates.ci.failureFallback') }}
|
|
313
340
|
</p>
|
|
314
341
|
</template>
|
|
315
342
|
|
|
316
343
|
<!-- Conflicts: verdict + the resolver's account of what it left -->
|
|
317
344
|
<template v-else>
|
|
318
345
|
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
319
|
-
|
|
346
|
+
{{ t('gates.conflicts.mergeability') }}
|
|
320
347
|
</h3>
|
|
321
348
|
<div
|
|
322
349
|
class="flex items-center gap-2 rounded-md border border-slate-800 bg-slate-950/40 px-3 py-2"
|
|
@@ -344,7 +371,7 @@ const conflictVerdict = computed(() => {
|
|
|
344
371
|
rel="noopener"
|
|
345
372
|
class="mt-2 inline-flex items-center gap-1 text-[12px] text-sky-300 hover:text-sky-200 hover:underline"
|
|
346
373
|
>
|
|
347
|
-
|
|
374
|
+
{{ t('gates.conflicts.viewPr') }}
|
|
348
375
|
<UIcon name="i-lucide-external-link" class="h-3 w-3" />
|
|
349
376
|
</a>
|
|
350
377
|
</template>
|
|
@@ -352,7 +379,7 @@ const conflictVerdict = computed(() => {
|
|
|
352
379
|
<!-- Attempt history (both gates): what each helper run did and how it ended. -->
|
|
353
380
|
<section v-if="attempts.length" class="mt-5">
|
|
354
381
|
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
355
|
-
{{ helperMeta.label }}
|
|
382
|
+
{{ t('gates.attemptsHeading', { helper: helperMeta.label }) }}
|
|
356
383
|
</h3>
|
|
357
384
|
<ol class="space-y-2">
|
|
358
385
|
<li
|
|
@@ -361,14 +388,14 @@ const conflictVerdict = computed(() => {
|
|
|
361
388
|
class="rounded-md border border-slate-800 bg-slate-950/40 px-3 py-2"
|
|
362
389
|
>
|
|
363
390
|
<div class="flex items-center gap-2">
|
|
364
|
-
<span class="text-[12px] font-semibold text-slate-200"
|
|
365
|
-
|
|
366
|
-
>
|
|
391
|
+
<span class="text-[12px] font-semibold text-slate-200">{{
|
|
392
|
+
t('gates.attempt', { number: a.attempt })
|
|
393
|
+
}}</span>
|
|
367
394
|
<UBadge
|
|
368
395
|
:color="a.outcome === 'failed' ? 'error' : 'neutral'"
|
|
369
396
|
variant="subtle"
|
|
370
397
|
size="sm"
|
|
371
|
-
>{{ a.outcome }}</UBadge
|
|
398
|
+
>{{ OUTCOME_LABELS[a.outcome] }}</UBadge
|
|
372
399
|
>
|
|
373
400
|
<span v-if="formatClock(a.at)" class="ml-auto text-[11px] text-slate-500">{{
|
|
374
401
|
formatClock(a.at)
|
|
@@ -392,7 +419,7 @@ const conflictVerdict = computed(() => {
|
|
|
392
419
|
>
|
|
393
420
|
<div v-if="gate">
|
|
394
421
|
<h4 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
395
|
-
|
|
422
|
+
{{ t('gates.sidebar.state') }}
|
|
396
423
|
</h4>
|
|
397
424
|
<div class="flex items-center gap-2 text-[13px]">
|
|
398
425
|
<UIcon
|
|
@@ -412,21 +439,29 @@ const conflictVerdict = computed(() => {
|
|
|
412
439
|
<!-- The human-review gate's budget is effectively unbounded (it waits for a human
|
|
413
440
|
indefinitely), so render a plain round count rather than "0/9007199254740991". -->
|
|
414
441
|
<template v-if="isHumanReview">
|
|
415
|
-
{{ gate.attempts }
|
|
442
|
+
{{ t('gates.sidebar.fixRounds', { count: gate.attempts }, gate.attempts) }}
|
|
416
443
|
</template>
|
|
417
444
|
<template v-else>
|
|
418
|
-
{{
|
|
419
|
-
|
|
445
|
+
{{
|
|
446
|
+
t(
|
|
447
|
+
'gates.sidebar.attempts',
|
|
448
|
+
{ attempts: gate.attempts, max: gate.maxAttempts },
|
|
449
|
+
gate.maxAttempts,
|
|
450
|
+
)
|
|
420
451
|
}}
|
|
421
452
|
</template>
|
|
422
|
-
<template v-if="gate.phase === 'working'">
|
|
423
|
-
|
|
453
|
+
<template v-if="gate.phase === 'working'">
|
|
454
|
+
{{ t('gates.sidebar.suffixRunning') }}</template
|
|
455
|
+
>
|
|
456
|
+
<template v-else-if="gate.attempts === 0">
|
|
457
|
+
{{ t('gates.sidebar.suffixNotNeeded') }}</template
|
|
458
|
+
>
|
|
424
459
|
</p>
|
|
425
460
|
</div>
|
|
426
461
|
|
|
427
462
|
<div v-if="shortSha">
|
|
428
463
|
<h4 class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
429
|
-
|
|
464
|
+
{{ t('gates.sidebar.gatedCommit') }}
|
|
430
465
|
</h4>
|
|
431
466
|
<p class="font-mono text-[12px] text-slate-300">{{ shortSha }}</p>
|
|
432
467
|
</div>
|
|
@@ -444,8 +479,7 @@ const conflictVerdict = computed(() => {
|
|
|
444
479
|
/>
|
|
445
480
|
|
|
446
481
|
<p class="mt-auto text-[10px] leading-relaxed text-slate-600">
|
|
447
|
-
|
|
448
|
-
{{ helperMeta.label }} when it fails — a green check advances with nothing spun up.
|
|
482
|
+
{{ t('gates.sidebar.footer', { helper: helperMeta.label }) }}
|
|
449
483
|
</p>
|
|
450
484
|
</aside>
|
|
451
485
|
</div>
|
|
@@ -4,6 +4,7 @@ import { useLocalStorage } from '@vueuse/core'
|
|
|
4
4
|
import type { AgentKind } from '~/types/domain'
|
|
5
5
|
import { AGENT_CATEGORIES, OBSERVABILITY_GATE_ARCHETYPE } from '~/utils/catalog'
|
|
6
6
|
|
|
7
|
+
const { t } = useI18n()
|
|
7
8
|
const agents = useAgentsStore()
|
|
8
9
|
const releaseHealth = useReleaseHealthStore()
|
|
9
10
|
defineEmits<{ (e: 'add', kind: AgentKind): void }>()
|
|
@@ -25,7 +26,8 @@ const groups = computed(() => {
|
|
|
25
26
|
agents: palette.value.filter((a) => a.category === cat.id),
|
|
26
27
|
}))
|
|
27
28
|
const custom = palette.value.filter((a) => !a.category)
|
|
28
|
-
if (custom.length)
|
|
29
|
+
if (custom.length)
|
|
30
|
+
ordered.push({ id: 'custom', label: t('palette.customAgents'), agents: custom })
|
|
29
31
|
return ordered.filter((g) => g.agents.length)
|
|
30
32
|
})
|
|
31
33
|
|
|
@@ -43,7 +45,7 @@ function toggle(id: string) {
|
|
|
43
45
|
|
|
44
46
|
<template>
|
|
45
47
|
<div class="space-y-2">
|
|
46
|
-
<p class="px-1 text-[11px] text-slate-500">
|
|
48
|
+
<p class="px-1 text-[11px] text-slate-500">{{ t('palette.hint') }}</p>
|
|
47
49
|
<div class="space-y-2">
|
|
48
50
|
<section v-for="g in groups" :key="g.id">
|
|
49
51
|
<button
|
|
@@ -7,7 +7,7 @@ import type { IterationCapChoice } from '~/types/execution'
|
|
|
7
7
|
// or stop and reset the task. Used by both the requirements-review window and the
|
|
8
8
|
// companion step detail (Spec Reviewer / Reviewer / Architect Companion), so the two
|
|
9
9
|
// gates present an identical choice rather than each rolling its own.
|
|
10
|
-
withDefaults(
|
|
10
|
+
const props = withDefaults(
|
|
11
11
|
defineProps<{
|
|
12
12
|
heading: string
|
|
13
13
|
detail: string
|
|
@@ -16,14 +16,17 @@ withDefaults(
|
|
|
16
16
|
proceedLabel?: string
|
|
17
17
|
stopLabel?: string
|
|
18
18
|
}>(),
|
|
19
|
-
{
|
|
20
|
-
loading: false,
|
|
21
|
-
extraRoundLabel: 'One more round',
|
|
22
|
-
proceedLabel: 'Proceed anyway',
|
|
23
|
-
stopLabel: 'Stop & reset task',
|
|
24
|
-
},
|
|
19
|
+
{ loading: false },
|
|
25
20
|
)
|
|
26
21
|
|
|
22
|
+
const { t } = useI18n()
|
|
23
|
+
|
|
24
|
+
// The three button labels default to the shared i18n copy, but a caller may override
|
|
25
|
+
// any of them with surface-specific wording.
|
|
26
|
+
const extraRound = computed(() => props.extraRoundLabel ?? t('pipeline.iterationCap.extraRound'))
|
|
27
|
+
const proceed = computed(() => props.proceedLabel ?? t('pipeline.iterationCap.proceed'))
|
|
28
|
+
const stop = computed(() => props.stopLabel ?? t('pipeline.iterationCap.stopReset'))
|
|
29
|
+
|
|
27
30
|
const emit = defineEmits<{ resolve: [choice: IterationCapChoice] }>()
|
|
28
31
|
</script>
|
|
29
32
|
|
|
@@ -43,7 +46,7 @@ const emit = defineEmits<{ resolve: [choice: IterationCapChoice] }>()
|
|
|
43
46
|
:loading="loading"
|
|
44
47
|
@click="emit('resolve', 'extra-round')"
|
|
45
48
|
>
|
|
46
|
-
{{
|
|
49
|
+
{{ extraRound }}
|
|
47
50
|
</UButton>
|
|
48
51
|
<UButton
|
|
49
52
|
color="warning"
|
|
@@ -53,7 +56,7 @@ const emit = defineEmits<{ resolve: [choice: IterationCapChoice] }>()
|
|
|
53
56
|
:loading="loading"
|
|
54
57
|
@click="emit('resolve', 'proceed')"
|
|
55
58
|
>
|
|
56
|
-
{{
|
|
59
|
+
{{ proceed }}
|
|
57
60
|
</UButton>
|
|
58
61
|
<UButton
|
|
59
62
|
color="error"
|
|
@@ -63,7 +66,7 @@ const emit = defineEmits<{ resolve: [choice: IterationCapChoice] }>()
|
|
|
63
66
|
:loading="loading"
|
|
64
67
|
@click="emit('resolve', 'stop-reset')"
|
|
65
68
|
>
|
|
66
|
-
{{
|
|
69
|
+
{{ stop }}
|
|
67
70
|
</UButton>
|
|
68
71
|
</div>
|
|
69
72
|
</div>
|