@mulmoclaude/collection-plugin 0.4.1 → 0.5.1

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.
Files changed (41) hide show
  1. package/dist/index.cjs +4 -47
  2. package/dist/index.js +1 -44
  3. package/dist/{promptSafety-BSO2gdsg.cjs → promptSafety-B0pPJkfQ.cjs} +60 -1
  4. package/dist/promptSafety-B0pPJkfQ.cjs.map +1 -0
  5. package/dist/{promptSafety-DShN_VCG.js → promptSafety-DIQ2yDca.js} +43 -2
  6. package/dist/promptSafety-DIQ2yDca.js.map +1 -0
  7. package/dist/style.css +102 -0
  8. package/dist/vue/chat/Preview.vue.d.ts +8 -0
  9. package/dist/vue/chat/Preview.vue.d.ts.map +1 -0
  10. package/dist/vue/chat/View.vue.d.ts +16 -0
  11. package/dist/vue/chat/View.vue.d.ts.map +1 -0
  12. package/dist/vue/components/CollectionEmbedView.vue.d.ts.map +1 -1
  13. package/dist/vue/components/CollectionRecordModal.vue.d.ts.map +1 -1
  14. package/dist/vue/components/CollectionRecordPanel.vue.d.ts.map +1 -1
  15. package/dist/vue/components/CollectionView.vue.d.ts.map +1 -1
  16. package/dist/vue/index.d.ts +6 -0
  17. package/dist/vue/index.d.ts.map +1 -1
  18. package/dist/vue/lang/de.d.ts.map +1 -1
  19. package/dist/vue/lang/en.d.ts +5 -0
  20. package/dist/vue/lang/en.d.ts.map +1 -1
  21. package/dist/vue/lang/es.d.ts.map +1 -1
  22. package/dist/vue/lang/fr.d.ts.map +1 -1
  23. package/dist/vue/lang/index.d.ts +5 -0
  24. package/dist/vue/lang/index.d.ts.map +1 -1
  25. package/dist/vue/lang/ja.d.ts.map +1 -1
  26. package/dist/vue/lang/ko.d.ts.map +1 -1
  27. package/dist/vue/lang/ptBR.d.ts.map +1 -1
  28. package/dist/vue/lang/zh.d.ts.map +1 -1
  29. package/dist/vue/refLink.d.ts +7 -0
  30. package/dist/vue/refLink.d.ts.map +1 -0
  31. package/dist/vue/uiContext.d.ts +18 -0
  32. package/dist/vue/uiContext.d.ts.map +1 -1
  33. package/dist/vue.cjs +914 -711
  34. package/dist/vue.cjs.map +1 -1
  35. package/dist/vue.js +913 -713
  36. package/dist/vue.js.map +1 -1
  37. package/package.json +1 -1
  38. package/dist/index.cjs.map +0 -1
  39. package/dist/index.js.map +0 -1
  40. package/dist/promptSafety-BSO2gdsg.cjs.map +0 -1
  41. package/dist/promptSafety-DShN_VCG.js.map +0 -1
package/dist/vue.js CHANGED
@@ -1,6 +1,6 @@
1
- import { A as buildUpdatedRecord, C as dateSortValue, D as numericSortValue, E as nextSortDirection, F as rowFromItem, I as actionVisible, L as fieldVisible, M as draftToRecord, N as emptyRow, O as sortItems, P as firstMissingRequiredField, T as isSortableField, W as resolveEnumColor, _ as ymdKey, a as assignLanes, b as labelFieldFor, i as MINUTES_PER_DAY, j as coerceInlineValue, k as stringSortValue, l as dateOf, n as shortHexId, o as bucketRecords, r as errorMessage, s as buildMonthGrid, t as defangForPrompt, u as daySlice, v as itemIdOf, w as enumSortValue, x as boolSortValue, y as itemLabelOf } from "./promptSafety-DShN_VCG.js";
1
+ import { A as buildUpdatedRecord, C as dateSortValue, D as numericSortValue, E as nextSortDirection, F as rowFromItem, G as TOOL_DEFINITION, I as actionVisible, L as fieldVisible, M as draftToRecord, N as emptyRow, O as sortItems, P as firstMissingRequiredField, T as isSortableField, W as resolveEnumColor, _ as ymdKey, a as assignLanes, b as labelFieldFor, i as MINUTES_PER_DAY, j as coerceInlineValue, k as stringSortValue, l as dateOf, n as shortHexId, o as bucketRecords, q as executePresentCollection, r as errorMessage, s as buildMonthGrid, t as defangForPrompt, u as daySlice, v as itemIdOf, w as enumSortValue, x as boolSortValue, y as itemLabelOf } from "./promptSafety-DIQ2yDca.js";
2
2
  import { t as deriveAll } from "./deriveAll-BHcs1erT.js";
3
- import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, effectScope, mergeModels, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, resolveComponent, resolveDynamicComponent, toDisplayString, unref, useModel, vModelCheckbox, vModelDynamic, vModelSelect, vModelText, watch, watchEffect, withCtx, withDirectives, withKeys, withModifiers } from "vue";
3
+ import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, effectScope, mergeModels, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useModel, vModelCheckbox, vModelDynamic, vModelSelect, vModelText, watch, watchEffect, withCtx, withDirectives, withKeys, withModifiers } from "vue";
4
4
  import { createI18n } from "vue-i18n";
5
5
  import draggable from "vuedraggable";
6
6
  //#region src/vue/uiContext.ts
@@ -14,395 +14,6 @@ function collectionUi() {
14
14
  return current;
15
15
  }
16
16
  //#endregion
17
- //#region src/vue/useCollectionRendering.ts
18
- function useCollectionRendering(collection, locale) {
19
- const refCache = ref({});
20
- const refRecordCache = ref({});
21
- const embedCache = ref({});
22
- function resetLinkedCaches() {
23
- refCache.value = {};
24
- refRecordCache.value = {};
25
- embedCache.value = {};
26
- }
27
- function uniqueRefTargets(schema) {
28
- const targets = /* @__PURE__ */ new Set();
29
- const walk = (fields) => {
30
- for (const field of Object.values(fields)) {
31
- if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
32
- if (field.type === "table" && field.of) walk(field.of);
33
- }
34
- };
35
- walk(schema.fields);
36
- return [...targets];
37
- }
38
- function uniqueEmbedTargets(schema) {
39
- const targets = /* @__PURE__ */ new Set();
40
- for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
41
- return [...targets];
42
- }
43
- function buildRefDisplayMap(detail) {
44
- const { fields, primaryKey } = detail.collection.schema;
45
- const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
46
- const map = {};
47
- for (const item of detail.items) {
48
- const slugRaw = item[primaryKey];
49
- if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
50
- const displayRaw = item[displayField];
51
- map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
52
- }
53
- return map;
54
- }
55
- function buildRefRecordMap(detail) {
56
- const { schema } = detail.collection;
57
- const map = {};
58
- for (const item of detail.items) {
59
- const slugRaw = item[schema.primaryKey];
60
- if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = deriveAll(schema, item, {});
61
- }
62
- return map;
63
- }
64
- async function loadLinkedCollections(schema, expectedSlug) {
65
- const refTargets = new Set(uniqueRefTargets(schema));
66
- const embedTargets = new Set(uniqueEmbedTargets(schema));
67
- const allTargets = [...new Set([...refTargets, ...embedTargets])];
68
- if (allTargets.length === 0) return;
69
- const binding = collectionUi();
70
- const results = await Promise.all(allTargets.map(async (target) => {
71
- try {
72
- return {
73
- target,
74
- result: await binding.fetchCollectionDetail(target)
75
- };
76
- } catch {
77
- return {
78
- target,
79
- result: { ok: false }
80
- };
81
- }
82
- }));
83
- if (collection.value?.slug !== expectedSlug) return;
84
- const nextRef = {};
85
- const nextRefRecords = {};
86
- const nextEmbed = {};
87
- for (const { target, result } of results) {
88
- if (!result.ok) continue;
89
- if (refTargets.has(target)) {
90
- nextRef[target] = buildRefDisplayMap(result.data);
91
- nextRefRecords[target] = buildRefRecordMap(result.data);
92
- }
93
- if (embedTargets.has(target)) nextEmbed[target] = {
94
- schema: result.data.collection.schema,
95
- items: result.data.items
96
- };
97
- }
98
- refCache.value = nextRef;
99
- refRecordCache.value = nextRefRecords;
100
- embedCache.value = nextEmbed;
101
- }
102
- function refDisplay(targetSlug, itemSlug) {
103
- const map = refCache.value[targetSlug];
104
- return map && map[itemSlug] || itemSlug;
105
- }
106
- function refOptions(targetSlug) {
107
- const map = refCache.value[targetSlug];
108
- if (!map) return [];
109
- return Object.entries(map).map(([slug, display]) => ({
110
- slug,
111
- display
112
- })).sort((left, right) => left.display.localeCompare(right.display));
113
- }
114
- function resolveEmbed(field) {
115
- if (field.type !== "embed" || !field.to || !field.id) return {
116
- schema: null,
117
- item: null
118
- };
119
- const data = embedCache.value[field.to];
120
- if (!data) return {
121
- schema: null,
122
- item: null
123
- };
124
- const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === field.id) ?? null;
125
- return {
126
- schema: data.schema,
127
- item
128
- };
129
- }
130
- function embedValue(field, value, record) {
131
- if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale.value);
132
- return detailText(value);
133
- }
134
- const embedViews = computed(() => {
135
- const out = {};
136
- if (!collection.value) return out;
137
- for (const [key, field] of Object.entries(collection.value.schema.fields)) {
138
- if (field.type !== "embed") continue;
139
- const { schema, item } = resolveEmbed(field);
140
- const rows = [];
141
- if (schema && item) for (const [subKey, subField] of Object.entries(schema.fields)) {
142
- const value = item[subKey];
143
- if (value === void 0 || value === null || value === "") continue;
144
- rows.push({
145
- key: subKey,
146
- label: subField.label,
147
- type: subField.type,
148
- value,
149
- display: embedValue(subField, value, item)
150
- });
151
- }
152
- out[key] = {
153
- found: Boolean(item),
154
- rows,
155
- targetSlug: field.to ?? "",
156
- recordId: field.id ?? ""
157
- };
158
- }
159
- return out;
160
- });
161
- function resolveCurrency(field, record) {
162
- if (field.currencyField && record) {
163
- const code = record[field.currencyField];
164
- if (typeof code === "string" && code.trim().length > 0) return code;
165
- }
166
- return field.currency;
167
- }
168
- function currencySymbol(currency) {
169
- const code = currency && currency.length > 0 ? currency : "USD";
170
- try {
171
- return new Intl.NumberFormat(locale.value, {
172
- style: "currency",
173
- currency: code
174
- }).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
175
- } catch {
176
- return code;
177
- }
178
- }
179
- function formatMoney(value, currency, displayLocale) {
180
- if (value === void 0 || value === "") return "—";
181
- const amount = typeof value === "number" ? value : Number(value);
182
- if (!Number.isFinite(amount)) return String(value);
183
- const currencyCode = currency && currency.length > 0 ? currency : "USD";
184
- try {
185
- return new Intl.NumberFormat(displayLocale, {
186
- style: "currency",
187
- currency: currencyCode
188
- }).format(amount);
189
- } catch {
190
- return String(amount);
191
- }
192
- }
193
- function formatCell(value, type) {
194
- if (value === void 0 || value === null || value === "") return "—";
195
- if (type === "markdown" && typeof value === "string") return value.length > 80 ? `${value.slice(0, 80)}…` : value;
196
- if (typeof value === "string" || typeof value === "number") return String(value);
197
- return JSON.stringify(value);
198
- }
199
- function isExternalUrl(value) {
200
- return typeof value === "string" && /^https?:\/\//i.test(value);
201
- }
202
- function artifactUrl(value) {
203
- return collectionUi().fileAssetUrl(value);
204
- }
205
- function fileRoutePath(value) {
206
- return collectionUi().fileRoutePath(value);
207
- }
208
- function detailText(value) {
209
- if (value === void 0 || value === null || value === "") return "—";
210
- return String(value);
211
- }
212
- function tableRows(value) {
213
- if (!Array.isArray(value)) return [];
214
- return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
215
- }
216
- function hasTableRows(value) {
217
- return tableRows(value).length > 0;
218
- }
219
- function formatSubCell(subField, value, record) {
220
- if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
221
- if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
222
- return formatCell(value, subField.type);
223
- }
224
- function inputTypeFor(type) {
225
- if (type === "email") return "email";
226
- if (type === "number") return "number";
227
- if (type === "money") return "number";
228
- if (type === "date") return "date";
229
- if (type === "datetime") return "datetime-local";
230
- return "text";
231
- }
232
- function evaluateDerivedAgainstItem(field, fieldKey, item) {
233
- if (!field.formula || !collection.value) return null;
234
- const result = deriveAll(collection.value.schema, item, refRecordCache.value)[fieldKey];
235
- return typeof result === "number" && Number.isFinite(result) ? result : null;
236
- }
237
- function derivedDisplay(field, computedValue, record) {
238
- if (computedValue === null || computedValue === void 0) return "—";
239
- if (field.display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale.value);
240
- return formatCell(computedValue, field.display ?? "number");
241
- }
242
- return {
243
- refCache,
244
- refRecordCache,
245
- embedCache,
246
- resetLinkedCaches,
247
- loadLinkedCollections,
248
- refDisplay,
249
- refOptions,
250
- embedViews,
251
- resolveCurrency,
252
- currencySymbol,
253
- formatMoney,
254
- formatCell,
255
- detailText,
256
- isExternalUrl,
257
- artifactUrl,
258
- fileRoutePath,
259
- tableRows,
260
- hasTableRows,
261
- formatSubCell,
262
- inputTypeFor,
263
- deriveAll,
264
- evaluateDerivedAgainstItem,
265
- derivedDisplay
266
- };
267
- }
268
- //#endregion
269
- //#region src/vue/collectionViewMode.ts
270
- var STORAGE_KEY = "collection_view_modes";
271
- var SORT_STORAGE_KEY = "collection_sorts";
272
- var BUILT_IN_MODES = [
273
- "table",
274
- "calendar",
275
- "kanban"
276
- ];
277
- /** A persisted mode is valid if it's a known built-in OR any `custom:<id>`
278
- * key (the id is validated against the live schema at render time, so an
279
- * unknown custom id simply collapses to the table there). Takes `unknown`
280
- * and type-guards `string` first: a corrupted localStorage entry could hold a
281
- * number/object, and calling `.startsWith` on that would throw. */
282
- function isValidViewMode(value) {
283
- return typeof value === "string" && (BUILT_IN_MODES.includes(value) || value.startsWith("custom:"));
284
- }
285
- function readAll() {
286
- try {
287
- const raw = localStorage.getItem(STORAGE_KEY);
288
- if (!raw) return {};
289
- const parsed = JSON.parse(raw);
290
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
291
- } catch {
292
- return {};
293
- }
294
- }
295
- function readCollectionViewMode(slug) {
296
- const stored = readAll()[slug];
297
- return isValidViewMode(stored) ? stored : null;
298
- }
299
- function writeCollectionViewMode(slug, view) {
300
- try {
301
- const all = readAll();
302
- all[slug] = view;
303
- localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
304
- } catch {}
305
- }
306
- function isSortState(value) {
307
- if (!value || typeof value !== "object") return false;
308
- const rec = value;
309
- return typeof rec.field === "string" && (rec.direction === "asc" || rec.direction === "desc");
310
- }
311
- function readAllSorts() {
312
- try {
313
- const raw = localStorage.getItem(SORT_STORAGE_KEY);
314
- if (!raw) return {};
315
- const parsed = JSON.parse(raw);
316
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
317
- const out = {};
318
- for (const [slug, value] of Object.entries(parsed)) if (isSortState(value)) out[slug] = value;
319
- return out;
320
- } catch {
321
- return {};
322
- }
323
- }
324
- function readCollectionSort(slug) {
325
- return readAllSorts()[slug] ?? null;
326
- }
327
- /** Persist (or, when `sort` is null, clear) the slug's active column sort. */
328
- function writeCollectionSort(slug, sort) {
329
- try {
330
- const all = Object.fromEntries(Object.entries(readAllSorts()).filter(([key]) => key !== slug));
331
- if (sort) all[slug] = sort;
332
- localStorage.setItem(SORT_STORAGE_KEY, JSON.stringify(all));
333
- } catch {}
334
- }
335
- //#endregion
336
- //#region src/vue/components/CollectionRecordModal.vue
337
- var CollectionRecordModal_default = /* @__PURE__ */ defineComponent({
338
- __name: "CollectionRecordModal",
339
- emits: ["close"],
340
- setup(__props, { emit: __emit }) {
341
- const emit = __emit;
342
- const dialogEl = ref(null);
343
- let previouslyFocused = null;
344
- const FOCUSABLE_SELECTOR = [
345
- "a[href]",
346
- "button:not([disabled])",
347
- "input:not([disabled])",
348
- "select:not([disabled])",
349
- "textarea:not([disabled])",
350
- "[tabindex]:not([tabindex=\"-1\"])"
351
- ].join(",");
352
- /** Visible, focusable controls inside the dialog, in DOM order. */
353
- function focusableItems() {
354
- if (!dialogEl.value) return [];
355
- return Array.from(dialogEl.value.querySelectorAll(FOCUSABLE_SELECTOR)).filter((node) => node.offsetParent !== null);
356
- }
357
- /** Trap Tab / Shift+Tab inside the dialog so focus can't escape to the
358
- * page behind the overlay. Wraps at both ends; the dialog container
359
- * itself (tabindex -1) counts as "before the first item". */
360
- function onTab(event) {
361
- const items = focusableItems();
362
- if (items.length === 0) {
363
- event.preventDefault();
364
- dialogEl.value?.focus();
365
- return;
366
- }
367
- const [first] = items;
368
- const last = items[items.length - 1];
369
- const active = document.activeElement;
370
- if (event.shiftKey) {
371
- if (active === first || active === dialogEl.value) {
372
- event.preventDefault();
373
- last.focus();
374
- }
375
- } else if (active === last) {
376
- event.preventDefault();
377
- first.focus();
378
- }
379
- }
380
- onMounted(async () => {
381
- previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
382
- await nextTick();
383
- dialogEl.value?.focus();
384
- });
385
- onBeforeUnmount(() => {
386
- previouslyFocused?.focus?.();
387
- });
388
- return (_ctx, _cache) => {
389
- return openBlock(), createBlock(Teleport, { to: "body" }, [createElementVNode("div", {
390
- class: "fixed inset-0 z-40 flex items-center justify-center bg-slate-900/40 p-4",
391
- "data-testid": "collections-record-modal",
392
- onClick: _cache[1] || (_cache[1] = withModifiers(($event) => emit("close"), ["self"]))
393
- }, [createElementVNode("div", {
394
- ref_key: "dialogEl",
395
- ref: dialogEl,
396
- class: "flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl bg-white shadow-xl focus:outline-none",
397
- role: "dialog",
398
- "aria-modal": "true",
399
- tabindex: "-1",
400
- onKeydown: [_cache[0] || (_cache[0] = withKeys(($event) => emit("close"), ["esc"])), withKeys(onTab, ["tab"])]
401
- }, [renderSlot(_ctx.$slots, "default")], 544)])]);
402
- };
403
- }
404
- });
405
- //#endregion
406
17
  //#region src/vue/lang/index.ts
