@cat-factory/app 0.200.2 → 0.200.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.
@@ -1,15 +1,21 @@
1
1
  <script setup lang="ts">
2
2
  // Repo sources of foundational-service definitions (backend/docs/adr/0031-foundational-services.md).
3
- // Two link shapes, and the choice is the whole reason this form has a mode switch:
3
+ // Three link shapes, and the choice is the whole reason this form has a mode switch:
4
4
  // - `directory` — every immediate subdirectory of the linked path is a service, identified by
5
5
  // its `service.md`, with its contract files beside it. The "we keep our specs in a repo" case.
6
+ // - `folder` — the whole of one folder (optionally its subfolders too) is the contract set of
7
+ // the ONE service the link names. The "here is our spec directory" case.
6
8
  // - `files` — an explicit list of contract files, all describing the ONE service the link
7
- // names. The "just point at my openapi.yaml" case, where there is no directory convention to
8
- // adopt which is why the link itself must supply the identity, and the form demands it.
9
+ // names. The "just point at my openapi.yaml" case.
10
+ // `folder` and `files` both name their service on the link (there is no `service.md` convention to
11
+ // read identity from, which is why the form demands it), and differ in WHEN the file set is
12
+ // decided: `files` pins the paths, `folder` rediscovers them on every sync — so a spec directory
13
+ // that grows wants the folder shape.
9
14
  // Mirrors the skill library's sources UI: with the GitHub App connected the user searches a repo
10
15
  // and browses to a path; otherwise the manual owner/name fields are the fallback.
11
16
  import { computed, reactive, ref, watch } from 'vue'
