@cat-factory/app 0.251.0 → 0.252.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,13 @@ const props = defineProps<{
16
16
  referenceId: string | null | undefined
17
17
  blobs: ArtifactBlobs
18
18
  busy?: boolean
19
+ /**
20
+ * Where the reference came from, when the caller knows. A frame rendered from a linked design
21
+ * and a mock a person attached are different claims about what "the reference" is, so the
22
+ * caption says which; absent leaves the plain label, which is what an unattributed reference
23
+ * honestly is.
24
+ */
25
+ referenceOrigin?: 'upload' | 'design' | null
19
26
  }>()
20
27
 
21
28
  const emit = defineEmits<{
@@ -196,6 +203,9 @@ function onRefInput(e: Event) {
196
203
  <figure class="space-y-1">
197
204
  <figcaption class="text-[10px] uppercase tracking-wide text-slate-500">
198
205
  {{ t('media.compare.reference') }}
206
+ <span v-if="referenceOrigin === 'design'" class="text-amber-300/80">
207
+ {{ t('media.compare.fromLinkedDesign') }}
208
+ </span>
199
209
  </figcaption>
200
210
  <button
201
211
  v-if="refUrl"
@@ -8,7 +8,7 @@
8
8
  // (per-view notes + a freeform box, composed into the Tester's fixer findings), or recapture.
9
9
  // References can be dropped straight onto a pair, or uploaded for any view below.
10
10
  import { computed, onUnmounted, reactive, ref, watch } from 'vue'
11
- import type { VisualConfirmStepState } from '~/types/execution'
11
+ import type { VisualConfirmDesignGapReason, VisualConfirmStepState } from '~/types/execution'
12
12
  import { useArtifactBlobs } from '~/composables/useArtifactBlobs'
13
13
  import ImageCompare from '~/components/media/ImageCompare.vue'
14
14
  import ArtifactLightbox from '~/components/media/ArtifactLightbox.vue'
@@ -63,6 +63,23 @@ const OUTCOME_LABELS = computed<Record<'completed' | 'failed', string>>(() => ({
63
63
  failed: t('visualConfirm.outcome.failed'),
64
64
  }))
65
65
 
66
+ // What the task's LINKED DESIGNS contributed. Present whenever a design is linked, including
67
+ // when everything worked: a reviewer comparing a screen against a Figma frame needs to know the
68
+ // frame is the design's own, and one seeing no design frames needs to know whether a design is
69
+ // linked at all. Absent ⇒ the task links none, which this panel must not invent a line about.
70
+ const design = computed(() => vc.value?.designReferences ?? null)
71
+
72
+ // Exhaustive map of the gap vocabulary → copy, literal-keyed for the same drift-guard reason as
73
+ // the outcome labels above. Each names a DIFFERENT fix, which is why the backend keeps them apart
74
+ // instead of collapsing them into one "no images" absence.
75
+ const DESIGN_GAP_LABELS = computed<Record<VisualConfirmDesignGapReason, string>>(() => ({
76
+ partial: t('visualConfirm.design.gap.partial'),
77
+ failed: t('visualConfirm.design.gap.failed'),
78
+ none: t('visualConfirm.design.gap.none'),
79
+ storage_unavailable: t('visualConfirm.design.gap.storage_unavailable'),
80
+ not_retained: t('visualConfirm.design.gap.not_retained'),
81
+ }))
82
+
66
83
  // Resolve every pair's artifacts (the gallery + the lightbox share this one cache).
67
84
  watch(
68
85
  pairs,
@@ -212,6 +229,35 @@ async function onFilePicked(e: Event) {
212
229
  {{ vc.degradedReason }}
213
230
  </p>
214
231
 
232
+ <!-- What the linked designs contributed. Rendered even when nothing is missing, so a
233
+ reference the reviewer is judging against is never anonymous. -->
234
+ <section
235
+ v-if="design"
236
+ class="rounded-lg border border-slate-800 bg-slate-900/60 px-3 py-2 text-[12px] text-slate-300"
237
+ >
238
+ <p class="flex items-center gap-1.5">
239
+ <UIcon name="i-lucide-figma" class="h-3.5 w-3.5 shrink-0 text-amber-300" />
240
+ <span>{{
241
+ t('visualConfirm.design.summary', { count: design.images }, design.images)
242
+ }}</span>
243
+ <span v-if="design.dropped" class="text-slate-500">
244
+ {{ t('visualConfirm.design.dropped', { count: design.dropped }, design.dropped) }}
245
+ </span>
246
+ </p>
247
+ <!-- One line per short design, carrying both ways it can fall short: what its source
248
+ kept, and what this gallery's shared ceiling cut from it. A design the ceiling shut
249
+ out entirely reads as one with no frames unless it is named here. -->
250
+ <ul v-if="design.gaps?.length" class="mt-1.5 space-y-1 text-[11px] text-amber-300/90">
251
+ <li v-for="gap in design.gaps" :key="`${gap.title}-${gap.reason ?? 'capped'}`">
252
+ {{ t('visualConfirm.design.gapLine', { title: gap.title }) }}
253
+ <template v-if="gap.reason">{{ DESIGN_GAP_LABELS[gap.reason] }}</template>
254
+ <template v-if="gap.dropped">{{
255
+ t('visualConfirm.design.gapDropped', { count: gap.dropped }, gap.dropped)
256
+ }}</template>
257
+ </li>
258
+ </ul>
259
+ </section>
260
+
215
261
  <p
216
262
  v-if="working"
217
263
  class="flex items-center gap-2 rounded-lg border border-slate-800 bg-slate-950/40 px-3 py-2 text-[12px] text-slate-300"
@@ -228,6 +274,7 @@ async function onFilePicked(e: Event) {
228
274
  :view="p.view"
229
275
  :actual-id="p.actualArtifactId"
230
276
  :reference-id="p.referenceArtifactId"
277
+ :reference-origin="p.referenceOrigin"
231
278
  :blobs="blobs"
232
279
  :busy="busy"
233
280
  @expand="expand"
@@ -104,6 +104,10 @@ export type {
104
104
  HumanTestStepState,
105
105
  VisualConfirmStepState,
106
106
  VisualConfirmPair,
107
+ VisualConfirmReferenceOrigin,
108
+ VisualConfirmDesignGap,
109
+ VisualConfirmDesignGapReason,
110
+ VisualConfirmDesignReferences,
107
111
  VisualConfirmRound,
108
112
  ExecutionInstance,
109
113
  // The historical frontend name for a per-block review comment is the contract's
@@ -6153,6 +6153,19 @@
6153
6153
  "history": {
6154
6154
  "heading": "Verlauf (keine Runden) | Verlauf ({count} Runde) | Verlauf ({count} Runden)",
6155
6155
  "fixRequested": "Korrektur angefordert"
6156
+ },
6157
+ "design": {
6158
+ "summary": "Keine Frames stammen aus dem verknüpften Design. | 1 Frame aus dem verknüpften Design. | {count} Frames aus dem verknüpften Design.",
6159
+ "dropped": "Es sind keine weiteren ausgeblendet. | 1 weiterer wird hier nicht angezeigt. | {count} weitere werden hier nicht angezeigt.",
6160
+ "gapDropped": "Keiner seiner Frames ist ausgeblendet. | 1 seiner Frames wird hier nicht angezeigt. | {count} seiner Frames werden hier nicht angezeigt.",
6161
+ "gapLine": "{title}:",
6162
+ "gap": {
6163
+ "partial": "nur ein Teil seiner Frames wurde gespeichert; aktualisiere das Dokument, um es erneut zu versuchen.",
6164
+ "failed": "seine Frames konnten beim letzten Import nicht geladen werden; aktualisiere das Dokument, um es erneut zu versuchen.",
6165
+ "none": "es enthält keine Frames zum Anzeigen.",
6166
+ "storage_unavailable": "beim Import war kein Bildspeicher konfiguriert, daher wurde nichts heruntergeladen.",
6167
+ "not_retained": "es sind keine Bilder dazu gespeichert. Die Quelle rendert möglicherweise keine Frames, oder das Dokument wurde importiert, bevor Frames aufbewahrt wurden; ein erneuter Import zeigt, was zutrifft."
6168
+ }
6156
6169
  }
6157
6170
  },
6158
6171
  "outcome": {
@@ -6399,6 +6412,7 @@
6399
6412
  },
6400
6413
  "actual": "Tatsächlich",
6401
6414
  "reference": "Referenz",
6415
+ "fromLinkedDesign": "· aus dem verknüpften Design",
6402
6416
  "actualAlt": "{view} (tatsächlich)",
6403
6417
  "referenceAlt": "{view} (Referenz)",
6404
6418
  "replace": "Ersetzen",
@@ -5866,6 +5866,19 @@
5866
5866
  "history": {
5867
5867
  "heading": "History (no rounds) | History ({count} round) | History ({count} rounds)",
5868
5868
  "fixRequested": "Fix requested"
5869
+ },
5870
+ "design": {
5871
+ "summary": "No frames came from the linked design. | 1 frame from the linked design. | {count} frames from the linked design.",
5872
+ "dropped": "No others are hidden. | 1 more is not shown here. | {count} more are not shown here.",
5873
+ "gapDropped": "None of its frames are hidden. | 1 of its frames is not shown here. | {count} of its frames are not shown here.",
5874
+ "gapLine": "{title}:",
5875
+ "gap": {
5876
+ "partial": "only part of its frames were retained; refresh the document to try again.",
5877
+ "failed": "its frames could not be downloaded at the last import; refresh the document to retry.",
5878
+ "none": "it has no frames to show.",
5879
+ "storage_unavailable": "no image storage was configured when it was imported, so nothing was downloaded.",
5880
+ "not_retained": "no images are held for it. Its source may not render frames, or it was imported before frames were kept; re-importing tells the two apart."
5881
+ }
5869
5882
  }
5870
5883
  },
5871
5884
  "outcome": {
@@ -6118,6 +6131,7 @@
6118
6131
  },
6119
6132
  "actual": "Actual",
6120
6133
  "reference": "Reference",
6134
+ "fromLinkedDesign": "· from the linked design",
6121
6135
  "actualAlt": "{view} (actual)",
6122
6136
  "referenceAlt": "{view} (reference)",
6123
6137
  "replace": "Replace",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "Historial (sin rondas) | Historial ({count} ronda) | Historial ({count} rondas)",
5599
5599
  "fixRequested": "Corrección solicitada"
5600
+ },
5601
+ "design": {
5602
+ "summary": "Ningún marco procede del diseño vinculado. | 1 marco del diseño vinculado. | {count} marcos del diseño vinculado.",
5603
+ "dropped": "No hay otros ocultos. | 1 más no se muestra aquí. | {count} más no se muestran aquí.",
5604
+ "gapDropped": "Ninguno de sus marcos está oculto. | 1 de sus marcos no se muestra aquí. | {count} de sus marcos no se muestran aquí.",
5605
+ "gapLine": "{title}:",
5606
+ "gap": {
5607
+ "partial": "solo se conservó parte de sus marcos; actualiza el documento para volver a intentarlo.",
5608
+ "failed": "no se pudieron descargar sus marcos en la última importación; actualiza el documento para reintentarlo.",
5609
+ "none": "no tiene marcos que mostrar.",
5610
+ "storage_unavailable": "no había almacenamiento de imágenes configurado cuando se importó, así que no se descargó nada.",
5611
+ "not_retained": "no hay imágenes guardadas para él. Puede que su origen no genere marcos, o que se importara antes de que se conservaran; volver a importarlo lo aclara."
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "Real",
5845
5858
  "reference": "Referencia",
5859
+ "fromLinkedDesign": "· del diseño vinculado",
5846
5860
  "actualAlt": "{view} (real)",
5847
5861
  "referenceAlt": "{view} (referencia)",
5848
5862
  "replace": "Reemplazar",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "Historique (aucun tour) | Historique ({count} tour) | Historique ({count} tours)",
5599
5599
  "fixRequested": "Correction demandée"
5600
+ },
5601
+ "design": {
5602
+ "summary": "Aucun cadre ne provient de la maquette liée. | 1 cadre issu de la maquette liée. | {count} cadres issus de la maquette liée.",
5603
+ "dropped": "Aucun autre n'est masqué. | 1 de plus n'est pas affiché ici. | {count} de plus ne sont pas affichés ici.",
5604
+ "gapDropped": "Aucun de ses cadres n'est masqué. | 1 de ses cadres n'est pas affiché ici. | {count} de ses cadres ne sont pas affichés ici.",
5605
+ "gapLine": "{title} :",
5606
+ "gap": {
5607
+ "partial": "seule une partie de ses cadres a été conservée ; actualisez le document pour réessayer.",
5608
+ "failed": "ses cadres n'ont pas pu être téléchargés lors du dernier import ; actualisez le document pour réessayer.",
5609
+ "none": "elle n'a aucun cadre à afficher.",
5610
+ "storage_unavailable": "aucun stockage d'images n'était configuré lors de l'import, donc rien n'a été téléchargé.",
5611
+ "not_retained": "aucune image n'est conservée pour elle. Sa source ne produit peut-être pas de cadres, ou elle a été importée avant leur conservation ; un nouvel import permet de trancher."
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "Réel",
5845
5858
  "reference": "Référence",
5859
+ "fromLinkedDesign": "· issu de la maquette liée",
5846
5860
  "actualAlt": "{view} (réel)",
5847
5861
  "referenceAlt": "{view} (référence)",
5848
5862
  "replace": "Remplacer",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "היסטוריה (אין סבבים) | היסטוריה (סבב אחד) | היסטוריה (שני סבבים) | היסטוריה ({count} סבבים)",
5599
5599
  "fixRequested": "התבקש תיקון"
5600
+ },
5601
+ "design": {
5602
+ "summary": "לא התקבלו מסגרות מהעיצוב המקושר. | מסגרת אחת מהעיצוב המקושר. | שתי מסגרות מהעיצוב המקושר. | {count} מסגרות מהעיצוב המקושר.",
5603
+ "dropped": "אין נוספות שאינן מוצגות. | עוד אחת אינה מוצגת כאן. | עוד שתיים אינן מוצגות כאן. | עוד {count} אינן מוצגות כאן.",
5604
+ "gapDropped": "אף אחת מהמסגרות שלו אינה מוסתרת. | מסגרת אחת שלו אינה מוצגת כאן. | שתי מסגרות שלו אינן מוצגות כאן. | {count} מסגרות שלו אינן מוצגות כאן.",
5605
+ "gapLine": "{title}:",
5606
+ "gap": {
5607
+ "partial": "רק חלק מהמסגרות שלו נשמרו; רענן את המסמך כדי לנסות שוב.",
5608
+ "failed": "לא ניתן היה להוריד את המסגרות שלו בייבוא האחרון; רענן את המסמך כדי לנסות שוב.",
5609
+ "none": "אין בו מסגרות להצגה.",
5610
+ "storage_unavailable": "בעת הייבוא לא הוגדר אחסון תמונות, ולכן לא הורד דבר.",
5611
+ "not_retained": "לא נשמרו עבורו תמונות. ייתכן שהמקור אינו מייצר מסגרות, או שהמסמך יובא לפני שמסגרות נשמרו; ייבוא מחדש יבהיר מה מהשניים."
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "בפועל",
5845
5858
  "reference": "ייחוס",
5859
+ "fromLinkedDesign": "· מהעיצוב המקושר",
5846
5860
  "actualAlt": "{view} (בפועל)",
5847
5861
  "referenceAlt": "{view} (ייחוס)",
5848
5862
  "replace": "החלף",
@@ -6153,6 +6153,19 @@
6153
6153
  "history": {
6154
6154
  "heading": "Cronologia (nessun round) | Cronologia ({count} round) | Cronologia ({count} round)",
6155
6155
  "fixRequested": "Correzione richiesta"
6156
+ },
6157
+ "design": {
6158
+ "summary": "Nessun frame proviene dal design collegato. | 1 frame dal design collegato. | {count} frame dal design collegato.",
6159
+ "dropped": "Non ce ne sono altri nascosti. | 1 altro non è mostrato qui. | Altri {count} non sono mostrati qui.",
6160
+ "gapDropped": "Nessuno dei suoi frame è nascosto. | 1 dei suoi frame non è mostrato qui. | {count} dei suoi frame non sono mostrati qui.",
6161
+ "gapLine": "{title}:",
6162
+ "gap": {
6163
+ "partial": "solo una parte dei suoi frame è stata conservata; aggiorna il documento per riprovare.",
6164
+ "failed": "non è stato possibile scaricare i suoi frame nell'ultimo import; aggiorna il documento per riprovare.",
6165
+ "none": "non ha frame da mostrare.",
6166
+ "storage_unavailable": "quando è stato importato non era configurato alcuno spazio per le immagini, quindi non è stato scaricato nulla.",
6167
+ "not_retained": "non ci sono immagini conservate per esso. La sua origine potrebbe non generare frame, oppure è stato importato prima che i frame venissero conservati; un nuovo import chiarisce quale dei due."
6168
+ }
6156
6169
  }
6157
6170
  },
6158
6171
  "outcome": {
@@ -6399,6 +6412,7 @@
6399
6412
  },
6400
6413
  "actual": "Effettivo",
6401
6414
  "reference": "Riferimento",
6415
+ "fromLinkedDesign": "· dal design collegato",
6402
6416
  "actualAlt": "{view} (effettivo)",
6403
6417
  "referenceAlt": "{view} (riferimento)",
6404
6418
  "replace": "Sostituisci",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "履歴(ラウンドなし) | 履歴({count} ラウンド) | 履歴({count} ラウンド)",
5599
5599
  "fixRequested": "修正をリクエスト済み"
5600
+ },
5601
+ "design": {
5602
+ "summary": "リンクされたデザインからのフレームはありません。 | リンクされたデザインから {count} 件のフレーム。 | リンクされたデザインから {count} 件のフレーム。",
5603
+ "dropped": "非表示のものはありません。 | 他に {count} 件はここに表示されていません。 | 他に {count} 件はここに表示されていません。",
5604
+ "gapDropped": "非表示のフレームはありません。 | このデザインのフレーム {count} 件はここに表示されていません。 | このデザインのフレーム {count} 件はここに表示されていません。",
5605
+ "gapLine": "{title}:",
5606
+ "gap": {
5607
+ "partial": "フレームの一部のみが保持されました。ドキュメントを更新して再試行してください。",
5608
+ "failed": "前回のインポートでフレームをダウンロードできませんでした。ドキュメントを更新して再試行してください。",
5609
+ "none": "表示できるフレームがありません。",
5610
+ "storage_unavailable": "インポート時に画像ストレージが設定されていなかったため、何もダウンロードされていません。",
5611
+ "not_retained": "画像は保持されていません。ソースがフレームを生成しないか、フレームの保持が始まる前にインポートされた可能性があります。再インポートすると判別できます。"
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "実際",
5845
5858
  "reference": "リファレンス",
5859
+ "fromLinkedDesign": "· リンクされたデザインから",
5846
5860
  "actualAlt": "{view}(実際)",
5847
5861
  "referenceAlt": "{view}(リファレンス)",
5848
5862
  "replace": "置き換え",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "Historia ({count} runda) | Historia ({count} rundy) | Historia ({count} rund)",
5599
5599
  "fixRequested": "Poproszono o poprawkę"
5600
+ },
5601
+ "design": {
5602
+ "summary": "1 klatka z połączonego projektu. | {count} klatki z połączonego projektu. | Klatek z połączonego projektu: {count}.",
5603
+ "dropped": "Jeszcze 1 nie jest tu pokazana. | Jeszcze {count} nie są tu pokazane. | Nie pokazano tu jeszcze {count}.",
5604
+ "gapDropped": "1 jej klatka nie jest tu pokazana. | {count} jej klatki nie są tu pokazane. | Nie pokazano tu {count} jej klatek.",
5605
+ "gapLine": "{title}:",
5606
+ "gap": {
5607
+ "partial": "zachowano tylko część jego klatek; odśwież dokument, aby spróbować ponownie.",
5608
+ "failed": "przy ostatnim imporcie nie udało się pobrać jego klatek; odśwież dokument, aby spróbować ponownie.",
5609
+ "none": "nie ma klatek do pokazania.",
5610
+ "storage_unavailable": "podczas importu nie skonfigurowano magazynu obrazów, więc nic nie pobrano.",
5611
+ "not_retained": "nie ma dla niego zapisanych obrazów. Jego źródło może nie generować klatek albo został zaimportowany, zanim klatki były zachowywane; ponowny import to rozstrzygnie."
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "Rzeczywiste",
5845
5858
  "reference": "Wzorzec",
5859
+ "fromLinkedDesign": "· z połączonego projektu",
5846
5860
  "actualAlt": "{view} (rzeczywiste)",
5847
5861
  "referenceAlt": "{view} (wzorzec)",
5848
5862
  "replace": "Zamień",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "Geçmiş (tur yok) | Geçmiş ({count} tur) | Geçmiş ({count} tur)",
5599
5599
  "fixRequested": "Düzeltme istendi"
5600
+ },
5601
+ "design": {
5602
+ "summary": "Bağlantılı tasarımdan hiç çerçeve gelmedi. | Bağlantılı tasarımdan {count} çerçeve. | Bağlantılı tasarımdan {count} çerçeve.",
5603
+ "dropped": "Gizlenen başka çerçeve yok. | {count} çerçeve daha burada gösterilmiyor. | {count} çerçeve daha burada gösterilmiyor.",
5604
+ "gapDropped": "Gizlenen çerçevesi yok. | Bu tasarımın {count} çerçevesi burada gösterilmiyor. | Bu tasarımın {count} çerçevesi burada gösterilmiyor.",
5605
+ "gapLine": "{title}:",
5606
+ "gap": {
5607
+ "partial": "çerçevelerinin yalnızca bir kısmı saklandı; yeniden denemek için belgeyi yenileyin.",
5608
+ "failed": "son içe aktarmada çerçeveleri indirilemedi; yeniden denemek için belgeyi yenileyin.",
5609
+ "none": "gösterilecek çerçevesi yok.",
5610
+ "storage_unavailable": "içe aktarıldığında görsel depolama yapılandırılmamıştı, bu yüzden hiçbir şey indirilmedi.",
5611
+ "not_retained": "bunun için saklanan görsel yok. Kaynağı çerçeve üretmiyor olabilir ya da çerçeveler saklanmaya başlamadan önce içe aktarılmış olabilir; yeniden içe aktarmak hangisi olduğunu gösterir."
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "Gerçek",
5845
5858
  "reference": "Referans",
5859
+ "fromLinkedDesign": "· bağlantılı tasarımdan",
5846
5860
  "actualAlt": "{view} (gerçek)",
5847
5861
  "referenceAlt": "{view} (referans)",
5848
5862
  "replace": "Değiştir",
@@ -5597,6 +5597,19 @@
5597
5597
  "history": {
5598
5598
  "heading": "Історія ({count} раунд) | Історія ({count} раунди) | Історія ({count} раундів)",
5599
5599
  "fixRequested": "Запитано виправлення"
5600
+ },
5601
+ "design": {
5602
+ "summary": "{count} кадр із пов'язаного дизайну. | {count} кадри із пов'язаного дизайну. | Кадрів із пов'язаного дизайну: {count}.",
5603
+ "dropped": "Ще {count} кадр не показано тут. | Ще {count} кадри не показано тут. | Ще {count} кадрів не показано тут.",
5604
+ "gapDropped": "{count} його кадр не показано тут. | {count} його кадри не показано тут. | {count} його кадрів не показано тут.",
5605
+ "gapLine": "{title}:",
5606
+ "gap": {
5607
+ "partial": "збережено лише частину його кадрів; оновіть документ, щоб спробувати ще раз.",
5608
+ "failed": "під час останнього імпорту не вдалося завантажити його кадри; оновіть документ, щоб повторити.",
5609
+ "none": "у ньому немає кадрів для показу.",
5610
+ "storage_unavailable": "під час імпорту сховище зображень не було налаштоване, тому нічого не завантажено.",
5611
+ "not_retained": "для нього не збережено зображень. Його джерело може не створювати кадрів, або він імпортований до того, як кадри почали зберігати; повторний імпорт це покаже."
5612
+ }
5600
5613
  }
5601
5614
  },
5602
5615
  "outcome": {
@@ -5843,6 +5856,7 @@
5843
5856
  },
5844
5857
  "actual": "Фактичне",
5845
5858
  "reference": "Еталон",
5859
+ "fromLinkedDesign": "· із пов'язаного дизайну",
5846
5860
  "actualAlt": "{view} (фактичне)",
5847
5861
  "referenceAlt": "{view} (еталон)",
5848
5862
  "replace": "Замінити",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.251.0",
3
+ "version": "0.252.0",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,7 +40,7 @@
40
40
  "valibot": "^1.4.2",
41
41
  "vue": "3.5.40",
42
42
  "wretch": "^3.0.9",
43
- "@cat-factory/contracts": "0.273.0"
43
+ "@cat-factory/contracts": "0.274.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",