407
18
  var i18n = createI18n({
408
19
  legacy: false,
@@ -503,6 +114,11 @@ var i18n = createI18n({
503
114
  save: "Save",
504
115
  saving: "Saving...",
505
116
  yes: "Yes"
117
+ },
118
+ pluginPresentCollection: {
119
+ fallbackTitle: "Collection",
120
+ itemLabel: "Item: {id}",
121
+ listLabel: "All records"
506
122
  }
507
123
  },
508
124
  ja: {
@@ -599,6 +215,11 @@ var i18n = createI18n({
599
215
  save: "保存",
600
216
  saving: "保存中...",
601
217
  yes: "はい"
218
+ },
219
+ pluginPresentCollection: {
220
+ fallbackTitle: "コレクション",
221
+ itemLabel: "項目: {id}",
222
+ listLabel: "すべてのレコード"
602
223
  }
603
224
  },
604
225
  zh: {
@@ -695,6 +316,11 @@ var i18n = createI18n({
695
316
  save: "保存",
696
317
  saving: "保存中...",
697
318
  yes: "是"
319
+ },
320
+ pluginPresentCollection: {
321
+ fallbackTitle: "集合",
322
+ itemLabel: "项目:{id}",
323
+ listLabel: "全部记录"
698
324
  }
699
325
  },
700
326
  ko: {
@@ -791,6 +417,11 @@ var i18n = createI18n({
791
417
  save: "저장",
792
418
  saving: "저장 중...",
793
419
  yes: "예"
420
+ },
421
+ pluginPresentCollection: {
422
+ fallbackTitle: "컬렉션",
423
+ itemLabel: "항목: {id}",
424
+ listLabel: "모든 레코드"
794
425
  }
795
426
  },
796
427
  es: {
@@ -887,6 +518,11 @@ var i18n = createI18n({
887
518
  save: "Guardar",
888
519
  saving: "Guardando...",
889
520
  yes: "Sí"
521
+ },
522
+ pluginPresentCollection: {
523
+ fallbackTitle: "Colección",
524
+ itemLabel: "Elemento: {id}",
525
+ listLabel: "Todos los registros"
890
526
  }
891
527
  },
892
528
  "pt-BR": {
@@ -983,6 +619,11 @@ var i18n = createI18n({
983
619
  save: "Salvar",
984
620
  saving: "Salvando...",
985
621
  yes: "Sim"
622
+ },
623
+ pluginPresentCollection: {
624
+ fallbackTitle: "Coleção",
625
+ itemLabel: "Item: {id}",
626
+ listLabel: "Todos os registros"
986
627
  }
987
628
  },
988
629
  fr: {
@@ -1079,6 +720,11 @@ var i18n = createI18n({
1079
720
  save: "Enregistrer",
1080
721
  saving: "Enregistrement...",
1081
722
  yes: "Oui"
723
+ },
724
+ pluginPresentCollection: {
725
+ fallbackTitle: "Collection",
726
+ itemLabel: "Élément : {id}",
727
+ listLabel: "Tous les enregistrements"
1082
728
  }
1083
729
  },
1084
730
  de: {
@@ -1175,6 +821,11 @@ var i18n = createI18n({
1175
821
  save: "Speichern",
1176
822
  saving: "Wird gespeichert...",
1177
823
  yes: "Ja"
824
+ },
825
+ pluginPresentCollection: {
826
+ fallbackTitle: "Sammlung",
827
+ itemLabel: "Eintrag: {id}",
828
+ listLabel: "Alle Datensätze"
1178
829
  }
1179
830
  }
1180
831
  }
@@ -1206,121 +857,106 @@ function useCollectionI18n() {
1206
857
  };
1207
858
  }
1208
859
  //#endregion
1209
- //#region src/vue/components/CollectionEmbedView.vue?vue&type=script&setup=true&lang.ts
1210
- var _hoisted_1$9 = { class: "flex items-center justify-between text-[10px] font-bold text-indigo-600/90 tracking-wider uppercase" };
1211
- var _hoisted_2$9 = { class: "flex items-center gap-1.5" };
1212
- var _hoisted_3$9 = { class: "bg-indigo-100/60 text-indigo-700 px-1.5 py-0.5 rounded font-mono font-medium lowercase" };
1213
- var _hoisted_4$9 = { class: "grid gap-x-4 gap-y-3 grid-cols-2" };
1214
- var _hoisted_5$8 = { class: "text-[10px] font-semibold text-slate-400 uppercase tracking-wide" };
1215
- var _hoisted_6$8 = ["data-testid"];
1216
- var _hoisted_7$8 = {
1217
- key: 0,
1218
- class: "material-icons text-emerald-600 text-sm align-middle"
1219
- };
1220
- var _hoisted_8$8 = {
1221
- key: 1,
1222
- class: "text-slate-300"
1223
- };
1224
- var _hoisted_9$8 = {
1225
- key: 1,
1226
- class: "whitespace-pre-wrap font-normal text-slate-600"
1227
- };
1228
- var _hoisted_10$8 = { key: 2 };
1229
- var _hoisted_11$8 = ["data-testid"];
1230
- var _hoisted_12$7 = { class: "flex items-start gap-3" };
1231
- var _hoisted_13$6 = { class: "flex-1 min-w-0" };
1232
- var _hoisted_14$5 = { class: "text-xs font-semibold text-red-800 uppercase tracking-wider mb-1" };
1233
- var _hoisted_15$5 = ["data-testid"];
1234
- //#endregion
1235
- //#region src/vue/components/CollectionEmbedView.vue
1236
- var CollectionEmbedView_default = /* @__PURE__ */ defineComponent({
1237
- __name: "CollectionEmbedView",
1238
- props: {
1239
- view: {},
1240
- fieldKey: {}
1241
- },
1242
- setup(__props) {
1243
- const { t } = useCollectionI18n();
1244
- return (_ctx, _cache) => {
1245
- const _component_router_link = resolveComponent("router-link");
1246
- return __props.view.found ? (openBlock(), createBlock(_component_router_link, {
1247
- key: 0,
1248
- to: {
1249
- path: `/collections/${__props.view.targetSlug}`,
1250
- query: { selected: __props.view.recordId }
1251
- },
1252
- class: "group block relative rounded-xl border border-slate-200 bg-slate-50/50 p-4 pl-5 space-y-3 hover:bg-indigo-50/20 hover:border-indigo-200 transition-all duration-300 shadow-sm",
1253
- "data-testid": `collections-embed-${__props.fieldKey}`
1254
- }, {
1255
- default: withCtx(() => [
1256
- _cache[1] || (_cache[1] = createElementVNode("div", { class: "absolute left-0 top-0 bottom-0 w-1 bg-indigo-500 rounded-l-xl transition-all duration-300 group-hover:w-1.5 group-hover:bg-indigo-600" }, null, -1)),
1257
- createElementVNode("div", _hoisted_1$9, [createElementVNode("div", _hoisted_2$9, [_cache[0] || (_cache[0] = createElementVNode("span", { class: "material-icons text-sm" }, "link", -1)), createElementVNode("span", null, toDisplayString(__props.view.targetSlug), 1)]), createElementVNode("span", _hoisted_3$9, toDisplayString(__props.view.recordId), 1)]),
1258
- createElementVNode("div", _hoisted_4$9, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.view.rows, (row) => {
1259
- return openBlock(), createElementBlock("div", {
1260
- key: row.key,
1261
- class: "space-y-0.5"
1262
- }, [createElementVNode("div", _hoisted_5$8, toDisplayString(row.label), 1), createElementVNode("div", {
1263
- class: "text-xs text-slate-700 font-medium break-words",
1264
- "data-testid": `collections-embed-${__props.fieldKey}-${row.key}`
1265
- }, [row.type === "boolean" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [row.value === true ? (openBlock(), createElementBlock("span", _hoisted_7$8, "check_circle")) : (openBlock(), createElementBlock("span", _hoisted_8$8, "—"))], 64)) : row.type === "markdown" ? (openBlock(), createElementBlock("p", _hoisted_9$8, toDisplayString(row.display), 1)) : (openBlock(), createElementBlock("span", _hoisted_10$8, toDisplayString(row.display), 1))], 8, _hoisted_6$8)]);
1266
- }), 128))])
1267
- ]),
1268
- _: 1
1269
- }, 8, ["to", "data-testid"])) : (openBlock(), createElementBlock("div", {
1270
- key: 1,
1271
- class: "relative rounded-xl border border-red-100 bg-red-50/30 p-4 pl-5 shadow-sm",
1272
- "data-testid": `collections-embed-${__props.fieldKey}`
1273
- }, [_cache[4] || (_cache[4] = createElementVNode("div", { class: "absolute left-0 top-0 bottom-0 w-1 bg-red-400 rounded-l-xl" }, null, -1)), createElementVNode("div", _hoisted_12$7, [_cache[3] || (_cache[3] = createElementVNode("span", { class: "material-icons text-red-500 text-lg mt-0.5" }, "error_outline", -1)), createElementVNode("div", _hoisted_13$6, [
1274
- createElementVNode("p", _hoisted_14$5, toDisplayString(unref(t)("collectionsView.embedMissingTitle")), 1),
1275
- createElementVNode("p", {
1276
- class: "text-xs text-red-600",
1277
- "data-testid": `collections-embed-missing-${__props.fieldKey}`
1278
- }, toDisplayString(unref(t)("collectionsView.embedMissing", {
1279
- collection: __props.view.targetSlug,
1280
- id: __props.view.recordId
1281
- })), 9, _hoisted_15$5),
1282
- __props.view.targetSlug ? (openBlock(), createBlock(_component_router_link, {
1283
- key: 0,
1284
- to: { path: `/collections/${__props.view.targetSlug}` },
1285
- class: "inline-flex items-center gap-0.5 text-xs text-indigo-600 hover:text-indigo-800 font-semibold mt-2 hover:underline"
1286
- }, {
1287
- default: withCtx(() => [createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.embedCreate")), 1), _cache[2] || (_cache[2] = createElementVNode("span", { class: "material-icons text-xs" }, "arrow_forward", -1))]),
1288
- _: 1
1289
- }, 8, ["to"])) : createCommentVNode("", true)
1290
- ])])], 8, _hoisted_11$8));
860
+ //#region src/vue/components/CollectionRecordModal.vue
861
+ var CollectionRecordModal_default = /* @__PURE__ */ defineComponent({
862
+ __name: "CollectionRecordModal",
863
+ emits: ["close"],
864
+ setup(__props, { emit: __emit }) {
865
+ const emit = __emit;
866
+ const teleportTarget = collectionUi().modalTeleportTarget?.() ?? "body";
867
+ const dialogEl = ref(null);
868
+ let previouslyFocused = null;
869
+ const FOCUSABLE_SELECTOR = [
870
+ "a[href]",
871
+ "button:not([disabled])",
872
+ "input:not([disabled])",
873
+ "select:not([disabled])",
874
+ "textarea:not([disabled])",
875
+ "[tabindex]:not([tabindex=\"-1\"])"
876
+ ].join(",");
877
+ /** Visible, focusable controls inside the dialog, in DOM order. */
878
+ function focusableItems() {
879
+ if (!dialogEl.value) return [];
880
+ return Array.from(dialogEl.value.querySelectorAll(FOCUSABLE_SELECTOR)).filter((node) => node.offsetParent !== null);
881
+ }
882
+ /** Trap Tab / Shift+Tab inside the dialog so focus can't escape to the
883
+ * page behind the overlay. Wraps at both ends; the dialog container
884
+ * itself (tabindex -1) counts as "before the first item". */
885
+ function onTab(event) {
886
+ const items = focusableItems();
887
+ if (items.length === 0) {
888
+ event.preventDefault();
889
+ dialogEl.value?.focus();
890
+ return;
891
+ }
892
+ const [first] = items;
893
+ const last = items[items.length - 1];
894
+ const active = document.activeElement;
895
+ if (event.shiftKey) {
896
+ if (active === first || active === dialogEl.value) {
897
+ event.preventDefault();
898
+ last.focus();
899
+ }
900
+ } else if (active === last) {
901
+ event.preventDefault();
902
+ first.focus();
903
+ }
904
+ }
905
+ onMounted(async () => {
906
+ previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
907
+ await nextTick();
908
+ dialogEl.value?.focus();
909
+ });
910
+ onBeforeUnmount(() => {
911
+ previouslyFocused?.focus?.();
912
+ });
913
+ return (_ctx, _cache) => {
914
+ return openBlock(), createBlock(Teleport, { to: unref(teleportTarget) }, [createElementVNode("div", {
915
+ class: "fixed inset-0 z-40 flex items-center justify-center bg-slate-900/40 p-4",
916
+ "data-testid": "collections-record-modal",
917
+ onClick: _cache[1] || (_cache[1] = withModifiers(($event) => emit("close"), ["self"]))
918
+ }, [createElementVNode("div", {
919
+ ref_key: "dialogEl",
920
+ ref: dialogEl,
921
+ class: "flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl bg-white shadow-xl focus:outline-none",
922
+ role: "dialog",
923
+ "aria-modal": "true",
924
+ tabindex: "-1",
925
+ onKeydown: [_cache[0] || (_cache[0] = withKeys(($event) => emit("close"), ["esc"])), withKeys(onTab, ["tab"])]
926
+ }, [renderSlot(_ctx.$slots, "default")], 544)])], 8, ["to"]);
1291
927
  };
1292
928
  }
1293
929
  });
1294
930
  //#endregion
1295
931
  //#region src/vue/components/CollectionCalendarView.vue?vue&type=script&setup=true&lang.ts
1296
- var _hoisted_1$8 = {
932
+ var _hoisted_1$11 = {
1297
933
  class: "flex flex-col gap-3",
1298
934
  "data-testid": "collection-calendar"
1299
935
  };
1300
- var _hoisted_2$8 = { class: "flex items-center gap-2" };
1301
- var _hoisted_3$8 = ["aria-label"];
1302
- var _hoisted_4$8 = ["aria-label"];
1303
- var _hoisted_5$7 = {
936
+ var _hoisted_2$10 = { class: "flex items-center gap-2" };
937
+ var _hoisted_3$10 = ["aria-label"];
938
+ var _hoisted_4$10 = ["aria-label"];
939
+ var _hoisted_5$9 = {
1304
940
  class: "text-sm font-bold text-slate-800 flex-1",
1305
941
  "data-testid": "collection-calendar-month"
1306
942
  };
1307
- var _hoisted_6$7 = { class: "grid grid-cols-7 gap-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider select-none" };
1308
- var _hoisted_7$7 = { class: "grid grid-cols-7 gap-1" };
1309
- var _hoisted_8$7 = [
943
+ var _hoisted_6$8 = { class: "grid grid-cols-7 gap-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider select-none" };
944
+ var _hoisted_7$8 = { class: "grid grid-cols-7 gap-1" };
945
+ var _hoisted_8$8 = [
1310
946
  "aria-label",
1311
947
  "data-testid",
1312
948
  "onClick",
1313
949
  "onKeydown"
1314
950
  ];
1315
- var _hoisted_9$7 = { class: "flex items-center justify-end" };
1316
- var _hoisted_10$7 = ["data-testid", "onClick"];
1317
- var _hoisted_11$7 = {
951
+ var _hoisted_9$8 = { class: "flex items-center justify-end" };
952
+ var _hoisted_10$8 = ["data-testid", "onClick"];
953
+ var _hoisted_11$8 = {
1318
954
  key: 0,
1319
955
  class: "flex flex-wrap items-center gap-1.5 pt-1",
1320
956
  "data-testid": "collection-calendar-no-date"
1321
957
  };
1322
- var _hoisted_12$6 = { class: "text-[10px] font-bold text-slate-400 uppercase tracking-wider mr-1" };
1323
- var _hoisted_13$5 = ["data-testid", "onClick"];
958
+ var _hoisted_12$7 = { class: "text-[10px] font-bold text-slate-400 uppercase tracking-wider mr-1" };
959
+ var _hoisted_13$6 = ["data-testid", "onClick"];
1324
960
  /** Sort key for ordering a day's chips by start time: earliest first, with
1325
961
  * clock-less all-day records sinking to the bottom (matching the day view). */
1326
962
  var DAY_CHIP_DEFAULT = "bg-indigo-50 text-indigo-700 border-indigo-100 hover:bg-indigo-100";
@@ -1434,23 +1070,23 @@ var CollectionCalendarView_default = /* @__PURE__ */ defineComponent({
1434
1070
  viewMonth.value = now.getMonth() + 1;
1435
1071
  }
1436
1072
  return (_ctx, _cache) => {
1437
- return openBlock(), createElementBlock("div", _hoisted_1$8, [
1438
- createElementVNode("div", _hoisted_2$8, [
1073
+ return openBlock(), createElementBlock("div", _hoisted_1$11, [
1074
+ createElementVNode("div", _hoisted_2$10, [
1439
1075
  createElementVNode("button", {
1440
1076
  type: "button",
1441
1077
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
1442
1078
  "aria-label": unref(t)("collectionsView.calendarPrevMonth"),
1443
1079
  "data-testid": "collection-calendar-prev",
1444
1080
  onClick: _cache[0] || (_cache[0] = ($event) => stepMonth(-1))
1445
- }, [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "material-icons text-lg" }, "chevron_left", -1)])], 8, _hoisted_3$8),
1081
+ }, [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "material-icons text-lg" }, "chevron_left", -1)])], 8, _hoisted_3$10),
1446
1082
  createElementVNode("button", {
1447
1083
  type: "button",
1448
1084
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
1449
1085
  "aria-label": unref(t)("collectionsView.calendarNextMonth"),
1450
1086
  "data-testid": "collection-calendar-next",
1451
1087
  onClick: _cache[1] || (_cache[1] = ($event) => stepMonth(1))
1452
- }, [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "material-icons text-lg" }, "chevron_right", -1)])], 8, _hoisted_4$8),
1453
- createElementVNode("h3", _hoisted_5$7, toDisplayString(monthLabel.value), 1),
1088
+ }, [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "material-icons text-lg" }, "chevron_right", -1)])], 8, _hoisted_4$10),
1089
+ createElementVNode("h3", _hoisted_5$9, toDisplayString(monthLabel.value), 1),
1454
1090
  createElementVNode("button", {
1455
1091
  type: "button",
1456
1092
  class: "h-8 px-2.5 flex items-center gap-1 rounded border border-slate-200 bg-white text-slate-600 hover:bg-slate-50 text-xs font-bold transition-colors",
@@ -1458,13 +1094,13 @@ var CollectionCalendarView_default = /* @__PURE__ */ defineComponent({
1458
1094
  onClick: goToday
1459
1095
  }, toDisplayString(unref(t)("collectionsView.calendarToday")), 1)
1460
1096
  ]),
1461
- createElementVNode("div", _hoisted_6$7, [(openBlock(true), createElementBlock(Fragment, null, renderList(weekdayLabels.value, (label, idx) => {
1097
+ createElementVNode("div", _hoisted_6$8, [(openBlock(true), createElementBlock(Fragment, null, renderList(weekdayLabels.value, (label, idx) => {
1462
1098
  return openBlock(), createElementBlock("div", {
1463
1099
  key: idx,
1464
1100
  class: "px-1 py-1 text-center"
1465
1101
  }, toDisplayString(label), 1);
1466
1102
  }), 128))]),
1467
- createElementVNode("div", _hoisted_7$7, [(openBlock(true), createElementBlock(Fragment, null, renderList(cells.value, ({ cell, entries }) => {
1103
+ createElementVNode("div", _hoisted_7$8, [(openBlock(true), createElementBlock(Fragment, null, renderList(cells.value, ({ cell, entries }) => {
1468
1104
  return openBlock(), createElementBlock("div", {
1469
1105
  key: cell.key,
1470
1106
  class: normalizeClass(["min-h-[5.5rem] rounded-lg border p-1 flex flex-col gap-1 overflow-hidden transition-colors cursor-pointer hover:border-indigo-300 focus:outline-none focus:ring-2 focus:ring-indigo-500/30", cell.inMonth ? "bg-white border-slate-200" : "bg-slate-50/50 border-slate-100"]),
@@ -1474,24 +1110,24 @@ var CollectionCalendarView_default = /* @__PURE__ */ defineComponent({
1474
1110
  "data-testid": `collection-calendar-day-${cell.key}`,
1475
1111
  onClick: ($event) => emit("openDay", cell.ymd),
1476
1112
  onKeydown: [withKeys(withModifiers(($event) => emit("openDay", cell.ymd), ["self", "prevent"]), ["enter"]), withKeys(withModifiers(($event) => emit("openDay", cell.ymd), ["self", "prevent"]), ["space"])]
1477
- }, [createElementVNode("div", _hoisted_9$7, [createElementVNode("span", { class: normalizeClass(["text-[11px] font-bold h-5 min-w-5 px-1 inline-flex items-center justify-center rounded-full", cell.key === unref(todayKey) ? "bg-indigo-600 text-white" : cell.inMonth ? "text-slate-500" : "text-slate-300"]) }, toDisplayString(cell.ymd.day), 3)]), (openBlock(true), createElementBlock(Fragment, null, renderList(entries, (entry) => {
1113
+ }, [createElementVNode("div", _hoisted_9$8, [createElementVNode("span", { class: normalizeClass(["text-[11px] font-bold h-5 min-w-5 px-1 inline-flex items-center justify-center rounded-full", cell.key === unref(todayKey) ? "bg-indigo-600 text-white" : cell.inMonth ? "text-slate-500" : "text-slate-300"]) }, toDisplayString(cell.ymd.day), 3)]), (openBlock(true), createElementBlock(Fragment, null, renderList(entries, (entry) => {
1478
1114
  return openBlock(), createElementBlock("button", {
1479
1115
  key: entry.id,
1480
1116
  type: "button",
1481
1117
  class: normalizeClass(["text-left text-[11px] leading-tight font-semibold truncate rounded px-1.5 py-0.5 border transition-colors", chipClass(entry, DAY_CHIP_DEFAULT)]),
1482
1118
  "data-testid": `collection-calendar-chip-${entry.id}`,
1483
1119
  onClick: withModifiers(($event) => emit("select", entry.id), ["stop"])
1484
- }, toDisplayString(entry.label), 11, _hoisted_10$7);
1485
- }), 128))], 42, _hoisted_8$7);
1120
+ }, toDisplayString(entry.label), 11, _hoisted_10$8);
1121
+ }), 128))], 42, _hoisted_8$8);
1486
1122
  }), 128))]),
1487
- bucketed.value.noDate.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_11$7, [createElementVNode("span", _hoisted_12$6, toDisplayString(unref(t)("collectionsView.calendarNoDate")), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(undatedEntries.value, (entry) => {
1123
+ bucketed.value.noDate.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_11$8, [createElementVNode("span", _hoisted_12$7, toDisplayString(unref(t)("collectionsView.calendarNoDate")), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(undatedEntries.value, (entry) => {
1488
1124
  return openBlock(), createElementBlock("button", {
1489
1125
  key: entry.id,
1490
1126
  type: "button",
1491
1127
  class: normalizeClass(["text-[11px] font-semibold truncate rounded px-1.5 py-0.5 border transition-colors", chipClass(entry, UNDATED_CHIP_DEFAULT)]),
1492
1128
  "data-testid": `collection-calendar-undated-${entry.id}`,
1493
1129
  onClick: ($event) => emit("select", entry.id)
1494
- }, toDisplayString(entry.label), 11, _hoisted_13$5);
1130
+ }, toDisplayString(entry.label), 11, _hoisted_13$6);
1495
1131
  }), 128))])) : createCommentVNode("", true)
1496
1132
  ]);
1497
1133
  };
@@ -1499,40 +1135,40 @@ var CollectionCalendarView_default = /* @__PURE__ */ defineComponent({
1499
1135
  });
1500
1136
  //#endregion
1501
1137
  //#region src/vue/components/CollectionDayView.vue?vue&type=script&setup=true&lang.ts
1502
- var _hoisted_1$7 = { class: "flex items-center gap-2 border-b border-slate-200 px-4 py-3" };
1503
- var _hoisted_2$7 = {
1138
+ var _hoisted_1$10 = { class: "flex items-center gap-2 border-b border-slate-200 px-4 py-3" };
1139
+ var _hoisted_2$9 = {
1504
1140
  class: "flex-1 text-sm font-bold text-slate-800",
1505
1141
  "data-testid": "collection-day-view-title"
1506
1142
  };
1507
- var _hoisted_3$7 = ["aria-label"];
1508
- var _hoisted_4$7 = ["aria-label"];
1509
- var _hoisted_5$6 = {
1143
+ var _hoisted_3$9 = ["aria-label"];
1144
+ var _hoisted_4$9 = ["aria-label"];
1145
+ var _hoisted_5$8 = {
1510
1146
  key: 0,
1511
1147
  class: "px-4 py-10 text-center text-sm text-slate-400"
1512
1148
  };
1513
- var _hoisted_6$6 = { class: "absolute -top-2 left-0 w-10 pr-1 text-right text-[10px] tabular-nums text-slate-400" };
1514
- var _hoisted_7$6 = {
1149
+ var _hoisted_6$7 = { class: "absolute -top-2 left-0 w-10 pr-1 text-right text-[10px] tabular-nums text-slate-400" };
1150
+ var _hoisted_7$7 = {
1515
1151
  class: "absolute inset-y-0 right-0",
1516
1152
  style: { "left": "2.75rem" }
1517
1153
  };
1518
- var _hoisted_8$6 = ["data-testid", "onClick"];
1519
- var _hoisted_9$6 = { class: "block truncate text-[11px] font-semibold leading-tight" };
1520
- var _hoisted_10$6 = {
1154
+ var _hoisted_8$7 = ["data-testid", "onClick"];
1155
+ var _hoisted_9$7 = { class: "block truncate text-[11px] font-semibold leading-tight" };
1156
+ var _hoisted_10$7 = {
1521
1157
  key: 0,
1522
1158
  "aria-hidden": "true"
1523
1159
  };
1524
- var _hoisted_11$6 = {
1160
+ var _hoisted_11$7 = {
1525
1161
  key: 1,
1526
1162
  "aria-hidden": "true"
1527
1163
  };
1528
- var _hoisted_12$5 = {
1164
+ var _hoisted_12$6 = {
1529
1165
  key: 2,
1530
1166
  class: "flex flex-wrap items-center gap-1.5 border-t border-slate-200 px-4 py-2",
1531
1167
  "data-testid": "collection-day-view-all-day"
1532
1168
  };
1533
- var _hoisted_13$4 = { class: "mr-1 text-[10px] font-bold uppercase tracking-wider text-slate-400" };
1534
- var _hoisted_14$4 = ["data-testid", "onClick"];
1535
- var _hoisted_15$4 = {
1169
+ var _hoisted_13$5 = { class: "mr-1 text-[10px] font-bold uppercase tracking-wider text-slate-400" };
1170
+ var _hoisted_14$5 = ["data-testid", "onClick"];
1171
+ var _hoisted_15$5 = {
1536
1172
  key: 0,
1537
1173
  class: "min-w-0 flex-1 overflow-y-auto",
1538
1174
  "data-testid": "collection-day-view-detail"
@@ -1691,8 +1327,8 @@ var CollectionDayView_default = /* @__PURE__ */ defineComponent({
1691
1327
  role: "dialog",
1692
1328
  "aria-modal": "true"
1693
1329
  }, [createElementVNode("div", { class: normalizeClass(["flex min-h-0 flex-col", __props.showDetail ? "w-80 shrink-0 border-r border-slate-200" : "w-full"]) }, [
1694
- createElementVNode("div", _hoisted_1$7, [
1695
- createElementVNode("h3", _hoisted_2$7, toDisplayString(dayLabel.value), 1),
1330
+ createElementVNode("div", _hoisted_1$10, [
1331
+ createElementVNode("h3", _hoisted_2$9, toDisplayString(dayLabel.value), 1),
1696
1332
  __props.canCreate ? (openBlock(), createElementBlock("button", {
1697
1333
  key: 0,
1698
1334
  type: "button",
@@ -1700,16 +1336,16 @@ var CollectionDayView_default = /* @__PURE__ */ defineComponent({
1700
1336
  "aria-label": unref(t)("collectionsView.calendarCreateOn", { date: dayKey.value }),
1701
1337
  "data-testid": "collection-day-view-create",
1702
1338
  onClick: onCreate
1703
- }, [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "material-icons text-lg" }, "add", -1)])], 8, _hoisted_3$7)) : createCommentVNode("", true),
1339
+ }, [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "material-icons text-lg" }, "add", -1)])], 8, _hoisted_3$9)) : createCommentVNode("", true),
1704
1340
  createElementVNode("button", {
1705
1341
  type: "button",
1706
1342
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
1707
1343
  "aria-label": unref(t)("collectionsView.dayViewClose"),
1708
1344
  "data-testid": "collection-day-view-close",
1709
1345
  onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
1710
- }, [..._cache[4] || (_cache[4] = [createElementVNode("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_4$7)
1346
+ }, [..._cache[4] || (_cache[4] = [createElementVNode("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_4$9)
1711
1347
  ]),
1712
- timedEntries.value.length === 0 && allDayEntries.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_5$6, toDisplayString(unref(t)("collectionsView.dayViewEmpty")), 1)) : (openBlock(), createElementBlock("div", {
1348
+ timedEntries.value.length === 0 && allDayEntries.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_5$8, toDisplayString(unref(t)("collectionsView.dayViewEmpty")), 1)) : (openBlock(), createElementBlock("div", {
1713
1349
  key: 1,
1714
1350
  ref_key: "scrollEl",
1715
1351
  ref: scrollEl,
@@ -1723,8 +1359,8 @@ var CollectionDayView_default = /* @__PURE__ */ defineComponent({
1723
1359
  key: hour,
1724
1360
  class: "absolute left-0 right-0 border-t border-slate-100",
1725
1361
  style: normalizeStyle({ top: `${(hour - 1) * HOUR_PX}px` })
1726
- }, [createElementVNode("span", _hoisted_6$6, toDisplayString(hourLabel(hour - 1)), 1)], 4);
1727
- }), 64)), createElementVNode("div", _hoisted_7$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(timedEntries.value, (entry) => {
1362
+ }, [createElementVNode("span", _hoisted_6$7, toDisplayString(hourLabel(hour - 1)), 1)], 4);
1363
+ }), 64)), createElementVNode("div", _hoisted_7$7, [(openBlock(true), createElementBlock(Fragment, null, renderList(timedEntries.value, (entry) => {
1728
1364
  return openBlock(), createElementBlock("button", {
1729
1365
  key: entry.id,
1730
1366
  type: "button",
@@ -1732,56 +1368,56 @@ var CollectionDayView_default = /* @__PURE__ */ defineComponent({
1732
1368
  style: normalizeStyle(entry.style),
1733
1369
  "data-testid": `collection-day-view-chip-${entry.id}`,
1734
1370
  onClick: ($event) => onSelect(entry.id)
1735
- }, [createElementVNode("span", _hoisted_9$6, [
1736
- entry.slice.bleedsBefore ? (openBlock(), createElementBlock("span", _hoisted_10$6, "▲ ")) : createCommentVNode("", true),
1371
+ }, [createElementVNode("span", _hoisted_9$7, [
1372
+ entry.slice.bleedsBefore ? (openBlock(), createElementBlock("span", _hoisted_10$7, "▲ ")) : createCommentVNode("", true),
1737
1373
  createTextVNode(toDisplayString(entry.label), 1),
1738
- entry.slice.bleedsAfter ? (openBlock(), createElementBlock("span", _hoisted_11$6, " ▼")) : createCommentVNode("", true)
1374
+ entry.slice.bleedsAfter ? (openBlock(), createElementBlock("span", _hoisted_11$7, " ▼")) : createCommentVNode("", true)
1739
1375
  ]), (openBlock(true), createElementBlock(Fragment, null, renderList(entry.secondary, (text, i) => {
1740
1376
  return openBlock(), createElementBlock("span", {
1741
1377
  key: i,
1742
1378
  class: "block truncate text-[10px] leading-tight opacity-70"
1743
1379
  }, toDisplayString(text), 1);
1744
- }), 128))], 14, _hoisted_8$6);
1380
+ }), 128))], 14, _hoisted_8$7);
1745
1381
  }), 128))])], 4)], 512)),
1746
- allDayEntries.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_12$5, [createElementVNode("span", _hoisted_13$4, toDisplayString(unref(t)("collectionsView.dayViewAllDay")), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(allDayEntries.value, (entry) => {
1382
+ allDayEntries.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_12$6, [createElementVNode("span", _hoisted_13$5, toDisplayString(unref(t)("collectionsView.dayViewAllDay")), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(allDayEntries.value, (entry) => {
1747
1383
  return openBlock(), createElementBlock("button", {
1748
1384
  key: entry.id,
1749
1385
  type: "button",
1750
1386
  class: normalizeClass(["truncate rounded border px-1.5 py-0.5 text-[11px] font-semibold transition-colors", allDayChipClass(entry)]),
1751
1387
  "data-testid": `collection-day-view-allday-${entry.id}`,
1752
1388
  onClick: ($event) => onSelect(entry.id)
1753
- }, toDisplayString(entry.label), 11, _hoisted_14$4);
1389
+ }, toDisplayString(entry.label), 11, _hoisted_14$5);
1754
1390
  }), 128))])) : createCommentVNode("", true)
1755
- ], 2), __props.showDetail ? (openBlock(), createElementBlock("div", _hoisted_15$4, [renderSlot(_ctx.$slots, "detail")])) : createCommentVNode("", true)], 2)], 32);
1391
+ ], 2), __props.showDetail ? (openBlock(), createElementBlock("div", _hoisted_15$5, [renderSlot(_ctx.$slots, "detail")])) : createCommentVNode("", true)], 2)], 32);
1756
1392
  };
