@cat-factory/app 0.296.3 → 0.296.4
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/board/AddTaskModal.vue +61 -1
- package/app/components/bugFishing/BugFishingWindow.vue +170 -48
- package/app/components/common/DescriptorFields.vue +7 -0
- package/app/types/execution.ts +4 -0
- package/i18n/locales/de.json +18 -0
- package/i18n/locales/en.json +18 -0
- package/i18n/locales/es.json +18 -0
- package/i18n/locales/fr.json +18 -0
- package/i18n/locales/he.json +18 -0
- package/i18n/locales/it.json +18 -0
- package/i18n/locales/ja.json +18 -0
- package/i18n/locales/pl.json +18 -0
- package/i18n/locales/tr.json +18 -0
- package/i18n/locales/uk.json +18 -0
- package/package.json +2 -2
|
@@ -22,7 +22,11 @@ import type {
|
|
|
22
22
|
TaskTypeFields,
|
|
23
23
|
} from '~/types/domain'
|
|
24
24
|
import { DOC_KINDS, DOC_KIND_FIELDS } from '~/types/domain'
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
BUG_FISHING_DEFAULT_PASS_BUDGET,
|
|
27
|
+
BUG_FISHING_MAX_PASS_BUDGET,
|
|
28
|
+
BUG_FISHING_PHASES,
|
|
29
|
+
} from '@cat-factory/contracts'
|
|
26
30
|
import { resolveComponentRegistry } from '@modular-vue/core'
|
|
27
31
|
import { useReactiveSlots } from '@modular-vue/runtime'
|
|
28
32
|
import type { AppSlots, ResultViewContribution } from '~/modular/slots'
|
|
@@ -138,6 +142,8 @@ const timeboxHours = ref<number | undefined>(undefined)
|
|
|
138
142
|
// the deliberate act) plus an optional focus folded into every angle's prompt.
|
|
139
143
|
const fishingPhaseIds = ref<string[]>([])
|
|
140
144
|
const fishingFocus = ref('')
|
|
145
|
+
/** Held as a string because the input is a text field; parsed at submit, blank ⇒ the default. */
|
|
146
|
+
const fishingMaxPasses = ref('')
|
|
141
147
|
// Spike research criteria — folded into the spike agent's prompt (see the backend `spike` kind).
|
|
142
148
|
const spikeResearchQuestion = ref('')
|
|
143
149
|
const spikeSuccessCriteria = ref('')
|
|
@@ -304,12 +310,37 @@ function buildCustomTypeFields(): TaskTypeFields | undefined {
|
|
|
304
310
|
* Its own function rather than another arm of {@link buildTypeFields}, whose per-type chain is at
|
|
305
311
|
* its complexity ceiling — a budget is a split trigger, not a number to raise.
|
|
306
312
|
*/
|
|
313
|
+
/**
|
|
314
|
+
* The typed pass budget, or undefined when the field is blank.
|
|
315
|
+
*
|
|
316
|
+
* `null` is the third answer: something was typed that is not a budget. Kept distinct from blank
|
|
317
|
+
* so {@link fishingMaxPassesProblem} can refuse it HERE, where the person can see which field is
|
|
318
|
+
* wrong, rather than letting the create call come back as a generic 422 whose only detail is a
|
|
319
|
+
* valibot path.
|
|
320
|
+
*/
|
|
321
|
+
const fishingMaxPassesValue = computed<number | null | undefined>(() => {
|
|
322
|
+
const raw = fishingMaxPasses.value.trim()
|
|
323
|
+
if (!raw) return undefined
|
|
324
|
+
const parsed = Number(raw)
|
|
325
|
+
if (!Number.isInteger(parsed)) return null
|
|
326
|
+
return parsed >= 1 && parsed <= BUG_FISHING_MAX_PASS_BUDGET ? parsed : null
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
/** The message shown under the pass-budget field, or null when it is fine. */
|
|
330
|
+
const fishingMaxPassesProblem = computed(() =>
|
|
331
|
+
fishingMaxPassesValue.value === null
|
|
332
|
+
? t('board.addTask.bugFishingFields.maxPasses.problem', { max: BUG_FISHING_MAX_PASS_BUDGET })
|
|
333
|
+
: null,
|
|
334
|
+
)
|
|
335
|
+
|
|
307
336
|
function buildBugFishingFields(): TaskTypeFields | undefined {
|
|
308
337
|
const f: TaskTypeFields = {}
|
|
309
338
|
if (fishingPhaseIds.value.length && fishingPhaseIds.value.length < BUG_FISHING_PHASES.length) {
|
|
310
339
|
f.fishingPhaseIds = [...fishingPhaseIds.value]
|
|
311
340
|
}
|
|
312
341
|
if (fishingFocus.value.trim()) f.fishingFocus = fishingFocus.value.trim()
|
|
342
|
+
const maxPasses = fishingMaxPassesValue.value
|
|
343
|
+
if (typeof maxPasses === 'number') f.fishingMaxPasses = maxPasses
|
|
313
344
|
return Object.keys(f).length ? f : undefined
|
|
314
345
|
}
|
|
315
346
|
|
|
@@ -597,6 +628,7 @@ watch(open, (isOpen) => {
|
|
|
597
628
|
timeboxHours.value = undefined
|
|
598
629
|
fishingPhaseIds.value = []
|
|
599
630
|
fishingFocus.value = ''
|
|
631
|
+
fishingMaxPasses.value = ''
|
|
600
632
|
spikeResearchQuestion.value = ''
|
|
601
633
|
spikeSuccessCriteria.value = ''
|
|
602
634
|
spikeOptionsToCompare.value = ''
|
|
@@ -708,6 +740,10 @@ const canAdd = computed(() => {
|
|
|
708
740
|
return false
|
|
709
741
|
// A custom type's collected form must satisfy its descriptor (the same rule the server enforces).
|
|
710
742
|
if (customFieldProblems.value.length > 0) return false
|
|
743
|
+
// The pass budget is bounded by `taskTypeFieldsSchema`, so a value outside it is refused at
|
|
744
|
+
// creation whatever this form does. Refusing it here is what turns that into a message beside
|
|
745
|
+
// the field rather than a generic failure toast.
|
|
746
|
+
if (taskType.value === 'bug-fishing' && fishingMaxPassesProblem.value) return false
|
|
711
747
|
return true
|
|
712
748
|
})
|
|
713
749
|
|
|
@@ -1105,6 +1141,30 @@ function openReviewFrictionDialog(conflict: NonNullable<ReturnType<typeof parseC
|
|
|
1105
1141
|
class="w-full"
|
|
1106
1142
|
/>
|
|
1107
1143
|
</UFormField>
|
|
1144
|
+
<!-- An OVERRIDE, so it is hidden at the basic tier and what remains is exactly the
|
|
1145
|
+
shipped default it would have shown. It bites only on a codebase large enough to
|
|
1146
|
+
be split into territories, where the plan is territories x angles. -->
|
|
1147
|
+
<UFormField
|
|
1148
|
+
v-if="uiMode.isAdvanced"
|
|
1149
|
+
:label="t('board.addTask.bugFishingFields.maxPasses.label')"
|
|
1150
|
+
:hint="t('board.addTask.optional')"
|
|
1151
|
+
:description="
|
|
1152
|
+
t('board.addTask.bugFishingFields.maxPasses.hint', {
|
|
1153
|
+
count: BUG_FISHING_DEFAULT_PASS_BUDGET,
|
|
1154
|
+
})
|
|
1155
|
+
"
|
|
1156
|
+
:error="fishingMaxPassesProblem ?? undefined"
|
|
1157
|
+
>
|
|
1158
|
+
<UInput
|
|
1159
|
+
v-model="fishingMaxPasses"
|
|
1160
|
+
type="number"
|
|
1161
|
+
:min="1"
|
|
1162
|
+
:max="BUG_FISHING_MAX_PASS_BUDGET"
|
|
1163
|
+
:step="1"
|
|
1164
|
+
:placeholder="String(BUG_FISHING_DEFAULT_PASS_BUDGET)"
|
|
1165
|
+
data-testid="add-task-fishing-max-passes"
|
|
1166
|
+
/>
|
|
1167
|
+
</UFormField>
|
|
1108
1168
|
</div>
|
|
1109
1169
|
|
|
1110
1170
|
<div v-else-if="taskType === 'spike'" class="space-y-3">
|
|
@@ -68,11 +68,44 @@ const severityRank = (s: BugFishingSeverity) => {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
71
|
+
* A pass is an ANGLE over a TERRITORY, so a phase id alone no longer identifies one: on a
|
|
72
|
+
* partitioned codebase the same angle runs once per territory. The rail selects, filters and
|
|
73
|
+
* counts by this pair, which is also why it is derived in one place rather than spelled at each
|
|
74
|
+
* site.
|
|
75
|
+
*/
|
|
76
|
+
function passKey(pass: { id: string; territoryId?: string | null }): string {
|
|
77
|
+
return `${pass.territoryId ?? ''}::${pass.id}`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Which pass's findings the reader is looking at. `null` is "everything caught so far", which
|
|
82
|
+
* is the default because an expedition's value is the whole catch — the per-pass filter exists
|
|
73
83
|
* for someone working through one angle at a time, not as the primary reading.
|
|
74
84
|
*/
|
|
75
|
-
const
|
|
85
|
+
const selectedPassKey = ref<string | null>(null)
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The passes grouped by the territory they fished, in plan order.
|
|
89
|
+
*
|
|
90
|
+
* One group with no territory is the pass-through: a codebase small enough to fish whole (or an
|
|
91
|
+
* expedition planned before territories existed) renders exactly the flat angle rail it always
|
|
92
|
+
* did, because its group has no header to show.
|
|
93
|
+
*/
|
|
94
|
+
const territoryGroups = computed(() => {
|
|
95
|
+
const groups: { id: string | null; label: string | null; passes: typeof phases.value }[] = []
|
|
96
|
+
for (const phase of phases.value) {
|
|
97
|
+
const id = phase.territoryId ?? null
|
|
98
|
+
const last = groups[groups.length - 1]
|
|
99
|
+
if (last && last.id === id) last.passes.push(phase)
|
|
100
|
+
else groups.push({ id, label: phase.territoryLabel ?? null, passes: [phase] })
|
|
101
|
+
}
|
|
102
|
+
return groups
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
/** The expedition's plan: the budget, what it cut, and what the survey could not see. */
|
|
106
|
+
const plan = computed(() => state.value?.plan ?? null)
|
|
107
|
+
/** The cells the pass budget cut, so the window can name the ground nobody looked at. */
|
|
108
|
+
const unfished = computed(() => plan.value?.unfished ?? [])
|
|
76
109
|
|
|
77
110
|
/** Whether findings whose decision has been made are shown. Off by default: what is left to
|
|
78
111
|
* decide is the working list, and a triaged finding that stays in it reads as untriaged. */
|
|
@@ -92,10 +125,12 @@ function isOpen(f: BugFishingFinding): boolean {
|
|
|
92
125
|
}
|
|
93
126
|
|
|
94
127
|
const visibleFindings = computed<BugFishingFinding[]>(() => {
|
|
95
|
-
const
|
|
96
|
-
? findings.value.filter(
|
|
128
|
+
const byPass = selectedPassKey.value
|
|
129
|
+
? findings.value.filter(
|
|
130
|
+
(f) => passKey({ id: f.phaseId, territoryId: f.territoryId }) === selectedPassKey.value,
|
|
131
|
+
)
|
|
97
132
|
: findings.value
|
|
98
|
-
const triaged = showTriaged.value ?
|
|
133
|
+
const triaged = showTriaged.value ? byPass : byPass.filter(isOpen)
|
|
99
134
|
return [...triaged].sort((a, b) => severityRank(a.severity) - severityRank(b.severity))
|
|
100
135
|
})
|
|
101
136
|
|
|
@@ -106,19 +141,38 @@ const spawnedCount = computed(
|
|
|
106
141
|
() => findings.value.filter((f) => f.spawn?.status === 'spawned').length,
|
|
107
142
|
)
|
|
108
143
|
|
|
109
|
-
/** The
|
|
144
|
+
/** The pass the rail has selected, when one is. */
|
|
110
145
|
const selectedPhase = computed(() =>
|
|
111
|
-
|
|
146
|
+
selectedPassKey.value
|
|
147
|
+
? (phases.value.find((p) => passKey(p) === selectedPassKey.value) ?? null)
|
|
148
|
+
: null,
|
|
112
149
|
)
|
|
113
150
|
|
|
151
|
+
/**
|
|
152
|
+
* What share of its territory's manifest a settled pass reported reading, as a percentage, or
|
|
153
|
+
* null when there is nothing honest to show.
|
|
154
|
+
*
|
|
155
|
+
* Null covers two different absences on purpose, and neither renders as 0%. A pass that reported
|
|
156
|
+
* no paths said nothing about what it read, and a whole-codebase pass had no manifest to be a
|
|
157
|
+
* share of; showing either as "0% covered" would accuse a pass that may have read everything.
|
|
158
|
+
*/
|
|
159
|
+
function coverageShare(pass: { coverage?: { filesRead: number; manifestFiles: number } | null }) {
|
|
160
|
+
const coverage = pass.coverage
|
|
161
|
+
if (!coverage || coverage.manifestFiles <= 0) return null
|
|
162
|
+
return Math.min(100, Math.round((coverage.filesRead / coverage.manifestFiles) * 100))
|
|
163
|
+
}
|
|
164
|
+
|
|
114
165
|
/** How many angles have settled (completed or failed) — what the still-fishing banner counts. */
|
|
115
166
|
const settledPhaseCount = computed(
|
|
116
167
|
() => phases.value.filter((p) => p.status === 'completed' || p.status === 'failed').length,
|
|
117
168
|
)
|
|
118
169
|
|
|
119
|
-
/** How many findings each
|
|
120
|
-
function phaseFindingCount(
|
|
121
|
-
|
|
170
|
+
/** How many findings each pass contributed, for the rail's per-pass count. */
|
|
171
|
+
function phaseFindingCount(pass: { id: string; territoryId?: string | null }): number {
|
|
172
|
+
const key = passKey(pass)
|
|
173
|
+
return findings.value.filter(
|
|
174
|
+
(f) => passKey({ id: f.phaseId, territoryId: f.territoryId }) === key,
|
|
175
|
+
).length
|
|
122
176
|
}
|
|
123
177
|
|
|
124
178
|
/**
|
|
@@ -216,50 +270,80 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
216
270
|
type="button"
|
|
217
271
|
class="mb-1 w-full rounded-md px-2 py-1.5 text-left text-[12px]"
|
|
218
272
|
:class="
|
|
219
|
-
|
|
273
|
+
selectedPassKey === null
|
|
220
274
|
? 'bg-slate-800 text-slate-100'
|
|
221
275
|
: 'text-slate-400 hover:bg-slate-800/60'
|
|
222
276
|
"
|
|
223
|
-
@click="
|
|
277
|
+
@click="selectedPassKey = null"
|
|
224
278
|
>
|
|
225
279
|
{{ t('bugFishing.phases.all', { count: findings.length }) }}
|
|
226
280
|
</button>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
class="
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
{
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
281
|
+
<!-- Grouped by TERRITORY on a partitioned codebase. A codebase small enough to fish
|
|
282
|
+
whole has one group with no label, which renders as the flat angle rail. -->
|
|
283
|
+
<div v-for="group in territoryGroups" :key="group.id ?? 'whole'" class="mb-2">
|
|
284
|
+
<p
|
|
285
|
+
v-if="group.label"
|
|
286
|
+
class="mb-1 mt-2 truncate px-1 text-[10px] font-semibold uppercase tracking-wide text-sky-400/80"
|
|
287
|
+
:title="group.label"
|
|
288
|
+
>
|
|
289
|
+
{{ group.label }}
|
|
290
|
+
</p>
|
|
291
|
+
<ul class="space-y-0.5">
|
|
292
|
+
<li v-for="phase in group.passes" :key="passKey(phase)">
|
|
293
|
+
<button
|
|
294
|
+
type="button"
|
|
295
|
+
class="flex w-full items-start gap-2 rounded-md px-2 py-1.5 text-left"
|
|
296
|
+
:class="
|
|
297
|
+
selectedPassKey === passKey(phase)
|
|
298
|
+
? 'bg-slate-800 text-slate-100'
|
|
299
|
+
: 'text-slate-400 hover:bg-slate-800/60'
|
|
300
|
+
"
|
|
301
|
+
:data-testid="`bug-fishing-phase-${phase.id}`"
|
|
302
|
+
@click="selectedPassKey = passKey(phase)"
|
|
303
|
+
>
|
|
304
|
+
<UIcon
|
|
305
|
+
:name="PHASE_ICON[phase.status] ?? 'i-lucide-circle-dashed'"
|
|
306
|
+
class="mt-0.5 h-3.5 w-3.5 shrink-0"
|
|
307
|
+
:class="{
|
|
308
|
+
'animate-spin text-sky-300': phase.status === 'fishing',
|
|
309
|
+
'text-emerald-400': phase.status === 'completed',
|
|
310
|
+
'text-amber-400': phase.status === 'failed',
|
|
311
|
+
'text-slate-600': phase.status === 'pending',
|
|
312
|
+
}"
|
|
313
|
+
/>
|
|
314
|
+
<span class="min-w-0 flex-1">
|
|
315
|
+
<span class="block truncate text-[12px]">{{ phase.title }}</span>
|
|
316
|
+
<span class="block text-[10px] text-slate-500">
|
|
317
|
+
{{
|
|
318
|
+
phase.status === 'completed' || phase.status === 'failed'
|
|
319
|
+
? t('bugFishing.phases.found', { count: phaseFindingCount(phase) })
|
|
320
|
+
: t(`bugFishing.phases.status.${phase.status}`)
|
|
321
|
+
}}
|
|
322
|
+
</span>
|
|
323
|
+
<!-- The coverage rail. A LOW share is what tells a reader that "found nothing"
|
|
324
|
+
here means "did not look"; absent (a pass that reported no reads, or one
|
|
325
|
+
with no manifest to be a share of) shows nothing at all rather than 0%. -->
|
|
326
|
+
<span
|
|
327
|
+
v-if="coverageShare(phase) !== null"
|
|
328
|
+
class="mt-1 block"
|
|
329
|
+
:data-testid="`bug-fishing-coverage-${phase.id}`"
|
|
330
|
+
:title="t('bugFishing.coverage.tooltip', { percent: coverageShare(phase) })"
|
|
331
|
+
>
|
|
332
|
+
<span class="block h-0.5 w-full rounded-full bg-slate-700">
|
|
333
|
+
<span
|
|
334
|
+
class="block h-0.5 rounded-full bg-sky-500/70"
|
|
335
|
+
:style="{ width: `${coverageShare(phase)}%` }"
|
|
336
|
+
/>
|
|
337
|
+
</span>
|
|
338
|
+
<span class="mt-0.5 block text-[10px] text-slate-600">
|
|
339
|
+
{{ t('bugFishing.coverage.share', { percent: coverageShare(phase) }) }}
|
|
340
|
+
</span>
|
|
341
|
+
</span>
|
|
258
342
|
</span>
|
|
259
|
-
</
|
|
260
|
-
</
|
|
261
|
-
</
|
|
262
|
-
</
|
|
343
|
+
</button>
|
|
344
|
+
</li>
|
|
345
|
+
</ul>
|
|
346
|
+
</div>
|
|
263
347
|
</aside>
|
|
264
348
|
|
|
265
349
|
<div class="min-w-0 flex-1 overflow-y-auto px-5 py-4">
|
|
@@ -303,6 +387,44 @@ const PHASE_ICON: Record<string, string> = {
|
|
|
303
387
|
</span>
|
|
304
388
|
</div>
|
|
305
389
|
|
|
390
|
+
<!-- What this expedition did NOT cover. Three separate facts, each with its own fix, so
|
|
391
|
+
none of them is folded into the others: the pass budget cut cells nobody fished, the
|
|
392
|
+
provider truncated the tree the territories were computed from, or the codebase could
|
|
393
|
+
not be surveyed at all. A cap silent about its tail teaches the reader that the tail
|
|
394
|
+
was clean. -->
|
|
395
|
+
<div
|
|
396
|
+
v-if="plan && (unfished.length > 0 || plan.treeTruncated || plan.surveyUnavailableReason)"
|
|
397
|
+
data-testid="bug-fishing-plan"
|
|
398
|
+
class="mb-4 rounded-lg border border-amber-500/25 bg-amber-500/5 px-3 py-2 text-[12px] text-amber-200"
|
|
399
|
+
>
|
|
400
|
+
<p v-if="plan.surveyUnavailableReason" data-testid="bug-fishing-survey-unavailable">
|
|
401
|
+
{{ t('bugFishing.plan.surveyUnavailable', { reason: plan.surveyUnavailableReason }) }}
|
|
402
|
+
</p>
|
|
403
|
+
<p v-if="plan.treeTruncated" data-testid="bug-fishing-tree-truncated" class="mt-1">
|
|
404
|
+
{{ t('bugFishing.plan.treeTruncated') }}
|
|
405
|
+
</p>
|
|
406
|
+
<template v-if="unfished.length > 0">
|
|
407
|
+
<p class="mt-1" data-testid="bug-fishing-unfished">
|
|
408
|
+
{{
|
|
409
|
+
t('bugFishing.plan.unfished', {
|
|
410
|
+
fished: phases.length,
|
|
411
|
+
planned: plan.plannedCells,
|
|
412
|
+
})
|
|
413
|
+
}}
|
|
414
|
+
</p>
|
|
415
|
+
<ul class="mt-1 space-y-0.5 text-[11px] text-amber-200/80">
|
|
416
|
+
<li v-for="cell in unfished" :key="`${cell.territoryId}::${cell.phaseId}`">
|
|
417
|
+
{{
|
|
418
|
+
t('bugFishing.plan.unfishedCell', {
|
|
419
|
+
territory: cell.territoryLabel,
|
|
420
|
+
angle: cell.phaseTitle,
|
|
421
|
+
})
|
|
422
|
+
}}
|
|
423
|
+
</li>
|
|
424
|
+
</ul>
|
|
425
|
+
</template>
|
|
426
|
+
</div>
|
|
427
|
+
|
|
306
428
|
<!-- What the marks will run. Stated before anything is created, because the pipeline is
|
|
307
429
|
the shape of the work a mark causes. -->
|
|
308
430
|
<div
|
|
@@ -166,11 +166,18 @@ function selectItems(field: DescriptorField) {
|
|
|
166
166
|
@update:model-value="(v: string) => set(field.key, v)"
|
|
167
167
|
/>
|
|
168
168
|
|
|
169
|
+
<!-- The declared bounds are INPUT HINTS only: `validateDescriptorFields` is what actually
|
|
170
|
+
refuses a value, here and at every other door, so a stepper that respects them is a
|
|
171
|
+
convenience rather than the check. `integer` steps by one; a field that admits
|
|
172
|
+
fractions leaves `step` unset so the browser does not round the value away. -->
|
|
169
173
|
<UInput
|
|
170
174
|
v-else-if="field.type === 'number'"
|
|
171
175
|
:model-value="numberStr(field.key)"
|
|
172
176
|
type="number"
|
|
173
177
|
class="w-full font-mono"
|
|
178
|
+
:min="field.min"
|
|
179
|
+
:max="field.max"
|
|
180
|
+
:step="field.integer ? 1 : undefined"
|
|
174
181
|
:placeholder="field.placeholder"
|
|
175
182
|
@update:model-value="(v: string) => set(field.key, v === '' ? undefined : Number(v))"
|
|
176
183
|
/>
|
package/app/types/execution.ts
CHANGED
package/i18n/locales/de.json
CHANGED
|
@@ -3125,6 +3125,11 @@
|
|
|
3125
3125
|
"focus": {
|
|
3126
3126
|
"label": "Worauf konzentrieren",
|
|
3127
3127
|
"placeholder": "Teilsysteme, Verzeichnisse oder die Fehlerart, die dieses Team bisher Zeit gekostet hat"
|
|
3128
|
+
},
|
|
3129
|
+
"maxPasses": {
|
|
3130
|
+
"label": "Maximale Durchgänge",
|
|
3131
|
+
"hint": "Höchstzahl der Nur-Lese-Durchgänge dieser Expedition. Leer lassen für {count}. Bei einer großen Codebasis wird jeder Blickwinkel pro Gebiet durchsucht; was das Budget streicht, wird als nicht durchsucht vermerkt.",
|
|
3132
|
+
"problem": "Geben Sie eine ganze Zahl zwischen 1 und {max} ein oder lassen Sie das Feld leer für den Standardwert."
|
|
3128
3133
|
}
|
|
3129
3134
|
}
|
|
3130
3135
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Alle Blickwinkel wurden untersucht.",
|
|
8439
8444
|
"parkedWithUntriaged": "Über {count} Befund(e) wurde noch nicht entschieden.",
|
|
8440
8445
|
"finish": "Sichtung abschließen"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "{percent}% dieses Gebiets gelesen",
|
|
8449
|
+
"tooltip": "Dieser Durchgang hat angegeben, {percent}% seines Gebiets gelesen zu haben. Selbstauskunft des Agenten."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "Die Codebasis wurde nicht kartiert, daher wurde sie als ein Ganzes durchsucht: {reason}",
|
|
8453
|
+
"treeTruncated": "Der Repository-Baum kam gekürzt zurück, daher decken die Gebiete unten nur den lesbaren Teil ab.",
|
|
8454
|
+
"unfished": "{fished} von {planned} geplanten Durchgängen ausgeführt. In diesem Lauf nicht durchsucht:",
|
|
8455
|
+
"unfishedCell": "{territory}, unter {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Gebiete"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/en.json
CHANGED
|
@@ -388,6 +388,11 @@
|
|
|
388
388
|
"focus": {
|
|
389
389
|
"label": "Where to concentrate",
|
|
390
390
|
"placeholder": "Subsystems, directories, or the kind of defect that has been costing this team"
|
|
391
|
+
},
|
|
392
|
+
"maxPasses": {
|
|
393
|
+
"label": "Maximum passes",
|
|
394
|
+
"hint": "Most read-only passes this expedition may run. Leave empty for {count}. On a large codebase each angle is fished per territory, and whatever the budget cuts is recorded as unfished.",
|
|
395
|
+
"problem": "Enter a whole number between 1 and {max}, or leave it empty for the default."
|
|
391
396
|
}
|
|
392
397
|
}
|
|
393
398
|
},
|
|
@@ -8736,6 +8741,19 @@
|
|
|
8736
8741
|
"parked": "Every angle has been fished.",
|
|
8737
8742
|
"parkedWithUntriaged": "{count} finding(s) still undecided.",
|
|
8738
8743
|
"finish": "Finish triage"
|
|
8744
|
+
},
|
|
8745
|
+
"coverage": {
|
|
8746
|
+
"share": "{percent}% of this territory read",
|
|
8747
|
+
"tooltip": "This pass reported reading {percent}% of its territory. Self-reported by the agent."
|
|
8748
|
+
},
|
|
8749
|
+
"plan": {
|
|
8750
|
+
"surveyUnavailable": "The codebase was not surveyed, so this expedition fished it as one whole: {reason}",
|
|
8751
|
+
"treeTruncated": "The repository tree came back truncated, so the territories below cover only the part that could be read.",
|
|
8752
|
+
"unfished": "Fished {fished} of {planned} planned passes. Not fished this run:",
|
|
8753
|
+
"unfishedCell": "{territory}, under {angle}"
|
|
8754
|
+
},
|
|
8755
|
+
"territories": {
|
|
8756
|
+
"heading": "Territories"
|
|
8739
8757
|
}
|
|
8740
8758
|
}
|
|
8741
8759
|
}
|
package/i18n/locales/es.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "Dónde concentrarse",
|
|
345
345
|
"placeholder": "Subsistemas, directorios o el tipo de defecto que más tiempo le está costando a este equipo"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "Pasadas máximas",
|
|
349
|
+
"hint": "Número máximo de pasadas de solo lectura de esta expedición. Déjalo vacío para {count}. En una base de código grande cada ángulo se pesca por territorio, y lo que el presupuesto recorte queda registrado como no pescado.",
|
|
350
|
+
"problem": "Introduce un número entero entre 1 y {max}, o déjalo vacío para el valor predeterminado."
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Se han explorado todos los ángulos.",
|
|
8439
8444
|
"parkedWithUntriaged": "Quedan {count} hallazgo(s) sin decidir.",
|
|
8440
8445
|
"finish": "Terminar el triaje"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "{percent}% de este territorio leído",
|
|
8449
|
+
"tooltip": "Esta pasada declaró haber leído el {percent}% de su territorio. Dato declarado por el agente."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "No se pudo inspeccionar el código, así que esta expedición lo pescó como un todo: {reason}",
|
|
8453
|
+
"treeTruncated": "El árbol del repositorio llegó truncado, así que los territorios de abajo cubren solo la parte que se pudo leer.",
|
|
8454
|
+
"unfished": "Se pescaron {fished} de {planned} pasadas previstas. Sin pescar en esta ejecución:",
|
|
8455
|
+
"unfishedCell": "{territory}, bajo {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Territorios"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/fr.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "Où se concentrer",
|
|
345
345
|
"placeholder": "Sous-systèmes, répertoires, ou le type de défaut qui coûte le plus de temps à cette équipe"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "Passes maximum",
|
|
349
|
+
"hint": "Nombre maximal de passes en lecture seule de cette expédition. Laisser vide pour {count}. Sur un gros code, chaque angle est pêché par territoire, et ce que le budget coupe est consigné comme non pêché.",
|
|
350
|
+
"problem": "Saisissez un nombre entier compris entre 1 et {max}, ou laissez le champ vide pour la valeur par défaut."
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Tous les angles ont été explorés.",
|
|
8439
8444
|
"parkedWithUntriaged": "{count} constatation(s) encore non tranchée(s).",
|
|
8440
8445
|
"finish": "Terminer le tri"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "{percent}% de ce territoire lu",
|
|
8449
|
+
"tooltip": "Cette passe déclare avoir lu {percent}% de son territoire. Donnée déclarée par l'agent."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "Le code n'a pas été cartographié, cette expédition l'a donc pêché d'un seul tenant : {reason}",
|
|
8453
|
+
"treeTruncated": "L'arbre du dépôt est revenu tronqué : les territoires ci-dessous ne couvrent que la partie lisible.",
|
|
8454
|
+
"unfished": "{fished} passes pêchées sur {planned} prévues. Non pêché lors de cette exécution :",
|
|
8455
|
+
"unfishedCell": "{territory}, sous {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Territoires"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/he.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "במה להתמקד",
|
|
345
345
|
"placeholder": "תת-מערכות, תיקיות או סוג התקלות שעולה לצוות הזה הכי הרבה זמן"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "מספר מעברים מרבי",
|
|
349
|
+
"hint": "מספר המעברים לקריאה בלבד המרבי של המשלחת הזו. השאירו ריק עבור {count}. בבסיס קוד גדול כל זווית נסרקת לכל טריטוריה בנפרד, ומה שהתקציב חותך נרשם כלא נסרק.",
|
|
350
|
+
"problem": "הזינו מספר שלם בין 1 ל-{max}, או השאירו ריק לערך ברירת המחדל."
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "כל הזוויות נסרקו.",
|
|
8439
8444
|
"parkedWithUntriaged": "נותרו {count} ממצאים ללא הכרעה.",
|
|
8440
8445
|
"finish": "סיום המיון"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "{percent}% מהטריטוריה הזו נקראו",
|
|
8449
|
+
"tooltip": "המעבר הזה דיווח שקרא {percent}% מהטריטוריה שלו. דיווח עצמי של הסוכן."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "בסיס הקוד לא נסקר, ולכן המשלחת סרקה אותו כמקשה אחת: {reason}",
|
|
8453
|
+
"treeTruncated": "עץ המאגר חזר קטוע, ולכן הטריטוריות שלהלן מכסות רק את החלק שניתן היה לקרוא.",
|
|
8454
|
+
"unfished": "בוצעו {fished} מתוך {planned} מעברים מתוכננים. לא נסרק בהרצה הזו:",
|
|
8455
|
+
"unfishedCell": "{territory}, בזווית {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "טריטוריות"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/it.json
CHANGED
|
@@ -3125,6 +3125,11 @@
|
|
|
3125
3125
|
"focus": {
|
|
3126
3126
|
"label": "Dove concentrarsi",
|
|
3127
3127
|
"placeholder": "Sottosistemi, cartelle o il tipo di difetto che sta costando più tempo a questo team"
|
|
3128
|
+
},
|
|
3129
|
+
"maxPasses": {
|
|
3130
|
+
"label": "Passaggi massimi",
|
|
3131
|
+
"hint": "Numero massimo di passaggi in sola lettura di questa spedizione. Lascia vuoto per {count}. Su una codebase grande ogni angolazione viene pescata per territorio, e ciò che il budget taglia resta registrato come non pescato.",
|
|
3132
|
+
"problem": "Inserisci un numero intero compreso tra 1 e {max}, oppure lascia vuoto per il valore predefinito."
|
|
3128
3133
|
}
|
|
3129
3134
|
}
|
|
3130
3135
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Tutte le angolazioni sono state esplorate.",
|
|
8439
8444
|
"parkedWithUntriaged": "Restano {count} rilievi senza decisione.",
|
|
8440
8445
|
"finish": "Concludi il triage"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "{percent}% di questo territorio letto",
|
|
8449
|
+
"tooltip": "Questo passaggio ha dichiarato di aver letto il {percent}% del suo territorio. Dato dichiarato dall'agente."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "La codebase non è stata mappata, quindi questa spedizione l'ha pescata per intero: {reason}",
|
|
8453
|
+
"treeTruncated": "L'albero del repository è arrivato troncato, quindi i territori qui sotto coprono solo la parte leggibile.",
|
|
8454
|
+
"unfished": "Pescati {fished} passaggi su {planned} previsti. Non pescati in questa esecuzione:",
|
|
8455
|
+
"unfishedCell": "{territory}, sotto {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Territori"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/ja.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "重点を置く箇所",
|
|
345
345
|
"placeholder": "サブシステム、ディレクトリ、またはこのチームで手間になっている不具合の種類"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "最大パス数",
|
|
349
|
+
"hint": "この遠征が実行できる読み取り専用パスの上限です。空欄なら {count} 回。大規模なコードベースでは各観点を領域ごとに探索し、予算で削られた分は未探索として記録されます。",
|
|
350
|
+
"problem": "1 から {max} までの整数を入力してください。空欄のままにすると既定値が使われます。"
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "すべての観点の探索が終わりました。",
|
|
8439
8444
|
"parkedWithUntriaged": "未判断の指摘が {count} 件あります。",
|
|
8440
8445
|
"finish": "トリアージを終了"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "この領域の {percent}% を読了",
|
|
8449
|
+
"tooltip": "このパスは自身の領域の {percent}% を読んだと報告しました。エージェントの自己申告です。"
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "コードベースを調査できなかったため、この遠征は全体を一括で探索しました: {reason}",
|
|
8453
|
+
"treeTruncated": "リポジトリのツリーが切り詰められて返されたため、以下の領域は読み取れた範囲のみを対象としています。",
|
|
8454
|
+
"unfished": "予定 {planned} パス中 {fished} パスを実施しました。今回未探索:",
|
|
8455
|
+
"unfishedCell": "{territory} の {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "領域"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/pl.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "Na czym się skupić",
|
|
345
345
|
"placeholder": "Podsystemy, katalogi albo rodzaj usterki, który najbardziej kosztuje ten zespół"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "Maksymalna liczba przejść",
|
|
349
|
+
"hint": "Najwyższa liczba przejść tylko do odczytu w tej wyprawie. Zostaw puste, aby użyć {count}. W dużej bazie kodu każdy kąt przeszukiwany jest osobno dla każdego terytorium, a to, co utnie budżet, zapisujemy jako nieprzeszukane.",
|
|
350
|
+
"problem": "Podaj liczbę całkowitą od 1 do {max} albo pozostaw pole puste, aby użyć wartości domyślnej."
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Przeszukano wszystkie perspektywy.",
|
|
8439
8444
|
"parkedWithUntriaged": "Pozostało {count} nierozstrzygniętych znalezisk.",
|
|
8440
8445
|
"finish": "Zakończ segregację"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "Przeczytano {percent}% tego terytorium",
|
|
8449
|
+
"tooltip": "To przejście zgłosiło przeczytanie {percent}% swojego terytorium. Dane podane przez agenta."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "Baza kodu nie została zbadana, więc ta wyprawa przeszukała ją w całości: {reason}",
|
|
8453
|
+
"treeTruncated": "Drzewo repozytorium wróciło obcięte, więc terytoria poniżej obejmują tylko tę część, którą udało się odczytać.",
|
|
8454
|
+
"unfished": "Wykonano {fished} z {planned} zaplanowanych przejść. Nieprzeszukane w tym uruchomieniu:",
|
|
8455
|
+
"unfishedCell": "{territory}, pod kątem {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Terytoria"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/tr.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "Nereye yoğunlaşılsın",
|
|
345
345
|
"placeholder": "Alt sistemler, dizinler ya da bu ekibe en çok zaman kaybettiren kusur türü"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "En fazla geçiş",
|
|
349
|
+
"hint": "Bu seferin yapabileceği en fazla salt okunur geçiş sayısı. {count} için boş bırakın. Büyük bir kod tabanında her açı her bölge için ayrı taranır ve bütçenin kestikleri taranmamış olarak kaydedilir.",
|
|
350
|
+
"problem": "1 ile {max} arasında bir tam sayı girin veya varsayılan değer için boş bırakın."
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Tüm açılar tarandı.",
|
|
8439
8444
|
"parkedWithUntriaged": "{count} bulgu hâlâ karara bağlanmadı.",
|
|
8440
8445
|
"finish": "Triyajı bitir"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "Bu bölgenin %{percent} kadarı okundu",
|
|
8449
|
+
"tooltip": "Bu geçiş, bölgesinin %{percent} kadarını okuduğunu bildirdi. Ajanın kendi beyanı."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "Kod tabanı taranamadı, bu nedenle bu sefer tek parça olarak tarandı: {reason}",
|
|
8453
|
+
"treeTruncated": "Depo ağacı kırpılmış olarak döndü, bu nedenle aşağıdaki bölgeler yalnızca okunabilen kısmı kapsıyor.",
|
|
8454
|
+
"unfished": "Planlanan {planned} geçişten {fished} tanesi yapıldı. Bu çalışmada taranmayanlar:",
|
|
8455
|
+
"unfishedCell": "{angle} açısından {territory}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Bölgeler"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/i18n/locales/uk.json
CHANGED
|
@@ -343,6 +343,11 @@
|
|
|
343
343
|
"focus": {
|
|
344
344
|
"label": "На чому зосередитися",
|
|
345
345
|
"placeholder": "Підсистеми, каталоги або тип дефектів, що найбільше коштує цій команді"
|
|
346
|
+
},
|
|
347
|
+
"maxPasses": {
|
|
348
|
+
"label": "Максимум проходів",
|
|
349
|
+
"hint": "Найбільша кількість проходів лише для читання в цій експедиції. Залиште порожнім, щоб узяти {count}. У великій кодовій базі кожен кут перевіряється для кожної території, а зрізане бюджетом записується як неперевірене.",
|
|
350
|
+
"problem": "Введіть ціле число від 1 до {max} або залиште поле порожнім, щоб використати типове значення."
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
},
|
|
@@ -8438,6 +8443,19 @@
|
|
|
8438
8443
|
"parked": "Пройдено всі кути.",
|
|
8439
8444
|
"parkedWithUntriaged": "Залишилося {count} нерозглянутих знахідок.",
|
|
8440
8445
|
"finish": "Завершити сортування"
|
|
8446
|
+
},
|
|
8447
|
+
"coverage": {
|
|
8448
|
+
"share": "Прочитано {percent}% цієї території",
|
|
8449
|
+
"tooltip": "Цей прохід повідомив, що прочитав {percent}% своєї території. Дані з власних слів агента."
|
|
8450
|
+
},
|
|
8451
|
+
"plan": {
|
|
8452
|
+
"surveyUnavailable": "Кодову базу не обстежено, тож ця експедиція шукала в ній як у єдиному цілому: {reason}",
|
|
8453
|
+
"treeTruncated": "Дерево репозиторію повернулося обрізаним, тож території нижче охоплюють лише прочитану частину.",
|
|
8454
|
+
"unfished": "Виконано {fished} із {planned} запланованих проходів. Не перевірено цього разу:",
|
|
8455
|
+
"unfishedCell": "{territory}, під кутом {angle}"
|
|
8456
|
+
},
|
|
8457
|
+
"territories": {
|
|
8458
|
+
"heading": "Території"
|
|
8441
8459
|
}
|
|
8442
8460
|
}
|
|
8443
8461
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.296.
|
|
3
|
+
"version": "0.296.4",
|
|
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",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@cat-factory/contracts": "0.
|
|
21
|
+
"@cat-factory/contracts": "0.347.0",
|
|
22
22
|
"@modular-frontend/core": "0.6.0",
|
|
23
23
|
"@modular-vue/core": "^1.5.0",
|
|
24
24
|
"@modular-vue/journeys": "^1.4.0",
|