12
17
  import type {
18
+ FolderScanCoverage,
13
19
  FoundationalServiceOwnerKind,
14
20
  FoundationalServiceSourceMode,
15
21
  GitHubAvailableRepo,
@@ -63,6 +69,7 @@ const mode = ref<FoundationalServiceSourceMode>('directory')
63
69
  const repoId = ref<number | undefined>(undefined)
64
70
  const repo = ref<GitHubAvailableRepo | undefined>(undefined)
65
71
  const dirPath = ref<string | undefined>(undefined)
72
+ const recursive = ref(false)
66
73
  const filePaths = ref<string[]>([])
67
74
  const gitRef = ref('')
68
75
  const manual = reactive({ repoOwner: '', repoName: '', dirPath: '', filePaths: '' })
@@ -77,9 +84,34 @@ watch(repoId, () => {
77
84
 
78
85
  const modeItems = computed(() => [
79
86
  { value: 'directory' as const, label: t('foundational.sources.mode.directory') },
87
+ { value: 'folder' as const, label: t('foundational.sources.mode.folder') },
80
88
  { value: 'files' as const, label: t('foundational.sources.mode.files') },
81
89
  ])
82
90
 
91
+ // An exhaustive Record of STATIC literal keys, not a key assembled from `mode` — the typed-key
92
+ // check cannot see a runtime-built key, and this fails to compile the day a fourth mode lands.
93
+ const modeHints = computed<Record<FoundationalServiceSourceMode, string>>(() => ({
94
+ directory: t('foundational.sources.mode.directoryHint'),
95
+ folder: t('foundational.sources.mode.folderHint'),
96
+ files: t('foundational.sources.mode.filesHint'),
97
+ }))
98
+
99
+ /**
100
+ * What a folder scan's coverage adds to the sync toast, or null when there is nothing to say.
101
+ *
102
+ * An exhaustive Record over the union rather than an `if` per state: `complete` having no note
103
+ * is then a written-down decision, and a fourth coverage value fails to compile until someone
104
+ * decides whether it needs one.
105
+ */
106
+ const folderScanNotes = computed<Record<FolderScanCoverage, string | null>>(() => ({
107
+ complete: null,
108
+ truncated: t('foundational.toast.syncTruncated'),
109
+ missing: t('foundational.toast.syncFolderMissing'),
110
+ }))
111
+
112
+ /** Both single-service modes name their service on the link; only `files` enumerates paths. */
113
+ const namesService = computed(() => mode.value === 'folder' || mode.value === 'files')
114
+
83
115
  const ownerName = computed<{ owner: string; name: string } | null>(() => {
84
116
  if (githubReady.value) {
85
117
  return repo.value ? { owner: repo.value.owner, name: repo.value.name } : null
@@ -99,18 +131,20 @@ const linkedFiles = computed(() =>
99
131
  .filter(Boolean),
100
132
  )
101
133
 
102
- // A `files` source names the service its files describe — the backend refuses the link
103
- // otherwise, so the button is disabled rather than letting the user discover it as a 422.
134
+ // A `folder`/`files` source names the service its contracts describe — the backend refuses the
135
+ // link otherwise, so the button is disabled rather than letting the user discover it as a 422.
104
136
  const valid = computed(() => {
105
137
  if (!ownerName.value) return false
106
- if (mode.value !== 'files') return true
107
- return Boolean(named.serviceId.trim() && named.serviceName.trim() && linkedFiles.value.length)
138
+ if (!namesService.value) return true
139
+ if (!named.serviceId.trim() || !named.serviceName.trim()) return false
140
+ return mode.value !== 'files' || linkedFiles.value.length > 0
108
141
  })
109
142
 
110
143
  function resetDraft() {
111
144
  repoId.value = undefined
112
145
  repo.value = undefined
113
146
  dirPath.value = undefined
147
+ recursive.value = false
114
148
  filePaths.value = []
115
149
  gitRef.value = ''
116
150
  Object.assign(manual, { repoOwner: '', repoName: '', dirPath: '', filePaths: '' })
@@ -134,9 +168,10 @@ async function link() {
134
168
  gitRef: gitRef.value.trim() || undefined,
135
169
  mode: mode.value,
136
170
  dirPath: (githubReady.value ? dirPath.value : manual.dirPath.trim()) || undefined,
137
- ...(mode.value === 'files'
171
+ ...(mode.value === 'folder' ? { recursive: recursive.value } : {}),
172
+ ...(mode.value === 'files' ? { filePaths: linkedFiles.value } : {}),
173
+ ...(namesService.value
138
174
  ? {
139
- filePaths: linkedFiles.value,
140
175
  serviceId: named.serviceId.trim(),
141
176
  serviceName: named.serviceName.trim(),
142
177
  serviceSummary: named.serviceSummary.trim() || undefined,
@@ -157,13 +192,25 @@ async function sync(id: string) {
157
192
  await withRow(`sync:${id}`, async () => {
158
193
  try {
159
194
  const result = await catalog.syncSource(id)
195
+ // A folder link that quietly produced fewer contracts than its author expected has no
196
+ // other explanation available to them, so the losses ride the same toast as the counts.
197
+ const notes: string[] = []
198
+ if (result.skippedFiles > 0)
199
+ notes.push(
200
+ t('foundational.toast.syncSkipped', { count: result.skippedFiles }, result.skippedFiles),
201
+ )
202
+ const coverage = result.folderScan ? folderScanNotes.value[result.folderScan] : null
203
+ if (coverage) notes.push(coverage)
160
204
  toast.add({
161
205
  title: t('foundational.toast.synced', {
162
206
  updated: result.upserted,
163
207
  removed: result.tombstoned,
164
208
  }),
209
+ ...(notes.length ? { description: notes.join(' ') } : {}),
165
210
  icon: 'i-lucide-refresh-cw',
166
- color: 'info',
211
+ // A folder we could not fully see, or could not find at all, is the one sync outcome a
212
+ // human has to act on — the counts alone would read as an ordinary quiet success.
213
+ color: coverage ? 'warning' : 'info',
167
214
  })
168
215
  } catch (e) {
169
216
  notifyError(t('foundational.toast.syncFailed'), e)
@@ -223,14 +270,26 @@ async function unlink(id: string) {
223
270
  {{ s.repoOwner }}/{{ s.repoName }}<span class="text-slate-500">/{{ s.dirPath }}</span>
224
271
  </span>
225
272
  <p class="text-xs text-slate-500">
226
- {{
227
- s.mode === 'files'
228
- ? t('foundational.sources.metaFiles', {
229
- service: s.serviceName ?? s.serviceId ?? '',
230
- count: s.filePaths.length,
231
- })
232
- : t('foundational.sources.metaDirectory')
233
- }}
273
+ <template v-if="s.mode === 'files'">
274
+ {{
275
+ t('foundational.sources.metaFiles', {
276
+ service: s.serviceName ?? s.serviceId ?? '',
277
+ count: s.filePaths.length,
278
+ })
279
+ }}
280
+ </template>
281
+ <template v-else-if="s.mode === 'folder'">
282
+ {{
283
+ s.recursive
284
+ ? t('foundational.sources.metaFolderRecursive', {
285
+ service: s.serviceName ?? s.serviceId ?? '',
286
+ })
287
+ : t('foundational.sources.metaFolder', {
288
+ service: s.serviceName ?? s.serviceId ?? '',
289
+ })
290
+ }}
291
+ </template>
292
+ <template v-else>{{ t('foundational.sources.metaDirectory') }}</template>
234
293
  </p>
235
294
  <p class="text-xs text-slate-500">
236
295
  {{
@@ -295,13 +354,7 @@ async function unlink(id: string) {
295
354
  <p class="mb-2 text-sm font-medium">{{ t('foundational.sources.linkTitle') }}</p>
296
355
  <div class="flex flex-col gap-2">
297
356
  <URadioGroup v-model="mode" :items="modeItems" orientation="horizontal" size="sm" />
298
- <p class="text-xs text-slate-500">
299
- {{
300
- mode === 'files'
301
- ? t('foundational.sources.mode.filesHint')
302
- : t('foundational.sources.mode.directoryHint')
303
- }}
304
- </p>
357
+ <p class="text-xs text-slate-500">{{ modeHints[mode] }}</p>
305
358
 
306
359
  <!-- Connected: search a repo, then browse to the folder / pick the contract files -->
307
360
  <template v-if="githubReady">
@@ -359,9 +412,20 @@ async function unlink(id: string) {
359
412
  />
360
413
  </template>
361
414
 
362
- <!-- `files` mode carries no directory convention to read identity from, so the link
363
- supplies it. -->
364
- <template v-if="mode === 'files'">
415
+ <!-- Subfolders are opt-in: a folder link pointed near a repo root would otherwise walk
416
+ far more of the tree than its author meant to offer. -->
417
+ <USwitch
418
+ v-if="mode === 'folder'"
419
+ v-model="recursive"
420
+ size="sm"
421
+ :label="t('foundational.sources.recursive')"
422
+ :description="t('foundational.sources.recursiveHint')"
423
+ data-testid="foundational-source-recursive"
424
+ />
425
+
426
+ <!-- Neither single-service mode has a directory convention to read identity from, so the
427
+ link supplies it. -->
428
+ <template v-if="namesService">
365
429
  <div class="flex gap-2">
366
430
  <UInput
367
431
  v-model="named.serviceId"
@@ -16,6 +16,7 @@ export type {
16
16
  ApiContractFormat,
17
17
  ApiContractSummary,
18
18
  CreateFoundationalServiceInput,
19
+ FolderScanCoverage,
19
20
  FoundationalService,
20
21
  FoundationalServiceOwnerKind,
21
22
  FoundationalServiceSelection,
@@ -4289,12 +4289,16 @@
4289
4289
  "metaSynced": "synchronisiert {date} · Ref {ref}",
4290
4290
  "metaNever": "nie synchronisiert · Ref {ref}",
4291
4291
  "metaFiles": "{service} · {count} Vertragsdateien",
4292
+ "metaFolder": "{service} · Verträge aus diesem Ordner",
4293
+ "metaFolderRecursive": "{service} · Verträge aus diesem Ordner und seinen Unterordnern",
4292
4294
  "metaDirectory": "ein Dienst je Unterverzeichnis",
4293
4295
  "changes": "Änderungen",
4294
4296
  "check": "Auf Änderungen prüfen",
4295
4297
  "sync": "Erneut synchronisieren",
4296
4298
  "unlink": "Verknüpfung lösen",
4297
4299
  "selectedDir": "Verzeichnis:",
4300
+ "recursive": "Unterordner einbeziehen",
4301
+ "recursiveHint": "Auch Vertragsdateien unterhalb des verknüpften Ordners lesen.",
4298
4302
  "selectedFiles": "Ausgewählte Dateien: {count}",
4299
4303
  "wholeRepo": "Gesamtes Repository (Wurzel).",
4300
4304
  "ownerPlaceholder": "Owner",
@@ -4308,8 +4312,10 @@
4308
4312
  "serviceSummaryPlaceholder": "einzeilige Zusammenfassung (optional)",
4309
4313
  "mode": {
4310
4314
  "directory": "Ein Ordner mit Diensten",
4315
+ "folder": "Ganzer Ordner mit Verträgen, ein Dienst",
4311
4316
  "files": "Bestimmte Dateien, ein Dienst",
4312
4317
  "directoryHint": "Jedes Unterverzeichnis des verknüpften Pfads ist ein Dienst, beschrieben durch eine service.md mit den Vertragsdateien daneben.",
4318
+ "folderHint": "Jede Vertragsdatei im verknüpften Ordner beschreibt den einen Dienst, den du hier benennst; später hinzugefügte Dateien werden bei der nächsten Synchronisierung übernommen.",
4313
4319
  "filesHint": "Die aufgeführten Dateien beschreiben alle den einen Dienst, den du hier benennst. Nutze das, wenn es keine Ordnerkonvention gibt."
4314
4320
  }
4315
4321
  },
@@ -4338,6 +4344,9 @@
4338
4344
  "sourceLinked": "Repo verknüpft & synchronisiert",
4339
4345
  "linkSourceFailed": "Repo konnte nicht verknüpft werden",
4340
4346
  "synced": "Synchronisiert: {updated} aktualisiert, {removed} entfernt",
4347
+ "syncSkipped": "{count} Datei sah wie ein Vertrag aus, war aber nicht verwendbar. | {count} Dateien sahen wie Verträge aus, waren aber nicht verwendbar.",
4348
+ "syncTruncated": "Der Ordner enthält mehr, als eine Synchronisierung aufnehmen kann; es wurde nur ein Teil gelesen.",
4349
+ "syncFolderMissing": "Der verknüpfte Ordner ist nicht im Repository vorhanden. Prüfe den Pfad oder verknüpfe die Quelle neu, falls er verschoben wurde.",
4341
4350
  "syncFailed": "Repo konnte nicht synchronisiert werden",
4342
4351
  "changesAvailable": "Stromaufwärts liegen Änderungen vor",
4343
4352
  "upToDate": "Bereits aktuell",
@@ -5497,12 +5497,16 @@
5497
5497
  "metaSynced": "synced {date} · ref {ref}",
5498
5498
  "metaNever": "never synced · ref {ref}",
5499
5499
  "metaFiles": "{service} · {count} contract files",
5500
+ "metaFolder": "{service} · contracts from this folder",
5501
+ "metaFolderRecursive": "{service} · contracts from this folder and its subfolders",
5500
5502
  "metaDirectory": "one service per subdirectory",
5501
5503
  "changes": "Changes",
5502
5504
  "check": "Check for changes",
5503
5505
  "sync": "Resync",
5504
5506
  "unlink": "Unlink",
5505
5507
  "selectedDir": "Directory:",
5508
+ "recursive": "Include subfolders",
5509
+ "recursiveHint": "Also read contract files nested below the linked folder.",
5506
5510
  "selectedFiles": "Files picked: {count}",
5507
5511
  "wholeRepo": "Whole repository (root).",
5508
5512
  "ownerPlaceholder": "owner",
@@ -5516,8 +5520,10 @@
5516
5520
  "serviceSummaryPlaceholder": "one-line summary (optional)",
5517
5521
  "mode": {
5518
5522
  "directory": "A folder of services",
5523
+ "folder": "Whole folder of contracts, one service",
5519
5524
  "files": "Specific files, one service",
5520
5525
  "directoryHint": "Every subdirectory of the linked path is one service, described by a service.md with its contract files beside it.",
5526
+ "folderHint": "Every contract file in the linked folder describes the one service you name here, and files added later are picked up on the next sync.",
5521
5527
  "filesHint": "The listed files all describe the one service you name here. Use this when there is no folder convention to adopt."
5522
5528
  }
5523
5529
  },
@@ -5546,6 +5552,9 @@
5546
5552
  "sourceLinked": "Repo linked & synced",
5547
5553
  "linkSourceFailed": "Could not link the repo",
5548
5554
  "synced": "Synced: {updated} updated, {removed} removed",
5555
+ "syncSkipped": "{count} file looked like a contract but could not be used. | {count} files looked like contracts but could not be used.",
5556
+ "syncTruncated": "The folder holds more than one sync can take, so only part of it was read.",
5557
+ "syncFolderMissing": "The linked folder is not in the repository. Check the path, or relink the source if it moved.",
5549
5558
  "syncFailed": "Could not sync the repo",
5550
5559
  "changesAvailable": "Changes available upstream",
5551
5560
  "upToDate": "Already up to date",
@@ -5248,12 +5248,16 @@
5248
5248
  "metaSynced": "sincronizado {date} · ref {ref}",
5249
5249
  "metaNever": "nunca sincronizado · ref {ref}",
5250
5250
  "metaFiles": "{service} · {count} archivos de contrato",
5251
+ "metaFolder": "{service} · contratos de esta carpeta",
5252
+ "metaFolderRecursive": "{service} · contratos de esta carpeta y sus subcarpetas",
5251
5253
  "metaDirectory": "un servicio por subdirectorio",
5252
5254
  "changes": "Cambios",
5253
5255
  "check": "Buscar cambios",
5254
5256
  "sync": "Volver a sincronizar",
5255
5257
  "unlink": "Desvincular",
5256
5258
  "selectedDir": "Directorio:",
5259
+ "recursive": "Incluir subcarpetas",
5260
+ "recursiveHint": "Leer también los archivos de contrato anidados bajo la carpeta vinculada.",
5257
5261
  "selectedFiles": "Archivos elegidos: {count}",
5258
5262
  "wholeRepo": "Todo el repositorio (raíz).",
5259
5263
  "ownerPlaceholder": "propietario",
@@ -5267,8 +5271,10 @@
5267
5271
  "serviceSummaryPlaceholder": "resumen de una línea (opcional)",
5268
5272
  "mode": {
5269
5273
  "directory": "Una carpeta de servicios",
5274
+ "folder": "Carpeta entera de contratos, un servicio",
5270
5275
  "files": "Archivos concretos, un servicio",
5271
5276
  "directoryHint": "Cada subdirectorio de la ruta vinculada es un servicio, descrito por un service.md con sus archivos de contrato al lado.",
5277
+ "folderHint": "Cada archivo de contrato de la carpeta vinculada describe el único servicio que nombras aquí, y los archivos que se añadan después se recogen en la siguiente sincronización.",
5272
5278
  "filesHint": "Los archivos listados describen el único servicio que nombras aquí. Úsalo cuando no haya una convención de carpetas que adoptar."
5273
5279
  }
5274
5280
  },
@@ -5297,6 +5303,9 @@
5297
5303
  "sourceLinked": "Repositorio vinculado y sincronizado",
5298
5304
  "linkSourceFailed": "No se pudo vincular el repositorio",
5299
5305
  "synced": "Sincronizado: {updated} actualizados, {removed} eliminados",
5306
+ "syncSkipped": "{count} archivo parecía un contrato pero no se pudo usar. | {count} archivos parecían contratos pero no se pudieron usar.",
5307
+ "syncTruncated": "La carpeta contiene más de lo que cabe en una sincronización, así que solo se leyó una parte.",
5308
+ "syncFolderMissing": "La carpeta enlazada no está en el repositorio. Revisa la ruta o vuelve a enlazar la fuente si se movió.",
5300
5309
  "syncFailed": "No se pudo sincronizar el repositorio",
5301
5310
  "changesAvailable": "Hay cambios en el origen",
5302
5311
  "upToDate": "Ya está actualizado",
@@ -5248,12 +5248,16 @@
5248
5248
  "metaSynced": "synchronisé le {date} · réf {ref}",
5249
5249
  "metaNever": "jamais synchronisé · réf {ref}",
5250
5250
  "metaFiles": "{service} · {count} fichiers de contrat",
5251
+ "metaFolder": "{service} · contrats de ce dossier",
5252
+ "metaFolderRecursive": "{service} · contrats de ce dossier et de ses sous-dossiers",
5251
5253
  "metaDirectory": "un service par sous-répertoire",
5252
5254
  "changes": "Changements",
5253
5255
  "check": "Vérifier les changements",
5254
5256
  "sync": "Resynchroniser",
5255
5257
  "unlink": "Délier",
5256
5258
  "selectedDir": "Répertoire :",
5259
+ "recursive": "Inclure les sous-dossiers",
5260
+ "recursiveHint": "Lire aussi les fichiers de contrat imbriqués sous le dossier lié.",
5257
5261
  "selectedFiles": "Fichiers choisis : {count}",
5258
5262
  "wholeRepo": "Dépôt entier (racine).",
5259
5263
  "ownerPlaceholder": "propriétaire",
@@ -5267,8 +5271,10 @@
5267
5271
  "serviceSummaryPlaceholder": "résumé d'une ligne (facultatif)",
5268
5272
  "mode": {
5269
5273
  "directory": "Un dossier de services",
5274
+ "folder": "Dossier entier de contrats, un seul service",
5270
5275
  "files": "Des fichiers précis, un seul service",
5271
5276
  "directoryHint": "Chaque sous-répertoire du chemin lié est un service, décrit par un service.md avec ses fichiers de contrat à côté.",
5277
+ "folderHint": "Chaque fichier de contrat du dossier lié décrit l'unique service que vous nommez ici, et les fichiers ajoutés ensuite sont repris à la synchronisation suivante.",
5272
5278
  "filesHint": "Les fichiers listés décrivent tous l'unique service que vous nommez ici. À utiliser quand il n'y a pas de convention de dossiers à adopter."
5273
5279
  }
5274
5280
  },
@@ -5297,6 +5303,9 @@
5297
5303
  "sourceLinked": "Dépôt lié et synchronisé",
5298
5304
  "linkSourceFailed": "Impossible de lier le dépôt",
5299
5305
  "synced": "Synchronisé : {updated} mis à jour, {removed} retirés",
5306
+ "syncSkipped": "{count} fichier ressemblait à un contrat mais n'a pas pu être utilisé. | {count} fichiers ressemblaient à des contrats mais n'ont pas pu être utilisés.",
5307
+ "syncTruncated": "Le dossier contient plus que ce qu'une synchronisation peut prendre : seule une partie a été lue.",
5308
+ "syncFolderMissing": "Le dossier lié n'existe pas dans le dépôt. Vérifiez le chemin, ou reliez la source si elle a été déplacée.",
5300
5309
  "syncFailed": "Impossible de synchroniser le dépôt",
5301
5310
  "changesAvailable": "Des changements sont disponibles en amont",
5302
5311
  "upToDate": "Déjà à jour",
@@ -5259,12 +5259,16 @@
5259
5259
  "metaSynced": "סונכרן {date} · ref {ref}",
5260
5260
  "metaNever": "מעולם לא סונכרן · ref {ref}",
5261
5261
  "metaFiles": "{service} · {count} קבצי חוזה",
5262
+ "metaFolder": "{service} · חוזים מתיקייה זו",
5263
+ "metaFolderRecursive": "{service} · חוזים מתיקייה זו ומתיקיות המשנה שלה",
5262
5264
  "metaDirectory": "שירות אחד לכל תיקיית משנה",
5263
5265
  "changes": "שינויים",
5264
5266
  "check": "בדיקת שינויים",
5265
5267
  "sync": "סנכרון מחדש",
5266
5268
  "unlink": "ניתוק",
5267
5269
  "selectedDir": "תיקייה:",
5270
+ "recursive": "לכלול תיקיות משנה",
5271
+ "recursiveHint": "לקרוא גם קבצי חוזה המקוננים מתחת לתיקייה המקושרת.",
5268
5272
  "selectedFiles": "קבצים שנבחרו: {count}",
5269
5273
  "wholeRepo": "כל המאגר (השורש).",
5270
5274
  "ownerPlaceholder": "בעלים",
@@ -5278,8 +5282,10 @@
5278
5282
  "serviceSummaryPlaceholder": "תקציר בשורה אחת (רשות)",
5279
5283
  "mode": {
5280
5284
  "directory": "תיקייה של שירותים",
5285
+ "folder": "תיקייה שלמה של חוזים, שירות אחד",
5281
5286
  "files": "קבצים מסוימים, שירות אחד",
5282
5287
  "directoryHint": "כל תיקיית משנה בנתיב המקושר היא שירות, המתואר בקובץ service.md עם קבצי החוזה לצידו.",
5288
+ "folderHint": "כל קובץ חוזה בתיקייה המקושרת מתאר את השירות היחיד שאתם מציינים כאן, וקבצים שיתווספו בהמשך ייקלטו בסנכרון הבא.",
5283
5289
  "filesHint": "כל הקבצים ברשימה מתארים את השירות היחיד שאתם מציינים כאן. השתמשו בזה כשאין מוסכמת תיקיות לאמץ."
5284
5290
  }
5285
5291
  },
@@ -5308,6 +5314,9 @@
5308
5314
  "sourceLinked": "המאגר קושר וסונכרן",
5309
5315
  "linkSourceFailed": "לא ניתן היה לקשר את המאגר",
5310
5316
  "synced": "סונכרן: {updated} עודכנו, {removed} הוסרו",
5317
+ "syncSkipped": "קובץ אחד נראה כמו חוזה אך לא ניתן היה להשתמש בו. | {count} קבצים נראו כמו חוזים אך לא ניתן היה להשתמש בהם.",
5318
+ "syncTruncated": "התיקייה מכילה יותר ממה שסנכרון אחד יכול לקלוט, ולכן נקרא רק חלק ממנה.",
5319
+ "syncFolderMissing": "התיקייה המקושרת אינה נמצאת במאגר. בדקו את הנתיב, או קשרו מחדש את המקור אם הוא הועבר.",
5311
5320
  "syncFailed": "לא ניתן היה לסנכרן את המאגר",
5312
5321
  "changesAvailable": "יש שינויים במקור",
5313
5322
  "upToDate": "כבר מעודכן",
@@ -4289,12 +4289,16 @@
4289
4289
  "metaSynced": "sincronizzato {date} · ref {ref}",
4290
4290
  "metaNever": "mai sincronizzato · ref {ref}",
4291
4291
  "metaFiles": "{service} · {count} file di contratto",
4292
+ "metaFolder": "{service} · contratti da questa cartella",
4293
+ "metaFolderRecursive": "{service} · contratti da questa cartella e dalle sue sottocartelle",
4292
4294
  "metaDirectory": "un servizio per sottocartella",
4293
4295
  "changes": "Modifiche",
4294
4296
  "check": "Cerca modifiche",
4295
4297
  "sync": "Risincronizza",
4296
4298
  "unlink": "Scollega",
4297
4299
  "selectedDir": "Cartella:",
4300
+ "recursive": "Includi le sottocartelle",
4301
+ "recursiveHint": "Leggi anche i file di contratto annidati sotto la cartella collegata.",
4298
4302
  "selectedFiles": "File scelti: {count}",
4299
4303
  "wholeRepo": "Intero repository (radice).",
4300
4304
  "ownerPlaceholder": "proprietario",
@@ -4308,8 +4312,10 @@
4308
4312
  "serviceSummaryPlaceholder": "riepilogo di una riga (facoltativo)",
4309
4313
  "mode": {
4310
4314
  "directory": "Una cartella di servizi",
4315
+ "folder": "Intera cartella di contratti, un solo servizio",
4311
4316
  "files": "File specifici, un solo servizio",
4312
4317
  "directoryHint": "Ogni sottocartella del percorso collegato è un servizio, descritto da un service.md con accanto i suoi file di contratto.",
4318
+ "folderHint": "Ogni file di contratto nella cartella collegata descrive l'unico servizio che indichi qui, e i file aggiunti in seguito vengono raccolti alla sincronizzazione successiva.",
4313
4319
  "filesHint": "I file elencati descrivono tutti l'unico servizio che indichi qui. Usalo quando non c'è una convenzione di cartelle da adottare."
4314
4320
  }
4315
4321
  },
@@ -4338,6 +4344,9 @@
4338
4344
  "sourceLinked": "Repository collegato e sincronizzato",
4339
4345
  "linkSourceFailed": "Impossibile collegare il repository",
4340
4346
  "synced": "Sincronizzato: {updated} aggiornati, {removed} rimossi",
4347
+ "syncSkipped": "{count} file sembrava un contratto ma non è stato utilizzabile. | {count} file sembravano contratti ma non sono stati utilizzabili.",
4348
+ "syncTruncated": "La cartella contiene più di quanto una sincronizzazione possa prendere, quindi ne è stata letta solo una parte.",
4349
+ "syncFolderMissing": "La cartella collegata non è presente nel repository. Controlla il percorso oppure ricollega la sorgente se è stata spostata.",
4341
4350
  "syncFailed": "Impossibile sincronizzare il repository",
4342
4351
  "changesAvailable": "Sono disponibili modifiche a monte",
4343
4352
  "upToDate": "Già aggiornato",
@@ -5260,12 +5260,16 @@
5260
5260
  "metaSynced": "同期日時 {date} · ref {ref}",
5261
5261
  "metaNever": "未同期 · ref {ref}",
5262
5262
  "metaFiles": "{service} · コントラクトファイル {count} 件",
5263
+ "metaFolder": "{service} · このフォルダのコントラクト",
5264
+ "metaFolderRecursive": "{service} · このフォルダとサブフォルダのコントラクト",
5263
5265
  "metaDirectory": "サブディレクトリごとに 1 サービス",
5264
5266
  "changes": "変更あり",
5265
5267
  "check": "変更を確認",
5266
5268
  "sync": "再同期",
5267
5269
  "unlink": "連携解除",
5268
5270
  "selectedDir": "ディレクトリ:",
5271
+ "recursive": "サブフォルダを含める",
5272
+ "recursiveHint": "連携したフォルダの下にネストされたコントラクトファイルも読み込みます。",
5269
5273
  "selectedFiles": "選択したファイル: {count}",
5270
5274
  "wholeRepo": "リポジトリ全体(ルート)。",
5271
5275
  "ownerPlaceholder": "オーナー",
@@ -5279,8 +5283,10 @@
5279
5283
  "serviceSummaryPlaceholder": "1 行の要約(任意)",
5280
5284
  "mode": {
5281
5285
  "directory": "サービスをまとめたフォルダ",
5286
+ "folder": "フォルダ全体のコントラクト、1 サービス",
5282
5287
  "files": "特定のファイル、1 サービス",
5283
5288
  "directoryHint": "連携したパスの各サブディレクトリが 1 つのサービスで、service.md で説明され、その横にコントラクトファイルが置かれます。",
5289
+ "folderHint": "連携したフォルダ内のすべてのコントラクトファイルが、ここで指定する 1 つのサービスを説明します。後から追加されたファイルは次回の同期で取り込まれます。",
5284
5290
  "filesHint": "列挙したファイルはすべて、ここで指定する 1 つのサービスを説明します。従うべきフォルダ規約がない場合に使います。"
5285
5291
  }
5286
5292
  },
@@ -5309,6 +5315,9 @@
5309
5315
  "sourceLinked": "リポジトリを連携して同期しました",
5310
5316
  "linkSourceFailed": "リポジトリを連携できませんでした",
5311
5317
  "synced": "同期完了: {updated} 件更新、{removed} 件削除",
5318
+ "syncSkipped": "コントラクトらしき {count} 件のファイルを利用できませんでした。 | コントラクトらしき {count} 件のファイルを利用できませんでした。",
5319
+ "syncTruncated": "フォルダの内容が 1 回の同期で扱える量を超えているため、一部のみ読み込みました。",
5320
+ "syncFolderMissing": "連携したフォルダがリポジトリに存在しません。パスを確認するか、移動した場合はソースを連携し直してください。",
5312
5321
  "syncFailed": "リポジトリを同期できませんでした",
5313
5322
  "changesAvailable": "上流に変更があります",
5314
5323
  "upToDate": "すでに最新です",
@@ -5248,12 +5248,16 @@
5248
5248
  "metaSynced": "zsynchronizowano {date} · ref {ref}",
5249
5249
  "metaNever": "nigdy nie synchronizowano · ref {ref}",
5250
5250
  "metaFiles": "{service} · {count} plików kontraktów",
5251
+ "metaFolder": "{service} · kontrakty z tego folderu",
5252
+ "metaFolderRecursive": "{service} · kontrakty z tego folderu i jego podfolderów",
5251
5253
  "metaDirectory": "jedna usługa na podkatalog",
5252
5254
  "changes": "Zmiany",
5253
5255
  "check": "Sprawdź zmiany",
5254
5256
  "sync": "Zsynchronizuj ponownie",
5255
5257
  "unlink": "Odłącz",
5256
5258
  "selectedDir": "Katalog:",
5259
+ "recursive": "Uwzględnij podfoldery",
5260
+ "recursiveHint": "Czytaj także pliki kontraktów zagnieżdżone poniżej powiązanego folderu.",
5257
5261
  "selectedFiles": "Wybrane pliki: {count}",
5258
5262
  "wholeRepo": "Całe repozytorium (katalog główny).",
5259
5263
  "ownerPlaceholder": "właściciel",
@@ -5267,8 +5271,10 @@
5267
5271
  "serviceSummaryPlaceholder": "jednozdaniowe streszczenie (opcjonalne)",
5268
5272
  "mode": {
5269
5273
  "directory": "Katalog z usługami",
5274
+ "folder": "Cały folder kontraktów, jedna usługa",
5270
5275
  "files": "Wybrane pliki, jedna usługa",
5271
5276
  "directoryHint": "Każdy podkatalog powiązanej ścieżki to jedna usługa, opisana plikiem service.md, obok którego leżą jej pliki kontraktów.",
5277
+ "folderHint": "Każdy plik kontraktu w powiązanym folderze opisuje tę jedną usługę, którą tu nazywasz, a pliki dodane później zostaną pobrane przy następnej synchronizacji.",
5272
5278
  "filesHint": "Wymienione pliki opisują tę jedną usługę, którą tu nazywasz. Użyj tego, gdy nie ma konwencji katalogów do przyjęcia."
5273
5279
  }
5274
5280
  },
@@ -5297,6 +5303,9 @@
5297
5303
  "sourceLinked": "Repozytorium powiązane i zsynchronizowane",
5298
5304
  "linkSourceFailed": "Nie udało się powiązać repozytorium",
5299
5305
  "synced": "Zsynchronizowano: {updated} zaktualizowanych, {removed} usuniętych",
5306
+ "syncSkipped": "{count} plik wyglądał jak kontrakt, ale nie dało się go użyć. | {count} pliki wyglądały jak kontrakty, ale nie dało się ich użyć. | {count} plików wyglądało jak kontrakty, ale nie dało się ich użyć.",
5307
+ "syncTruncated": "Folder zawiera więcej, niż mieści jedna synchronizacja, więc odczytano tylko jego część.",
5308
+ "syncFolderMissing": "Połączony folder nie istnieje w repozytorium. Sprawdź ścieżkę lub połącz źródło ponownie, jeśli zostało przeniesione.",
5300
5309
  "syncFailed": "Nie udało się zsynchronizować repozytorium",
5301
5310
  "changesAvailable": "W źródle są zmiany",
5302
5311
  "upToDate": "Już aktualne",
@@ -5260,12 +5260,16 @@
5260
5260
  "metaSynced": "eşitlendi {date} · ref {ref}",
5261
5261
  "metaNever": "hiç eşitlenmedi · ref {ref}",
5262
5262
  "metaFiles": "{service} · {count} sözleşme dosyası",
5263
+ "metaFolder": "{service} · bu klasördeki sözleşmeler",
5264
+ "metaFolderRecursive": "{service} · bu klasördeki ve alt klasörlerindeki sözleşmeler",
5263
5265
  "metaDirectory": "her alt dizin için bir hizmet",
5264
5266
  "changes": "Değişiklikler",
5265
5267
  "check": "Değişiklikleri denetle",
5266
5268
  "sync": "Yeniden eşitle",
5267
5269
  "unlink": "Bağlantıyı kaldır",
5268
5270
  "selectedDir": "Dizin:",
5271
+ "recursive": "Alt klasörleri dahil et",
5272
+ "recursiveHint": "Bağlanan klasörün altında yer alan sözleşme dosyalarını da oku.",
5269
5273
  "selectedFiles": "Seçilen dosyalar: {count}",
5270
5274
  "wholeRepo": "Tüm depo (kök).",
5271
5275
  "ownerPlaceholder": "sahip",
@@ -5279,8 +5283,10 @@
5279
5283
  "serviceSummaryPlaceholder": "tek satırlık özet (isteğe bağlı)",
5280
5284
  "mode": {
5281
5285
  "directory": "Hizmetlerin bulunduğu bir klasör",
5286
+ "folder": "Sözleşmelerin tamamının bulunduğu klasör, tek hizmet",
5282
5287
  "files": "Belirli dosyalar, tek hizmet",
5283
5288
  "directoryHint": "Bağlanan yolun her alt dizini bir hizmettir; bir service.md ile tanımlanır ve sözleşme dosyaları onun yanında durur.",
5289
+ "folderHint": "Bağlanan klasördeki her sözleşme dosyası burada adlandırdığınız tek hizmeti tanımlar; sonradan eklenen dosyalar bir sonraki eşitlemede alınır.",
5284
5290
  "filesHint": "Listelenen dosyaların tamamı burada adlandırdığınız tek hizmeti tanımlar. Benimsenecek bir klasör düzeni yoksa bunu kullanın."
5285
5291
  }
5286
5292
  },
@@ -5309,6 +5315,9 @@
5309
5315
  "sourceLinked": "Depo bağlandı ve eşitlendi",
5310
5316
  "linkSourceFailed": "Depo bağlanamadı",
5311
5317
  "synced": "Eşitlendi: {updated} güncellendi, {removed} kaldırıldı",
5318
+ "syncSkipped": "{count} dosya sözleşmeye benziyordu ama kullanılamadı. | {count} dosya sözleşmeye benziyordu ama kullanılamadı.",
5319
+ "syncTruncated": "Klasör tek bir eşitlemenin alabileceğinden fazlasını içeriyor, bu yüzden yalnızca bir kısmı okundu.",
5320
+ "syncFolderMissing": "Bağlanan klasör depoda yok. Yolu kontrol edin veya taşındıysa kaynağı yeniden bağlayın.",
5312
5321
  "syncFailed": "Depo eşitlenemedi",
5313
5322
  "changesAvailable": "Kaynakta değişiklikler var",
5314
5323
  "upToDate": "Zaten güncel",
@@ -5248,12 +5248,16 @@
5248
5248
  "metaSynced": "синхронізовано {date} · ref {ref}",
5249
5249
  "metaNever": "ніколи не синхронізовано · ref {ref}",
5250
5250
  "metaFiles": "{service} · {count} файлів контрактів",
5251
+ "metaFolder": "{service} · контракти з цієї теки",
5252
+ "metaFolderRecursive": "{service} · контракти з цієї теки та її підтек",
5251
5253
  "metaDirectory": "один сервіс на підкаталог",
5252
5254
  "changes": "Зміни",
5253
5255
  "check": "Перевірити зміни",
5254
5256
  "sync": "Синхронізувати знову",
5255
5257
  "unlink": "Від'єднати",
5256
5258
  "selectedDir": "Каталог:",
5259
+ "recursive": "Включати підтеки",
5260
+ "recursiveHint": "Читати також файли контрактів, вкладені під приєднаною текою.",
5257
5261
  "selectedFiles": "Обрані файли: {count}",
5258
5262
  "wholeRepo": "Увесь репозиторій (корінь).",
5259
5263
  "ownerPlaceholder": "власник",
@@ -5267,8 +5271,10 @@
5267
5271
  "serviceSummaryPlaceholder": "однорядковий опис (необов'язково)",
5268
5272
  "mode": {
5269
5273
  "directory": "Каталог із сервісами",
5274
+ "folder": "Уся тека контрактів, один сервіс",
5270
5275
  "files": "Окремі файли, один сервіс",
5271
5276
  "directoryHint": "Кожен підкаталог приєднаного шляху - це один сервіс, описаний файлом service.md, поруч з яким лежать його файли контрактів.",
5277
+ "folderHint": "Кожен файл контракту в приєднаній теці описує той єдиний сервіс, який ви тут називаєте, а файли, додані згодом, буде підхоплено під час наступної синхронізації.",
5272
5278
  "filesHint": "Усі перелічені файли описують той єдиний сервіс, який ви тут називаєте. Використовуйте це, коли немає угоди про каталоги."
5273
5279
  }
5274
5280
  },
@@ -5297,6 +5303,9 @@
5297
5303
  "sourceLinked": "Репозиторій приєднано та синхронізовано",
5298
5304
  "linkSourceFailed": "Не вдалося приєднати репозиторій",
5299
5305
  "synced": "Синхронізовано: {updated} оновлено, {removed} вилучено",
5306
+ "syncSkipped": "{count} файл був схожий на контракт, але його не вдалося використати. | {count} файли були схожі на контракти, але їх не вдалося використати. | {count} файлів були схожі на контракти, але їх не вдалося використати.",
5307
+ "syncTruncated": "Тека містить більше, ніж вміщає одна синхронізація, тому прочитано лише її частину.",
5308
+ "syncFolderMissing": "Приєднаної теки немає в репозиторії. Перевірте шлях або приєднайте джерело заново, якщо його перенесено.",
5300
5309
  "syncFailed": "Не вдалося синхронізувати репозиторій",
5301
5310
  "changesAvailable": "У джерелі є зміни",
5302
5311
  "upToDate": "Уже актуально",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.200.2",
3
+ "version": "0.200.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",
@@ -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.207.0"
43
+ "@cat-factory/contracts": "0.209.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@toad-contracts/testing": "0.3.2",