1757
1393
  }
1758
1394
  });
1759
1395
  //#endregion
1760
1396
  //#region src/vue/components/CollectionKanbanView.vue?vue&type=script&setup=true&lang.ts
1761
- var _hoisted_1$6 = {
1397
+ var _hoisted_1$9 = {
1762
1398
  class: "h-full overflow-x-auto overflow-y-hidden",
1763
1399
  "data-testid": "collection-kanban"
1764
1400
  };
1765
- var _hoisted_2$6 = { class: "flex gap-3 h-full p-1 min-w-max" };
1766
- var _hoisted_3$6 = ["data-testid"];
1767
- var _hoisted_4$6 = { class: "flex items-center justify-between px-3 py-2 border-b border-slate-200" };
1768
- var _hoisted_5$5 = { class: "flex items-center gap-2 min-w-0" };
1769
- var _hoisted_6$5 = ["title"];
1770
- var _hoisted_7$5 = { class: "text-[11px] text-slate-400 shrink-0" };
1771
- var _hoisted_8$5 = [
1401
+ var _hoisted_2$8 = { class: "flex gap-3 h-full p-1 min-w-max" };
1402
+ var _hoisted_3$8 = ["data-testid"];
1403
+ var _hoisted_4$8 = { class: "flex items-center justify-between px-3 py-2 border-b border-slate-200" };
1404
+ var _hoisted_5$7 = { class: "flex items-center gap-2 min-w-0" };
1405
+ var _hoisted_6$6 = ["title"];
1406
+ var _hoisted_7$6 = { class: "text-[11px] text-slate-400 shrink-0" };
1407
+ var _hoisted_8$6 = [
1772
1408
  "data-testid",
1773
1409
  "aria-label",
1774
1410
  "onClick",
1775
1411
  "onKeydown"
1776
1412
  ];
1777
- var _hoisted_9$5 = { class: "flex items-start gap-2" };
1778
- var _hoisted_10$5 = [
1413
+ var _hoisted_9$6 = { class: "flex items-start gap-2" };
1414
+ var _hoisted_10$6 = [
1779
1415
  "checked",
1780
1416
  "aria-label",
1781
1417
  "data-testid",
1782
1418
  "onChange"
1783
1419
  ];
1784
- var _hoisted_11$5 = { class: "text-sm font-medium text-slate-800 truncate" };
1420
+ var _hoisted_11$6 = { class: "text-sm font-medium text-slate-800 truncate" };
1785
1421
  var UNCATEGORIZED = "";
1786
1422
  //#endregion
1787
1423
  //#region src/vue/components/CollectionKanbanView.vue
@@ -1879,15 +1515,15 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
1879
1515
  emit("move", itemId(item), next);
1880
1516
  }
1881
1517
  return (_ctx, _cache) => {
1882
- return openBlock(), createElementBlock("div", _hoisted_1$6, [createElementVNode("div", _hoisted_2$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(columns.value, (column) => {
1518
+ return openBlock(), createElementBlock("div", _hoisted_1$9, [createElementVNode("div", _hoisted_2$8, [(openBlock(true), createElementBlock(Fragment, null, renderList(columns.value, (column) => {
1883
1519
  return openBlock(), createElementBlock("div", {
1884
1520
  key: column.value,
1885
1521
  "data-testid": `collection-kanban-column-${column.value || "uncategorized"}`,
1886
1522
  class: "w-72 shrink-0 flex flex-col bg-slate-100 rounded-lg"
1887
- }, [createElementVNode("div", _hoisted_4$6, [createElementVNode("div", _hoisted_5$5, [createElementVNode("span", { class: normalizeClass(["w-2 h-2 rounded-full shrink-0", unref(resolveEnumColor)(__props.schema, __props.groupField, column.value).dot]) }, null, 2), createElementVNode("span", {
1523
+ }, [createElementVNode("div", _hoisted_4$8, [createElementVNode("div", _hoisted_5$7, [createElementVNode("span", { class: normalizeClass(["w-2 h-2 rounded-full shrink-0", unref(resolveEnumColor)(__props.schema, __props.groupField, column.value).dot]) }, null, 2), createElementVNode("span", {
1888
1524
  class: "font-semibold text-xs text-slate-600 truncate",
1889
1525
  title: column.label
1890
- }, toDisplayString(column.label), 9, _hoisted_6$5)]), createElementVNode("span", _hoisted_7$5, toDisplayString(itemsByColumn(column.value).length), 1)]), createVNode(unref(draggable), {
1526
+ }, toDisplayString(column.label), 9, _hoisted_6$6)]), createElementVNode("span", _hoisted_7$6, toDisplayString(itemsByColumn(column.value).length), 1)]), createVNode(unref(draggable), {
1891
1527
  "model-value": itemsByColumn(column.value),
1892
1528
  "item-key": __props.schema.primaryKey,
1893
1529
  group: "collection-kanban-cards",
@@ -1903,7 +1539,7 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
1903
1539
  class: normalizeClass(["bg-white border border-slate-200 rounded shadow-sm p-2 cursor-grab hover:shadow active:cursor-grabbing focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400", [itemId(element) === __props.selected ? "ring-2 ring-indigo-500 border-indigo-300" : "", notifyAccentClass(element)]]),
1904
1540
  onClick: ($event) => emit("select", itemId(element)),
1905
1541
  onKeydown: [withKeys(withModifiers((e) => !e.repeat && emit("select", itemId(element)), ["prevent", "self"]), ["enter"]), withKeys(withModifiers((e) => !e.repeat && emit("select", itemId(element)), ["prevent", "self"]), ["space"])]
1906
- }, [createElementVNode("div", _hoisted_9$5, [cardToggle.value ? (openBlock(), createElementBlock("input", {
1542
+ }, [createElementVNode("div", _hoisted_9$6, [cardToggle.value ? (openBlock(), createElementBlock("input", {
1907
1543
  key: 0,
1908
1544
  type: "checkbox",
1909
1545
  checked: cardChecked(element),
@@ -1912,24 +1548,138 @@ var CollectionKanbanView_default = /* @__PURE__ */ defineComponent({
1912
1548
  "data-testid": `collection-kanban-toggle-${itemId(element)}`,
1913
1549
  onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"])),
1914
1550
  onChange: ($event) => onCardToggle(element)
1915
- }, null, 40, _hoisted_10$5)) : createCommentVNode("", true), createElementVNode("div", _hoisted_11$5, toDisplayString(itemLabel(element)), 1)])], 42, _hoisted_8$5)]),
1551
+ }, null, 40, _hoisted_10$6)) : createCommentVNode("", true), createElementVNode("div", _hoisted_11$6, toDisplayString(itemLabel(element)), 1)])], 42, _hoisted_8$6)]),
1916
1552
  _: 1
1917
1553
  }, 8, [
1918
1554
  "model-value",
1919
1555
  "item-key",
1920
1556
  "onChange"
1921
- ])], 8, _hoisted_3$6);
1557
+ ])], 8, _hoisted_3$8);
1922
1558
  }), 128))])]);
1923
1559
  };
1924
1560
  }
1925
1561
  });
1926
1562
  //#endregion
1563
+ //#region src/vue/refLink.ts
1564
+ function isModifiedClick(event) {
1565
+ return event instanceof MouseEvent && (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey);
1566
+ }
1567
+ /** Activate a ref/embed link → navigate to `targetSlug` (optionally a record). */
1568
+ function activateRefLink(event, targetSlug, recordId, stop = false) {
1569
+ if (stop) event.stopPropagation();
1570
+ if (isModifiedClick(event)) return;
1571
+ event.preventDefault();
1572
+ collectionUi().navigateToRecord(targetSlug, recordId);
1573
+ }
1574
+ /** Activate a `file` cell link → navigate to a host path. When the host has no
1575
+ * `navigate` capability, do NOT preventDefault so the `href` still works (or, in
1576
+ * a router-less host that also has no path, the link simply isn't rendered). */
1577
+ function activatePathLink(event, path, stop = false) {
1578
+ if (stop) event.stopPropagation();
1579
+ if (isModifiedClick(event)) return;
1580
+ const nav = collectionUi().navigate;
1581
+ if (!nav) return;
1582
+ event.preventDefault();
1583
+ nav(path);
1584
+ }
1585
+ //#endregion
1586
+ //#region src/vue/components/CollectionEmbedView.vue?vue&type=script&setup=true&lang.ts
1587
+ var _hoisted_1$8 = [
1588
+ "href",
1589
+ "tabindex",
1590
+ "data-testid"
1591
+ ];
1592
+ var _hoisted_2$7 = { class: "flex items-center justify-between text-[10px] font-bold text-indigo-600/90 tracking-wider uppercase" };
1593
+ var _hoisted_3$7 = { class: "flex items-center gap-1.5" };
1594
+ var _hoisted_4$7 = { class: "bg-indigo-100/60 text-indigo-700 px-1.5 py-0.5 rounded font-mono font-medium lowercase" };
1595
+ var _hoisted_5$6 = { class: "grid gap-x-4 gap-y-3 grid-cols-2" };
1596
+ var _hoisted_6$5 = { class: "text-[10px] font-semibold text-slate-400 uppercase tracking-wide" };
1597
+ var _hoisted_7$5 = ["data-testid"];
1598
+ var _hoisted_8$5 = {
1599
+ key: 0,
1600
+ class: "material-icons text-emerald-600 text-sm align-middle"
1601
+ };
1602
+ var _hoisted_9$5 = {
1603
+ key: 1,
1604
+ class: "text-slate-300"
1605
+ };
1606
+ var _hoisted_10$5 = {
1607
+ key: 1,
1608
+ class: "whitespace-pre-wrap font-normal text-slate-600"
1609
+ };
1610
+ var _hoisted_11$5 = { key: 2 };
1611
+ var _hoisted_12$5 = ["data-testid"];
1612
+ var _hoisted_13$4 = { class: "flex items-start gap-3" };
1613
+ var _hoisted_14$4 = { class: "flex-1 min-w-0" };
1614
+ var _hoisted_15$4 = { class: "text-xs font-semibold text-red-800 uppercase tracking-wider mb-1" };
1615
+ var _hoisted_16$3 = ["data-testid"];
1616
+ var _hoisted_17$3 = ["href", "tabindex"];
1617
+ //#endregion
1618
+ //#region src/vue/components/CollectionEmbedView.vue
1619
+ var CollectionEmbedView_default = /* @__PURE__ */ defineComponent({
1620
+ __name: "CollectionEmbedView",
1621
+ props: {
1622
+ view: {},
1623
+ fieldKey: {}
1624
+ },
1625
+ setup(__props) {
1626
+ const { t } = useCollectionI18n();
1627
+ const cui = collectionUi();
1628
+ return (_ctx, _cache) => {
1629
+ return __props.view.found ? (openBlock(), createElementBlock("a", {
1630
+ key: 0,
1631
+ href: unref(cui).recordHref?.(__props.view.targetSlug, __props.view.recordId),
1632
+ tabindex: unref(cui).recordHref?.(__props.view.targetSlug, __props.view.recordId) ? void 0 : 0,
1633
+ role: "link",
1634
+ class: "group block relative rounded-xl border border-slate-200 bg-slate-50/50 p-4 pl-5 space-y-3 hover:bg-indigo-50/20 hover:border-indigo-200 transition-all duration-300 shadow-sm",
1635
+ "data-testid": `collections-embed-${__props.fieldKey}`,
1636
+ onClick: _cache[0] || (_cache[0] = ($event) => unref(activateRefLink)($event, __props.view.targetSlug, __props.view.recordId)),
1637
+ onKeydown: [_cache[1] || (_cache[1] = withKeys(($event) => unref(activateRefLink)($event, __props.view.targetSlug, __props.view.recordId), ["enter"])), _cache[2] || (_cache[2] = withKeys(($event) => unref(activateRefLink)($event, __props.view.targetSlug, __props.view.recordId), ["space"]))]
1638
+ }, [
1639
+ _cache[7] || (_cache[7] = createElementVNode("div", { class: "absolute left-0 top-0 bottom-0 w-1 bg-indigo-500 rounded-l-xl transition-all duration-300 group-hover:w-1.5 group-hover:bg-indigo-600" }, null, -1)),
1640
+ createElementVNode("div", _hoisted_2$7, [createElementVNode("div", _hoisted_3$7, [_cache[6] || (_cache[6] = createElementVNode("span", { class: "material-icons text-sm" }, "link", -1)), createElementVNode("span", null, toDisplayString(__props.view.targetSlug), 1)]), createElementVNode("span", _hoisted_4$7, toDisplayString(__props.view.recordId), 1)]),
1641
+ createElementVNode("div", _hoisted_5$6, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.view.rows, (row) => {
1642
+ return openBlock(), createElementBlock("div", {
1643
+ key: row.key,
1644
+ class: "space-y-0.5"
1645
+ }, [createElementVNode("div", _hoisted_6$5, toDisplayString(row.label), 1), createElementVNode("div", {
1646
+ class: "text-xs text-slate-700 font-medium break-words",
1647
+ "data-testid": `collections-embed-${__props.fieldKey}-${row.key}`
1648
+ }, [row.type === "boolean" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [row.value === true ? (openBlock(), createElementBlock("span", _hoisted_8$5, "check_circle")) : (openBlock(), createElementBlock("span", _hoisted_9$5, "—"))], 64)) : row.type === "markdown" ? (openBlock(), createElementBlock("p", _hoisted_10$5, toDisplayString(row.display), 1)) : (openBlock(), createElementBlock("span", _hoisted_11$5, toDisplayString(row.display), 1))], 8, _hoisted_7$5)]);
1649
+ }), 128))])
1650
+ ], 40, _hoisted_1$8)) : (openBlock(), createElementBlock("div", {
1651
+ key: 1,
1652
+ class: "relative rounded-xl border border-red-100 bg-red-50/30 p-4 pl-5 shadow-sm",
1653
+ "data-testid": `collections-embed-${__props.fieldKey}`
1654
+ }, [_cache[10] || (_cache[10] = createElementVNode("div", { class: "absolute left-0 top-0 bottom-0 w-1 bg-red-400 rounded-l-xl" }, null, -1)), createElementVNode("div", _hoisted_13$4, [_cache[9] || (_cache[9] = createElementVNode("span", { class: "material-icons text-red-500 text-lg mt-0.5" }, "error_outline", -1)), createElementVNode("div", _hoisted_14$4, [
1655
+ createElementVNode("p", _hoisted_15$4, toDisplayString(unref(t)("collectionsView.embedMissingTitle")), 1),
1656
+ createElementVNode("p", {
1657
+ class: "text-xs text-red-600",
1658
+ "data-testid": `collections-embed-missing-${__props.fieldKey}`
1659
+ }, toDisplayString(unref(t)("collectionsView.embedMissing", {
1660
+ collection: __props.view.targetSlug,
1661
+ id: __props.view.recordId
1662
+ })), 9, _hoisted_16$3),
1663
+ __props.view.targetSlug ? (openBlock(), createElementBlock("a", {
1664
+ key: 0,
1665
+ href: unref(cui).recordHref?.(__props.view.targetSlug),
1666
+ tabindex: unref(cui).recordHref?.(__props.view.targetSlug) ? void 0 : 0,
1667
+ role: "link",
1668
+ class: "inline-flex items-center gap-0.5 text-xs text-indigo-600 hover:text-indigo-800 font-semibold mt-2 hover:underline",
1669
+ onClick: _cache[3] || (_cache[3] = ($event) => unref(activateRefLink)($event, __props.view.targetSlug)),
1670
+ onKeydown: [_cache[4] || (_cache[4] = withKeys(($event) => unref(activateRefLink)($event, __props.view.targetSlug), ["enter"])), _cache[5] || (_cache[5] = withKeys(($event) => unref(activateRefLink)($event, __props.view.targetSlug), ["space"]))]
1671
+ }, [createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.embedCreate")), 1), _cache[8] || (_cache[8] = createElementVNode("span", { class: "material-icons text-xs" }, "arrow_forward", -1))], 40, _hoisted_17$3)) : createCommentVNode("", true)
1672
+ ])])], 8, _hoisted_12$5));
1673
+ };
1674
+ }
1675
+ });
1676
+ //#endregion
1927
1677
  //#region src/vue/components/CollectionRecordPanel.vue?vue&type=script&setup=true&lang.ts
1928
- var _hoisted_1$5 = { class: "flex items-center gap-2 mb-4" };
1929
- var _hoisted_2$5 = { class: "flex-1 min-w-0" };
1930
- var _hoisted_3$5 = { class: "block text-[9px] font-bold text-slate-400 uppercase tracking-wider" };
1931
- var _hoisted_4$5 = ["data-testid"];
1932
- var _hoisted_5$4 = ["disabled"];
1678
+ var _hoisted_1$7 = { class: "flex items-center gap-2 mb-4" };
1679
+ var _hoisted_2$6 = { class: "flex-1 min-w-0" };
1680
+ var _hoisted_3$6 = { class: "block text-[9px] font-bold text-slate-400 uppercase tracking-wider" };
1681
+ var _hoisted_4$6 = ["data-testid"];
1682
+ var _hoisted_5$5 = ["disabled"];
1933
1683
  var _hoisted_6$4 = {
1934
1684
  key: 1,
1935
1685
  class: "flex items-center gap-2"
@@ -2065,49 +1815,61 @@ var _hoisted_52$1 = {
2065
1815
  key: 2,
2066
1816
  class: "text-slate-300"
2067
1817
  };
2068
- var _hoisted_53$1 = {
1818
+ var _hoisted_53$1 = [
1819
+ "href",
1820
+ "tabindex",
1821
+ "data-testid",
1822
+ "onClick",
1823
+ "onKeydown"
1824
+ ];
1825
+ var _hoisted_54$1 = {
2069
1826
  key: 3,
2070
1827
  class: "font-semibold text-slate-900 tabular-nums text-sm"
2071
1828
  };
2072
- var _hoisted_54$1 = {
1829
+ var _hoisted_55$1 = {
2073
1830
  key: 4,
2074
1831
  class: "inline-block truncate tabular-nums font-bold text-indigo-900 bg-indigo-50/50 px-2 py-0.5 rounded border border-indigo-100/50"
2075
1832
  };
2076
- var _hoisted_55$1 = {
1833
+ var _hoisted_56$1 = {
2077
1834
  key: 5,
2078
1835
  class: "border border-slate-200/80 rounded-xl overflow-hidden shadow-sm mt-1"
2079
1836
  };
2080
- var _hoisted_56$1 = { class: "w-full text-[11px] text-slate-600 bg-white" };
2081
- var _hoisted_57$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
2082
- var _hoisted_58$1 = { class: "divide-y divide-slate-100" };
2083
- var _hoisted_59$1 = {
1837
+ var _hoisted_57$1 = { class: "w-full text-[11px] text-slate-600 bg-white" };
1838
+ var _hoisted_58$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
1839
+ var _hoisted_59$1 = { class: "divide-y divide-slate-100" };
1840
+ var _hoisted_60$1 = {
2084
1841
  key: 0,
2085
1842
  class: "material-icons text-emerald-600 text-base"
2086
1843
  };
2087
- var _hoisted_60$1 = {
1844
+ var _hoisted_61$1 = {
2088
1845
  key: 1,
2089
1846
  class: "text-slate-300"
2090
1847
  };
2091
- var _hoisted_61$1 = {
1848
+ var _hoisted_62$1 = {
2092
1849
  key: 6,
2093
1850
  class: "text-slate-400 italic"
2094
1851
  };
2095
- var _hoisted_62$1 = {
1852
+ var _hoisted_63$1 = {
2096
1853
  key: 7,
2097
1854
  class: "bg-slate-50 rounded-xl p-4 border border-slate-200/60 text-slate-600 text-xs whitespace-pre-wrap leading-relaxed max-h-[30vh] overflow-y-auto"
2098
1855
  };
2099
- var _hoisted_63$1 = [
1856
+ var _hoisted_64$1 = [
2100
1857
  "src",
2101
1858
  "alt",
2102
1859
  "data-testid"
2103
1860
  ];
2104
- var _hoisted_64$1 = ["href", "data-testid"];
2105
1861
  var _hoisted_65$1 = ["href", "data-testid"];
2106
- var _hoisted_66$1 = {
1862
+ var _hoisted_66$1 = ["href", "data-testid"];
1863
+ var _hoisted_67$1 = [
1864
+ "href",
1865
+ "data-testid",
1866
+ "onClick"
1867
+ ];
1868
+ var _hoisted_68$1 = {
2107
1869
  key: 13,
2108
1870
  class: "text-slate-800 font-semibold"
2109
1871
  };
2110
- var _hoisted_67$1 = {
1872
+ var _hoisted_69$1 = {
2111
1873
  key: 0,
2112
1874
  class: "col-span-full text-xs font-semibold text-red-600 bg-red-50 border border-red-100 p-2.5 rounded-xl"
2113
1875
  };
@@ -2142,7 +1904,8 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2142
1904
  "runAction"
2143
1905
  ], ["update:editing"]),
2144
1906
  setup(__props, { emit: __emit }) {
2145
- const resolveImageSrc = collectionUi().imageSrc;
1907
+ const cui = collectionUi();
1908
+ const resolveImageSrc = cui.imageSrc;
2146
1909
  const props = __props;
2147
1910
  const editing = useModel(__props, "editing");
2148
1911
  const emit = __emit;
@@ -2222,17 +1985,16 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2222
1985
  if (rows) rows.splice(index, 1);
2223
1986
  }
2224
1987
  return (_ctx, _cache) => {
2225
- const _component_router_link = resolveComponent("router-link");
2226
1988
  return openBlock(), createBlock(resolveDynamicComponent(editing.value ? "form" : "div"), {
2227
1989
  class: "px-6 py-5 max-h-[60vh] overflow-y-auto",
2228
1990
  "data-testid": rootTestid.value,
2229
1991
  onSubmit: _cache[4] || (_cache[4] = withModifiers(($event) => emit("submit"), ["prevent"]))
2230
1992
  }, {
2231
1993
  default: withCtx(() => [
2232
- createElementVNode("div", _hoisted_1$5, [createElementVNode("div", _hoisted_2$5, [createElementVNode("span", _hoisted_3$5, toDisplayString(__props.collection.title), 1), createElementVNode("h2", {
1994
+ createElementVNode("div", _hoisted_1$7, [createElementVNode("div", _hoisted_2$6, [createElementVNode("span", _hoisted_3$6, toDisplayString(__props.collection.title), 1), createElementVNode("h2", {
2233
1995
  class: "text-base font-bold text-slate-800 truncate",
2234
1996
  "data-testid": editing.value ? "collections-edit-title" : "collections-detail-title"
2235
- }, toDisplayString(headerTitle.value), 9, _hoisted_4$5)]), editing.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("button", {
1997
+ }, toDisplayString(headerTitle.value), 9, _hoisted_4$6)]), editing.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createElementVNode("button", {
2236
1998
  type: "button",
2237
1999
  class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
2238
2000
  "data-testid": "collections-editor-cancel",
@@ -2242,7 +2004,7 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2242
2004
  class: "h-8 px-2.5 rounded bg-indigo-600 text-white font-bold text-xs hover:bg-indigo-700 disabled:opacity-50 transition-all shadow-sm shadow-indigo-600/10",
2243
2005
  disabled: __props.saving,
2244
2006
  "data-testid": "collections-editor-save"
2245
- }, toDisplayString(__props.saving ? unref(t)("common.saving") : unref(t)("common.save")), 9, _hoisted_5$4)], 64)) : (openBlock(), createElementBlock("div", _hoisted_6$4, [
2007
+ }, toDisplayString(__props.saving ? unref(t)("common.saving") : unref(t)("common.save")), 9, _hoisted_5$5)], 64)) : (openBlock(), createElementBlock("div", _hoisted_6$4, [
2246
2008
  (openBlock(true), createElementBlock(Fragment, null, renderList(__props.visibleActions, (action) => {
2247
2009
  return openBlock(), createElementBlock("button", {
2248
2010
  key: action.id,
@@ -2418,23 +2180,21 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2418
2180
  key: 1,
2419
2181
  class: "text-xs font-medium text-slate-700 break-words",
2420
2182
  "data-testid": `collections-detail-value-${key}`
2421
- }, [field.type === "toggle" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [field.field !== void 0 && String(detailRecord.value[field.field] ?? "") === field.onValue ? (openBlock(), createElementBlock("span", _hoisted_48$1, [_cache[11] || (_cache[11] = createElementVNode("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), createTextVNode(" " + toDisplayString(unref(t)("common.yes")), 1)])) : (openBlock(), createElementBlock("span", _hoisted_49$1, toDisplayString(unref(t)("common.no")), 1))], 64)) : field.type === "boolean" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [detailRecord.value[key] === true ? (openBlock(), createElementBlock("span", _hoisted_50$1, [_cache[12] || (_cache[12] = createElementVNode("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), createTextVNode(" " + toDisplayString(unref(t)("common.yes")), 1)])) : detailRecord.value[key] === false ? (openBlock(), createElementBlock("span", _hoisted_51$1, toDisplayString(unref(t)("common.no")), 1)) : (openBlock(), createElementBlock("span", _hoisted_52$1, "—"))], 64)) : field.type === "ref" && field.to && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? (openBlock(), createBlock(_component_router_link, {
2183
+ }, [field.type === "toggle" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [field.field !== void 0 && String(detailRecord.value[field.field] ?? "") === field.onValue ? (openBlock(), createElementBlock("span", _hoisted_48$1, [_cache[11] || (_cache[11] = createElementVNode("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), createTextVNode(" " + toDisplayString(unref(t)("common.yes")), 1)])) : (openBlock(), createElementBlock("span", _hoisted_49$1, toDisplayString(unref(t)("common.no")), 1))], 64)) : field.type === "boolean" ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [detailRecord.value[key] === true ? (openBlock(), createElementBlock("span", _hoisted_50$1, [_cache[12] || (_cache[12] = createElementVNode("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), createTextVNode(" " + toDisplayString(unref(t)("common.yes")), 1)])) : detailRecord.value[key] === false ? (openBlock(), createElementBlock("span", _hoisted_51$1, toDisplayString(unref(t)("common.no")), 1)) : (openBlock(), createElementBlock("span", _hoisted_52$1, "—"))], 64)) : field.type === "ref" && field.to && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? (openBlock(), createElementBlock("a", {
2422
2184
  key: 2,
2423
- to: {
2424
- path: `/collections/${field.to}`,
2425
- query: { selected: String(detailRecord.value[key]) }
2426
- },
2185
+ href: unref(cui).recordHref?.(field.to, String(detailRecord.value[key])),
2186
+ tabindex: unref(cui).recordHref?.(field.to, String(detailRecord.value[key])) ? void 0 : 0,
2187
+ role: "link",
2427
2188
  class: "text-indigo-600 hover:text-indigo-800 font-bold hover:underline",
2428
- "data-testid": `collections-detail-ref-${key}`
2429
- }, {
2430
- default: withCtx(() => [createTextVNode(toDisplayString(__props.render.refDisplay(field.to, String(detailRecord.value[key]))), 1)]),
2431
- _: 2
2432
- }, 1032, ["to", "data-testid"])) : field.type === "money" ? (openBlock(), createElementBlock("span", _hoisted_53$1, toDisplayString(__props.render.formatMoney(detailRecord.value[key], __props.render.resolveCurrency(field, detailRecord.value), __props.locale)), 1)) : field.type === "derived" ? (openBlock(), createElementBlock("span", _hoisted_54$1, toDisplayString(__props.render.derivedDisplay(field, __props.render.evaluateDerivedAgainstItem(field, String(key), detailRecord.value), detailRecord.value)), 1)) : field.type === "table" && field.of && __props.render.hasTableRows(detailRecord.value[key]) ? (openBlock(), createElementBlock("div", _hoisted_55$1, [createElementVNode("table", _hoisted_56$1, [createElementVNode("thead", _hoisted_57$1, [createElementVNode("tr", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(field.of, (subField, subKey) => {
2189
+ "data-testid": `collections-detail-ref-${key}`,
2190
+ onClick: ($event) => unref(activateRefLink)($event, field.to, String(detailRecord.value[key])),
2191
+ onKeydown: [withKeys(($event) => unref(activateRefLink)($event, field.to, String(detailRecord.value[key])), ["enter"]), withKeys(($event) => unref(activateRefLink)($event, field.to, String(detailRecord.value[key])), ["space"])]
2192
+ }, toDisplayString(__props.render.refDisplay(field.to, String(detailRecord.value[key]))), 41, _hoisted_53$1)) : field.type === "money" ? (openBlock(), createElementBlock("span", _hoisted_54$1, toDisplayString(__props.render.formatMoney(detailRecord.value[key], __props.render.resolveCurrency(field, detailRecord.value), __props.locale)), 1)) : field.type === "derived" ? (openBlock(), createElementBlock("span", _hoisted_55$1, toDisplayString(__props.render.derivedDisplay(field, __props.render.evaluateDerivedAgainstItem(field, String(key), detailRecord.value), detailRecord.value)), 1)) : field.type === "table" && field.of && __props.render.hasTableRows(detailRecord.value[key]) ? (openBlock(), createElementBlock("div", _hoisted_56$1, [createElementVNode("table", _hoisted_57$1, [createElementVNode("thead", _hoisted_58$1, [createElementVNode("tr", null, [(openBlock(true), createElementBlock(Fragment, null, renderList(field.of, (subField, subKey) => {
2433
2193
  return openBlock(), createElementBlock("th", {
2434
2194
  key: subKey,
2435
2195
  class: "text-left px-4 py-2 font-bold"
2436
2196
  }, toDisplayString(subField.label), 1);
2437
- }), 128))])]), createElementVNode("tbody", _hoisted_58$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.render.tableRows(detailRecord.value[key]), (row, rowIdx) => {
2197
+ }), 128))])]), createElementVNode("tbody", _hoisted_59$1, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.render.tableRows(detailRecord.value[key]), (row, rowIdx) => {
2438
2198
  return openBlock(), createElementBlock("tr", {
2439
2199
  key: rowIdx,
2440
2200
  class: "hover:bg-slate-50/50"
@@ -2442,12 +2202,12 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2442
2202
  return openBlock(), createElementBlock("td", {
2443
2203
  key: subKey,
2444
2204
  class: "px-4 py-2 align-middle font-medium"
2445
- }, [subField.type === "boolean" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [row[subKey] === true ? (openBlock(), createElementBlock("span", _hoisted_59$1, "check_circle")) : (openBlock(), createElementBlock("span", _hoisted_60$1, "—"))], 64)) : (openBlock(), createElementBlock("span", {
2205
+ }, [subField.type === "boolean" ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [row[subKey] === true ? (openBlock(), createElementBlock("span", _hoisted_60$1, "check_circle")) : (openBlock(), createElementBlock("span", _hoisted_61$1, "—"))], 64)) : (openBlock(), createElementBlock("span", {
2446
2206
  key: 1,
2447
2207
  class: normalizeClass([subField.type === "money" ? "font-bold text-slate-800 tabular-nums" : ""])
2448
2208
  }, toDisplayString(__props.render.formatSubCell(subField, row[subKey], detailRecord.value)), 3))]);
2449
2209
  }), 128))]);
2450
- }), 128))])])])) : field.type === "table" ? (openBlock(), createElementBlock("span", _hoisted_61$1, toDisplayString(unref(t)("collectionsView.noRows")), 1)) : field.type === "markdown" ? (openBlock(), createElementBlock("div", _hoisted_62$1, toDisplayString(__props.render.detailText(detailRecord.value[key])), 1)) : field.type === "embed" && embedViews.value[key] ? (openBlock(), createBlock(CollectionEmbedView_default, {
2210
+ }), 128))])])])) : field.type === "table" ? (openBlock(), createElementBlock("span", _hoisted_62$1, toDisplayString(unref(t)("collectionsView.noRows")), 1)) : field.type === "markdown" ? (openBlock(), createElementBlock("div", _hoisted_63$1, toDisplayString(__props.render.detailText(detailRecord.value[key])), 1)) : field.type === "embed" && embedViews.value[key] ? (openBlock(), createBlock(CollectionEmbedView_default, {
2451
2211
  key: 8,
2452
2212
  view: embedViews.value[key],
2453
2213
  "field-key": String(key)
@@ -2457,30 +2217,28 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2457
2217
  alt: field.label,
2458
2218
  class: "max-h-64 max-w-full object-contain rounded-lg border border-slate-200 bg-slate-50",
2459
2219
  "data-testid": `collections-detail-image-${key}`
2460
- }, null, 8, _hoisted_63$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? (openBlock(), createElementBlock("a", {
2220
+ }, null, 8, _hoisted_64$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? (openBlock(), createElementBlock("a", {
2461
2221
  key: 10,
2462
2222
  href: String(detailRecord.value[key]),
2463
2223
  target: "_blank",
2464
2224
  rel: "noopener noreferrer",
2465
2225
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2466
2226
  "data-testid": `collections-detail-url-${key}`
2467
- }, toDisplayString(String(detailRecord.value[key])), 9, _hoisted_64$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? (openBlock(), createElementBlock("a", {
2227
+ }, toDisplayString(String(detailRecord.value[key])), 9, _hoisted_65$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? (openBlock(), createElementBlock("a", {
2468
2228
  key: 11,
2469
2229
  href: __props.render.artifactUrl(detailRecord.value[key]) ?? void 0,
2470
2230
  target: "_blank",
2471
2231
  rel: "noopener noreferrer",
2472
2232
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2473
2233
  "data-testid": `collections-detail-file-${key}`
2474
- }, toDisplayString(String(detailRecord.value[key])), 9, _hoisted_65$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? (openBlock(), createBlock(_component_router_link, {
2234
+ }, toDisplayString(String(detailRecord.value[key])), 9, _hoisted_66$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? (openBlock(), createElementBlock("a", {
2475
2235
  key: 12,
2476
- to: __props.render.fileRoutePath(detailRecord.value[key]) ?? "",
2236
+ href: __props.render.fileRoutePath(detailRecord.value[key]) ?? void 0,
2477
2237
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2478
- "data-testid": `collections-detail-file-${key}`
2479
- }, {
2480
- default: withCtx(() => [createTextVNode(toDisplayString(String(detailRecord.value[key])), 1)]),
2481
- _: 2
2482
- }, 1032, ["to", "data-testid"])) : (openBlock(), createElementBlock("span", _hoisted_66$1, toDisplayString(__props.render.formatCell(detailRecord.value[key], field.type)), 1))], 8, _hoisted_47$1))], 2)) : createCommentVNode("", true)], 64);
2483
- }), 128)), editing.value && __props.saveError ? (openBlock(), createElementBlock("p", _hoisted_67$1, toDisplayString(__props.saveError), 1)) : createCommentVNode("", true)])
2238
+ "data-testid": `collections-detail-file-${key}`,
2239
+ onClick: ($event) => unref(activatePathLink)($event, __props.render.fileRoutePath(detailRecord.value[key]) ?? "")
2240
+ }, toDisplayString(String(detailRecord.value[key])), 9, _hoisted_67$1)) : (openBlock(), createElementBlock("span", _hoisted_68$1, toDisplayString(__props.render.formatCell(detailRecord.value[key], field.type)), 1))], 8, _hoisted_47$1))], 2)) : createCommentVNode("", true)], 64);
2241
+ }), 128)), editing.value && __props.saveError ? (openBlock(), createElementBlock("p", _hoisted_69$1, toDisplayString(__props.saveError), 1)) : createCommentVNode("", true)])
2484
2242
  ]),
2485
2243
  _: 1
2486
2244
  }, 40, ["data-testid"]);
@@ -2489,14 +2247,14 @@ var CollectionRecordPanel_default = /* @__PURE__ */ defineComponent({
2489
2247
  });
2490
2248
  //#endregion
2491
2249
  //#region src/vue/components/CollectionViewConfigModal.vue?vue&type=script&setup=true&lang.ts
2492
- var _hoisted_1$4 = {
2250
+ var _hoisted_1$6 = {
2493
2251
  "data-testid": "collection-config-modal",
2494
2252
  class: "flex flex-col overflow-hidden"
2495
2253
  };
2496
- var _hoisted_2$4 = { class: "flex items-center justify-between gap-2 border-b border-slate-100 px-5 py-3" };
2497
- var _hoisted_3$4 = { class: "text-sm font-bold text-slate-700" };
2498
- var _hoisted_4$4 = ["title", "aria-label"];
2499
- var _hoisted_5$3 = { class: "overflow-y-auto px-5 py-4" };
2254
+ var _hoisted_2$5 = { class: "flex items-center justify-between gap-2 border-b border-slate-100 px-5 py-3" };
2255
+ var _hoisted_3$5 = { class: "text-sm font-bold text-slate-700" };
2256
+ var _hoisted_4$5 = ["title", "aria-label"];
2257
+ var _hoisted_5$4 = { class: "overflow-y-auto px-5 py-4" };
2500
2258
  var _hoisted_6$3 = { class: "mb-2 text-[10px] font-bold uppercase tracking-wider text-slate-400" };
2501
2259
  var _hoisted_7$3 = {
2502
2260
  key: 0,
@@ -2561,14 +2319,14 @@ var CollectionViewConfigModal_default = /* @__PURE__ */ defineComponent({
2561
2319
  }
2562
2320
  return (_ctx, _cache) => {
2563
2321
  return openBlock(), createBlock(CollectionRecordModal_default, { onClose: _cache[1] || (_cache[1] = ($event) => emit("close")) }, {
2564
- default: withCtx(() => [createElementVNode("div", _hoisted_1$4, [createElementVNode("header", _hoisted_2$4, [createElementVNode("h2", _hoisted_3$4, toDisplayString(unref(t)("collectionsView.config.title", { title: __props.title })), 1), createElementVNode("button", {
2322
+ default: withCtx(() => [createElementVNode("div", _hoisted_1$6, [createElementVNode("header", _hoisted_2$5, [createElementVNode("h2", _hoisted_3$5, toDisplayString(unref(t)("collectionsView.config.title", { title: __props.title })), 1), createElementVNode("button", {
2565
2323
  type: "button",
2566
2324
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-400 hover:bg-slate-50 hover:text-slate-600",
2567
2325
  title: unref(t)("common.close"),
2568
2326
  "aria-label": unref(t)("common.close"),
2569
2327
  "data-testid": "collection-config-close",
2570
2328
  onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
2571
- }, [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_4$4)]), createElementVNode("div", _hoisted_5$3, [
2329
+ }, [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_4$5)]), createElementVNode("div", _hoisted_5$4, [
2572
2330
  createElementVNode("h3", _hoisted_6$3, toDisplayString(unref(t)("collectionsView.config.viewsHeading")), 1),
2573
2331
  error.value ? (openBlock(), createElementBlock("p", _hoisted_7$3, toDisplayString(error.value), 1)) : createCommentVNode("", true),
2574
2332
  __props.views.length > 0 ? (openBlock(), createElementBlock("ul", _hoisted_8$3, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.views, (view) => {
@@ -2597,19 +2355,19 @@ var CollectionViewConfigModal_default = /* @__PURE__ */ defineComponent({
2597
2355
  });
2598
2356
  //#endregion
2599
2357
  //#region src/vue/components/CollectionCustomView.vue?vue&type=script&setup=true&lang.ts
2600
- var _hoisted_1$3 = { class: "custom-view-container" };
2601
- var _hoisted_2$3 = {
2358
+ var _hoisted_1$5 = { class: "custom-view-container" };
2359
+ var _hoisted_2$4 = {
2602
2360
  key: 0,
2603
2361
  class: "custom-view-message",
2604
2362
  role: "alert",
2605
2363
  "data-testid": "collection-custom-view-error"
2606
2364
  };
2607
- var _hoisted_3$3 = {
2365
+ var _hoisted_3$4 = {
2608
2366
  key: 1,
2609
2367
  class: "custom-view-message",
2610
2368
  "data-testid": "collection-custom-view-loading"
2611
2369
  };
2612
- var _hoisted_4$3 = ["title", "srcdoc"];
2370
+ var _hoisted_4$4 = ["title", "srcdoc"];
2613
2371
  var REMINT_LEAD_MS = 6e4;
2614
2372
  var MIN_REMINT_DELAY_MS = 1e4;
2615
2373
  var CollectionCustomView_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
@@ -2665,46 +2423,365 @@ var CollectionCustomView_vue_vue_type_script_setup_true_lang_default = /* @__PUR
2665
2423
  dataUrl: mint.data.dataUrl,
2666
2424
  origin: window.location.origin
2667
2425
  });
2668
- } catch (err) {
2669
- if (!stale()) error.value = errorMessage(err);
2670
- } finally {
2671
- if (!stale()) loading.value = false;
2426
+ } catch (err) {
2427
+ if (!stale()) error.value = errorMessage(err);
2428
+ } finally {
2429
+ if (!stale()) loading.value = false;
2430
+ }
2431
+ }
2432
+ watch([() => props.slug, () => props.view.id], () => void load(), { immediate: true });
2433
+ onBeforeUnmount(clearRefresh);
2434
+ return (_ctx, _cache) => {
2435
+ return openBlock(), createElementBlock("div", _hoisted_1$5, [error.value ? (openBlock(), createElementBlock("div", _hoisted_2$4, toDisplayString(unref(t)("collectionsView.customViewError", { error: error.value })), 1)) : loading.value ? (openBlock(), createElementBlock("div", _hoisted_3$4, toDisplayString(unref(t)("collectionsView.customViewLoading")), 1)) : srcdoc.value ? (openBlock(), createElementBlock("iframe", {
2436
+ key: __props.view.id,
2437
+ "data-testid": "collection-custom-view-iframe",
2438
+ title: __props.view.label,
2439
+ srcdoc: srcdoc.value,
2440
+ sandbox: "allow-scripts allow-popups allow-popups-to-escape-sandbox",
2441
+ class: "w-full h-full border-0"
2442
+ }, null, 8, _hoisted_4$4)) : createCommentVNode("", true)]);
2443
+ };
2444
+ }
2445
+ });
2446
+ //#endregion
2447
+ //#region \0plugin-vue:export-helper
2448
+ var _plugin_vue_export_helper_default = (sfc, props) => {
2449
+ const target = sfc.__vccOpts || sfc;
2450
+ for (const [key, val] of props) target[key] = val;
2451
+ return target;
2452
+ };
2453
+ //#endregion
2454
+ //#region src/vue/components/CollectionCustomView.vue
2455
+ var CollectionCustomView_default = /* @__PURE__ */ _plugin_vue_export_helper_default(CollectionCustomView_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3ab71997"]]);
2456
+ //#endregion
2457
+ //#region src/vue/useCollectionRendering.ts
2458
+ function useCollectionRendering(collection, locale) {
2459
+ const refCache = ref({});
2460
+ const refRecordCache = ref({});
2461
+ const embedCache = ref({});
2462
+ function resetLinkedCaches() {
2463
+ refCache.value = {};
2464
+ refRecordCache.value = {};
2465
+ embedCache.value = {};
2466
+ }
2467
+ function uniqueRefTargets(schema) {
2468
+ const targets = /* @__PURE__ */ new Set();
2469
+ const walk = (fields) => {
2470
+ for (const field of Object.values(fields)) {
2471
+ if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
2472
+ if (field.type === "table" && field.of) walk(field.of);
2473
+ }
2474
+ };
2475
+ walk(schema.fields);
2476
+ return [...targets];
2477
+ }
2478
+ function uniqueEmbedTargets(schema) {
2479
+ const targets = /* @__PURE__ */ new Set();
2480
+ for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
2481
+ return [...targets];
2482
+ }
2483
+ function buildRefDisplayMap(detail) {
2484
+ const { fields, primaryKey } = detail.collection.schema;
2485
+ const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
2486
+ const map = {};
2487
+ for (const item of detail.items) {
2488
+ const slugRaw = item[primaryKey];
2489
+ if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
2490
+ const displayRaw = item[displayField];
2491
+ map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
2492
+ }
2493
+ return map;
2494
+ }
2495
+ function buildRefRecordMap(detail) {
2496
+ const { schema } = detail.collection;
2497
+ const map = {};
2498
+ for (const item of detail.items) {
2499
+ const slugRaw = item[schema.primaryKey];
2500
+ if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = deriveAll(schema, item, {});
2501
+ }
2502
+ return map;
2503
+ }
2504
+ async function loadLinkedCollections(schema, expectedSlug) {
2505
+ const refTargets = new Set(uniqueRefTargets(schema));
2506
+ const embedTargets = new Set(uniqueEmbedTargets(schema));
2507
+ const allTargets = [...new Set([...refTargets, ...embedTargets])];
2508
+ if (allTargets.length === 0) return;
2509
+ const binding = collectionUi();
2510
+ const results = await Promise.all(allTargets.map(async (target) => {
2511
+ try {
2512
+ return {
2513
+ target,
2514
+ result: await binding.fetchCollectionDetail(target)
2515
+ };
2516
+ } catch {
2517
+ return {
2518
+ target,
2519
+ result: { ok: false }
2520
+ };
2521
+ }
2522
+ }));
2523
+ if (collection.value?.slug !== expectedSlug) return;
2524
+ const nextRef = {};
2525
+ const nextRefRecords = {};
2526
+ const nextEmbed = {};
2527
+ for (const { target, result } of results) {
2528
+ if (!result.ok) continue;
2529
+ if (refTargets.has(target)) {
2530
+ nextRef[target] = buildRefDisplayMap(result.data);
2531
+ nextRefRecords[target] = buildRefRecordMap(result.data);
2532
+ }
2533
+ if (embedTargets.has(target)) nextEmbed[target] = {
2534
+ schema: result.data.collection.schema,
2535
+ items: result.data.items
2536
+ };
2537
+ }
2538
+ refCache.value = nextRef;
2539
+ refRecordCache.value = nextRefRecords;
2540
+ embedCache.value = nextEmbed;
2541
+ }
2542
+ function refDisplay(targetSlug, itemSlug) {
2543
+ const map = refCache.value[targetSlug];
2544
+ return map && map[itemSlug] || itemSlug;
2545
+ }
2546
+ function refOptions(targetSlug) {
2547
+ const map = refCache.value[targetSlug];
2548
+ if (!map) return [];
2549
+ return Object.entries(map).map(([slug, display]) => ({
2550
+ slug,
2551
+ display
2552
+ })).sort((left, right) => left.display.localeCompare(right.display));
2553
+ }
2554
+ function resolveEmbed(field) {
2555
+ if (field.type !== "embed" || !field.to || !field.id) return {
2556
+ schema: null,
2557
+ item: null
2558
+ };
2559
+ const data = embedCache.value[field.to];
2560
+ if (!data) return {
2561
+ schema: null,
2562
+ item: null
2563
+ };
2564
+ const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === field.id) ?? null;
2565
+ return {
2566
+ schema: data.schema,
2567
+ item
2568
+ };
2569
+ }
2570
+ function embedValue(field, value, record) {
2571
+ if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale.value);
2572
+ return detailText(value);
2573
+ }
2574
+ const embedViews = computed(() => {
2575
+ const out = {};
2576
+ if (!collection.value) return out;
2577
+ for (const [key, field] of Object.entries(collection.value.schema.fields)) {
2578
+ if (field.type !== "embed") continue;
2579
+ const { schema, item } = resolveEmbed(field);
2580
+ const rows = [];
2581
+ if (schema && item) for (const [subKey, subField] of Object.entries(schema.fields)) {
2582
+ const value = item[subKey];
2583
+ if (value === void 0 || value === null || value === "") continue;
2584
+ rows.push({
2585
+ key: subKey,
2586
+ label: subField.label,
2587
+ type: subField.type,
2588
+ value,
2589
+ display: embedValue(subField, value, item)
2590
+ });
2672
2591
  }
2592
+ out[key] = {
2593
+ found: Boolean(item),
2594
+ rows,
2595
+ targetSlug: field.to ?? "",
2596
+ recordId: field.id ?? ""
2597
+ };
2673
2598
  }
2674
- watch([() => props.slug, () => props.view.id], () => void load(), { immediate: true });
2675
- onBeforeUnmount(clearRefresh);
2676
- return (_ctx, _cache) => {
2677
- return openBlock(), createElementBlock("div", _hoisted_1$3, [error.value ? (openBlock(), createElementBlock("div", _hoisted_2$3, toDisplayString(unref(t)("collectionsView.customViewError", { error: error.value })), 1)) : loading.value ? (openBlock(), createElementBlock("div", _hoisted_3$3, toDisplayString(unref(t)("collectionsView.customViewLoading")), 1)) : srcdoc.value ? (openBlock(), createElementBlock("iframe", {
2678
- key: __props.view.id,
2679
- "data-testid": "collection-custom-view-iframe",
2680
- title: __props.view.label,
2681
- srcdoc: srcdoc.value,
2682
- sandbox: "allow-scripts allow-popups allow-popups-to-escape-sandbox",
2683
- class: "w-full h-full border-0"
2684
- }, null, 8, _hoisted_4$3)) : createCommentVNode("", true)]);
2685
- };
2599
+ return out;
2600
+ });
2601
+ function resolveCurrency(field, record) {
2602
+ if (field.currencyField && record) {
2603
+ const code = record[field.currencyField];
2604
+ if (typeof code === "string" && code.trim().length > 0) return code;
2605
+ }
2606
+ return field.currency;
2686
2607
  }
2687
- });
2688
- //#endregion
2689
- //#region \0plugin-vue:export-helper
2690
- var _plugin_vue_export_helper_default = (sfc, props) => {
2691
- const target = sfc.__vccOpts || sfc;
2692
- for (const [key, val] of props) target[key] = val;
2693
- return target;
2694
- };
2608
+ function currencySymbol(currency) {
2609
+ const code = currency && currency.length > 0 ? currency : "USD";
2610
+ try {
2611
+ return new Intl.NumberFormat(locale.value, {
2612
+ style: "currency",
2613
+ currency: code
2614
+ }).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
2615
+ } catch {
2616
+ return code;
2617
+ }
2618
+ }
2619
+ function formatMoney(value, currency, displayLocale) {
2620
+ if (value === void 0 || value === "") return "—";
2621
+ const amount = typeof value === "number" ? value : Number(value);
2622
+ if (!Number.isFinite(amount)) return String(value);
2623
+ const currencyCode = currency && currency.length > 0 ? currency : "USD";
2624
+ try {
2625
+ return new Intl.NumberFormat(displayLocale, {
2626
+ style: "currency",
2627
+ currency: currencyCode
2628
+ }).format(amount);
2629
+ } catch {
2630
+ return String(amount);
2631
+ }
2632
+ }
2633
+ function formatCell(value, type) {
2634
+ if (value === void 0 || value === null || value === "") return "—";
2635
+ if (type === "markdown" && typeof value === "string") return value.length > 80 ? `${value.slice(0, 80)}…` : value;
2636
+ if (typeof value === "string" || typeof value === "number") return String(value);
2637
+ return JSON.stringify(value);
2638
+ }
2639
+ function isExternalUrl(value) {
2640
+ return typeof value === "string" && /^https?:\/\//i.test(value);
2641
+ }
2642
+ function artifactUrl(value) {
2643
+ return collectionUi().fileAssetUrl(value);
2644
+ }
2645
+ function fileRoutePath(value) {
2646
+ return collectionUi().fileRoutePath(value);
2647
+ }
2648
+ function detailText(value) {
2649
+ if (value === void 0 || value === null || value === "") return "—";
2650
+ return String(value);
2651
+ }
2652
+ function tableRows(value) {
2653
+ if (!Array.isArray(value)) return [];
2654
+ return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
2655
+ }
2656
+ function hasTableRows(value) {
2657
+ return tableRows(value).length > 0;
2658
+ }
2659
+ function formatSubCell(subField, value, record) {
2660
+ if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
2661
+ if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
2662
+ return formatCell(value, subField.type);
2663
+ }
2664
+ function inputTypeFor(type) {
2665
+ if (type === "email") return "email";
2666
+ if (type === "number") return "number";
2667
+ if (type === "money") return "number";
2668
+ if (type === "date") return "date";
2669
+ if (type === "datetime") return "datetime-local";
2670
+ return "text";
2671
+ }
2672
+ function evaluateDerivedAgainstItem(field, fieldKey, item) {
2673
+ if (!field.formula || !collection.value) return null;
2674
+ const result = deriveAll(collection.value.schema, item, refRecordCache.value)[fieldKey];
2675
+ return typeof result === "number" && Number.isFinite(result) ? result : null;
2676
+ }
2677
+ function derivedDisplay(field, computedValue, record) {
2678
+ if (computedValue === null || computedValue === void 0) return "—";
2679
+ if (field.display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale.value);
2680
+ return formatCell(computedValue, field.display ?? "number");
2681
+ }
2682
+ return {
2683
+ refCache,
2684
+ refRecordCache,
2685
+ embedCache,
2686
+ resetLinkedCaches,
2687
+ loadLinkedCollections,
2688
+ refDisplay,
2689
+ refOptions,
2690
+ embedViews,
2691
+ resolveCurrency,
2692
+ currencySymbol,
2693
+ formatMoney,
2694
+ formatCell,
2695
+ detailText,
2696
+ isExternalUrl,
2697
+ artifactUrl,
2698
+ fileRoutePath,
2699
+ tableRows,
2700
+ hasTableRows,
2701
+ formatSubCell,
2702
+ inputTypeFor,
2703
+ deriveAll,
2704
+ evaluateDerivedAgainstItem,
2705
+ derivedDisplay
2706
+ };
2707
+ }
2695
2708
  //#endregion
2696
- //#region src/vue/components/CollectionCustomView.vue
2697
- var CollectionCustomView_default = /* @__PURE__ */ _plugin_vue_export_helper_default(CollectionCustomView_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3ab71997"]]);
2709
+ //#region src/vue/collectionViewMode.ts
2710
+ var STORAGE_KEY = "collection_view_modes";
2711
+ var SORT_STORAGE_KEY = "collection_sorts";
2712
+ var BUILT_IN_MODES = [
2713
+ "table",
2714
+ "calendar",
2715
+ "kanban"
2716
+ ];
2717
+ /** A persisted mode is valid if it's a known built-in OR any `custom:<id>`
2718
+ * key (the id is validated against the live schema at render time, so an
2719
+ * unknown custom id simply collapses to the table there). Takes `unknown`
2720
+ * and type-guards `string` first: a corrupted localStorage entry could hold a
2721
+ * number/object, and calling `.startsWith` on that would throw. */
2722
+ function isValidViewMode(value) {
2723
+ return typeof value === "string" && (BUILT_IN_MODES.includes(value) || value.startsWith("custom:"));
2724
+ }
2725
+ function readAll() {
2726
+ try {
2727
+ const raw = localStorage.getItem(STORAGE_KEY);
2728
+ if (!raw) return {};
2729
+ const parsed = JSON.parse(raw);
2730
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
2731
+ } catch {
2732
+ return {};
2733
+ }
2734
+ }
2735
+ function readCollectionViewMode(slug) {
2736
+ const stored = readAll()[slug];
2737
+ return isValidViewMode(stored) ? stored : null;
2738
+ }
2739
+ function writeCollectionViewMode(slug, view) {
2740
+ try {
2741
+ const all = readAll();
2742
+ all[slug] = view;
2743
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
2744
+ } catch {}
2745
+ }
2746
+ function isSortState(value) {
2747
+ if (!value || typeof value !== "object") return false;
2748
+ const rec = value;
2749
+ return typeof rec.field === "string" && (rec.direction === "asc" || rec.direction === "desc");
2750
+ }
2751
+ function readAllSorts() {
2752
+ try {
2753
+ const raw = localStorage.getItem(SORT_STORAGE_KEY);
2754
+ if (!raw) return {};
2755
+ const parsed = JSON.parse(raw);
2756
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
2757
+ const out = {};
2758
+ for (const [slug, value] of Object.entries(parsed)) if (isSortState(value)) out[slug] = value;
2759
+ return out;
2760
+ } catch {
2761
+ return {};
2762
+ }
2763
+ }
2764
+ function readCollectionSort(slug) {
2765
+ return readAllSorts()[slug] ?? null;
2766
+ }
2767
+ /** Persist (or, when `sort` is null, clear) the slug's active column sort. */
2768
+ function writeCollectionSort(slug, sort) {
2769
+ try {
2770
+ const all = Object.fromEntries(Object.entries(readAllSorts()).filter(([key]) => key !== slug));
2771
+ if (sort) all[slug] = sort;
2772
+ localStorage.setItem(SORT_STORAGE_KEY, JSON.stringify(all));
2773
+ } catch {}
2774
+ }
2698
2775
  //#endregion
2699
2776
  //#region src/vue/components/CollectionView.vue?vue&type=script&setup=true&lang.ts
2700
- var _hoisted_1$2 = { class: "h-full flex flex-col bg-slate-50/30" };
2701
- var _hoisted_2$2 = { class: "flex items-center gap-3 px-6 py-2 border-b border-slate-200 bg-white" };
2702
- var _hoisted_3$2 = ["title", "aria-label"];
2703
- var _hoisted_4$2 = {
2777
+ var _hoisted_1$4 = { class: "h-full flex flex-col bg-slate-50/30" };
2778
+ var _hoisted_2$3 = { class: "flex items-center gap-3 px-6 py-2 border-b border-slate-200 bg-white" };
2779
+ var _hoisted_3$3 = ["title", "aria-label"];
2780
+ var _hoisted_4$3 = {
2704
2781
  key: 1,
2705
2782
  class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100"
2706
2783
  };
2707
- var _hoisted_5$2 = { class: "material-symbols-outlined text-xl" };
2784
+ var _hoisted_5$3 = { class: "material-symbols-outlined text-xl" };
2708
2785
  var _hoisted_6$2 = { class: "flex-1 min-w-0" };
2709
2786
  var _hoisted_7$2 = { class: "text-base font-bold text-slate-800 truncate" };
2710
2787
  var _hoisted_8$2 = {
@@ -2851,48 +2928,60 @@ var _hoisted_65 = {
2851
2928
  class: "block truncate"
2852
2929
  };
2853
2930
  var _hoisted_66 = [
2931
+ "href",
2932
+ "tabindex",
2933
+ "data-testid",
2934
+ "onClick",
2935
+ "onKeydown"
2936
+ ];
2937
+ var _hoisted_67 = [
2854
2938
  "value",
2855
2939
  "disabled",
2856
2940
  "data-testid",
2857
2941
  "aria-label",
2858
2942
  "onChange"
2859
2943
  ];
2860
- var _hoisted_67 = {
2944
+ var _hoisted_68 = {
2861
2945
  key: 0,
2862
2946
  value: ""
2863
2947
  };
2864
- var _hoisted_68 = ["value"];
2865
- var _hoisted_69 = {
2948
+ var _hoisted_69 = ["value"];
2949
+ var _hoisted_70 = {
2866
2950
  key: 4,
2867
2951
  class: "block truncate tabular-nums font-semibold text-slate-900"
2868
2952
  };
2869
- var _hoisted_70 = {
2953
+ var _hoisted_71 = {
2870
2954
  key: 5,
2871
2955
  class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-lg text-[10px] font-bold bg-slate-100 text-slate-600 border border-slate-200/40"
2872
2956
  };
2873
- var _hoisted_71 = {
2957
+ var _hoisted_72 = {
2874
2958
  key: 6,
2875
2959
  class: "inline-block truncate tabular-nums font-bold text-indigo-900 bg-indigo-50/50 px-1.5 py-0.5 rounded border border-indigo-100/50"
2876
2960
  };
2877
- var _hoisted_72 = ["href", "data-testid"];
2878
2961
  var _hoisted_73 = ["href", "data-testid"];
2879
- var _hoisted_74 = {
2962
+ var _hoisted_74 = ["href", "data-testid"];
2963
+ var _hoisted_75 = [
2964
+ "href",
2965
+ "data-testid",
2966
+ "onClick"
2967
+ ];
2968
+ var _hoisted_76 = {
2880
2969
  key: 10,
2881
2970
  class: "block truncate text-slate-600"
2882
2971
  };
2883
- var _hoisted_75 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
2884
- var _hoisted_76 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
2885
- var _hoisted_77 = { class: "flex-1" };
2886
- var _hoisted_78 = {
2972
+ var _hoisted_77 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
2973
+ var _hoisted_78 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
2974
+ var _hoisted_79 = { class: "flex-1" };
2975
+ var _hoisted_80 = {
2887
2976
  id: "collections-chat-title",
2888
2977
  class: "text-sm font-bold text-slate-800 uppercase tracking-wide"
2889
2978
  };
2890
- var _hoisted_79 = { class: "text-xs text-slate-400 font-semibold" };
2891
- var _hoisted_80 = ["aria-label"];
2892
- var _hoisted_81 = { class: "px-6 py-5" };
2893
- var _hoisted_82 = ["placeholder", "onKeydown"];
2894
- var _hoisted_83 = { class: "px-6 py-3.5 border-t border-slate-100 flex items-center justify-end gap-2 bg-slate-50/50" };
2895
- var _hoisted_84 = ["disabled"];
2979
+ var _hoisted_81 = { class: "text-xs text-slate-400 font-semibold" };
2980
+ var _hoisted_82 = ["aria-label"];
2981
+ var _hoisted_83 = { class: "px-6 py-5" };
2982
+ var _hoisted_84 = ["placeholder", "onKeydown"];
2983
+ var _hoisted_85 = { class: "px-6 py-3.5 border-t border-slate-100 flex items-center justify-end gap-2 bg-slate-50/50" };
2984
+ var _hoisted_86 = ["disabled"];
2896
2985
  //#endregion
2897
2986
  //#region src/vue/components/CollectionView.vue
2898
2987
  var CollectionView_default = /* @__PURE__ */ defineComponent({
@@ -3859,9 +3948,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3859
3948
  openDay.value = viewing.value ? dayOfItem(viewing.value) : null;
3860
3949
  });
3861
3950
  return (_ctx, _cache) => {
3862
- const _component_router_link = resolveComponent("router-link");
3863
- return openBlock(), createElementBlock("div", _hoisted_1$2, [
3864
- createElementVNode("header", _hoisted_2$2, [
3951
+ return openBlock(), createElementBlock("div", _hoisted_1$4, [
3952
+ createElementVNode("header", _hoisted_2$3, [
3865
3953
  !embedded.value ? (openBlock(), createElementBlock("button", {
3866
3954
  key: 0,
3867
3955
  type: "button",
@@ -3870,8 +3958,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3870
3958
  "aria-label": unref(t)("collectionsView.backToIndex"),
3871
3959
  "data-testid": "collections-back",
3872
3960
  onClick: goBack
3873
- }, [..._cache[25] || (_cache[25] = [createElementVNode("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$2)) : createCommentVNode("", true),
3874
- collection.value ? (openBlock(), createElementBlock("div", _hoisted_4$2, [createElementVNode("span", _hoisted_5$2, toDisplayString(collection.value.icon), 1)])) : createCommentVNode("", true),
3961
+ }, [..._cache[23] || (_cache[23] = [createElementVNode("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$3)) : createCommentVNode("", true),
3962
+ collection.value ? (openBlock(), createElementBlock("div", _hoisted_4$3, [createElementVNode("span", _hoisted_5$3, toDisplayString(collection.value.icon), 1)])) : createCommentVNode("", true),
3875
3963
  createElementVNode("div", _hoisted_6$2, [createElementVNode("h1", _hoisted_7$2, toDisplayString(collection.value?.title ?? unref(t)("collectionsView.title")), 1), collection.value ? (openBlock(), createElementBlock("span", _hoisted_8$2, toDisplayString(collection.value.slug), 1)) : createCommentVNode("", true)]),
3876
3964
  collection.value && !embedded.value ? (openBlock(), createBlock(resolveDynamicComponent(unref(pinToggle)), {
3877
3965
  key: 2,
@@ -3899,7 +3987,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3899
3987
  class: "h-8 px-2.5 flex items-center gap-1 rounded border border-indigo-200 bg-white hover:bg-indigo-50 text-indigo-600 font-bold text-xs transition-colors",
3900
3988
  "data-testid": "collections-chat",
3901
3989
  onClick: openChat
3902
- }, [_cache[26] || (_cache[26] = createElementVNode("span", { class: "material-icons text-sm" }, "forum", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.chat")), 1)])) : createCommentVNode("", true),
3990
+ }, [_cache[24] || (_cache[24] = createElementVNode("span", { class: "material-icons text-sm" }, "forum", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.chat")), 1)])) : createCommentVNode("", true),
3903
3991
  (openBlock(true), createElementBlock(Fragment, null, renderList(collectionActions.value, (action) => {
3904
3992
  return openBlock(), createElementBlock("button", {
3905
3993
  key: action.id,
@@ -3916,7 +4004,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3916
4004
  class: "h-8 px-2.5 flex items-center gap-1 rounded bg-indigo-600 hover:bg-indigo-700 text-white font-bold text-xs transition-colors shadow-sm",
3917
4005
  "data-testid": "collections-add-item",
3918
4006
  onClick: openCreate
3919
- }, [_cache[27] || (_cache[27] = createElementVNode("span", { class: "material-icons text-sm" }, "add", -1)), createElementVNode("span", null, toDisplayString(unref(t)("common.add")), 1)])) : createCommentVNode("", true),
4007
+ }, [_cache[25] || (_cache[25] = createElementVNode("span", { class: "material-icons text-sm" }, "add", -1)), createElementVNode("span", null, toDisplayString(unref(t)("common.add")), 1)])) : createCommentVNode("", true),
3920
4008
  canDeleteCollection.value && !embedded.value ? (openBlock(), createElementBlock("button", {
3921
4009
  key: 6,
3922
4010
  type: "button",
@@ -3925,7 +4013,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3925
4013
  "aria-label": unref(t)("collectionsView.deleteCollection"),
3926
4014
  "data-testid": "collections-delete",
3927
4015
  onClick: confirmCollectionDelete
3928
- }, [..._cache[28] || (_cache[28] = [createElementVNode("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$2)) : createCommentVNode("", true),
4016
+ }, [..._cache[26] || (_cache[26] = [createElementVNode("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$2)) : createCommentVNode("", true),
3929
4017
  canDeleteFeed.value && !embedded.value ? (openBlock(), createElementBlock("button", {
3930
4018
  key: 7,
3931
4019
  type: "button",
@@ -3934,10 +4022,10 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3934
4022
  "aria-label": unref(t)("collectionsView.deleteFeed"),
3935
4023
  "data-testid": "feeds-delete",
3936
4024
  onClick: confirmFeedDelete
3937
- }, [..._cache[29] || (_cache[29] = [createElementVNode("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$2)) : createCommentVNode("", true)
4025
+ }, [..._cache[27] || (_cache[27] = [createElementVNode("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$2)) : createCommentVNode("", true)
3938
4026
  ]),
3939
4027
  collection.value && (items.value.length > 0 || hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value) ? (openBlock(), createElementBlock("div", _hoisted_15$2, [items.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_16$1, [
3940
- _cache[31] || (_cache[31] = createElementVNode("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [createElementVNode("span", { class: "material-icons text-lg" }, "search")], -1)),
4028
+ _cache[29] || (_cache[29] = createElementVNode("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [createElementVNode("span", { class: "material-icons text-lg" }, "search")], -1)),
3941
4029
  withDirectives(createElementVNode("input", {
3942
4030
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
3943
4031
  type: "text",
@@ -3951,7 +4039,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3951
4039
  "aria-label": unref(t)("collectionsView.clearSearch"),
3952
4040
  class: "absolute inset-y-0 right-0 flex items-center pr-2.5 text-slate-400 hover:text-slate-600",
3953
4041
  onClick: _cache[1] || (_cache[1] = ($event) => searchQuery.value = "")
3954
- }, [..._cache[30] || (_cache[30] = [createElementVNode("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_18$1)) : createCommentVNode("", true)
4042
+ }, [..._cache[28] || (_cache[28] = [createElementVNode("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_18$1)) : createCommentVNode("", true)
3955
4043
  ])) : createCommentVNode("", true), createElementVNode("div", _hoisted_19$1, [
3956
4044
  hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value ? (openBlock(), createElementBlock("div", {
3957
4045
  key: 0,
@@ -3965,7 +4053,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3965
4053
  "aria-pressed": activeView.value === "table",
3966
4054
  "data-testid": "collection-view-toggle-table",
3967
4055
  onClick: _cache[2] || (_cache[2] = ($event) => setView("table"))
3968
- }, [_cache[32] || (_cache[32] = createElementVNode("span", { class: "material-icons text-sm" }, "table_rows", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewTable")), 1)], 10, _hoisted_21$1),
4056
+ }, [_cache[30] || (_cache[30] = createElementVNode("span", { class: "material-icons text-sm" }, "table_rows", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewTable")), 1)], 10, _hoisted_21$1),
3969
4057
  hasCalendar.value ? (openBlock(), createElementBlock("button", {
3970
4058
  key: 0,
3971
4059
  type: "button",
@@ -3973,7 +4061,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3973
4061
  "aria-pressed": activeView.value === "calendar",
3974
4062
  "data-testid": "collection-view-toggle-calendar",
3975
4063
  onClick: _cache[3] || (_cache[3] = ($event) => setView("calendar"))
3976
- }, [_cache[33] || (_cache[33] = createElementVNode("span", { class: "material-icons text-sm" }, "calendar_month", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_22$1)) : createCommentVNode("", true),
4064
+ }, [_cache[31] || (_cache[31] = createElementVNode("span", { class: "material-icons text-sm" }, "calendar_month", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_22$1)) : createCommentVNode("", true),
3977
4065
  hasKanban.value ? (openBlock(), createElementBlock("button", {
3978
4066
  key: 1,
3979
4067
  type: "button",
@@ -3981,7 +4069,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
3981
4069
  "aria-pressed": activeView.value === "kanban",
3982
4070
  "data-testid": "collection-view-toggle-kanban",
3983
4071
  onClick: _cache[4] || (_cache[4] = ($event) => setView("kanban"))
3984
- }, [_cache[34] || (_cache[34] = createElementVNode("span", { class: "material-icons text-sm" }, "view_kanban", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_23$1)) : createCommentVNode("", true),
4072
+ }, [_cache[32] || (_cache[32] = createElementVNode("span", { class: "material-icons text-sm" }, "view_kanban", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_23$1)) : createCommentVNode("", true),
3985
4073
  (openBlock(true), createElementBlock(Fragment, null, renderList(customViews.value, (cv) => {
3986
4074
  return openBlock(), createElementBlock("button", {
3987
4075
  key: cv.id,
@@ -4000,7 +4088,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4000
4088
  "aria-label": unref(t)("collectionsView.addView"),
4001
4089
  "data-testid": "collection-view-add",
4002
4090
  onClick: addCustomView
4003
- }, [..._cache[35] || (_cache[35] = [createElementVNode("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_26)) : createCommentVNode("", true),
4091
+ }, [..._cache[33] || (_cache[33] = [createElementVNode("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_26)) : createCommentVNode("", true),
4004
4092
  canConfigureViews.value ? (openBlock(), createElementBlock("button", {
4005
4093
  key: 3,
4006
4094
  type: "button",
@@ -4009,7 +4097,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4009
4097
  "aria-label": unref(t)("collectionsView.config.open"),
4010
4098
  "data-testid": "collection-config-open",
4011
4099
  onClick: _cache[5] || (_cache[5] = ($event) => configOpen.value = true)
4012
- }, [..._cache[36] || (_cache[36] = [createElementVNode("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_27)) : createCommentVNode("", true)
4100
+ }, [..._cache[34] || (_cache[34] = [createElementVNode("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_27)) : createCommentVNode("", true)
4013
4101
  ], 8, _hoisted_20$1)) : createCommentVNode("", true),
4014
4102
  calendarActive.value && dateFields.value.length > 1 ? (openBlock(), createElementBlock("select", {
4015
4103
  key: 1,
@@ -4043,16 +4131,16 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4043
4131
  })), 1)) : createCommentVNode("", true)
4044
4132
  ])])) : createCommentVNode("", true),
4045
4133
  collection.value && dataIssues.value.length > 0 ? (openBlock(), createElementBlock("div", _hoisted_33, [
4046
- _cache[38] || (_cache[38] = createElementVNode("span", { class: "material-icons text-amber-600" }, "warning", -1)),
4134
+ _cache[36] || (_cache[36] = createElementVNode("span", { class: "material-icons text-amber-600" }, "warning", -1)),
4047
4135
  createElementVNode("span", _hoisted_34, toDisplayString(unref(t)("collectionsView.dataIssuesDetected", { count: dataIssues.value.length })), 1),
4048
4136
  createElementVNode("button", {
4049
4137
  type: "button",
4050
4138
  class: "h-8 px-2.5 flex items-center gap-1 rounded border border-amber-300 bg-white hover:bg-amber-100 text-amber-700 font-bold text-xs transition-colors",
4051
4139
  "data-testid": "collections-repair",
4052
4140
  onClick: repairCollection
4053
- }, [_cache[37] || (_cache[37] = createElementVNode("span", { class: "material-icons text-sm" }, "build", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.repair")), 1)])
4141
+ }, [_cache[35] || (_cache[35] = createElementVNode("span", { class: "material-icons text-sm" }, "build", -1)), createElementVNode("span", null, toDisplayString(unref(t)("collectionsView.repair")), 1)])
4054
4142
  ])) : createCommentVNode("", true),
4055
- createElementVNode("div", _hoisted_35, [loading.value ? (openBlock(), createElementBlock("div", _hoisted_36, [_cache[39] || (_cache[39] = createElementVNode("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), createElementVNode("span", null, toDisplayString(unref(t)("common.loading")), 1)])) : loadError.value ? (openBlock(), createElementBlock("div", _hoisted_37, [_cache[40] || (_cache[40] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)), createElementVNode("span", null, toDisplayString(loadError.value === "not-found" ? unref(t)("collectionsView.notFound") : `${unref(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? (openBlock(), createElementBlock("div", _hoisted_38)) : calendarActive.value ? (openBlock(), createElementBlock("div", _hoisted_39, [createVNode(CollectionCalendarView_default, {
4143
+ createElementVNode("div", _hoisted_35, [loading.value ? (openBlock(), createElementBlock("div", _hoisted_36, [_cache[37] || (_cache[37] = createElementVNode("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), createElementVNode("span", null, toDisplayString(unref(t)("common.loading")), 1)])) : loadError.value ? (openBlock(), createElementBlock("div", _hoisted_37, [_cache[38] || (_cache[38] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)), createElementVNode("span", null, toDisplayString(loadError.value === "not-found" ? unref(t)("collectionsView.notFound") : `${unref(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? (openBlock(), createElementBlock("div", _hoisted_38)) : calendarActive.value ? (openBlock(), createElementBlock("div", _hoisted_39, [createVNode(CollectionCalendarView_default, {
4056
4144
  schema: collection.value.schema,
4057
4145
  items: filteredItems.value,
4058
4146
  "anchor-field": calendarAnchorField.value,
@@ -4137,14 +4225,14 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4137
4225
  "can-create",
4138
4226
  "show-detail"
4139
4227
  ])) : createCommentVNode("", true)])) : kanbanActive.value ? (openBlock(), createElementBlock("div", _hoisted_40, [inlineError.value ? (openBlock(), createElementBlock("div", _hoisted_41, [
4140
- _cache[42] || (_cache[42] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)),
4228
+ _cache[40] || (_cache[40] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)),
4141
4229
  createElementVNode("span", _hoisted_42, toDisplayString(unref(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
4142
4230
  createElementVNode("button", {
4143
4231
  type: "button",
4144
4232
  class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
4145
4233
  "aria-label": unref(t)("common.close"),
4146
4234
  onClick: _cache[10] || (_cache[10] = ($event) => inlineError.value = null)
4147
- }, [..._cache[41] || (_cache[41] = [createElementVNode("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_43)
4235
+ }, [..._cache[39] || (_cache[39] = [createElementVNode("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_43)
4148
4236
  ])) : createCommentVNode("", true), createElementVNode("div", _hoisted_44, [createVNode(CollectionKanbanView_default, {
4149
4237
  schema: collection.value.schema,
4150
4238
  items: filteredItems.value,
@@ -4162,8 +4250,8 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4162
4250
  ])])])) : activeCustomView.value ? (openBlock(), createElementBlock("div", _hoisted_45, [createVNode(CollectionCustomView_default, {
4163
4251
  slug: collection.value.slug,
4164
4252
  view: activeCustomView.value
4165
- }, null, 8, ["slug", "view"])])) : items.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div", _hoisted_46, [_cache[43] || (_cache[43] = createElementVNode("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), createElementVNode("p", _hoisted_47, toDisplayString(unref(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div", _hoisted_48, [
4166
- _cache[44] || (_cache[44] = createElementVNode("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
4253
+ }, null, 8, ["slug", "view"])])) : items.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div", _hoisted_46, [_cache[41] || (_cache[41] = createElementVNode("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), createElementVNode("p", _hoisted_47, toDisplayString(unref(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? (openBlock(), createElementBlock("div", _hoisted_48, [
4254
+ _cache[42] || (_cache[42] = createElementVNode("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
4167
4255
  createElementVNode("p", _hoisted_49, toDisplayString(unref(t)("collectionsView.noMatchingItems")), 1),
4168
4256
  createElementVNode("button", {
4169
4257
  type: "button",
@@ -4171,14 +4259,14 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4171
4259
  onClick: _cache[11] || (_cache[11] = ($event) => searchQuery.value = "")
4172
4260
  }, toDisplayString(unref(t)("collectionsView.clearSearch")), 1)
4173
4261
  ])) : (openBlock(), createElementBlock("div", _hoisted_50, [inlineError.value ? (openBlock(), createElementBlock("div", _hoisted_51, [
4174
- _cache[46] || (_cache[46] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)),
4262
+ _cache[44] || (_cache[44] = createElementVNode("span", { class: "material-icons text-red-600" }, "error", -1)),
4175
4263
  createElementVNode("span", _hoisted_52, toDisplayString(unref(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
4176
4264
  createElementVNode("button", {
4177
4265
  type: "button",
4178
4266
  class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
4179
4267
  "aria-label": unref(t)("common.close"),
4180
4268
  onClick: _cache[12] || (_cache[12] = ($event) => inlineError.value = null)
4181
- }, [..._cache[45] || (_cache[45] = [createElementVNode("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_53)
4269
+ }, [..._cache[43] || (_cache[43] = [createElementVNode("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_53)
4182
4270
  ])) : createCommentVNode("", true), createElementVNode("table", _hoisted_54, [createElementVNode("thead", null, [createElementVNode("tr", _hoisted_55, [(openBlock(true), createElementBlock(Fragment, null, renderList(listColumnFields.value, ([key, field]) => {
4183
4271
  return openBlock(), createElementBlock("th", {
4184
4272
  key,
@@ -4231,57 +4319,51 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4231
4319
  "aria-label": field.label,
4232
4320
  onClick: _cache[15] || (_cache[15] = withModifiers(() => {}, ["stop"])),
4233
4321
  onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.checked)
4234
- }, null, 40, _hoisted_64)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? (openBlock(), createElementBlock("span", _hoisted_65, [createVNode(_component_router_link, {
4235
- to: {
4236
- path: `/collections/${field.to}`,
4237
- query: { selected: String(item[key]) }
4238
- },
4322
+ }, null, 40, _hoisted_64)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? (openBlock(), createElementBlock("span", _hoisted_65, [createElementVNode("a", {
4323
+ href: unref(cui).recordHref?.(field.to, String(item[key])),
4324
+ tabindex: unref(cui).recordHref?.(field.to, String(item[key])) ? void 0 : 0,
4325
+ role: "link",
4239
4326
  class: "text-indigo-600 hover:text-indigo-800 hover:underline font-semibold",
4240
4327
  "data-testid": `collections-ref-link-${key}-${item[key]}`,
4241
- onClick: _cache[16] || (_cache[16] = withModifiers(() => {}, ["stop"]))
4242
- }, {
4243
- default: withCtx(() => [createTextVNode(toDisplayString(unref(refDisplay)(field.to, String(item[key]))), 1)]),
4244
- _: 2
4245
- }, 1032, ["to", "data-testid"])])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? (openBlock(), createElementBlock("select", {
4328
+ onClick: ($event) => unref(activateRefLink)($event, field.to, String(item[key]), true),
4329
+ onKeydown: [withKeys(($event) => unref(activateRefLink)($event, field.to, String(item[key]), true), ["enter"]), withKeys(($event) => unref(activateRefLink)($event, field.to, String(item[key]), true), ["space"])]
4330
+ }, toDisplayString(unref(refDisplay)(field.to, String(item[key]))), 41, _hoisted_66)])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? (openBlock(), createElementBlock("select", {
4246
4331
  key: 3,
4247
4332
  value: item[key] == null ? "" : String(item[key]),
4248
4333
  disabled: isRowInlineSaving(item),
4249
4334
  class: normalizeClass(["rounded-lg border px-2 py-0.5 text-[11px] font-semibold focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed", enumControlClass(String(key), item[key])]),
4250
4335
  "data-testid": `collections-inline-enum-${key}-${item[collection.value.schema.primaryKey]}`,
4251
4336
  "aria-label": field.label,
4252
- onClick: _cache[17] || (_cache[17] = withModifiers(() => {}, ["stop"])),
4337
+ onClick: _cache[16] || (_cache[16] = withModifiers(() => {}, ["stop"])),
4253
4338
  onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.value)
4254
- }, [showEnumPlaceholder(item, String(key)) ? (openBlock(), createElementBlock("option", _hoisted_67, toDisplayString(unref(t)("collectionsView.selectPlaceholder")), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(field.values, (value) => {
4339
+ }, [showEnumPlaceholder(item, String(key)) ? (openBlock(), createElementBlock("option", _hoisted_68, toDisplayString(unref(t)("collectionsView.selectPlaceholder")), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(field.values, (value) => {
4255
4340
  return openBlock(), createElementBlock("option", {
4256
4341
  key: value,
4257
4342
  value
4258
- }, toDisplayString(value), 9, _hoisted_68);
4259
- }), 128))], 42, _hoisted_66)) : field.type === "money" ? (openBlock(), createElementBlock("span", _hoisted_69, toDisplayString(unref(formatMoney)(item[key], unref(resolveCurrency)(field, item), unref(locale))), 1)) : field.type === "table" ? (openBlock(), createElementBlock("span", _hoisted_70, [_cache[47] || (_cache[47] = createElementVNode("span", { class: "material-icons text-[11px]" }, "list", -1)), createElementVNode("span", null, toDisplayString(tableSummary(item[key])), 1)])) : field.type === "derived" ? (openBlock(), createElementBlock("span", _hoisted_71, toDisplayString(unref(derivedDisplay)(field, unref(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && unref(isExternalUrl)(item[key]) ? (openBlock(), createElementBlock("a", {
4343
+ }, toDisplayString(value), 9, _hoisted_69);
4344
+ }), 128))], 42, _hoisted_67)) : field.type === "money" ? (openBlock(), createElementBlock("span", _hoisted_70, toDisplayString(unref(formatMoney)(item[key], unref(resolveCurrency)(field, item), unref(locale))), 1)) : field.type === "table" ? (openBlock(), createElementBlock("span", _hoisted_71, [_cache[45] || (_cache[45] = createElementVNode("span", { class: "material-icons text-[11px]" }, "list", -1)), createElementVNode("span", null, toDisplayString(tableSummary(item[key])), 1)])) : field.type === "derived" ? (openBlock(), createElementBlock("span", _hoisted_72, toDisplayString(unref(derivedDisplay)(field, unref(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && unref(isExternalUrl)(item[key]) ? (openBlock(), createElementBlock("a", {
4260
4345
  key: 7,
4261
4346
  href: String(item[key]),
4262
4347
  target: "_blank",
4263
4348
  rel: "noopener noreferrer",
4264
4349
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
4265
4350
  "data-testid": `collections-url-link-${key}-${item[collection.value.schema.primaryKey]}`,
4266
- onClick: _cache[18] || (_cache[18] = withModifiers(() => {}, ["stop"]))
4267
- }, toDisplayString(String(item[key])), 9, _hoisted_72)) : field.type === "file" && unref(artifactUrl)(item[key]) ? (openBlock(), createElementBlock("a", {
4351
+ onClick: _cache[17] || (_cache[17] = withModifiers(() => {}, ["stop"]))
4352
+ }, toDisplayString(String(item[key])), 9, _hoisted_73)) : field.type === "file" && unref(artifactUrl)(item[key]) ? (openBlock(), createElementBlock("a", {
4268
4353
  key: 8,
4269
4354
  href: unref(artifactUrl)(item[key]) ?? void 0,
4270
4355
  target: "_blank",
4271
4356
  rel: "noopener noreferrer",
4272
4357
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
4273
4358
  "data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
4274
- onClick: _cache[19] || (_cache[19] = withModifiers(() => {}, ["stop"]))
4275
- }, toDisplayString(String(item[key])), 9, _hoisted_73)) : field.type === "file" && unref(fileRoutePath)(item[key]) ? (openBlock(), createBlock(_component_router_link, {
4359
+ onClick: _cache[18] || (_cache[18] = withModifiers(() => {}, ["stop"]))
4360
+ }, toDisplayString(String(item[key])), 9, _hoisted_74)) : field.type === "file" && unref(fileRoutePath)(item[key]) ? (openBlock(), createElementBlock("a", {
4276
4361
  key: 9,
4277
- to: unref(fileRoutePath)(item[key]) ?? "",
4362
+ href: unref(fileRoutePath)(item[key]) ?? void 0,
4278
4363
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
4279
4364
  "data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
4280
- onClick: _cache[20] || (_cache[20] = withModifiers(() => {}, ["stop"]))
4281
- }, {
4282
- default: withCtx(() => [createTextVNode(toDisplayString(String(item[key])), 1)]),
4283
- _: 2
4284
- }, 1032, ["to", "data-testid"])) : (openBlock(), createElementBlock("span", _hoisted_74, toDisplayString(unref(formatCell)(item[key], field.type)), 1))], 64)) : createCommentVNode("", true)]);
4365
+ onClick: ($event) => unref(activatePathLink)($event, unref(fileRoutePath)(item[key]) ?? "", true)
4366
+ }, toDisplayString(String(item[key])), 9, _hoisted_75)) : (openBlock(), createElementBlock("span", _hoisted_76, toDisplayString(unref(formatCell)(item[key], field.type)), 1))], 64)) : createCommentVNode("", true)]);
4285
4367
  }), 128))], 42, _hoisted_62);
4286
4368
  }), 128))])])]))]),
4287
4369
  collection.value && (viewing.value || editing.value) && !(calendarActive.value && openDay.value) ? (openBlock(), createBlock(CollectionRecordModal_default, {
@@ -4290,7 +4372,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4290
4372
  }, {
4291
4373
  default: withCtx(() => [createVNode(CollectionRecordPanel_default, {
4292
4374
  editing: editing.value,
4293
- "onUpdate:editing": _cache[21] || (_cache[21] = ($event) => editing.value = $event),
4375
+ "onUpdate:editing": _cache[19] || (_cache[19] = ($event) => editing.value = $event),
4294
4376
  collection: collection.value,
4295
4377
  viewing: viewing.value,
4296
4378
  saving: saving.value,
@@ -4308,7 +4390,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4308
4390
  onCancel: cancelEditor,
4309
4391
  onEdit: editFromView,
4310
4392
  onClose: closeView,
4311
- onDelete: _cache[22] || (_cache[22] = ($event) => viewing.value && confirmDelete(viewing.value)),
4393
+ onDelete: _cache[20] || (_cache[20] = ($event) => viewing.value && confirmDelete(viewing.value)),
4312
4394
  onRunAction: runAction
4313
4395
  }, null, 8, [
4314
4396
  "editing",
@@ -4334,7 +4416,7 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4334
4416
  title: collection.value.title,
4335
4417
  views: customViews.value,
4336
4418
  onChanged: onViewsChanged,
4337
- onClose: _cache[23] || (_cache[23] = ($event) => configOpen.value = false)
4419
+ onClose: _cache[21] || (_cache[21] = ($event) => configOpen.value = false)
4338
4420
  }, null, 8, [
4339
4421
  "slug",
4340
4422
  "title",
@@ -4349,29 +4431,29 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4349
4431
  "data-testid": "collections-chat-modal",
4350
4432
  onClick: withModifiers(closeChat, ["self"]),
4351
4433
  onKeydown: withKeys(closeChat, ["esc"])
4352
- }, [createElementVNode("div", _hoisted_75, [
4353
- createElementVNode("header", _hoisted_76, [
4354
- _cache[49] || (_cache[49] = createElementVNode("div", { class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100/50" }, [createElementVNode("span", { class: "material-icons text-lg" }, "forum")], -1)),
4355
- createElementVNode("div", _hoisted_77, [createElementVNode("h2", _hoisted_78, toDisplayString(unref(t)("collectionsView.chatTitle")), 1), createElementVNode("span", _hoisted_79, toDisplayString(collection.value.title), 1)]),
4434
+ }, [createElementVNode("div", _hoisted_77, [
4435
+ createElementVNode("header", _hoisted_78, [
4436
+ _cache[47] || (_cache[47] = createElementVNode("div", { class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100/50" }, [createElementVNode("span", { class: "material-icons text-lg" }, "forum")], -1)),
4437
+ createElementVNode("div", _hoisted_79, [createElementVNode("h2", _hoisted_80, toDisplayString(unref(t)("collectionsView.chatTitle")), 1), createElementVNode("span", _hoisted_81, toDisplayString(collection.value.title), 1)]),
4356
4438
  createElementVNode("button", {
4357
4439
  type: "button",
4358
4440
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-400 hover:bg-slate-200/50 hover:text-slate-600 transition-colors",
4359
4441
  "aria-label": unref(t)("common.close"),
4360
4442
  "data-testid": "collections-chat-close",
4361
4443
  onClick: closeChat
4362
- }, [..._cache[48] || (_cache[48] = [createElementVNode("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_80)
4444
+ }, [..._cache[46] || (_cache[46] = [createElementVNode("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_82)
4363
4445
  ]),
4364
- createElementVNode("div", _hoisted_81, [withDirectives(createElementVNode("textarea", {
4446
+ createElementVNode("div", _hoisted_83, [withDirectives(createElementVNode("textarea", {
4365
4447
  ref_key: "chatInputEl",
4366
4448
  ref: chatInputEl,
4367
- "onUpdate:modelValue": _cache[24] || (_cache[24] = ($event) => chatMessage.value = $event),
4449
+ "onUpdate:modelValue": _cache[22] || (_cache[22] = ($event) => chatMessage.value = $event),
4368
4450
  rows: "4",
4369
4451
  placeholder: unref(t)("collectionsView.chatPlaceholder"),
4370
4452
  class: "w-full bg-slate-50 border border-slate-200/80 rounded-xl px-3 py-2.5 text-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-indigo-500/20 focus:border-indigo-500 focus:bg-white transition-all resize-none",
4371
4453
  "data-testid": "collections-chat-input",
4372
4454
  onKeydown: [withKeys(withModifiers(submitChat, ["meta"]), ["enter"]), withKeys(withModifiers(submitChat, ["ctrl"]), ["enter"])]
4373
- }, null, 40, _hoisted_82), [[vModelText, chatMessage.value]])]),
4374
- createElementVNode("footer", _hoisted_83, [createElementVNode("button", {
4455
+ }, null, 40, _hoisted_84), [[vModelText, chatMessage.value]])]),
4456
+ createElementVNode("footer", _hoisted_85, [createElementVNode("button", {
4375
4457
  type: "button",
4376
4458
  class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
4377
4459
  "data-testid": "collections-chat-cancel",
@@ -4382,13 +4464,121 @@ var CollectionView_default = /* @__PURE__ */ defineComponent({
4382
4464
  disabled: !chatMessage.value.trim(),
4383
4465
  "data-testid": "collections-chat-send",
4384
4466
  onClick: submitChat
4385
- }, toDisplayString(unref(t)("collectionsView.chatStart")), 9, _hoisted_84)])
4467
+ }, toDisplayString(unref(t)("collectionsView.chatStart")), 9, _hoisted_86)])
4386
4468
  ])], 32)) : createCommentVNode("", true)
4387
4469
  ]);
4388
4470
  };
4389
4471
  }
4390
4472
  });
4391
4473
  //#endregion
4474
+ //#region src/vue/chat/View.vue?vue&type=script&setup=true&lang.ts
4475
+ var _hoisted_1$3 = {
4476
+ class: "w-full h-full",
4477
+ "data-testid": "present-collection"
4478
+ };
4479
+ //#endregion
4480
+ //#region src/vue/chat/View.vue
4481
+ var View_default = /* @__PURE__ */ defineComponent({
4482
+ __name: "View",
4483
+ props: {
4484
+ selectedResult: {},
4485
+ sendTextMessage: { type: Function }
4486
+ },
4487
+ emits: ["updateResult"],
4488
+ setup(__props, { emit: __emit }) {
4489
+ const props = __props;
4490
+ const emit = __emit;
4491
+ const data = computed(() => props.selectedResult?.data ?? props.selectedResult?.jsonData ?? null);
4492
+ const slug = computed(() => data.value?.collectionSlug);
4493
+ const viewState = computed(() => props.selectedResult?.viewState ?? null);
4494
+ /** Open record: the card-local `viewState.selected` once the user has
4495
+ * navigated within the card (including an explicit close → null), else
4496
+ * the tool's initial `itemId`. */
4497
+ const selected = computed(() => {
4498
+ const state = viewState.value;
4499
+ if (state && "selected" in state) return state.selected ?? void 0;
4500
+ return data.value?.itemId;
4501
+ });
4502
+ function onSelect(itemId) {
4503
+ if (!props.selectedResult) return;
4504
+ emit("updateResult", {
4505
+ ...props.selectedResult,
4506
+ viewState: {
4507
+ ...viewState.value,
4508
+ selected: itemId
4509
+ }
4510
+ });
4511
+ }
4512
+ function onViewStateChange(state) {
4513
+ if (!props.selectedResult) return;
4514
+ const current = viewState.value;
4515
+ if (current?.view === state.view && current?.anchorField === state.anchorField && current?.groupField === state.groupField) return;
4516
+ emit("updateResult", {
4517
+ ...props.selectedResult,
4518
+ viewState: {
4519
+ ...current,
4520
+ view: state.view,
4521
+ anchorField: state.anchorField,
4522
+ groupField: state.groupField
4523
+ }
4524
+ });
4525
+ }
4526
+ return (_ctx, _cache) => {
4527
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [slug.value ? (openBlock(), createBlock(CollectionView_default, {
4528
+ key: 0,
4529
+ slug: slug.value,
4530
+ selected: selected.value,
4531
+ "initial-view": viewState.value?.view,
4532
+ "initial-anchor-field": viewState.value?.anchorField,
4533
+ "initial-group-field": viewState.value?.groupField,
4534
+ "send-text-message": __props.sendTextMessage,
4535
+ onSelect,
4536
+ onViewStateChange
4537
+ }, null, 8, [
4538
+ "slug",
4539
+ "selected",
4540
+ "initial-view",
4541
+ "initial-anchor-field",
4542
+ "initial-group-field",
4543
+ "send-text-message"
4544
+ ])) : createCommentVNode("", true)]);
4545
+ };
4546
+ }
4547
+ });
4548
+ //#endregion
4549
+ //#region src/vue/chat/Preview.vue?vue&type=script&setup=true&lang.ts
4550
+ var _hoisted_1$2 = { class: "w-full h-full flex flex-col items-center justify-center p-4 bg-gradient-to-br from-indigo-50 to-slate-50 rounded-lg border-2 border-gray-200" };
4551
+ var _hoisted_2$2 = { class: "text-center" };
4552
+ var _hoisted_3$2 = { class: "text-gray-900 font-bold text-lg mb-1 line-clamp-2" };
4553
+ var _hoisted_4$2 = {
4554
+ key: 0,
4555
+ class: "text-gray-600 text-sm"
4556
+ };
4557
+ var _hoisted_5$2 = {
4558
+ key: 1,
4559
+ class: "text-gray-500 text-sm"
4560
+ };
4561
+ //#endregion
4562
+ //#region src/vue/chat/Preview.vue
4563
+ var Preview_default = /* @__PURE__ */ defineComponent({
4564
+ __name: "Preview",
4565
+ props: { result: {} },
4566
+ setup(__props) {
4567
+ const { t } = useCollectionI18n();
4568
+ const props = __props;
4569
+ const data = computed(() => props.result?.data ?? props.result?.jsonData ?? null);
4570
+ const collectionSlug = computed(() => data.value?.collectionSlug ?? "");
4571
+ const itemId = computed(() => data.value?.itemId);
4572
+ return (_ctx, _cache) => {
4573
+ return openBlock(), createElementBlock("div", _hoisted_1$2, [createElementVNode("div", _hoisted_2$2, [
4574
+ _cache[0] || (_cache[0] = createElementVNode("span", { class: "material-icons text-4xl text-indigo-600 mb-2" }, "collections_bookmark", -1)),
4575
+ createElementVNode("h3", _hoisted_3$2, toDisplayString(collectionSlug.value || unref(t)("pluginPresentCollection.fallbackTitle")), 1),
4576
+ itemId.value ? (openBlock(), createElementBlock("p", _hoisted_4$2, toDisplayString(unref(t)("pluginPresentCollection.itemLabel", { id: itemId.value })), 1)) : (openBlock(), createElementBlock("p", _hoisted_5$2, toDisplayString(unref(t)("pluginPresentCollection.listLabel")), 1))
4577
+ ])]);
4578
+ };
4579
+ }
4580
+ });
4581
+ //#endregion
4392
4582
  //#region src/vue/components/CollectionsIndexView.vue?vue&type=script&setup=true&lang.ts
4393
4583
  var _hoisted_1$1 = {
4394
4584
  class: "h-full overflow-y-auto bg-slate-50/50 px-6 py-6",
@@ -4696,6 +4886,16 @@ var FeedsView_default = /* @__PURE__ */ defineComponent({
4696
4886
  }
4697
4887
  });
4698
4888
  //#endregion
4699
- export { CollectionCalendarView_default as CollectionCalendarView, CollectionCustomView_default as CollectionCustomView, CollectionDayView_default as CollectionDayView, CollectionEmbedView_default as CollectionEmbedView, CollectionKanbanView_default as CollectionKanbanView, CollectionRecordModal_default as CollectionRecordModal, CollectionRecordPanel_default as CollectionRecordPanel, CollectionView_default as CollectionView, CollectionViewConfigModal_default as CollectionViewConfigModal, CollectionsIndexView_default as CollectionsIndexView, FeedsView_default as FeedsView, collectionUi, configureCollectionUi, readCollectionSort, readCollectionViewMode, useCollectionRendering, writeCollectionSort, writeCollectionViewMode };
4889
+ //#region src/vue/index.ts
4890
+ var plugin = {
4891
+ toolDefinition: TOOL_DEFINITION,
4892
+ execute: executePresentCollection,
4893
+ generatingMessage: "Loading collection...",
4894
+ isEnabled: () => true,
4895
+ viewComponent: View_default,
4896
+ previewComponent: Preview_default
4897
+ };
4898
+ //#endregion
4899
+ export { CollectionCalendarView_default as CollectionCalendarView, CollectionCustomView_default as CollectionCustomView, CollectionDayView_default as CollectionDayView, CollectionEmbedView_default as CollectionEmbedView, CollectionKanbanView_default as CollectionKanbanView, CollectionRecordModal_default as CollectionRecordModal, CollectionRecordPanel_default as CollectionRecordPanel, CollectionView_default as CollectionView, CollectionViewConfigModal_default as CollectionViewConfigModal, CollectionsIndexView_default as CollectionsIndexView, FeedsView_default as FeedsView, Preview_default as PresentCollectionPreview, View_default as PresentCollectionView, collectionUi, configureCollectionUi, plugin, readCollectionSort, readCollectionViewMode, useCollectionRendering, writeCollectionSort, writeCollectionViewMode };
4700
4900
 
4701
4901
  //# sourceMappingURL=vue.js.map