@mulmoclaude/collection-plugin 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) 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/uiContext.d.ts +18 -0
  30. package/dist/vue/uiContext.d.ts.map +1 -1
  31. package/dist/vue.cjs +882 -710
  32. package/dist/vue.cjs.map +1 -1
  33. package/dist/vue.js +881 -712
  34. package/dist/vue.js.map +1 -1
  35. package/package.json +1 -1
  36. package/dist/index.cjs.map +0 -1
  37. package/dist/index.js.map +0 -1
  38. package/dist/promptSafety-BSO2gdsg.cjs.map +0 -1
  39. package/dist/promptSafety-DShN_VCG.js.map +0 -1
package/dist/vue.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-CKQMccvm.cjs");
3
- const require_promptSafety = require("./promptSafety-BSO2gdsg.cjs");
3
+ const require_promptSafety = require("./promptSafety-B0pPJkfQ.cjs");
4
4
  const require_deriveAll = require("./deriveAll-BJ0Lvm1Q.cjs");
5
5
  let vue = require("vue");
6
6
  let vue_i18n = require("vue-i18n");
@@ -17,395 +17,6 @@ function collectionUi() {
17
17
  return current;
18
18
  }
19
19
  //#endregion
20
- //#region src/vue/useCollectionRendering.ts
21
- function useCollectionRendering(collection, locale) {
22
- const refCache = (0, vue.ref)({});
23
- const refRecordCache = (0, vue.ref)({});
24
- const embedCache = (0, vue.ref)({});
25
- function resetLinkedCaches() {
26
- refCache.value = {};
27
- refRecordCache.value = {};
28
- embedCache.value = {};
29
- }
30
- function uniqueRefTargets(schema) {
31
- const targets = /* @__PURE__ */ new Set();
32
- const walk = (fields) => {
33
- for (const field of Object.values(fields)) {
34
- if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
35
- if (field.type === "table" && field.of) walk(field.of);
36
- }
37
- };
38
- walk(schema.fields);
39
- return [...targets];
40
- }
41
- function uniqueEmbedTargets(schema) {
42
- const targets = /* @__PURE__ */ new Set();
43
- for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
44
- return [...targets];
45
- }
46
- function buildRefDisplayMap(detail) {
47
- const { fields, primaryKey } = detail.collection.schema;
48
- const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
49
- const map = {};
50
- for (const item of detail.items) {
51
- const slugRaw = item[primaryKey];
52
- if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
53
- const displayRaw = item[displayField];
54
- map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
55
- }
56
- return map;
57
- }
58
- function buildRefRecordMap(detail) {
59
- const { schema } = detail.collection;
60
- const map = {};
61
- for (const item of detail.items) {
62
- const slugRaw = item[schema.primaryKey];
63
- if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = require_deriveAll.deriveAll(schema, item, {});
64
- }
65
- return map;
66
- }
67
- async function loadLinkedCollections(schema, expectedSlug) {
68
- const refTargets = new Set(uniqueRefTargets(schema));
69
- const embedTargets = new Set(uniqueEmbedTargets(schema));
70
- const allTargets = [...new Set([...refTargets, ...embedTargets])];
71
- if (allTargets.length === 0) return;
72
- const binding = collectionUi();
73
- const results = await Promise.all(allTargets.map(async (target) => {
74
- try {
75
- return {
76
- target,
77
- result: await binding.fetchCollectionDetail(target)
78
- };
79
- } catch {
80
- return {
81
- target,
82
- result: { ok: false }
83
- };
84
- }
85
- }));
86
- if (collection.value?.slug !== expectedSlug) return;
87
- const nextRef = {};
88
- const nextRefRecords = {};
89
- const nextEmbed = {};
90
- for (const { target, result } of results) {
91
- if (!result.ok) continue;
92
- if (refTargets.has(target)) {
93
- nextRef[target] = buildRefDisplayMap(result.data);
94
- nextRefRecords[target] = buildRefRecordMap(result.data);
95
- }
96
- if (embedTargets.has(target)) nextEmbed[target] = {
97
- schema: result.data.collection.schema,
98
- items: result.data.items
99
- };
100
- }
101
- refCache.value = nextRef;
102
- refRecordCache.value = nextRefRecords;
103
- embedCache.value = nextEmbed;
104
- }
105
- function refDisplay(targetSlug, itemSlug) {
106
- const map = refCache.value[targetSlug];
107
- return map && map[itemSlug] || itemSlug;
108
- }
109
- function refOptions(targetSlug) {
110
- const map = refCache.value[targetSlug];
111
- if (!map) return [];
112
- return Object.entries(map).map(([slug, display]) => ({
113
- slug,
114
- display
115
- })).sort((left, right) => left.display.localeCompare(right.display));
116
- }
117
- function resolveEmbed(field) {
118
- if (field.type !== "embed" || !field.to || !field.id) return {
119
- schema: null,
120
- item: null
121
- };
122
- const data = embedCache.value[field.to];
123
- if (!data) return {
124
- schema: null,
125
- item: null
126
- };
127
- const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === field.id) ?? null;
128
- return {
129
- schema: data.schema,
130
- item
131
- };
132
- }
133
- function embedValue(field, value, record) {
134
- if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale.value);
135
- return detailText(value);
136
- }
137
- const embedViews = (0, vue.computed)(() => {
138
- const out = {};
139
- if (!collection.value) return out;
140
- for (const [key, field] of Object.entries(collection.value.schema.fields)) {
141
- if (field.type !== "embed") continue;
142
- const { schema, item } = resolveEmbed(field);
143
- const rows = [];
144
- if (schema && item) for (const [subKey, subField] of Object.entries(schema.fields)) {
145
- const value = item[subKey];
146
- if (value === void 0 || value === null || value === "") continue;
147
- rows.push({
148
- key: subKey,
149
- label: subField.label,
150
- type: subField.type,
151
- value,
152
- display: embedValue(subField, value, item)
153
- });
154
- }
155
- out[key] = {
156
- found: Boolean(item),
157
- rows,
158
- targetSlug: field.to ?? "",
159
- recordId: field.id ?? ""
160
- };
161
- }
162
- return out;
163
- });
164
- function resolveCurrency(field, record) {
165
- if (field.currencyField && record) {
166
- const code = record[field.currencyField];
167
- if (typeof code === "string" && code.trim().length > 0) return code;
168
- }
169
- return field.currency;
170
- }
171
- function currencySymbol(currency) {
172
- const code = currency && currency.length > 0 ? currency : "USD";
173
- try {
174
- return new Intl.NumberFormat(locale.value, {
175
- style: "currency",
176
- currency: code
177
- }).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
178
- } catch {
179
- return code;
180
- }
181
- }
182
- function formatMoney(value, currency, displayLocale) {
183
- if (value === void 0 || value === "") return "—";
184
- const amount = typeof value === "number" ? value : Number(value);
185
- if (!Number.isFinite(amount)) return String(value);
186
- const currencyCode = currency && currency.length > 0 ? currency : "USD";
187
- try {
188
- return new Intl.NumberFormat(displayLocale, {
189
- style: "currency",
190
- currency: currencyCode
191
- }).format(amount);
192
- } catch {
193
- return String(amount);
194
- }
195
- }
196
- function formatCell(value, type) {
197
- if (value === void 0 || value === null || value === "") return "—";
198
- if (type === "markdown" && typeof value === "string") return value.length > 80 ? `${value.slice(0, 80)}…` : value;
199
- if (typeof value === "string" || typeof value === "number") return String(value);
200
- return JSON.stringify(value);
201
- }
202
- function isExternalUrl(value) {
203
- return typeof value === "string" && /^https?:\/\//i.test(value);
204
- }
205
- function artifactUrl(value) {
206
- return collectionUi().fileAssetUrl(value);
207
- }
208
- function fileRoutePath(value) {
209
- return collectionUi().fileRoutePath(value);
210
- }
211
- function detailText(value) {
212
- if (value === void 0 || value === null || value === "") return "—";
213
- return String(value);
214
- }
215
- function tableRows(value) {
216
- if (!Array.isArray(value)) return [];
217
- return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
218
- }
219
- function hasTableRows(value) {
220
- return tableRows(value).length > 0;
221
- }
222
- function formatSubCell(subField, value, record) {
223
- if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
224
- if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
225
- return formatCell(value, subField.type);
226
- }
227
- function inputTypeFor(type) {
228
- if (type === "email") return "email";
229
- if (type === "number") return "number";
230
- if (type === "money") return "number";
231
- if (type === "date") return "date";
232
- if (type === "datetime") return "datetime-local";
233
- return "text";
234
- }
235
- function evaluateDerivedAgainstItem(field, fieldKey, item) {
236
- if (!field.formula || !collection.value) return null;
237
- const result = require_deriveAll.deriveAll(collection.value.schema, item, refRecordCache.value)[fieldKey];
238
- return typeof result === "number" && Number.isFinite(result) ? result : null;
239
- }
240
- function derivedDisplay(field, computedValue, record) {
241
- if (computedValue === null || computedValue === void 0) return "—";
242
- if (field.display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale.value);
243
- return formatCell(computedValue, field.display ?? "number");
244
- }
245
- return {
246
- refCache,
247
- refRecordCache,
248
- embedCache,
249
- resetLinkedCaches,
250
- loadLinkedCollections,
251
- refDisplay,
252
- refOptions,
253
- embedViews,
254
- resolveCurrency,
255
- currencySymbol,
256
- formatMoney,
257
- formatCell,
258
- detailText,
259
- isExternalUrl,
260
- artifactUrl,
261
- fileRoutePath,
262
- tableRows,
263
- hasTableRows,
264
- formatSubCell,
265
- inputTypeFor,
266
- deriveAll: require_deriveAll.deriveAll,
267
- evaluateDerivedAgainstItem,
268
- derivedDisplay
269
- };
270
- }
271
- //#endregion
272
- //#region src/vue/collectionViewMode.ts
273
- var STORAGE_KEY = "collection_view_modes";
274
- var SORT_STORAGE_KEY = "collection_sorts";
275
- var BUILT_IN_MODES = [
276
- "table",
277
- "calendar",
278
- "kanban"
279
- ];
280
- /** A persisted mode is valid if it's a known built-in OR any `custom:<id>`
281
- * key (the id is validated against the live schema at render time, so an
282
- * unknown custom id simply collapses to the table there). Takes `unknown`
283
- * and type-guards `string` first: a corrupted localStorage entry could hold a
284
- * number/object, and calling `.startsWith` on that would throw. */
285
- function isValidViewMode(value) {
286
- return typeof value === "string" && (BUILT_IN_MODES.includes(value) || value.startsWith("custom:"));
287
- }
288
- function readAll() {
289
- try {
290
- const raw = localStorage.getItem(STORAGE_KEY);
291
- if (!raw) return {};
292
- const parsed = JSON.parse(raw);
293
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
294
- } catch {
295
- return {};
296
- }
297
- }
298
- function readCollectionViewMode(slug) {
299
- const stored = readAll()[slug];
300
- return isValidViewMode(stored) ? stored : null;
301
- }
302
- function writeCollectionViewMode(slug, view) {
303
- try {
304
- const all = readAll();
305
- all[slug] = view;
306
- localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
307
- } catch {}
308
- }
309
- function isSortState(value) {
310
- if (!value || typeof value !== "object") return false;
311
- const rec = value;
312
- return typeof rec.field === "string" && (rec.direction === "asc" || rec.direction === "desc");
313
- }
314
- function readAllSorts() {
315
- try {
316
- const raw = localStorage.getItem(SORT_STORAGE_KEY);
317
- if (!raw) return {};
318
- const parsed = JSON.parse(raw);
319
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
320
- const out = {};
321
- for (const [slug, value] of Object.entries(parsed)) if (isSortState(value)) out[slug] = value;
322
- return out;
323
- } catch {
324
- return {};
325
- }
326
- }
327
- function readCollectionSort(slug) {
328
- return readAllSorts()[slug] ?? null;
329
- }
330
- /** Persist (or, when `sort` is null, clear) the slug's active column sort. */
331
- function writeCollectionSort(slug, sort) {
332
- try {
333
- const all = Object.fromEntries(Object.entries(readAllSorts()).filter(([key]) => key !== slug));
334
- if (sort) all[slug] = sort;
335
- localStorage.setItem(SORT_STORAGE_KEY, JSON.stringify(all));
336
- } catch {}
337
- }
338
- //#endregion
339
- //#region src/vue/components/CollectionRecordModal.vue
340
- var CollectionRecordModal_default = /* @__PURE__ */ (0, vue.defineComponent)({
341
- __name: "CollectionRecordModal",
342
- emits: ["close"],
343
- setup(__props, { emit: __emit }) {
344
- const emit = __emit;
345
- const dialogEl = (0, vue.ref)(null);
346
- let previouslyFocused = null;
347
- const FOCUSABLE_SELECTOR = [
348
- "a[href]",
349
- "button:not([disabled])",
350
- "input:not([disabled])",
351
- "select:not([disabled])",
352
- "textarea:not([disabled])",
353
- "[tabindex]:not([tabindex=\"-1\"])"
354
- ].join(",");
355
- /** Visible, focusable controls inside the dialog, in DOM order. */
356
- function focusableItems() {
357
- if (!dialogEl.value) return [];
358
- return Array.from(dialogEl.value.querySelectorAll(FOCUSABLE_SELECTOR)).filter((node) => node.offsetParent !== null);
359
- }
360
- /** Trap Tab / Shift+Tab inside the dialog so focus can't escape to the
361
- * page behind the overlay. Wraps at both ends; the dialog container
362
- * itself (tabindex -1) counts as "before the first item". */
363
- function onTab(event) {
364
- const items = focusableItems();
365
- if (items.length === 0) {
366
- event.preventDefault();
367
- dialogEl.value?.focus();
368
- return;
369
- }
370
- const [first] = items;
371
- const last = items[items.length - 1];
372
- const active = document.activeElement;
373
- if (event.shiftKey) {
374
- if (active === first || active === dialogEl.value) {
375
- event.preventDefault();
376
- last.focus();
377
- }
378
- } else if (active === last) {
379
- event.preventDefault();
380
- first.focus();
381
- }
382
- }
383
- (0, vue.onMounted)(async () => {
384
- previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
385
- await (0, vue.nextTick)();
386
- dialogEl.value?.focus();
387
- });
388
- (0, vue.onBeforeUnmount)(() => {
389
- previouslyFocused?.focus?.();
390
- });
391
- return (_ctx, _cache) => {
392
- return (0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, { to: "body" }, [(0, vue.createElementVNode)("div", {
393
- class: "fixed inset-0 z-40 flex items-center justify-center bg-slate-900/40 p-4",
394
- "data-testid": "collections-record-modal",
395
- onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(($event) => emit("close"), ["self"]))
396
- }, [(0, vue.createElementVNode)("div", {
397
- ref_key: "dialogEl",
398
- ref: dialogEl,
399
- class: "flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl bg-white shadow-xl focus:outline-none",
400
- role: "dialog",
401
- "aria-modal": "true",
402
- tabindex: "-1",
403
- onKeydown: [_cache[0] || (_cache[0] = (0, vue.withKeys)(($event) => emit("close"), ["esc"])), (0, vue.withKeys)(onTab, ["tab"])]
404
- }, [(0, vue.renderSlot)(_ctx.$slots, "default")], 544)])]);
405
- };
406
- }
407
- });
408
- //#endregion
409
20
  //#region src/vue/lang/index.ts
410
21
  var i18n = (0, vue_i18n.createI18n)({
411
22
  legacy: false,
@@ -506,6 +117,11 @@ var i18n = (0, vue_i18n.createI18n)({
506
117
  save: "Save",
507
118
  saving: "Saving...",
508
119
  yes: "Yes"
120
+ },
121
+ pluginPresentCollection: {
122
+ fallbackTitle: "Collection",
123
+ itemLabel: "Item: {id}",
124
+ listLabel: "All records"
509
125
  }
510
126
  },
511
127
  ja: {
@@ -602,6 +218,11 @@ var i18n = (0, vue_i18n.createI18n)({
602
218
  save: "保存",
603
219
  saving: "保存中...",
604
220
  yes: "はい"
221
+ },
222
+ pluginPresentCollection: {
223
+ fallbackTitle: "コレクション",
224
+ itemLabel: "項目: {id}",
225
+ listLabel: "すべてのレコード"
605
226
  }
606
227
  },
607
228
  zh: {
@@ -698,6 +319,11 @@ var i18n = (0, vue_i18n.createI18n)({
698
319
  save: "保存",
699
320
  saving: "保存中...",
700
321
  yes: "是"
322
+ },
323
+ pluginPresentCollection: {
324
+ fallbackTitle: "集合",
325
+ itemLabel: "项目:{id}",
326
+ listLabel: "全部记录"
701
327
  }
702
328
  },
703
329
  ko: {
@@ -794,6 +420,11 @@ var i18n = (0, vue_i18n.createI18n)({
794
420
  save: "저장",
795
421
  saving: "저장 중...",
796
422
  yes: "예"
423
+ },
424
+ pluginPresentCollection: {
425
+ fallbackTitle: "컬렉션",
426
+ itemLabel: "항목: {id}",
427
+ listLabel: "모든 레코드"
797
428
  }
798
429
  },
799
430
  es: {
@@ -890,6 +521,11 @@ var i18n = (0, vue_i18n.createI18n)({
890
521
  save: "Guardar",
891
522
  saving: "Guardando...",
892
523
  yes: "Sí"
524
+ },
525
+ pluginPresentCollection: {
526
+ fallbackTitle: "Colección",
527
+ itemLabel: "Elemento: {id}",
528
+ listLabel: "Todos los registros"
893
529
  }
894
530
  },
895
531
  "pt-BR": {
@@ -986,6 +622,11 @@ var i18n = (0, vue_i18n.createI18n)({
986
622
  save: "Salvar",
987
623
  saving: "Salvando...",
988
624
  yes: "Sim"
625
+ },
626
+ pluginPresentCollection: {
627
+ fallbackTitle: "Coleção",
628
+ itemLabel: "Item: {id}",
629
+ listLabel: "Todos os registros"
989
630
  }
990
631
  },
991
632
  fr: {
@@ -1082,6 +723,11 @@ var i18n = (0, vue_i18n.createI18n)({
1082
723
  save: "Enregistrer",
1083
724
  saving: "Enregistrement...",
1084
725
  yes: "Oui"
726
+ },
727
+ pluginPresentCollection: {
728
+ fallbackTitle: "Collection",
729
+ itemLabel: "Élément : {id}",
730
+ listLabel: "Tous les enregistrements"
1085
731
  }
1086
732
  },
1087
733
  de: {
@@ -1178,6 +824,11 @@ var i18n = (0, vue_i18n.createI18n)({
1178
824
  save: "Speichern",
1179
825
  saving: "Wird gespeichert...",
1180
826
  yes: "Ja"
827
+ },
828
+ pluginPresentCollection: {
829
+ fallbackTitle: "Sammlung",
830
+ itemLabel: "Eintrag: {id}",
831
+ listLabel: "Alle Datensätze"
1181
832
  }
1182
833
  }
1183
834
  }
@@ -1209,121 +860,106 @@ function useCollectionI18n() {
1209
860
  };
1210
861
  }
1211
862
  //#endregion
1212
- //#region src/vue/components/CollectionEmbedView.vue?vue&type=script&setup=true&lang.ts
1213
- var _hoisted_1$9 = { class: "flex items-center justify-between text-[10px] font-bold text-indigo-600/90 tracking-wider uppercase" };
1214
- var _hoisted_2$9 = { class: "flex items-center gap-1.5" };
1215
- var _hoisted_3$9 = { class: "bg-indigo-100/60 text-indigo-700 px-1.5 py-0.5 rounded font-mono font-medium lowercase" };
1216
- var _hoisted_4$9 = { class: "grid gap-x-4 gap-y-3 grid-cols-2" };
1217
- var _hoisted_5$8 = { class: "text-[10px] font-semibold text-slate-400 uppercase tracking-wide" };
1218
- var _hoisted_6$8 = ["data-testid"];
1219
- var _hoisted_7$8 = {
1220
- key: 0,
1221
- class: "material-icons text-emerald-600 text-sm align-middle"
1222
- };
1223
- var _hoisted_8$8 = {
1224
- key: 1,
1225
- class: "text-slate-300"
1226
- };
1227
- var _hoisted_9$8 = {
1228
- key: 1,
1229
- class: "whitespace-pre-wrap font-normal text-slate-600"
1230
- };
1231
- var _hoisted_10$8 = { key: 2 };
1232
- var _hoisted_11$8 = ["data-testid"];
1233
- var _hoisted_12$7 = { class: "flex items-start gap-3" };
1234
- var _hoisted_13$6 = { class: "flex-1 min-w-0" };
1235
- var _hoisted_14$5 = { class: "text-xs font-semibold text-red-800 uppercase tracking-wider mb-1" };
1236
- var _hoisted_15$5 = ["data-testid"];
1237
- //#endregion
1238
- //#region src/vue/components/CollectionEmbedView.vue
1239
- var CollectionEmbedView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1240
- __name: "CollectionEmbedView",
1241
- props: {
1242
- view: {},
1243
- fieldKey: {}
1244
- },
1245
- setup(__props) {
1246
- const { t } = useCollectionI18n();
863
+ //#region src/vue/components/CollectionRecordModal.vue
864
+ var CollectionRecordModal_default = /* @__PURE__ */ (0, vue.defineComponent)({
865
+ __name: "CollectionRecordModal",
866
+ emits: ["close"],
867
+ setup(__props, { emit: __emit }) {
868
+ const emit = __emit;
869
+ const teleportTarget = collectionUi().modalTeleportTarget?.() ?? "body";
870
+ const dialogEl = (0, vue.ref)(null);
871
+ let previouslyFocused = null;
872
+ const FOCUSABLE_SELECTOR = [
873
+ "a[href]",
874
+ "button:not([disabled])",
875
+ "input:not([disabled])",
876
+ "select:not([disabled])",
877
+ "textarea:not([disabled])",
878
+ "[tabindex]:not([tabindex=\"-1\"])"
879
+ ].join(",");
880
+ /** Visible, focusable controls inside the dialog, in DOM order. */
881
+ function focusableItems() {
882
+ if (!dialogEl.value) return [];
883
+ return Array.from(dialogEl.value.querySelectorAll(FOCUSABLE_SELECTOR)).filter((node) => node.offsetParent !== null);
884
+ }
885
+ /** Trap Tab / Shift+Tab inside the dialog so focus can't escape to the
886
+ * page behind the overlay. Wraps at both ends; the dialog container
887
+ * itself (tabindex -1) counts as "before the first item". */
888
+ function onTab(event) {
889
+ const items = focusableItems();
890
+ if (items.length === 0) {
891
+ event.preventDefault();
892
+ dialogEl.value?.focus();
893
+ return;
894
+ }
895
+ const [first] = items;
896
+ const last = items[items.length - 1];
897
+ const active = document.activeElement;
898
+ if (event.shiftKey) {
899
+ if (active === first || active === dialogEl.value) {
900
+ event.preventDefault();
901
+ last.focus();
902
+ }
903
+ } else if (active === last) {
904
+ event.preventDefault();
905
+ first.focus();
906
+ }
907
+ }
908
+ (0, vue.onMounted)(async () => {
909
+ previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
910
+ await (0, vue.nextTick)();
911
+ dialogEl.value?.focus();
912
+ });
913
+ (0, vue.onBeforeUnmount)(() => {
914
+ previouslyFocused?.focus?.();
915
+ });
1247
916
  return (_ctx, _cache) => {
1248
- const _component_router_link = (0, vue.resolveComponent)("router-link");
1249
- return __props.view.found ? ((0, vue.openBlock)(), (0, vue.createBlock)(_component_router_link, {
1250
- key: 0,
1251
- to: {
1252
- path: `/collections/${__props.view.targetSlug}`,
1253
- query: { selected: __props.view.recordId }
1254
- },
1255
- 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",
1256
- "data-testid": `collections-embed-${__props.fieldKey}`
1257
- }, {
1258
- default: (0, vue.withCtx)(() => [
1259
- _cache[1] || (_cache[1] = (0, vue.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)),
1260
- (0, vue.createElementVNode)("div", _hoisted_1$9, [(0, vue.createElementVNode)("div", _hoisted_2$9, [_cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "link", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(__props.view.targetSlug), 1)]), (0, vue.createElementVNode)("span", _hoisted_3$9, (0, vue.toDisplayString)(__props.view.recordId), 1)]),
1261
- (0, vue.createElementVNode)("div", _hoisted_4$9, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.view.rows, (row) => {
1262
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1263
- key: row.key,
1264
- class: "space-y-0.5"
1265
- }, [(0, vue.createElementVNode)("div", _hoisted_5$8, (0, vue.toDisplayString)(row.label), 1), (0, vue.createElementVNode)("div", {
1266
- class: "text-xs text-slate-700 font-medium break-words",
1267
- "data-testid": `collections-embed-${__props.fieldKey}-${row.key}`
1268
- }, [row.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [row.value === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_7$8, "check_circle")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$8, "—"))], 64)) : row.type === "markdown" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_9$8, (0, vue.toDisplayString)(row.display), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_10$8, (0, vue.toDisplayString)(row.display), 1))], 8, _hoisted_6$8)]);
1269
- }), 128))])
1270
- ]),
1271
- _: 1
1272
- }, 8, ["to", "data-testid"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1273
- key: 1,
1274
- class: "relative rounded-xl border border-red-100 bg-red-50/30 p-4 pl-5 shadow-sm",
1275
- "data-testid": `collections-embed-${__props.fieldKey}`
1276
- }, [_cache[4] || (_cache[4] = (0, vue.createElementVNode)("div", { class: "absolute left-0 top-0 bottom-0 w-1 bg-red-400 rounded-l-xl" }, null, -1)), (0, vue.createElementVNode)("div", _hoisted_12$7, [_cache[3] || (_cache[3] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-500 text-lg mt-0.5" }, "error_outline", -1)), (0, vue.createElementVNode)("div", _hoisted_13$6, [
1277
- (0, vue.createElementVNode)("p", _hoisted_14$5, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.embedMissingTitle")), 1),
1278
- (0, vue.createElementVNode)("p", {
1279
- class: "text-xs text-red-600",
1280
- "data-testid": `collections-embed-missing-${__props.fieldKey}`
1281
- }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.embedMissing", {
1282
- collection: __props.view.targetSlug,
1283
- id: __props.view.recordId
1284
- })), 9, _hoisted_15$5),
1285
- __props.view.targetSlug ? ((0, vue.openBlock)(), (0, vue.createBlock)(_component_router_link, {
1286
- key: 0,
1287
- to: { path: `/collections/${__props.view.targetSlug}` },
1288
- class: "inline-flex items-center gap-0.5 text-xs text-indigo-600 hover:text-indigo-800 font-semibold mt-2 hover:underline"
1289
- }, {
1290
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.embedCreate")), 1), _cache[2] || (_cache[2] = (0, vue.createElementVNode)("span", { class: "material-icons text-xs" }, "arrow_forward", -1))]),
1291
- _: 1
1292
- }, 8, ["to"])) : (0, vue.createCommentVNode)("", true)
1293
- ])])], 8, _hoisted_11$8));
917
+ return (0, vue.openBlock)(), (0, vue.createBlock)(vue.Teleport, { to: (0, vue.unref)(teleportTarget) }, [(0, vue.createElementVNode)("div", {
918
+ class: "fixed inset-0 z-40 flex items-center justify-center bg-slate-900/40 p-4",
919
+ "data-testid": "collections-record-modal",
920
+ onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(($event) => emit("close"), ["self"]))
921
+ }, [(0, vue.createElementVNode)("div", {
922
+ ref_key: "dialogEl",
923
+ ref: dialogEl,
924
+ class: "flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl bg-white shadow-xl focus:outline-none",
925
+ role: "dialog",
926
+ "aria-modal": "true",
927
+ tabindex: "-1",
928
+ onKeydown: [_cache[0] || (_cache[0] = (0, vue.withKeys)(($event) => emit("close"), ["esc"])), (0, vue.withKeys)(onTab, ["tab"])]
929
+ }, [(0, vue.renderSlot)(_ctx.$slots, "default")], 544)])], 8, ["to"]);
1294
930
  };
1295
931
  }
1296
932
  });
1297
933
  //#endregion
1298
934
  //#region src/vue/components/CollectionCalendarView.vue?vue&type=script&setup=true&lang.ts
1299
- var _hoisted_1$8 = {
935
+ var _hoisted_1$11 = {
1300
936
  class: "flex flex-col gap-3",
1301
937
  "data-testid": "collection-calendar"
1302
938
  };
1303
- var _hoisted_2$8 = { class: "flex items-center gap-2" };
1304
- var _hoisted_3$8 = ["aria-label"];
1305
- var _hoisted_4$8 = ["aria-label"];
1306
- var _hoisted_5$7 = {
939
+ var _hoisted_2$10 = { class: "flex items-center gap-2" };
940
+ var _hoisted_3$10 = ["aria-label"];
941
+ var _hoisted_4$10 = ["aria-label"];
942
+ var _hoisted_5$9 = {
1307
943
  class: "text-sm font-bold text-slate-800 flex-1",
1308
944
  "data-testid": "collection-calendar-month"
1309
945
  };
1310
- var _hoisted_6$7 = { class: "grid grid-cols-7 gap-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider select-none" };
1311
- var _hoisted_7$7 = { class: "grid grid-cols-7 gap-1" };
1312
- var _hoisted_8$7 = [
946
+ var _hoisted_6$8 = { class: "grid grid-cols-7 gap-1 text-[10px] font-bold text-slate-400 uppercase tracking-wider select-none" };
947
+ var _hoisted_7$8 = { class: "grid grid-cols-7 gap-1" };
948
+ var _hoisted_8$8 = [
1313
949
  "aria-label",
1314
950
  "data-testid",
1315
951
  "onClick",
1316
952
  "onKeydown"
1317
953
  ];
1318
- var _hoisted_9$7 = { class: "flex items-center justify-end" };
1319
- var _hoisted_10$7 = ["data-testid", "onClick"];
1320
- var _hoisted_11$7 = {
954
+ var _hoisted_9$8 = { class: "flex items-center justify-end" };
955
+ var _hoisted_10$8 = ["data-testid", "onClick"];
956
+ var _hoisted_11$8 = {
1321
957
  key: 0,
1322
958
  class: "flex flex-wrap items-center gap-1.5 pt-1",
1323
959
  "data-testid": "collection-calendar-no-date"
1324
960
  };
1325
- var _hoisted_12$6 = { class: "text-[10px] font-bold text-slate-400 uppercase tracking-wider mr-1" };
1326
- var _hoisted_13$5 = ["data-testid", "onClick"];
961
+ var _hoisted_12$7 = { class: "text-[10px] font-bold text-slate-400 uppercase tracking-wider mr-1" };
962
+ var _hoisted_13$6 = ["data-testid", "onClick"];
1327
963
  /** Sort key for ordering a day's chips by start time: earliest first, with
1328
964
  * clock-less all-day records sinking to the bottom (matching the day view). */
1329
965
  var DAY_CHIP_DEFAULT = "bg-indigo-50 text-indigo-700 border-indigo-100 hover:bg-indigo-100";
@@ -1437,23 +1073,23 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1437
1073
  viewMonth.value = now.getMonth() + 1;
1438
1074
  }
1439
1075
  return (_ctx, _cache) => {
1440
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$8, [
1441
- (0, vue.createElementVNode)("div", _hoisted_2$8, [
1076
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$11, [
1077
+ (0, vue.createElementVNode)("div", _hoisted_2$10, [
1442
1078
  (0, vue.createElementVNode)("button", {
1443
1079
  type: "button",
1444
1080
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
1445
1081
  "aria-label": (0, vue.unref)(t)("collectionsView.calendarPrevMonth"),
1446
1082
  "data-testid": "collection-calendar-prev",
1447
1083
  onClick: _cache[0] || (_cache[0] = ($event) => stepMonth(-1))
1448
- }, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_left", -1)])], 8, _hoisted_3$8),
1084
+ }, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_left", -1)])], 8, _hoisted_3$10),
1449
1085
  (0, vue.createElementVNode)("button", {
1450
1086
  type: "button",
1451
1087
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
1452
1088
  "aria-label": (0, vue.unref)(t)("collectionsView.calendarNextMonth"),
1453
1089
  "data-testid": "collection-calendar-next",
1454
1090
  onClick: _cache[1] || (_cache[1] = ($event) => stepMonth(1))
1455
- }, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_right", -1)])], 8, _hoisted_4$8),
1456
- (0, vue.createElementVNode)("h3", _hoisted_5$7, (0, vue.toDisplayString)(monthLabel.value), 1),
1091
+ }, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "chevron_right", -1)])], 8, _hoisted_4$10),
1092
+ (0, vue.createElementVNode)("h3", _hoisted_5$9, (0, vue.toDisplayString)(monthLabel.value), 1),
1457
1093
  (0, vue.createElementVNode)("button", {
1458
1094
  type: "button",
1459
1095
  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",
@@ -1461,13 +1097,13 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1461
1097
  onClick: goToday
1462
1098
  }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.calendarToday")), 1)
1463
1099
  ]),
1464
- (0, vue.createElementVNode)("div", _hoisted_6$7, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(weekdayLabels.value, (label, idx) => {
1100
+ (0, vue.createElementVNode)("div", _hoisted_6$8, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(weekdayLabels.value, (label, idx) => {
1465
1101
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1466
1102
  key: idx,
1467
1103
  class: "px-1 py-1 text-center"
1468
1104
  }, (0, vue.toDisplayString)(label), 1);
1469
1105
  }), 128))]),
1470
- (0, vue.createElementVNode)("div", _hoisted_7$7, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(cells.value, ({ cell, entries }) => {
1106
+ (0, vue.createElementVNode)("div", _hoisted_7$8, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(cells.value, ({ cell, entries }) => {
1471
1107
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1472
1108
  key: cell.key,
1473
1109
  class: (0, vue.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"]),
@@ -1477,24 +1113,24 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1477
1113
  "data-testid": `collection-calendar-day-${cell.key}`,
1478
1114
  onClick: ($event) => emit("openDay", cell.ymd),
1479
1115
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(($event) => emit("openDay", cell.ymd), ["self", "prevent"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(($event) => emit("openDay", cell.ymd), ["self", "prevent"]), ["space"])]
1480
- }, [(0, vue.createElementVNode)("div", _hoisted_9$7, [(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["text-[11px] font-bold h-5 min-w-5 px-1 inline-flex items-center justify-center rounded-full", cell.key === (0, vue.unref)(todayKey) ? "bg-indigo-600 text-white" : cell.inMonth ? "text-slate-500" : "text-slate-300"]) }, (0, vue.toDisplayString)(cell.ymd.day), 3)]), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(entries, (entry) => {
1116
+ }, [(0, vue.createElementVNode)("div", _hoisted_9$8, [(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["text-[11px] font-bold h-5 min-w-5 px-1 inline-flex items-center justify-center rounded-full", cell.key === (0, vue.unref)(todayKey) ? "bg-indigo-600 text-white" : cell.inMonth ? "text-slate-500" : "text-slate-300"]) }, (0, vue.toDisplayString)(cell.ymd.day), 3)]), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(entries, (entry) => {
1481
1117
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1482
1118
  key: entry.id,
1483
1119
  type: "button",
1484
1120
  class: (0, vue.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)]),
1485
1121
  "data-testid": `collection-calendar-chip-${entry.id}`,
1486
1122
  onClick: (0, vue.withModifiers)(($event) => emit("select", entry.id), ["stop"])
1487
- }, (0, vue.toDisplayString)(entry.label), 11, _hoisted_10$7);
1488
- }), 128))], 42, _hoisted_8$7);
1123
+ }, (0, vue.toDisplayString)(entry.label), 11, _hoisted_10$8);
1124
+ }), 128))], 42, _hoisted_8$8);
1489
1125
  }), 128))]),
1490
- bucketed.value.noDate.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_11$7, [(0, vue.createElementVNode)("span", _hoisted_12$6, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.calendarNoDate")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(undatedEntries.value, (entry) => {
1126
+ bucketed.value.noDate.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_11$8, [(0, vue.createElementVNode)("span", _hoisted_12$7, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.calendarNoDate")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(undatedEntries.value, (entry) => {
1491
1127
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1492
1128
  key: entry.id,
1493
1129
  type: "button",
1494
1130
  class: (0, vue.normalizeClass)(["text-[11px] font-semibold truncate rounded px-1.5 py-0.5 border transition-colors", chipClass(entry, UNDATED_CHIP_DEFAULT)]),
1495
1131
  "data-testid": `collection-calendar-undated-${entry.id}`,
1496
1132
  onClick: ($event) => emit("select", entry.id)
1497
- }, (0, vue.toDisplayString)(entry.label), 11, _hoisted_13$5);
1133
+ }, (0, vue.toDisplayString)(entry.label), 11, _hoisted_13$6);
1498
1134
  }), 128))])) : (0, vue.createCommentVNode)("", true)
1499
1135
  ]);
1500
1136
  };
@@ -1502,40 +1138,40 @@ var CollectionCalendarView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1502
1138
  });
1503
1139
  //#endregion
1504
1140
  //#region src/vue/components/CollectionDayView.vue?vue&type=script&setup=true&lang.ts
1505
- var _hoisted_1$7 = { class: "flex items-center gap-2 border-b border-slate-200 px-4 py-3" };
1506
- var _hoisted_2$7 = {
1141
+ var _hoisted_1$10 = { class: "flex items-center gap-2 border-b border-slate-200 px-4 py-3" };
1142
+ var _hoisted_2$9 = {
1507
1143
  class: "flex-1 text-sm font-bold text-slate-800",
1508
1144
  "data-testid": "collection-day-view-title"
1509
1145
  };
1510
- var _hoisted_3$7 = ["aria-label"];
1511
- var _hoisted_4$7 = ["aria-label"];
1512
- var _hoisted_5$6 = {
1146
+ var _hoisted_3$9 = ["aria-label"];
1147
+ var _hoisted_4$9 = ["aria-label"];
1148
+ var _hoisted_5$8 = {
1513
1149
  key: 0,
1514
1150
  class: "px-4 py-10 text-center text-sm text-slate-400"
1515
1151
  };
1516
- var _hoisted_6$6 = { class: "absolute -top-2 left-0 w-10 pr-1 text-right text-[10px] tabular-nums text-slate-400" };
1517
- var _hoisted_7$6 = {
1152
+ var _hoisted_6$7 = { class: "absolute -top-2 left-0 w-10 pr-1 text-right text-[10px] tabular-nums text-slate-400" };
1153
+ var _hoisted_7$7 = {
1518
1154
  class: "absolute inset-y-0 right-0",
1519
1155
  style: { "left": "2.75rem" }
1520
1156
  };
1521
- var _hoisted_8$6 = ["data-testid", "onClick"];
1522
- var _hoisted_9$6 = { class: "block truncate text-[11px] font-semibold leading-tight" };
1523
- var _hoisted_10$6 = {
1157
+ var _hoisted_8$7 = ["data-testid", "onClick"];
1158
+ var _hoisted_9$7 = { class: "block truncate text-[11px] font-semibold leading-tight" };
1159
+ var _hoisted_10$7 = {
1524
1160
  key: 0,
1525
1161
  "aria-hidden": "true"
1526
1162
  };
1527
- var _hoisted_11$6 = {
1163
+ var _hoisted_11$7 = {
1528
1164
  key: 1,
1529
1165
  "aria-hidden": "true"
1530
1166
  };
1531
- var _hoisted_12$5 = {
1167
+ var _hoisted_12$6 = {
1532
1168
  key: 2,
1533
1169
  class: "flex flex-wrap items-center gap-1.5 border-t border-slate-200 px-4 py-2",
1534
1170
  "data-testid": "collection-day-view-all-day"
1535
1171
  };
1536
- var _hoisted_13$4 = { class: "mr-1 text-[10px] font-bold uppercase tracking-wider text-slate-400" };
1537
- var _hoisted_14$4 = ["data-testid", "onClick"];
1538
- var _hoisted_15$4 = {
1172
+ var _hoisted_13$5 = { class: "mr-1 text-[10px] font-bold uppercase tracking-wider text-slate-400" };
1173
+ var _hoisted_14$5 = ["data-testid", "onClick"];
1174
+ var _hoisted_15$5 = {
1539
1175
  key: 0,
1540
1176
  class: "min-w-0 flex-1 overflow-y-auto",
1541
1177
  "data-testid": "collection-day-view-detail"
@@ -1694,8 +1330,8 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1694
1330
  role: "dialog",
1695
1331
  "aria-modal": "true"
1696
1332
  }, [(0, vue.createElementVNode)("div", { class: (0, vue.normalizeClass)(["flex min-h-0 flex-col", __props.showDetail ? "w-80 shrink-0 border-r border-slate-200" : "w-full"]) }, [
1697
- (0, vue.createElementVNode)("div", _hoisted_1$7, [
1698
- (0, vue.createElementVNode)("h3", _hoisted_2$7, (0, vue.toDisplayString)(dayLabel.value), 1),
1333
+ (0, vue.createElementVNode)("div", _hoisted_1$10, [
1334
+ (0, vue.createElementVNode)("h3", _hoisted_2$9, (0, vue.toDisplayString)(dayLabel.value), 1),
1699
1335
  __props.canCreate ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1700
1336
  key: 0,
1701
1337
  type: "button",
@@ -1703,16 +1339,16 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1703
1339
  "aria-label": (0, vue.unref)(t)("collectionsView.calendarCreateOn", { date: dayKey.value }),
1704
1340
  "data-testid": "collection-day-view-create",
1705
1341
  onClick: onCreate
1706
- }, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "add", -1)])], 8, _hoisted_3$7)) : (0, vue.createCommentVNode)("", true),
1342
+ }, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "add", -1)])], 8, _hoisted_3$9)) : (0, vue.createCommentVNode)("", true),
1707
1343
  (0, vue.createElementVNode)("button", {
1708
1344
  type: "button",
1709
1345
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-500 hover:bg-slate-100 transition-colors",
1710
1346
  "aria-label": (0, vue.unref)(t)("collectionsView.dayViewClose"),
1711
1347
  "data-testid": "collection-day-view-close",
1712
1348
  onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
1713
- }, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_4$7)
1349
+ }, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_4$9)
1714
1350
  ]),
1715
- timedEntries.value.length === 0 && allDayEntries.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5$6, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dayViewEmpty")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1351
+ timedEntries.value.length === 0 && allDayEntries.value.length === 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5$8, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dayViewEmpty")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1716
1352
  key: 1,
1717
1353
  ref_key: "scrollEl",
1718
1354
  ref: scrollEl,
@@ -1726,8 +1362,8 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1726
1362
  key: hour,
1727
1363
  class: "absolute left-0 right-0 border-t border-slate-100",
1728
1364
  style: (0, vue.normalizeStyle)({ top: `${(hour - 1) * HOUR_PX}px` })
1729
- }, [(0, vue.createElementVNode)("span", _hoisted_6$6, (0, vue.toDisplayString)(hourLabel(hour - 1)), 1)], 4);
1730
- }), 64)), (0, vue.createElementVNode)("div", _hoisted_7$6, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(timedEntries.value, (entry) => {
1365
+ }, [(0, vue.createElementVNode)("span", _hoisted_6$7, (0, vue.toDisplayString)(hourLabel(hour - 1)), 1)], 4);
1366
+ }), 64)), (0, vue.createElementVNode)("div", _hoisted_7$7, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(timedEntries.value, (entry) => {
1731
1367
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1732
1368
  key: entry.id,
1733
1369
  type: "button",
@@ -1735,56 +1371,56 @@ var CollectionDayView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1735
1371
  style: (0, vue.normalizeStyle)(entry.style),
1736
1372
  "data-testid": `collection-day-view-chip-${entry.id}`,
1737
1373
  onClick: ($event) => onSelect(entry.id)
1738
- }, [(0, vue.createElementVNode)("span", _hoisted_9$6, [
1739
- entry.slice.bleedsBefore ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_10$6, "▲ ")) : (0, vue.createCommentVNode)("", true),
1374
+ }, [(0, vue.createElementVNode)("span", _hoisted_9$7, [
1375
+ entry.slice.bleedsBefore ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_10$7, "▲ ")) : (0, vue.createCommentVNode)("", true),
1740
1376
  (0, vue.createTextVNode)((0, vue.toDisplayString)(entry.label), 1),
1741
- entry.slice.bleedsAfter ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_11$6, " ▼")) : (0, vue.createCommentVNode)("", true)
1377
+ entry.slice.bleedsAfter ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_11$7, " ▼")) : (0, vue.createCommentVNode)("", true)
1742
1378
  ]), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(entry.secondary, (text, i) => {
1743
1379
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
1744
1380
  key: i,
1745
1381
  class: "block truncate text-[10px] leading-tight opacity-70"
1746
1382
  }, (0, vue.toDisplayString)(text), 1);
1747
- }), 128))], 14, _hoisted_8$6);
1383
+ }), 128))], 14, _hoisted_8$7);
1748
1384
  }), 128))])], 4)], 512)),
1749
- allDayEntries.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12$5, [(0, vue.createElementVNode)("span", _hoisted_13$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dayViewAllDay")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(allDayEntries.value, (entry) => {
1385
+ allDayEntries.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_12$6, [(0, vue.createElementVNode)("span", _hoisted_13$5, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dayViewAllDay")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(allDayEntries.value, (entry) => {
1750
1386
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
1751
1387
  key: entry.id,
1752
1388
  type: "button",
1753
1389
  class: (0, vue.normalizeClass)(["truncate rounded border px-1.5 py-0.5 text-[11px] font-semibold transition-colors", allDayChipClass(entry)]),
1754
1390
  "data-testid": `collection-day-view-allday-${entry.id}`,
1755
1391
  onClick: ($event) => onSelect(entry.id)
1756
- }, (0, vue.toDisplayString)(entry.label), 11, _hoisted_14$4);
1392
+ }, (0, vue.toDisplayString)(entry.label), 11, _hoisted_14$5);
1757
1393
  }), 128))])) : (0, vue.createCommentVNode)("", true)
1758
- ], 2), __props.showDetail ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$4, [(0, vue.renderSlot)(_ctx.$slots, "detail")])) : (0, vue.createCommentVNode)("", true)], 2)], 32);
1394
+ ], 2), __props.showDetail ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$5, [(0, vue.renderSlot)(_ctx.$slots, "detail")])) : (0, vue.createCommentVNode)("", true)], 2)], 32);
1759
1395
  };
1760
1396
  }
1761
1397
  });
1762
1398
  //#endregion
1763
1399
  //#region src/vue/components/CollectionKanbanView.vue?vue&type=script&setup=true&lang.ts
1764
- var _hoisted_1$6 = {
1400
+ var _hoisted_1$9 = {
1765
1401
  class: "h-full overflow-x-auto overflow-y-hidden",
1766
1402
  "data-testid": "collection-kanban"
1767
1403
  };
1768
- var _hoisted_2$6 = { class: "flex gap-3 h-full p-1 min-w-max" };
1769
- var _hoisted_3$6 = ["data-testid"];
1770
- var _hoisted_4$6 = { class: "flex items-center justify-between px-3 py-2 border-b border-slate-200" };
1771
- var _hoisted_5$5 = { class: "flex items-center gap-2 min-w-0" };
1772
- var _hoisted_6$5 = ["title"];
1773
- var _hoisted_7$5 = { class: "text-[11px] text-slate-400 shrink-0" };
1774
- var _hoisted_8$5 = [
1404
+ var _hoisted_2$8 = { class: "flex gap-3 h-full p-1 min-w-max" };
1405
+ var _hoisted_3$8 = ["data-testid"];
1406
+ var _hoisted_4$8 = { class: "flex items-center justify-between px-3 py-2 border-b border-slate-200" };
1407
+ var _hoisted_5$7 = { class: "flex items-center gap-2 min-w-0" };
1408
+ var _hoisted_6$6 = ["title"];
1409
+ var _hoisted_7$6 = { class: "text-[11px] text-slate-400 shrink-0" };
1410
+ var _hoisted_8$6 = [
1775
1411
  "data-testid",
1776
1412
  "aria-label",
1777
1413
  "onClick",
1778
1414
  "onKeydown"
1779
1415
  ];
1780
- var _hoisted_9$5 = { class: "flex items-start gap-2" };
1781
- var _hoisted_10$5 = [
1416
+ var _hoisted_9$6 = { class: "flex items-start gap-2" };
1417
+ var _hoisted_10$6 = [
1782
1418
  "checked",
1783
1419
  "aria-label",
1784
1420
  "data-testid",
1785
1421
  "onChange"
1786
1422
  ];
1787
- var _hoisted_11$5 = { class: "text-sm font-medium text-slate-800 truncate" };
1423
+ var _hoisted_11$6 = { class: "text-sm font-medium text-slate-800 truncate" };
1788
1424
  var UNCATEGORIZED = "";
1789
1425
  //#endregion
1790
1426
  //#region src/vue/components/CollectionKanbanView.vue
@@ -1882,15 +1518,15 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1882
1518
  emit("move", itemId(item), next);
1883
1519
  }
1884
1520
  return (_ctx, _cache) => {
1885
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$6, [(0, vue.createElementVNode)("div", _hoisted_2$6, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(columns.value, (column) => {
1521
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$9, [(0, vue.createElementVNode)("div", _hoisted_2$8, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(columns.value, (column) => {
1886
1522
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1887
1523
  key: column.value,
1888
1524
  "data-testid": `collection-kanban-column-${column.value || "uncategorized"}`,
1889
1525
  class: "w-72 shrink-0 flex flex-col bg-slate-100 rounded-lg"
1890
- }, [(0, vue.createElementVNode)("div", _hoisted_4$6, [(0, vue.createElementVNode)("div", _hoisted_5$5, [(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["w-2 h-2 rounded-full shrink-0", (0, vue.unref)(require_promptSafety.resolveEnumColor)(__props.schema, __props.groupField, column.value).dot]) }, null, 2), (0, vue.createElementVNode)("span", {
1526
+ }, [(0, vue.createElementVNode)("div", _hoisted_4$8, [(0, vue.createElementVNode)("div", _hoisted_5$7, [(0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["w-2 h-2 rounded-full shrink-0", (0, vue.unref)(require_promptSafety.resolveEnumColor)(__props.schema, __props.groupField, column.value).dot]) }, null, 2), (0, vue.createElementVNode)("span", {
1891
1527
  class: "font-semibold text-xs text-slate-600 truncate",
1892
1528
  title: column.label
1893
- }, (0, vue.toDisplayString)(column.label), 9, _hoisted_6$5)]), (0, vue.createElementVNode)("span", _hoisted_7$5, (0, vue.toDisplayString)(itemsByColumn(column.value).length), 1)]), (0, vue.createVNode)((0, vue.unref)(vuedraggable.default), {
1529
+ }, (0, vue.toDisplayString)(column.label), 9, _hoisted_6$6)]), (0, vue.createElementVNode)("span", _hoisted_7$6, (0, vue.toDisplayString)(itemsByColumn(column.value).length), 1)]), (0, vue.createVNode)((0, vue.unref)(vuedraggable.default), {
1894
1530
  "model-value": itemsByColumn(column.value),
1895
1531
  "item-key": __props.schema.primaryKey,
1896
1532
  group: "collection-kanban-cards",
@@ -1906,7 +1542,7 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1906
1542
  class: (0, vue.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)]]),
1907
1543
  onClick: ($event) => emit("select", itemId(element)),
1908
1544
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)((e) => !e.repeat && emit("select", itemId(element)), ["prevent", "self"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)((e) => !e.repeat && emit("select", itemId(element)), ["prevent", "self"]), ["space"])]
1909
- }, [(0, vue.createElementVNode)("div", _hoisted_9$5, [cardToggle.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
1545
+ }, [(0, vue.createElementVNode)("div", _hoisted_9$6, [cardToggle.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
1910
1546
  key: 0,
1911
1547
  type: "checkbox",
1912
1548
  checked: cardChecked(element),
@@ -1915,24 +1551,105 @@ var CollectionKanbanView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1915
1551
  "data-testid": `collection-kanban-toggle-${itemId(element)}`,
1916
1552
  onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(() => {}, ["stop"])),
1917
1553
  onChange: ($event) => onCardToggle(element)
1918
- }, null, 40, _hoisted_10$5)) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_11$5, (0, vue.toDisplayString)(itemLabel(element)), 1)])], 42, _hoisted_8$5)]),
1554
+ }, null, 40, _hoisted_10$6)) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_11$6, (0, vue.toDisplayString)(itemLabel(element)), 1)])], 42, _hoisted_8$6)]),
1919
1555
  _: 1
1920
1556
  }, 8, [
1921
1557
  "model-value",
1922
1558
  "item-key",
1923
1559
  "onChange"
1924
- ])], 8, _hoisted_3$6);
1560
+ ])], 8, _hoisted_3$8);
1925
1561
  }), 128))])]);
1926
1562
  };
1927
1563
  }
1928
1564
  });
1929
1565
  //#endregion
1566
+ //#region src/vue/components/CollectionEmbedView.vue?vue&type=script&setup=true&lang.ts
1567
+ var _hoisted_1$8 = ["href", "data-testid"];
1568
+ var _hoisted_2$7 = { class: "flex items-center justify-between text-[10px] font-bold text-indigo-600/90 tracking-wider uppercase" };
1569
+ var _hoisted_3$7 = { class: "flex items-center gap-1.5" };
1570
+ var _hoisted_4$7 = { class: "bg-indigo-100/60 text-indigo-700 px-1.5 py-0.5 rounded font-mono font-medium lowercase" };
1571
+ var _hoisted_5$6 = { class: "grid gap-x-4 gap-y-3 grid-cols-2" };
1572
+ var _hoisted_6$5 = { class: "text-[10px] font-semibold text-slate-400 uppercase tracking-wide" };
1573
+ var _hoisted_7$5 = ["data-testid"];
1574
+ var _hoisted_8$5 = {
1575
+ key: 0,
1576
+ class: "material-icons text-emerald-600 text-sm align-middle"
1577
+ };
1578
+ var _hoisted_9$5 = {
1579
+ key: 1,
1580
+ class: "text-slate-300"
1581
+ };
1582
+ var _hoisted_10$5 = {
1583
+ key: 1,
1584
+ class: "whitespace-pre-wrap font-normal text-slate-600"
1585
+ };
1586
+ var _hoisted_11$5 = { key: 2 };
1587
+ var _hoisted_12$5 = ["data-testid"];
1588
+ var _hoisted_13$4 = { class: "flex items-start gap-3" };
1589
+ var _hoisted_14$4 = { class: "flex-1 min-w-0" };
1590
+ var _hoisted_15$4 = { class: "text-xs font-semibold text-red-800 uppercase tracking-wider mb-1" };
1591
+ var _hoisted_16$3 = ["data-testid"];
1592
+ var _hoisted_17$3 = ["href"];
1593
+ //#endregion
1594
+ //#region src/vue/components/CollectionEmbedView.vue
1595
+ var CollectionEmbedView_default = /* @__PURE__ */ (0, vue.defineComponent)({
1596
+ __name: "CollectionEmbedView",
1597
+ props: {
1598
+ view: {},
1599
+ fieldKey: {}
1600
+ },
1601
+ setup(__props) {
1602
+ const { t } = useCollectionI18n();
1603
+ const cui = collectionUi();
1604
+ return (_ctx, _cache) => {
1605
+ return __props.view.found ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
1606
+ key: 0,
1607
+ href: (0, vue.unref)(cui).recordHref?.(__props.view.targetSlug, __props.view.recordId),
1608
+ 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",
1609
+ "data-testid": `collections-embed-${__props.fieldKey}`,
1610
+ onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(($event) => (0, vue.unref)(cui).navigateToRecord(__props.view.targetSlug, __props.view.recordId), ["exact", "prevent"]))
1611
+ }, [
1612
+ _cache[3] || (_cache[3] = (0, vue.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)),
1613
+ (0, vue.createElementVNode)("div", _hoisted_2$7, [(0, vue.createElementVNode)("div", _hoisted_3$7, [_cache[2] || (_cache[2] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "link", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(__props.view.targetSlug), 1)]), (0, vue.createElementVNode)("span", _hoisted_4$7, (0, vue.toDisplayString)(__props.view.recordId), 1)]),
1614
+ (0, vue.createElementVNode)("div", _hoisted_5$6, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.view.rows, (row) => {
1615
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1616
+ key: row.key,
1617
+ class: "space-y-0.5"
1618
+ }, [(0, vue.createElementVNode)("div", _hoisted_6$5, (0, vue.toDisplayString)(row.label), 1), (0, vue.createElementVNode)("div", {
1619
+ class: "text-xs text-slate-700 font-medium break-words",
1620
+ "data-testid": `collections-embed-${__props.fieldKey}-${row.key}`
1621
+ }, [row.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [row.value === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$5, "check_circle")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_9$5, "—"))], 64)) : row.type === "markdown" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_10$5, (0, vue.toDisplayString)(row.display), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_11$5, (0, vue.toDisplayString)(row.display), 1))], 8, _hoisted_7$5)]);
1622
+ }), 128))])
1623
+ ], 8, _hoisted_1$8)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
1624
+ key: 1,
1625
+ class: "relative rounded-xl border border-red-100 bg-red-50/30 p-4 pl-5 shadow-sm",
1626
+ "data-testid": `collections-embed-${__props.fieldKey}`
1627
+ }, [_cache[6] || (_cache[6] = (0, vue.createElementVNode)("div", { class: "absolute left-0 top-0 bottom-0 w-1 bg-red-400 rounded-l-xl" }, null, -1)), (0, vue.createElementVNode)("div", _hoisted_13$4, [_cache[5] || (_cache[5] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-500 text-lg mt-0.5" }, "error_outline", -1)), (0, vue.createElementVNode)("div", _hoisted_14$4, [
1628
+ (0, vue.createElementVNode)("p", _hoisted_15$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.embedMissingTitle")), 1),
1629
+ (0, vue.createElementVNode)("p", {
1630
+ class: "text-xs text-red-600",
1631
+ "data-testid": `collections-embed-missing-${__props.fieldKey}`
1632
+ }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.embedMissing", {
1633
+ collection: __props.view.targetSlug,
1634
+ id: __props.view.recordId
1635
+ })), 9, _hoisted_16$3),
1636
+ __props.view.targetSlug ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
1637
+ key: 0,
1638
+ href: (0, vue.unref)(cui).recordHref?.(__props.view.targetSlug),
1639
+ class: "inline-flex items-center gap-0.5 text-xs text-indigo-600 hover:text-indigo-800 font-semibold mt-2 hover:underline",
1640
+ onClick: _cache[1] || (_cache[1] = (0, vue.withModifiers)(($event) => (0, vue.unref)(cui).navigateToRecord(__props.view.targetSlug), ["exact", "prevent"]))
1641
+ }, [(0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.embedCreate")), 1), _cache[4] || (_cache[4] = (0, vue.createElementVNode)("span", { class: "material-icons text-xs" }, "arrow_forward", -1))], 8, _hoisted_17$3)) : (0, vue.createCommentVNode)("", true)
1642
+ ])])], 8, _hoisted_12$5));
1643
+ };
1644
+ }
1645
+ });
1646
+ //#endregion
1930
1647
  //#region src/vue/components/CollectionRecordPanel.vue?vue&type=script&setup=true&lang.ts
1931
- var _hoisted_1$5 = { class: "flex items-center gap-2 mb-4" };
1932
- var _hoisted_2$5 = { class: "flex-1 min-w-0" };
1933
- var _hoisted_3$5 = { class: "block text-[9px] font-bold text-slate-400 uppercase tracking-wider" };
1934
- var _hoisted_4$5 = ["data-testid"];
1935
- var _hoisted_5$4 = ["disabled"];
1648
+ var _hoisted_1$7 = { class: "flex items-center gap-2 mb-4" };
1649
+ var _hoisted_2$6 = { class: "flex-1 min-w-0" };
1650
+ var _hoisted_3$6 = { class: "block text-[9px] font-bold text-slate-400 uppercase tracking-wider" };
1651
+ var _hoisted_4$6 = ["data-testid"];
1652
+ var _hoisted_5$5 = ["disabled"];
1936
1653
  var _hoisted_6$4 = {
1937
1654
  key: 1,
1938
1655
  class: "flex items-center gap-2"
@@ -2068,49 +1785,59 @@ var _hoisted_52$1 = {
2068
1785
  key: 2,
2069
1786
  class: "text-slate-300"
2070
1787
  };
2071
- var _hoisted_53$1 = {
1788
+ var _hoisted_53$1 = [
1789
+ "href",
1790
+ "data-testid",
1791
+ "onClick"
1792
+ ];
1793
+ var _hoisted_54$1 = {
2072
1794
  key: 3,
2073
1795
  class: "font-semibold text-slate-900 tabular-nums text-sm"
2074
1796
  };
2075
- var _hoisted_54$1 = {
1797
+ var _hoisted_55$1 = {
2076
1798
  key: 4,
2077
1799
  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"
2078
1800
  };
2079
- var _hoisted_55$1 = {
1801
+ var _hoisted_56$1 = {
2080
1802
  key: 5,
2081
1803
  class: "border border-slate-200/80 rounded-xl overflow-hidden shadow-sm mt-1"
2082
1804
  };
2083
- var _hoisted_56$1 = { class: "w-full text-[11px] text-slate-600 bg-white" };
2084
- var _hoisted_57$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
2085
- var _hoisted_58$1 = { class: "divide-y divide-slate-100" };
2086
- var _hoisted_59$1 = {
1805
+ var _hoisted_57$1 = { class: "w-full text-[11px] text-slate-600 bg-white" };
1806
+ var _hoisted_58$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
1807
+ var _hoisted_59$1 = { class: "divide-y divide-slate-100" };
1808
+ var _hoisted_60$1 = {
2087
1809
  key: 0,
2088
1810
  class: "material-icons text-emerald-600 text-base"
2089
1811
  };
2090
- var _hoisted_60$1 = {
1812
+ var _hoisted_61$1 = {
2091
1813
  key: 1,
2092
1814
  class: "text-slate-300"
2093
1815
  };
2094
- var _hoisted_61$1 = {
1816
+ var _hoisted_62$1 = {
2095
1817
  key: 6,
2096
1818
  class: "text-slate-400 italic"
2097
1819
  };
2098
- var _hoisted_62$1 = {
1820
+ var _hoisted_63$1 = {
2099
1821
  key: 7,
2100
1822
  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"
2101
1823
  };
2102
- var _hoisted_63$1 = [
1824
+ var _hoisted_64$1 = [
2103
1825
  "src",
2104
1826
  "alt",
2105
1827
  "data-testid"
2106
1828
  ];
2107
- var _hoisted_64$1 = ["href", "data-testid"];
2108
1829
  var _hoisted_65$1 = ["href", "data-testid"];
2109
- var _hoisted_66$1 = {
1830
+ var _hoisted_66$1 = ["href", "data-testid"];
1831
+ var _hoisted_67$1 = [
1832
+ "href",
1833
+ "data-testid",
1834
+ "onClick"
1835
+ ];
1836
+ var _hoisted_68$1 = {
2110
1837
  key: 13,
2111
1838
  class: "text-slate-800 font-semibold"
2112
1839
  };
2113
- var _hoisted_67$1 = {
1840
+ var _hoisted_69$1 = {
2114
1841
  key: 0,
2115
1842
  class: "col-span-full text-xs font-semibold text-red-600 bg-red-50 border border-red-100 p-2.5 rounded-xl"
2116
1843
  };
@@ -2145,7 +1872,8 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2145
1872
  "runAction"
2146
1873
  ], ["update:editing"]),
2147
1874
  setup(__props, { emit: __emit }) {
2148
- const resolveImageSrc = collectionUi().imageSrc;
1875
+ const cui = collectionUi();
1876
+ const resolveImageSrc = cui.imageSrc;
2149
1877
  const props = __props;
2150
1878
  const editing = (0, vue.useModel)(__props, "editing");
2151
1879
  const emit = __emit;
@@ -2225,17 +1953,16 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2225
1953
  if (rows) rows.splice(index, 1);
2226
1954
  }
2227
1955
  return (_ctx, _cache) => {
2228
- const _component_router_link = (0, vue.resolveComponent)("router-link");
2229
1956
  return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(editing.value ? "form" : "div"), {
2230
1957
  class: "px-6 py-5 max-h-[60vh] overflow-y-auto",
2231
1958
  "data-testid": rootTestid.value,
2232
1959
  onSubmit: _cache[4] || (_cache[4] = (0, vue.withModifiers)(($event) => emit("submit"), ["prevent"]))
2233
1960
  }, {
2234
1961
  default: (0, vue.withCtx)(() => [
2235
- (0, vue.createElementVNode)("div", _hoisted_1$5, [(0, vue.createElementVNode)("div", _hoisted_2$5, [(0, vue.createElementVNode)("span", _hoisted_3$5, (0, vue.toDisplayString)(__props.collection.title), 1), (0, vue.createElementVNode)("h2", {
1962
+ (0, vue.createElementVNode)("div", _hoisted_1$7, [(0, vue.createElementVNode)("div", _hoisted_2$6, [(0, vue.createElementVNode)("span", _hoisted_3$6, (0, vue.toDisplayString)(__props.collection.title), 1), (0, vue.createElementVNode)("h2", {
2236
1963
  class: "text-base font-bold text-slate-800 truncate",
2237
1964
  "data-testid": editing.value ? "collections-edit-title" : "collections-detail-title"
2238
- }, (0, vue.toDisplayString)(headerTitle.value), 9, _hoisted_4$5)]), editing.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createElementVNode)("button", {
1965
+ }, (0, vue.toDisplayString)(headerTitle.value), 9, _hoisted_4$6)]), editing.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [(0, vue.createElementVNode)("button", {
2239
1966
  type: "button",
2240
1967
  class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
2241
1968
  "data-testid": "collections-editor-cancel",
@@ -2245,7 +1972,7 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2245
1972
  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",
2246
1973
  disabled: __props.saving,
2247
1974
  "data-testid": "collections-editor-save"
2248
- }, (0, vue.toDisplayString)(__props.saving ? (0, vue.unref)(t)("common.saving") : (0, vue.unref)(t)("common.save")), 9, _hoisted_5$4)], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$4, [
1975
+ }, (0, vue.toDisplayString)(__props.saving ? (0, vue.unref)(t)("common.saving") : (0, vue.unref)(t)("common.save")), 9, _hoisted_5$5)], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_6$4, [
2249
1976
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.visibleActions, (action) => {
2250
1977
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
2251
1978
  key: action.id,
@@ -2421,23 +2148,18 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2421
2148
  key: 1,
2422
2149
  class: "text-xs font-medium text-slate-700 break-words",
2423
2150
  "data-testid": `collections-detail-value-${key}`
2424
- }, [field.type === "toggle" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [field.field !== void 0 && String(detailRecord.value[field.field] ?? "") === field.onValue ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_48$1, [_cache[11] || (_cache[11] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_49$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1))], 64)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [detailRecord.value[key] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_50$1, [_cache[12] || (_cache[12] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : detailRecord.value[key] === false ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_51$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_52$1, "—"))], 64)) : field.type === "ref" && field.to && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? ((0, vue.openBlock)(), (0, vue.createBlock)(_component_router_link, {
2151
+ }, [field.type === "toggle" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [field.field !== void 0 && String(detailRecord.value[field.field] ?? "") === field.onValue ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_48$1, [_cache[11] || (_cache[11] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_49$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1))], 64)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [detailRecord.value[key] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_50$1, [_cache[12] || (_cache[12] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : detailRecord.value[key] === false ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_51$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_52$1, "—"))], 64)) : field.type === "ref" && field.to && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2425
2152
  key: 2,
2426
- to: {
2427
- path: `/collections/${field.to}`,
2428
- query: { selected: String(detailRecord.value[key]) }
2429
- },
2153
+ href: (0, vue.unref)(cui).recordHref?.(field.to, String(detailRecord.value[key])),
2430
2154
  class: "text-indigo-600 hover:text-indigo-800 font-bold hover:underline",
2431
- "data-testid": `collections-detail-ref-${key}`
2432
- }, {
2433
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(__props.render.refDisplay(field.to, String(detailRecord.value[key]))), 1)]),
2434
- _: 2
2435
- }, 1032, ["to", "data-testid"])) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_53$1, (0, vue.toDisplayString)(__props.render.formatMoney(detailRecord.value[key], __props.render.resolveCurrency(field, detailRecord.value), __props.locale)), 1)) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_54$1, (0, vue.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]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_55$1, [(0, vue.createElementVNode)("table", _hoisted_56$1, [(0, vue.createElementVNode)("thead", _hoisted_57$1, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.of, (subField, subKey) => {
2155
+ "data-testid": `collections-detail-ref-${key}`,
2156
+ onClick: (0, vue.withModifiers)(($event) => (0, vue.unref)(cui).navigateToRecord(field.to, String(detailRecord.value[key])), ["exact", "prevent"])
2157
+ }, (0, vue.toDisplayString)(__props.render.refDisplay(field.to, String(detailRecord.value[key]))), 9, _hoisted_53$1)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_54$1, (0, vue.toDisplayString)(__props.render.formatMoney(detailRecord.value[key], __props.render.resolveCurrency(field, detailRecord.value), __props.locale)), 1)) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_55$1, (0, vue.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]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_56$1, [(0, vue.createElementVNode)("table", _hoisted_57$1, [(0, vue.createElementVNode)("thead", _hoisted_58$1, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.of, (subField, subKey) => {
2436
2158
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
2437
2159
  key: subKey,
2438
2160
  class: "text-left px-4 py-2 font-bold"
2439
2161
  }, (0, vue.toDisplayString)(subField.label), 1);
2440
- }), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_58$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.tableRows(detailRecord.value[key]), (row, rowIdx) => {
2162
+ }), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_59$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.tableRows(detailRecord.value[key]), (row, rowIdx) => {
2441
2163
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("tr", {
2442
2164
  key: rowIdx,
2443
2165
  class: "hover:bg-slate-50/50"
@@ -2445,12 +2167,12 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2445
2167
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("td", {
2446
2168
  key: subKey,
2447
2169
  class: "px-4 py-2 align-middle font-medium"
2448
- }, [subField.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [row[subKey] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_59$1, "check_circle")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_60$1, "—"))], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2170
+ }, [subField.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [row[subKey] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_60$1, "check_circle")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_61$1, "—"))], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2449
2171
  key: 1,
2450
2172
  class: (0, vue.normalizeClass)([subField.type === "money" ? "font-bold text-slate-800 tabular-nums" : ""])
2451
2173
  }, (0, vue.toDisplayString)(__props.render.formatSubCell(subField, row[subKey], detailRecord.value)), 3))]);
2452
2174
  }), 128))]);
2453
- }), 128))])])])) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_61$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 1)) : field.type === "markdown" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_62$1, (0, vue.toDisplayString)(__props.render.detailText(detailRecord.value[key])), 1)) : field.type === "embed" && embedViews.value[key] ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionEmbedView_default, {
2175
+ }), 128))])])])) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_62$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 1)) : field.type === "markdown" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_63$1, (0, vue.toDisplayString)(__props.render.detailText(detailRecord.value[key])), 1)) : field.type === "embed" && embedViews.value[key] ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionEmbedView_default, {
2454
2176
  key: 8,
2455
2177
  view: embedViews.value[key],
2456
2178
  "field-key": String(key)
@@ -2460,30 +2182,28 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2460
2182
  alt: field.label,
2461
2183
  class: "max-h-64 max-w-full object-contain rounded-lg border border-slate-200 bg-slate-50",
2462
2184
  "data-testid": `collections-detail-image-${key}`
2463
- }, null, 8, _hoisted_63$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2185
+ }, null, 8, _hoisted_64$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2464
2186
  key: 10,
2465
2187
  href: String(detailRecord.value[key]),
2466
2188
  target: "_blank",
2467
2189
  rel: "noopener noreferrer",
2468
2190
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2469
2191
  "data-testid": `collections-detail-url-${key}`
2470
- }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_64$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2192
+ }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_65$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2471
2193
  key: 11,
2472
2194
  href: __props.render.artifactUrl(detailRecord.value[key]) ?? void 0,
2473
2195
  target: "_blank",
2474
2196
  rel: "noopener noreferrer",
2475
2197
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2476
2198
  "data-testid": `collections-detail-file-${key}`
2477
- }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_65$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createBlock)(_component_router_link, {
2199
+ }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_66$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2478
2200
  key: 12,
2479
- to: __props.render.fileRoutePath(detailRecord.value[key]) ?? "",
2201
+ href: __props.render.fileRoutePath(detailRecord.value[key]) ?? void 0,
2480
2202
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2481
- "data-testid": `collections-detail-file-${key}`
2482
- }, {
2483
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(String(detailRecord.value[key])), 1)]),
2484
- _: 2
2485
- }, 1032, ["to", "data-testid"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_66$1, (0, vue.toDisplayString)(__props.render.formatCell(detailRecord.value[key], field.type)), 1))], 8, _hoisted_47$1))], 2)) : (0, vue.createCommentVNode)("", true)], 64);
2486
- }), 128)), editing.value && __props.saveError ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_67$1, (0, vue.toDisplayString)(__props.saveError), 1)) : (0, vue.createCommentVNode)("", true)])
2203
+ "data-testid": `collections-detail-file-${key}`,
2204
+ onClick: (0, vue.withModifiers)(($event) => (0, vue.unref)(cui).navigate?.(__props.render.fileRoutePath(detailRecord.value[key]) ?? ""), ["exact", "prevent"])
2205
+ }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_67$1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_68$1, (0, vue.toDisplayString)(__props.render.formatCell(detailRecord.value[key], field.type)), 1))], 8, _hoisted_47$1))], 2)) : (0, vue.createCommentVNode)("", true)], 64);
2206
+ }), 128)), editing.value && __props.saveError ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_69$1, (0, vue.toDisplayString)(__props.saveError), 1)) : (0, vue.createCommentVNode)("", true)])
2487
2207
  ]),
2488
2208
  _: 1
2489
2209
  }, 40, ["data-testid"]);
@@ -2492,14 +2212,14 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2492
2212
  });
2493
2213
  //#endregion
2494
2214
  //#region src/vue/components/CollectionViewConfigModal.vue?vue&type=script&setup=true&lang.ts
2495
- var _hoisted_1$4 = {
2215
+ var _hoisted_1$6 = {
2496
2216
  "data-testid": "collection-config-modal",
2497
2217
  class: "flex flex-col overflow-hidden"
2498
2218
  };
2499
- var _hoisted_2$4 = { class: "flex items-center justify-between gap-2 border-b border-slate-100 px-5 py-3" };
2500
- var _hoisted_3$4 = { class: "text-sm font-bold text-slate-700" };
2501
- var _hoisted_4$4 = ["title", "aria-label"];
2502
- var _hoisted_5$3 = { class: "overflow-y-auto px-5 py-4" };
2219
+ var _hoisted_2$5 = { class: "flex items-center justify-between gap-2 border-b border-slate-100 px-5 py-3" };
2220
+ var _hoisted_3$5 = { class: "text-sm font-bold text-slate-700" };
2221
+ var _hoisted_4$5 = ["title", "aria-label"];
2222
+ var _hoisted_5$4 = { class: "overflow-y-auto px-5 py-4" };
2503
2223
  var _hoisted_6$3 = { class: "mb-2 text-[10px] font-bold uppercase tracking-wider text-slate-400" };
2504
2224
  var _hoisted_7$3 = {
2505
2225
  key: 0,
@@ -2564,14 +2284,14 @@ var CollectionViewConfigModal_default = /* @__PURE__ */ (0, vue.defineComponent)
2564
2284
  }
2565
2285
  return (_ctx, _cache) => {
2566
2286
  return (0, vue.openBlock)(), (0, vue.createBlock)(CollectionRecordModal_default, { onClose: _cache[1] || (_cache[1] = ($event) => emit("close")) }, {
2567
- default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_1$4, [(0, vue.createElementVNode)("header", _hoisted_2$4, [(0, vue.createElementVNode)("h2", _hoisted_3$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.config.title", { title: __props.title })), 1), (0, vue.createElementVNode)("button", {
2287
+ default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", _hoisted_1$6, [(0, vue.createElementVNode)("header", _hoisted_2$5, [(0, vue.createElementVNode)("h2", _hoisted_3$5, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.config.title", { title: __props.title })), 1), (0, vue.createElementVNode)("button", {
2568
2288
  type: "button",
2569
2289
  class: "h-8 w-8 flex items-center justify-center rounded text-slate-400 hover:bg-slate-50 hover:text-slate-600",
2570
2290
  title: (0, vue.unref)(t)("common.close"),
2571
2291
  "aria-label": (0, vue.unref)(t)("common.close"),
2572
2292
  "data-testid": "collection-config-close",
2573
2293
  onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
2574
- }, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_4$4)]), (0, vue.createElementVNode)("div", _hoisted_5$3, [
2294
+ }, [..._cache[2] || (_cache[2] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_4$5)]), (0, vue.createElementVNode)("div", _hoisted_5$4, [
2575
2295
  (0, vue.createElementVNode)("h3", _hoisted_6$3, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.config.viewsHeading")), 1),
2576
2296
  error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_7$3, (0, vue.toDisplayString)(error.value), 1)) : (0, vue.createCommentVNode)("", true),
2577
2297
  __props.views.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("ul", _hoisted_8$3, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.views, (view) => {
@@ -2600,19 +2320,19 @@ var CollectionViewConfigModal_default = /* @__PURE__ */ (0, vue.defineComponent)
2600
2320
  });
2601
2321
  //#endregion
2602
2322
  //#region src/vue/components/CollectionCustomView.vue?vue&type=script&setup=true&lang.ts
2603
- var _hoisted_1$3 = { class: "custom-view-container" };
2604
- var _hoisted_2$3 = {
2323
+ var _hoisted_1$5 = { class: "custom-view-container" };
2324
+ var _hoisted_2$4 = {
2605
2325
  key: 0,
2606
2326
  class: "custom-view-message",
2607
2327
  role: "alert",
2608
2328
  "data-testid": "collection-custom-view-error"
2609
2329
  };
2610
- var _hoisted_3$3 = {
2330
+ var _hoisted_3$4 = {
2611
2331
  key: 1,
2612
2332
  class: "custom-view-message",
2613
2333
  "data-testid": "collection-custom-view-loading"
2614
2334
  };
2615
- var _hoisted_4$3 = ["title", "srcdoc"];
2335
+ var _hoisted_4$4 = ["title", "srcdoc"];
2616
2336
  var REMINT_LEAD_MS = 6e4;
2617
2337
  var MIN_REMINT_DELAY_MS = 1e4;
2618
2338
  var CollectionCustomView_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -2668,46 +2388,365 @@ var CollectionCustomView_vue_vue_type_script_setup_true_lang_default = /* @__PUR
2668
2388
  dataUrl: mint.data.dataUrl,
2669
2389
  origin: window.location.origin
2670
2390
  });
2671
- } catch (err) {
2672
- if (!stale()) error.value = require_promptSafety.errorMessage(err);
2673
- } finally {
2674
- if (!stale()) loading.value = false;
2391
+ } catch (err) {
2392
+ if (!stale()) error.value = require_promptSafety.errorMessage(err);
2393
+ } finally {
2394
+ if (!stale()) loading.value = false;
2395
+ }
2396
+ }
2397
+ (0, vue.watch)([() => props.slug, () => props.view.id], () => void load(), { immediate: true });
2398
+ (0, vue.onBeforeUnmount)(clearRefresh);
2399
+ return (_ctx, _cache) => {
2400
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$5, [error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.customViewError", { error: error.value })), 1)) : loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.customViewLoading")), 1)) : srcdoc.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("iframe", {
2401
+ key: __props.view.id,
2402
+ "data-testid": "collection-custom-view-iframe",
2403
+ title: __props.view.label,
2404
+ srcdoc: srcdoc.value,
2405
+ sandbox: "allow-scripts allow-popups allow-popups-to-escape-sandbox",
2406
+ class: "w-full h-full border-0"
2407
+ }, null, 8, _hoisted_4$4)) : (0, vue.createCommentVNode)("", true)]);
2408
+ };
2409
+ }
2410
+ });
2411
+ //#endregion
2412
+ //#region \0plugin-vue:export-helper
2413
+ var _plugin_vue_export_helper_default = (sfc, props) => {
2414
+ const target = sfc.__vccOpts || sfc;
2415
+ for (const [key, val] of props) target[key] = val;
2416
+ return target;
2417
+ };
2418
+ //#endregion
2419
+ //#region src/vue/components/CollectionCustomView.vue
2420
+ var CollectionCustomView_default = /* @__PURE__ */ _plugin_vue_export_helper_default(CollectionCustomView_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3ab71997"]]);
2421
+ //#endregion
2422
+ //#region src/vue/useCollectionRendering.ts
2423
+ function useCollectionRendering(collection, locale) {
2424
+ const refCache = (0, vue.ref)({});
2425
+ const refRecordCache = (0, vue.ref)({});
2426
+ const embedCache = (0, vue.ref)({});
2427
+ function resetLinkedCaches() {
2428
+ refCache.value = {};
2429
+ refRecordCache.value = {};
2430
+ embedCache.value = {};
2431
+ }
2432
+ function uniqueRefTargets(schema) {
2433
+ const targets = /* @__PURE__ */ new Set();
2434
+ const walk = (fields) => {
2435
+ for (const field of Object.values(fields)) {
2436
+ if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
2437
+ if (field.type === "table" && field.of) walk(field.of);
2438
+ }
2439
+ };
2440
+ walk(schema.fields);
2441
+ return [...targets];
2442
+ }
2443
+ function uniqueEmbedTargets(schema) {
2444
+ const targets = /* @__PURE__ */ new Set();
2445
+ for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
2446
+ return [...targets];
2447
+ }
2448
+ function buildRefDisplayMap(detail) {
2449
+ const { fields, primaryKey } = detail.collection.schema;
2450
+ const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
2451
+ const map = {};
2452
+ for (const item of detail.items) {
2453
+ const slugRaw = item[primaryKey];
2454
+ if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
2455
+ const displayRaw = item[displayField];
2456
+ map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
2457
+ }
2458
+ return map;
2459
+ }
2460
+ function buildRefRecordMap(detail) {
2461
+ const { schema } = detail.collection;
2462
+ const map = {};
2463
+ for (const item of detail.items) {
2464
+ const slugRaw = item[schema.primaryKey];
2465
+ if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = require_deriveAll.deriveAll(schema, item, {});
2466
+ }
2467
+ return map;
2468
+ }
2469
+ async function loadLinkedCollections(schema, expectedSlug) {
2470
+ const refTargets = new Set(uniqueRefTargets(schema));
2471
+ const embedTargets = new Set(uniqueEmbedTargets(schema));
2472
+ const allTargets = [...new Set([...refTargets, ...embedTargets])];
2473
+ if (allTargets.length === 0) return;
2474
+ const binding = collectionUi();
2475
+ const results = await Promise.all(allTargets.map(async (target) => {
2476
+ try {
2477
+ return {
2478
+ target,
2479
+ result: await binding.fetchCollectionDetail(target)
2480
+ };
2481
+ } catch {
2482
+ return {
2483
+ target,
2484
+ result: { ok: false }
2485
+ };
2486
+ }
2487
+ }));
2488
+ if (collection.value?.slug !== expectedSlug) return;
2489
+ const nextRef = {};
2490
+ const nextRefRecords = {};
2491
+ const nextEmbed = {};
2492
+ for (const { target, result } of results) {
2493
+ if (!result.ok) continue;
2494
+ if (refTargets.has(target)) {
2495
+ nextRef[target] = buildRefDisplayMap(result.data);
2496
+ nextRefRecords[target] = buildRefRecordMap(result.data);
2497
+ }
2498
+ if (embedTargets.has(target)) nextEmbed[target] = {
2499
+ schema: result.data.collection.schema,
2500
+ items: result.data.items
2501
+ };
2502
+ }
2503
+ refCache.value = nextRef;
2504
+ refRecordCache.value = nextRefRecords;
2505
+ embedCache.value = nextEmbed;
2506
+ }
2507
+ function refDisplay(targetSlug, itemSlug) {
2508
+ const map = refCache.value[targetSlug];
2509
+ return map && map[itemSlug] || itemSlug;
2510
+ }
2511
+ function refOptions(targetSlug) {
2512
+ const map = refCache.value[targetSlug];
2513
+ if (!map) return [];
2514
+ return Object.entries(map).map(([slug, display]) => ({
2515
+ slug,
2516
+ display
2517
+ })).sort((left, right) => left.display.localeCompare(right.display));
2518
+ }
2519
+ function resolveEmbed(field) {
2520
+ if (field.type !== "embed" || !field.to || !field.id) return {
2521
+ schema: null,
2522
+ item: null
2523
+ };
2524
+ const data = embedCache.value[field.to];
2525
+ if (!data) return {
2526
+ schema: null,
2527
+ item: null
2528
+ };
2529
+ const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === field.id) ?? null;
2530
+ return {
2531
+ schema: data.schema,
2532
+ item
2533
+ };
2534
+ }
2535
+ function embedValue(field, value, record) {
2536
+ if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale.value);
2537
+ return detailText(value);
2538
+ }
2539
+ const embedViews = (0, vue.computed)(() => {
2540
+ const out = {};
2541
+ if (!collection.value) return out;
2542
+ for (const [key, field] of Object.entries(collection.value.schema.fields)) {
2543
+ if (field.type !== "embed") continue;
2544
+ const { schema, item } = resolveEmbed(field);
2545
+ const rows = [];
2546
+ if (schema && item) for (const [subKey, subField] of Object.entries(schema.fields)) {
2547
+ const value = item[subKey];
2548
+ if (value === void 0 || value === null || value === "") continue;
2549
+ rows.push({
2550
+ key: subKey,
2551
+ label: subField.label,
2552
+ type: subField.type,
2553
+ value,
2554
+ display: embedValue(subField, value, item)
2555
+ });
2675
2556
  }
2557
+ out[key] = {
2558
+ found: Boolean(item),
2559
+ rows,
2560
+ targetSlug: field.to ?? "",
2561
+ recordId: field.id ?? ""
2562
+ };
2676
2563
  }
2677
- (0, vue.watch)([() => props.slug, () => props.view.id], () => void load(), { immediate: true });
2678
- (0, vue.onBeforeUnmount)(clearRefresh);
2679
- return (_ctx, _cache) => {
2680
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$3, [error.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2$3, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.customViewError", { error: error.value })), 1)) : loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3$3, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.customViewLoading")), 1)) : srcdoc.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("iframe", {
2681
- key: __props.view.id,
2682
- "data-testid": "collection-custom-view-iframe",
2683
- title: __props.view.label,
2684
- srcdoc: srcdoc.value,
2685
- sandbox: "allow-scripts allow-popups allow-popups-to-escape-sandbox",
2686
- class: "w-full h-full border-0"
2687
- }, null, 8, _hoisted_4$3)) : (0, vue.createCommentVNode)("", true)]);
2688
- };
2564
+ return out;
2565
+ });
2566
+ function resolveCurrency(field, record) {
2567
+ if (field.currencyField && record) {
2568
+ const code = record[field.currencyField];
2569
+ if (typeof code === "string" && code.trim().length > 0) return code;
2570
+ }
2571
+ return field.currency;
2689
2572
  }
2690
- });
2691
- //#endregion
2692
- //#region \0plugin-vue:export-helper
2693
- var _plugin_vue_export_helper_default = (sfc, props) => {
2694
- const target = sfc.__vccOpts || sfc;
2695
- for (const [key, val] of props) target[key] = val;
2696
- return target;
2697
- };
2573
+ function currencySymbol(currency) {
2574
+ const code = currency && currency.length > 0 ? currency : "USD";
2575
+ try {
2576
+ return new Intl.NumberFormat(locale.value, {
2577
+ style: "currency",
2578
+ currency: code
2579
+ }).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
2580
+ } catch {
2581
+ return code;
2582
+ }
2583
+ }
2584
+ function formatMoney(value, currency, displayLocale) {
2585
+ if (value === void 0 || value === "") return "—";
2586
+ const amount = typeof value === "number" ? value : Number(value);
2587
+ if (!Number.isFinite(amount)) return String(value);
2588
+ const currencyCode = currency && currency.length > 0 ? currency : "USD";
2589
+ try {
2590
+ return new Intl.NumberFormat(displayLocale, {
2591
+ style: "currency",
2592
+ currency: currencyCode
2593
+ }).format(amount);
2594
+ } catch {
2595
+ return String(amount);
2596
+ }
2597
+ }
2598
+ function formatCell(value, type) {
2599
+ if (value === void 0 || value === null || value === "") return "—";
2600
+ if (type === "markdown" && typeof value === "string") return value.length > 80 ? `${value.slice(0, 80)}…` : value;
2601
+ if (typeof value === "string" || typeof value === "number") return String(value);
2602
+ return JSON.stringify(value);
2603
+ }
2604
+ function isExternalUrl(value) {
2605
+ return typeof value === "string" && /^https?:\/\//i.test(value);
2606
+ }
2607
+ function artifactUrl(value) {
2608
+ return collectionUi().fileAssetUrl(value);
2609
+ }
2610
+ function fileRoutePath(value) {
2611
+ return collectionUi().fileRoutePath(value);
2612
+ }
2613
+ function detailText(value) {
2614
+ if (value === void 0 || value === null || value === "") return "—";
2615
+ return String(value);
2616
+ }
2617
+ function tableRows(value) {
2618
+ if (!Array.isArray(value)) return [];
2619
+ return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
2620
+ }
2621
+ function hasTableRows(value) {
2622
+ return tableRows(value).length > 0;
2623
+ }
2624
+ function formatSubCell(subField, value, record) {
2625
+ if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
2626
+ if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
2627
+ return formatCell(value, subField.type);
2628
+ }
2629
+ function inputTypeFor(type) {
2630
+ if (type === "email") return "email";
2631
+ if (type === "number") return "number";
2632
+ if (type === "money") return "number";
2633
+ if (type === "date") return "date";
2634
+ if (type === "datetime") return "datetime-local";
2635
+ return "text";
2636
+ }
2637
+ function evaluateDerivedAgainstItem(field, fieldKey, item) {
2638
+ if (!field.formula || !collection.value) return null;
2639
+ const result = require_deriveAll.deriveAll(collection.value.schema, item, refRecordCache.value)[fieldKey];
2640
+ return typeof result === "number" && Number.isFinite(result) ? result : null;
2641
+ }
2642
+ function derivedDisplay(field, computedValue, record) {
2643
+ if (computedValue === null || computedValue === void 0) return "—";
2644
+ if (field.display === "money") return formatMoney(computedValue, resolveCurrency(field, record), locale.value);
2645
+ return formatCell(computedValue, field.display ?? "number");
2646
+ }
2647
+ return {
2648
+ refCache,
2649
+ refRecordCache,
2650
+ embedCache,
2651
+ resetLinkedCaches,
2652
+ loadLinkedCollections,
2653
+ refDisplay,
2654
+ refOptions,
2655
+ embedViews,
2656
+ resolveCurrency,
2657
+ currencySymbol,
2658
+ formatMoney,
2659
+ formatCell,
2660
+ detailText,
2661
+ isExternalUrl,
2662
+ artifactUrl,
2663
+ fileRoutePath,
2664
+ tableRows,
2665
+ hasTableRows,
2666
+ formatSubCell,
2667
+ inputTypeFor,
2668
+ deriveAll: require_deriveAll.deriveAll,
2669
+ evaluateDerivedAgainstItem,
2670
+ derivedDisplay
2671
+ };
2672
+ }
2698
2673
  //#endregion
2699
- //#region src/vue/components/CollectionCustomView.vue
2700
- var CollectionCustomView_default = /* @__PURE__ */ _plugin_vue_export_helper_default(CollectionCustomView_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-3ab71997"]]);
2674
+ //#region src/vue/collectionViewMode.ts
2675
+ var STORAGE_KEY = "collection_view_modes";
2676
+ var SORT_STORAGE_KEY = "collection_sorts";
2677
+ var BUILT_IN_MODES = [
2678
+ "table",
2679
+ "calendar",
2680
+ "kanban"
2681
+ ];
2682
+ /** A persisted mode is valid if it's a known built-in OR any `custom:<id>`
2683
+ * key (the id is validated against the live schema at render time, so an
2684
+ * unknown custom id simply collapses to the table there). Takes `unknown`
2685
+ * and type-guards `string` first: a corrupted localStorage entry could hold a
2686
+ * number/object, and calling `.startsWith` on that would throw. */
2687
+ function isValidViewMode(value) {
2688
+ return typeof value === "string" && (BUILT_IN_MODES.includes(value) || value.startsWith("custom:"));
2689
+ }
2690
+ function readAll() {
2691
+ try {
2692
+ const raw = localStorage.getItem(STORAGE_KEY);
2693
+ if (!raw) return {};
2694
+ const parsed = JSON.parse(raw);
2695
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
2696
+ } catch {
2697
+ return {};
2698
+ }
2699
+ }
2700
+ function readCollectionViewMode(slug) {
2701
+ const stored = readAll()[slug];
2702
+ return isValidViewMode(stored) ? stored : null;
2703
+ }
2704
+ function writeCollectionViewMode(slug, view) {
2705
+ try {
2706
+ const all = readAll();
2707
+ all[slug] = view;
2708
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
2709
+ } catch {}
2710
+ }
2711
+ function isSortState(value) {
2712
+ if (!value || typeof value !== "object") return false;
2713
+ const rec = value;
2714
+ return typeof rec.field === "string" && (rec.direction === "asc" || rec.direction === "desc");
2715
+ }
2716
+ function readAllSorts() {
2717
+ try {
2718
+ const raw = localStorage.getItem(SORT_STORAGE_KEY);
2719
+ if (!raw) return {};
2720
+ const parsed = JSON.parse(raw);
2721
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
2722
+ const out = {};
2723
+ for (const [slug, value] of Object.entries(parsed)) if (isSortState(value)) out[slug] = value;
2724
+ return out;
2725
+ } catch {
2726
+ return {};
2727
+ }
2728
+ }
2729
+ function readCollectionSort(slug) {
2730
+ return readAllSorts()[slug] ?? null;
2731
+ }
2732
+ /** Persist (or, when `sort` is null, clear) the slug's active column sort. */
2733
+ function writeCollectionSort(slug, sort) {
2734
+ try {
2735
+ const all = Object.fromEntries(Object.entries(readAllSorts()).filter(([key]) => key !== slug));
2736
+ if (sort) all[slug] = sort;
2737
+ localStorage.setItem(SORT_STORAGE_KEY, JSON.stringify(all));
2738
+ } catch {}
2739
+ }
2701
2740
  //#endregion
2702
2741
  //#region src/vue/components/CollectionView.vue?vue&type=script&setup=true&lang.ts
2703
- var _hoisted_1$2 = { class: "h-full flex flex-col bg-slate-50/30" };
2704
- var _hoisted_2$2 = { class: "flex items-center gap-3 px-6 py-2 border-b border-slate-200 bg-white" };
2705
- var _hoisted_3$2 = ["title", "aria-label"];
2706
- var _hoisted_4$2 = {
2742
+ var _hoisted_1$4 = { class: "h-full flex flex-col bg-slate-50/30" };
2743
+ var _hoisted_2$3 = { class: "flex items-center gap-3 px-6 py-2 border-b border-slate-200 bg-white" };
2744
+ var _hoisted_3$3 = ["title", "aria-label"];
2745
+ var _hoisted_4$3 = {
2707
2746
  key: 1,
2708
2747
  class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100"
2709
2748
  };
2710
- var _hoisted_5$2 = { class: "material-symbols-outlined text-xl" };
2749
+ var _hoisted_5$3 = { class: "material-symbols-outlined text-xl" };
2711
2750
  var _hoisted_6$2 = { class: "flex-1 min-w-0" };
2712
2751
  var _hoisted_7$2 = { class: "text-base font-bold text-slate-800 truncate" };
2713
2752
  var _hoisted_8$2 = {
@@ -2854,48 +2893,58 @@ var _hoisted_65 = {
2854
2893
  class: "block truncate"
2855
2894
  };
2856
2895
  var _hoisted_66 = [
2896
+ "href",
2897
+ "data-testid",
2898
+ "onClick"
2899
+ ];
2900
+ var _hoisted_67 = [
2857
2901
  "value",
2858
2902
  "disabled",
2859
2903
  "data-testid",
2860
2904
  "aria-label",
2861
2905
  "onChange"
2862
2906
  ];
2863
- var _hoisted_67 = {
2907
+ var _hoisted_68 = {
2864
2908
  key: 0,
2865
2909
  value: ""
2866
2910
  };
2867
- var _hoisted_68 = ["value"];
2868
- var _hoisted_69 = {
2911
+ var _hoisted_69 = ["value"];
2912
+ var _hoisted_70 = {
2869
2913
  key: 4,
2870
2914
  class: "block truncate tabular-nums font-semibold text-slate-900"
2871
2915
  };
2872
- var _hoisted_70 = {
2916
+ var _hoisted_71 = {
2873
2917
  key: 5,
2874
2918
  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"
2875
2919
  };
2876
- var _hoisted_71 = {
2920
+ var _hoisted_72 = {
2877
2921
  key: 6,
2878
2922
  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"
2879
2923
  };
2880
- var _hoisted_72 = ["href", "data-testid"];
2881
2924
  var _hoisted_73 = ["href", "data-testid"];
2882
- var _hoisted_74 = {
2925
+ var _hoisted_74 = ["href", "data-testid"];
2926
+ var _hoisted_75 = [
2927
+ "href",
2928
+ "data-testid",
2929
+ "onClick"
2930
+ ];
2931
+ var _hoisted_76 = {
2883
2932
  key: 10,
2884
2933
  class: "block truncate text-slate-600"
2885
2934
  };
2886
- var _hoisted_75 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
2887
- var _hoisted_76 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
2888
- var _hoisted_77 = { class: "flex-1" };
2889
- var _hoisted_78 = {
2935
+ var _hoisted_77 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
2936
+ var _hoisted_78 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
2937
+ var _hoisted_79 = { class: "flex-1" };
2938
+ var _hoisted_80 = {
2890
2939
  id: "collections-chat-title",
2891
2940
  class: "text-sm font-bold text-slate-800 uppercase tracking-wide"
2892
2941
  };
2893
- var _hoisted_79 = { class: "text-xs text-slate-400 font-semibold" };
2894
- var _hoisted_80 = ["aria-label"];
2895
- var _hoisted_81 = { class: "px-6 py-5" };
2896
- var _hoisted_82 = ["placeholder", "onKeydown"];
2897
- 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" };
2898
- var _hoisted_84 = ["disabled"];
2942
+ var _hoisted_81 = { class: "text-xs text-slate-400 font-semibold" };
2943
+ var _hoisted_82 = ["aria-label"];
2944
+ var _hoisted_83 = { class: "px-6 py-5" };
2945
+ var _hoisted_84 = ["placeholder", "onKeydown"];
2946
+ 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" };
2947
+ var _hoisted_86 = ["disabled"];
2899
2948
  //#endregion
2900
2949
  //#region src/vue/components/CollectionView.vue
2901
2950
  var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -2916,6 +2965,18 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
2916
2965
  const cui = collectionUi();
2917
2966
  const { confirm: openConfirm, unpin, pinToggle, startChat } = cui;
2918
2967
  const appApi = { startNewChat: startChat };
2968
+ function onRefLinkClick(event, targetSlug, recordId) {
2969
+ event.stopPropagation();
2970
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
2971
+ event.preventDefault();
2972
+ cui.navigateToRecord(targetSlug, recordId);
2973
+ }
2974
+ function onFileLinkClick(event, path) {
2975
+ event.stopPropagation();
2976
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
2977
+ event.preventDefault();
2978
+ cui.navigate?.(path);
2979
+ }
2919
2980
  /** Embedded when a `slug` prop is supplied; standalone (route-driven)
2920
2981
  * otherwise. Switches the slug/selected source and the open/close
2921
2982
  * navigation behaviour. */
@@ -3862,9 +3923,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3862
3923
  openDay.value = viewing.value ? dayOfItem(viewing.value) : null;
3863
3924
  });
3864
3925
  return (_ctx, _cache) => {
3865
- const _component_router_link = (0, vue.resolveComponent)("router-link");
3866
- return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$2, [
3867
- (0, vue.createElementVNode)("header", _hoisted_2$2, [
3926
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$4, [
3927
+ (0, vue.createElementVNode)("header", _hoisted_2$3, [
3868
3928
  !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3869
3929
  key: 0,
3870
3930
  type: "button",
@@ -3873,8 +3933,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3873
3933
  "aria-label": (0, vue.unref)(t)("collectionsView.backToIndex"),
3874
3934
  "data-testid": "collections-back",
3875
3935
  onClick: goBack
3876
- }, [..._cache[25] || (_cache[25] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$2)) : (0, vue.createCommentVNode)("", true),
3877
- collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$2, [(0, vue.createElementVNode)("span", _hoisted_5$2, (0, vue.toDisplayString)(collection.value.icon), 1)])) : (0, vue.createCommentVNode)("", true),
3936
+ }, [..._cache[23] || (_cache[23] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$3)) : (0, vue.createCommentVNode)("", true),
3937
+ collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$3, [(0, vue.createElementVNode)("span", _hoisted_5$3, (0, vue.toDisplayString)(collection.value.icon), 1)])) : (0, vue.createCommentVNode)("", true),
3878
3938
  (0, vue.createElementVNode)("div", _hoisted_6$2, [(0, vue.createElementVNode)("h1", _hoisted_7$2, (0, vue.toDisplayString)(collection.value?.title ?? (0, vue.unref)(t)("collectionsView.title")), 1), collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$2, (0, vue.toDisplayString)(collection.value.slug), 1)) : (0, vue.createCommentVNode)("", true)]),
3879
3939
  collection.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(pinToggle)), {
3880
3940
  key: 2,
@@ -3902,7 +3962,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3902
3962
  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",
3903
3963
  "data-testid": "collections-chat",
3904
3964
  onClick: openChat
3905
- }, [_cache[26] || (_cache[26] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "forum", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chat")), 1)])) : (0, vue.createCommentVNode)("", true),
3965
+ }, [_cache[24] || (_cache[24] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "forum", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chat")), 1)])) : (0, vue.createCommentVNode)("", true),
3906
3966
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(collectionActions.value, (action) => {
3907
3967
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3908
3968
  key: action.id,
@@ -3919,7 +3979,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3919
3979
  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",
3920
3980
  "data-testid": "collections-add-item",
3921
3981
  onClick: openCreate
3922
- }, [_cache[27] || (_cache[27] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.add")), 1)])) : (0, vue.createCommentVNode)("", true),
3982
+ }, [_cache[25] || (_cache[25] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.add")), 1)])) : (0, vue.createCommentVNode)("", true),
3923
3983
  canDeleteCollection.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3924
3984
  key: 6,
3925
3985
  type: "button",
@@ -3928,7 +3988,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3928
3988
  "aria-label": (0, vue.unref)(t)("collectionsView.deleteCollection"),
3929
3989
  "data-testid": "collections-delete",
3930
3990
  onClick: confirmCollectionDelete
3931
- }, [..._cache[28] || (_cache[28] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$2)) : (0, vue.createCommentVNode)("", true),
3991
+ }, [..._cache[26] || (_cache[26] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$2)) : (0, vue.createCommentVNode)("", true),
3932
3992
  canDeleteFeed.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3933
3993
  key: 7,
3934
3994
  type: "button",
@@ -3937,10 +3997,10 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3937
3997
  "aria-label": (0, vue.unref)(t)("collectionsView.deleteFeed"),
3938
3998
  "data-testid": "feeds-delete",
3939
3999
  onClick: confirmFeedDelete
3940
- }, [..._cache[29] || (_cache[29] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$2)) : (0, vue.createCommentVNode)("", true)
4000
+ }, [..._cache[27] || (_cache[27] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$2)) : (0, vue.createCommentVNode)("", true)
3941
4001
  ]),
3942
4002
  collection.value && (items.value.length > 0 || hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$2, [items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_16$1, [
3943
- _cache[31] || (_cache[31] = (0, vue.createElementVNode)("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "search")], -1)),
4003
+ _cache[29] || (_cache[29] = (0, vue.createElementVNode)("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "search")], -1)),
3944
4004
  (0, vue.withDirectives)((0, vue.createElementVNode)("input", {
3945
4005
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
3946
4006
  type: "text",
@@ -3954,7 +4014,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3954
4014
  "aria-label": (0, vue.unref)(t)("collectionsView.clearSearch"),
3955
4015
  class: "absolute inset-y-0 right-0 flex items-center pr-2.5 text-slate-400 hover:text-slate-600",
3956
4016
  onClick: _cache[1] || (_cache[1] = ($event) => searchQuery.value = "")
3957
- }, [..._cache[30] || (_cache[30] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_18$1)) : (0, vue.createCommentVNode)("", true)
4017
+ }, [..._cache[28] || (_cache[28] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_18$1)) : (0, vue.createCommentVNode)("", true)
3958
4018
  ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_19$1, [
3959
4019
  hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
3960
4020
  key: 0,
@@ -3968,7 +4028,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3968
4028
  "aria-pressed": activeView.value === "table",
3969
4029
  "data-testid": "collection-view-toggle-table",
3970
4030
  onClick: _cache[2] || (_cache[2] = ($event) => setView("table"))
3971
- }, [_cache[32] || (_cache[32] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "table_rows", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewTable")), 1)], 10, _hoisted_21$1),
4031
+ }, [_cache[30] || (_cache[30] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "table_rows", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewTable")), 1)], 10, _hoisted_21$1),
3972
4032
  hasCalendar.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3973
4033
  key: 0,
3974
4034
  type: "button",
@@ -3976,7 +4036,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3976
4036
  "aria-pressed": activeView.value === "calendar",
3977
4037
  "data-testid": "collection-view-toggle-calendar",
3978
4038
  onClick: _cache[3] || (_cache[3] = ($event) => setView("calendar"))
3979
- }, [_cache[33] || (_cache[33] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "calendar_month", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_22$1)) : (0, vue.createCommentVNode)("", true),
4039
+ }, [_cache[31] || (_cache[31] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "calendar_month", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_22$1)) : (0, vue.createCommentVNode)("", true),
3980
4040
  hasKanban.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3981
4041
  key: 1,
3982
4042
  type: "button",
@@ -3984,7 +4044,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
3984
4044
  "aria-pressed": activeView.value === "kanban",
3985
4045
  "data-testid": "collection-view-toggle-kanban",
3986
4046
  onClick: _cache[4] || (_cache[4] = ($event) => setView("kanban"))
3987
- }, [_cache[34] || (_cache[34] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "view_kanban", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_23$1)) : (0, vue.createCommentVNode)("", true),
4047
+ }, [_cache[32] || (_cache[32] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "view_kanban", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_23$1)) : (0, vue.createCommentVNode)("", true),
3988
4048
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(customViews.value, (cv) => {
3989
4049
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
3990
4050
  key: cv.id,
@@ -4003,7 +4063,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4003
4063
  "aria-label": (0, vue.unref)(t)("collectionsView.addView"),
4004
4064
  "data-testid": "collection-view-add",
4005
4065
  onClick: addCustomView
4006
- }, [..._cache[35] || (_cache[35] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_26)) : (0, vue.createCommentVNode)("", true),
4066
+ }, [..._cache[33] || (_cache[33] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_26)) : (0, vue.createCommentVNode)("", true),
4007
4067
  canConfigureViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4008
4068
  key: 3,
4009
4069
  type: "button",
@@ -4012,7 +4072,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4012
4072
  "aria-label": (0, vue.unref)(t)("collectionsView.config.open"),
4013
4073
  "data-testid": "collection-config-open",
4014
4074
  onClick: _cache[5] || (_cache[5] = ($event) => configOpen.value = true)
4015
- }, [..._cache[36] || (_cache[36] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_27)) : (0, vue.createCommentVNode)("", true)
4075
+ }, [..._cache[34] || (_cache[34] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_27)) : (0, vue.createCommentVNode)("", true)
4016
4076
  ], 8, _hoisted_20$1)) : (0, vue.createCommentVNode)("", true),
4017
4077
  calendarActive.value && dateFields.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
4018
4078
  key: 1,
@@ -4046,16 +4106,16 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4046
4106
  })), 1)) : (0, vue.createCommentVNode)("", true)
4047
4107
  ])])) : (0, vue.createCommentVNode)("", true),
4048
4108
  collection.value && dataIssues.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_33, [
4049
- _cache[38] || (_cache[38] = (0, vue.createElementVNode)("span", { class: "material-icons text-amber-600" }, "warning", -1)),
4109
+ _cache[36] || (_cache[36] = (0, vue.createElementVNode)("span", { class: "material-icons text-amber-600" }, "warning", -1)),
4050
4110
  (0, vue.createElementVNode)("span", _hoisted_34, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dataIssuesDetected", { count: dataIssues.value.length })), 1),
4051
4111
  (0, vue.createElementVNode)("button", {
4052
4112
  type: "button",
4053
4113
  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",
4054
4114
  "data-testid": "collections-repair",
4055
4115
  onClick: repairCollection
4056
- }, [_cache[37] || (_cache[37] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "build", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.repair")), 1)])
4116
+ }, [_cache[35] || (_cache[35] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "build", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.repair")), 1)])
4057
4117
  ])) : (0, vue.createCommentVNode)("", true),
4058
- (0, vue.createElementVNode)("div", _hoisted_35, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_36, [_cache[39] || (_cache[39] = (0, vue.createElementVNode)("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.loading")), 1)])) : loadError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_37, [_cache[40] || (_cache[40] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(loadError.value === "not-found" ? (0, vue.unref)(t)("collectionsView.notFound") : `${(0, vue.unref)(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_38)) : calendarActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_39, [(0, vue.createVNode)(CollectionCalendarView_default, {
4118
+ (0, vue.createElementVNode)("div", _hoisted_35, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_36, [_cache[37] || (_cache[37] = (0, vue.createElementVNode)("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.loading")), 1)])) : loadError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_37, [_cache[38] || (_cache[38] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(loadError.value === "not-found" ? (0, vue.unref)(t)("collectionsView.notFound") : `${(0, vue.unref)(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_38)) : calendarActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_39, [(0, vue.createVNode)(CollectionCalendarView_default, {
4059
4119
  schema: collection.value.schema,
4060
4120
  items: filteredItems.value,
4061
4121
  "anchor-field": calendarAnchorField.value,
@@ -4140,14 +4200,14 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4140
4200
  "can-create",
4141
4201
  "show-detail"
4142
4202
  ])) : (0, vue.createCommentVNode)("", true)])) : kanbanActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_40, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_41, [
4143
- _cache[42] || (_cache[42] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
4203
+ _cache[40] || (_cache[40] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
4144
4204
  (0, vue.createElementVNode)("span", _hoisted_42, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
4145
4205
  (0, vue.createElementVNode)("button", {
4146
4206
  type: "button",
4147
4207
  class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
4148
4208
  "aria-label": (0, vue.unref)(t)("common.close"),
4149
4209
  onClick: _cache[10] || (_cache[10] = ($event) => inlineError.value = null)
4150
- }, [..._cache[41] || (_cache[41] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_43)
4210
+ }, [..._cache[39] || (_cache[39] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_43)
4151
4211
  ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_44, [(0, vue.createVNode)(CollectionKanbanView_default, {
4152
4212
  schema: collection.value.schema,
4153
4213
  items: filteredItems.value,
@@ -4165,8 +4225,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4165
4225
  ])])])) : activeCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_45, [(0, vue.createVNode)(CollectionCustomView_default, {
4166
4226
  slug: collection.value.slug,
4167
4227
  view: activeCustomView.value
4168
- }, null, 8, ["slug", "view"])])) : items.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_46, [_cache[43] || (_cache[43] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), (0, vue.createElementVNode)("p", _hoisted_47, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [
4169
- _cache[44] || (_cache[44] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
4228
+ }, null, 8, ["slug", "view"])])) : items.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_46, [_cache[41] || (_cache[41] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), (0, vue.createElementVNode)("p", _hoisted_47, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [
4229
+ _cache[42] || (_cache[42] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
4170
4230
  (0, vue.createElementVNode)("p", _hoisted_49, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noMatchingItems")), 1),
4171
4231
  (0, vue.createElementVNode)("button", {
4172
4232
  type: "button",
@@ -4174,14 +4234,14 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4174
4234
  onClick: _cache[11] || (_cache[11] = ($event) => searchQuery.value = "")
4175
4235
  }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.clearSearch")), 1)
4176
4236
  ])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_50, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_51, [
4177
- _cache[46] || (_cache[46] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
4237
+ _cache[44] || (_cache[44] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
4178
4238
  (0, vue.createElementVNode)("span", _hoisted_52, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
4179
4239
  (0, vue.createElementVNode)("button", {
4180
4240
  type: "button",
4181
4241
  class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
4182
4242
  "aria-label": (0, vue.unref)(t)("common.close"),
4183
4243
  onClick: _cache[12] || (_cache[12] = ($event) => inlineError.value = null)
4184
- }, [..._cache[45] || (_cache[45] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_53)
4244
+ }, [..._cache[43] || (_cache[43] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_53)
4185
4245
  ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("table", _hoisted_54, [(0, vue.createElementVNode)("thead", null, [(0, vue.createElementVNode)("tr", _hoisted_55, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(listColumnFields.value, ([key, field]) => {
4186
4246
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
4187
4247
  key,
@@ -4234,57 +4294,48 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4234
4294
  "aria-label": field.label,
4235
4295
  onClick: _cache[15] || (_cache[15] = (0, vue.withModifiers)(() => {}, ["stop"])),
4236
4296
  onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.checked)
4237
- }, null, 40, _hoisted_64)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_65, [(0, vue.createVNode)(_component_router_link, {
4238
- to: {
4239
- path: `/collections/${field.to}`,
4240
- query: { selected: String(item[key]) }
4241
- },
4297
+ }, null, 40, _hoisted_64)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_65, [(0, vue.createElementVNode)("a", {
4298
+ href: (0, vue.unref)(cui).recordHref?.(field.to, String(item[key])),
4242
4299
  class: "text-indigo-600 hover:text-indigo-800 hover:underline font-semibold",
4243
4300
  "data-testid": `collections-ref-link-${key}-${item[key]}`,
4244
- onClick: _cache[16] || (_cache[16] = (0, vue.withModifiers)(() => {}, ["stop"]))
4245
- }, {
4246
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(refDisplay)(field.to, String(item[key]))), 1)]),
4247
- _: 2
4248
- }, 1032, ["to", "data-testid"])])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
4301
+ onClick: ($event) => onRefLinkClick($event, field.to, String(item[key]))
4302
+ }, (0, vue.toDisplayString)((0, vue.unref)(refDisplay)(field.to, String(item[key]))), 9, _hoisted_66)])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
4249
4303
  key: 3,
4250
4304
  value: item[key] == null ? "" : String(item[key]),
4251
4305
  disabled: isRowInlineSaving(item),
4252
4306
  class: (0, vue.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])]),
4253
4307
  "data-testid": `collections-inline-enum-${key}-${item[collection.value.schema.primaryKey]}`,
4254
4308
  "aria-label": field.label,
4255
- onClick: _cache[17] || (_cache[17] = (0, vue.withModifiers)(() => {}, ["stop"])),
4309
+ onClick: _cache[16] || (_cache[16] = (0, vue.withModifiers)(() => {}, ["stop"])),
4256
4310
  onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.value)
4257
- }, [showEnumPlaceholder(item, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option", _hoisted_67, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1)) : (0, vue.createCommentVNode)("", true), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
4311
+ }, [showEnumPlaceholder(item, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option", _hoisted_68, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1)) : (0, vue.createCommentVNode)("", true), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
4258
4312
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
4259
4313
  key: value,
4260
4314
  value
4261
- }, (0, vue.toDisplayString)(value), 9, _hoisted_68);
4262
- }), 128))], 42, _hoisted_66)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_69, (0, vue.toDisplayString)((0, vue.unref)(formatMoney)(item[key], (0, vue.unref)(resolveCurrency)(field, item), (0, vue.unref)(locale))), 1)) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_70, [_cache[47] || (_cache[47] = (0, vue.createElementVNode)("span", { class: "material-icons text-[11px]" }, "list", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(tableSummary(item[key])), 1)])) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_71, (0, vue.toDisplayString)((0, vue.unref)(derivedDisplay)(field, (0, vue.unref)(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && (0, vue.unref)(isExternalUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4315
+ }, (0, vue.toDisplayString)(value), 9, _hoisted_69);
4316
+ }), 128))], 42, _hoisted_67)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_70, (0, vue.toDisplayString)((0, vue.unref)(formatMoney)(item[key], (0, vue.unref)(resolveCurrency)(field, item), (0, vue.unref)(locale))), 1)) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_71, [_cache[45] || (_cache[45] = (0, vue.createElementVNode)("span", { class: "material-icons text-[11px]" }, "list", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(tableSummary(item[key])), 1)])) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_72, (0, vue.toDisplayString)((0, vue.unref)(derivedDisplay)(field, (0, vue.unref)(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && (0, vue.unref)(isExternalUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4263
4317
  key: 7,
4264
4318
  href: String(item[key]),
4265
4319
  target: "_blank",
4266
4320
  rel: "noopener noreferrer",
4267
4321
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
4268
4322
  "data-testid": `collections-url-link-${key}-${item[collection.value.schema.primaryKey]}`,
4269
- onClick: _cache[18] || (_cache[18] = (0, vue.withModifiers)(() => {}, ["stop"]))
4270
- }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_72)) : field.type === "file" && (0, vue.unref)(artifactUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4323
+ onClick: _cache[17] || (_cache[17] = (0, vue.withModifiers)(() => {}, ["stop"]))
4324
+ }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_73)) : field.type === "file" && (0, vue.unref)(artifactUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4271
4325
  key: 8,
4272
4326
  href: (0, vue.unref)(artifactUrl)(item[key]) ?? void 0,
4273
4327
  target: "_blank",
4274
4328
  rel: "noopener noreferrer",
4275
4329
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
4276
4330
  "data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
4277
- onClick: _cache[19] || (_cache[19] = (0, vue.withModifiers)(() => {}, ["stop"]))
4278
- }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_73)) : field.type === "file" && (0, vue.unref)(fileRoutePath)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createBlock)(_component_router_link, {
4331
+ onClick: _cache[18] || (_cache[18] = (0, vue.withModifiers)(() => {}, ["stop"]))
4332
+ }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_74)) : field.type === "file" && (0, vue.unref)(fileRoutePath)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
4279
4333
  key: 9,
4280
- to: (0, vue.unref)(fileRoutePath)(item[key]) ?? "",
4334
+ href: (0, vue.unref)(fileRoutePath)(item[key]) ?? void 0,
4281
4335
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
4282
4336
  "data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
4283
- onClick: _cache[20] || (_cache[20] = (0, vue.withModifiers)(() => {}, ["stop"]))
4284
- }, {
4285
- default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)(String(item[key])), 1)]),
4286
- _: 2
4287
- }, 1032, ["to", "data-testid"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_74, (0, vue.toDisplayString)((0, vue.unref)(formatCell)(item[key], field.type)), 1))], 64)) : (0, vue.createCommentVNode)("", true)]);
4337
+ onClick: ($event) => onFileLinkClick($event, (0, vue.unref)(fileRoutePath)(item[key]) ?? "")
4338
+ }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_75)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_76, (0, vue.toDisplayString)((0, vue.unref)(formatCell)(item[key], field.type)), 1))], 64)) : (0, vue.createCommentVNode)("", true)]);
4288
4339
  }), 128))], 42, _hoisted_62);
4289
4340
  }), 128))])])]))]),
4290
4341
  collection.value && (viewing.value || editing.value) && !(calendarActive.value && openDay.value) ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionRecordModal_default, {
@@ -4293,7 +4344,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4293
4344
  }, {
4294
4345
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)(CollectionRecordPanel_default, {
4295
4346
  editing: editing.value,
4296
- "onUpdate:editing": _cache[21] || (_cache[21] = ($event) => editing.value = $event),
4347
+ "onUpdate:editing": _cache[19] || (_cache[19] = ($event) => editing.value = $event),
4297
4348
  collection: collection.value,
4298
4349
  viewing: viewing.value,
4299
4350
  saving: saving.value,
@@ -4311,7 +4362,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4311
4362
  onCancel: cancelEditor,
4312
4363
  onEdit: editFromView,
4313
4364
  onClose: closeView,
4314
- onDelete: _cache[22] || (_cache[22] = ($event) => viewing.value && confirmDelete(viewing.value)),
4365
+ onDelete: _cache[20] || (_cache[20] = ($event) => viewing.value && confirmDelete(viewing.value)),
4315
4366
  onRunAction: runAction
4316
4367
  }, null, 8, [
4317
4368
  "editing",
@@ -4337,7 +4388,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4337
4388
  title: collection.value.title,
4338
4389
  views: customViews.value,
4339
4390
  onChanged: onViewsChanged,
4340
- onClose: _cache[23] || (_cache[23] = ($event) => configOpen.value = false)
4391
+ onClose: _cache[21] || (_cache[21] = ($event) => configOpen.value = false)
4341
4392
  }, null, 8, [
4342
4393
  "slug",
4343
4394
  "title",
@@ -4352,29 +4403,29 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4352
4403
  "data-testid": "collections-chat-modal",
4353
4404
  onClick: (0, vue.withModifiers)(closeChat, ["self"]),
4354
4405
  onKeydown: (0, vue.withKeys)(closeChat, ["esc"])
4355
- }, [(0, vue.createElementVNode)("div", _hoisted_75, [
4356
- (0, vue.createElementVNode)("header", _hoisted_76, [
4357
- _cache[49] || (_cache[49] = (0, vue.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" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "forum")], -1)),
4358
- (0, vue.createElementVNode)("div", _hoisted_77, [(0, vue.createElementVNode)("h2", _hoisted_78, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatTitle")), 1), (0, vue.createElementVNode)("span", _hoisted_79, (0, vue.toDisplayString)(collection.value.title), 1)]),
4406
+ }, [(0, vue.createElementVNode)("div", _hoisted_77, [
4407
+ (0, vue.createElementVNode)("header", _hoisted_78, [
4408
+ _cache[47] || (_cache[47] = (0, vue.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" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "forum")], -1)),
4409
+ (0, vue.createElementVNode)("div", _hoisted_79, [(0, vue.createElementVNode)("h2", _hoisted_80, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatTitle")), 1), (0, vue.createElementVNode)("span", _hoisted_81, (0, vue.toDisplayString)(collection.value.title), 1)]),
4359
4410
  (0, vue.createElementVNode)("button", {
4360
4411
  type: "button",
4361
4412
  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",
4362
4413
  "aria-label": (0, vue.unref)(t)("common.close"),
4363
4414
  "data-testid": "collections-chat-close",
4364
4415
  onClick: closeChat
4365
- }, [..._cache[48] || (_cache[48] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_80)
4416
+ }, [..._cache[46] || (_cache[46] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_82)
4366
4417
  ]),
4367
- (0, vue.createElementVNode)("div", _hoisted_81, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
4418
+ (0, vue.createElementVNode)("div", _hoisted_83, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
4368
4419
  ref_key: "chatInputEl",
4369
4420
  ref: chatInputEl,
4370
- "onUpdate:modelValue": _cache[24] || (_cache[24] = ($event) => chatMessage.value = $event),
4421
+ "onUpdate:modelValue": _cache[22] || (_cache[22] = ($event) => chatMessage.value = $event),
4371
4422
  rows: "4",
4372
4423
  placeholder: (0, vue.unref)(t)("collectionsView.chatPlaceholder"),
4373
4424
  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",
4374
4425
  "data-testid": "collections-chat-input",
4375
4426
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(submitChat, ["meta"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(submitChat, ["ctrl"]), ["enter"])]
4376
- }, null, 40, _hoisted_82), [[vue.vModelText, chatMessage.value]])]),
4377
- (0, vue.createElementVNode)("footer", _hoisted_83, [(0, vue.createElementVNode)("button", {
4427
+ }, null, 40, _hoisted_84), [[vue.vModelText, chatMessage.value]])]),
4428
+ (0, vue.createElementVNode)("footer", _hoisted_85, [(0, vue.createElementVNode)("button", {
4378
4429
  type: "button",
4379
4430
  class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
4380
4431
  "data-testid": "collections-chat-cancel",
@@ -4385,13 +4436,121 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4385
4436
  disabled: !chatMessage.value.trim(),
4386
4437
  "data-testid": "collections-chat-send",
4387
4438
  onClick: submitChat
4388
- }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatStart")), 9, _hoisted_84)])
4439
+ }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatStart")), 9, _hoisted_86)])
4389
4440
  ])], 32)) : (0, vue.createCommentVNode)("", true)
4390
4441
  ]);
4391
4442
  };
4392
4443
  }
4393
4444
  });
4394
4445
  //#endregion
4446
+ //#region src/vue/chat/View.vue?vue&type=script&setup=true&lang.ts
4447
+ var _hoisted_1$3 = {
4448
+ class: "w-full h-full",
4449
+ "data-testid": "present-collection"
4450
+ };
4451
+ //#endregion
4452
+ //#region src/vue/chat/View.vue
4453
+ var View_default = /* @__PURE__ */ (0, vue.defineComponent)({
4454
+ __name: "View",
4455
+ props: {
4456
+ selectedResult: {},
4457
+ sendTextMessage: { type: Function }
4458
+ },
4459
+ emits: ["updateResult"],
4460
+ setup(__props, { emit: __emit }) {
4461
+ const props = __props;
4462
+ const emit = __emit;
4463
+ const data = (0, vue.computed)(() => props.selectedResult?.data ?? props.selectedResult?.jsonData ?? null);
4464
+ const slug = (0, vue.computed)(() => data.value?.collectionSlug);
4465
+ const viewState = (0, vue.computed)(() => props.selectedResult?.viewState ?? null);
4466
+ /** Open record: the card-local `viewState.selected` once the user has
4467
+ * navigated within the card (including an explicit close → null), else
4468
+ * the tool's initial `itemId`. */
4469
+ const selected = (0, vue.computed)(() => {
4470
+ const state = viewState.value;
4471
+ if (state && "selected" in state) return state.selected ?? void 0;
4472
+ return data.value?.itemId;
4473
+ });
4474
+ function onSelect(itemId) {
4475
+ if (!props.selectedResult) return;
4476
+ emit("updateResult", {
4477
+ ...props.selectedResult,
4478
+ viewState: {
4479
+ ...viewState.value,
4480
+ selected: itemId
4481
+ }
4482
+ });
4483
+ }
4484
+ function onViewStateChange(state) {
4485
+ if (!props.selectedResult) return;
4486
+ const current = viewState.value;
4487
+ if (current?.view === state.view && current?.anchorField === state.anchorField && current?.groupField === state.groupField) return;
4488
+ emit("updateResult", {
4489
+ ...props.selectedResult,
4490
+ viewState: {
4491
+ ...current,
4492
+ view: state.view,
4493
+ anchorField: state.anchorField,
4494
+ groupField: state.groupField
4495
+ }
4496
+ });
4497
+ }
4498
+ return (_ctx, _cache) => {
4499
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$3, [slug.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionView_default, {
4500
+ key: 0,
4501
+ slug: slug.value,
4502
+ selected: selected.value,
4503
+ "initial-view": viewState.value?.view,
4504
+ "initial-anchor-field": viewState.value?.anchorField,
4505
+ "initial-group-field": viewState.value?.groupField,
4506
+ "send-text-message": __props.sendTextMessage,
4507
+ onSelect,
4508
+ onViewStateChange
4509
+ }, null, 8, [
4510
+ "slug",
4511
+ "selected",
4512
+ "initial-view",
4513
+ "initial-anchor-field",
4514
+ "initial-group-field",
4515
+ "send-text-message"
4516
+ ])) : (0, vue.createCommentVNode)("", true)]);
4517
+ };
4518
+ }
4519
+ });
4520
+ //#endregion
4521
+ //#region src/vue/chat/Preview.vue?vue&type=script&setup=true&lang.ts
4522
+ 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" };
4523
+ var _hoisted_2$2 = { class: "text-center" };
4524
+ var _hoisted_3$2 = { class: "text-gray-900 font-bold text-lg mb-1 line-clamp-2" };
4525
+ var _hoisted_4$2 = {
4526
+ key: 0,
4527
+ class: "text-gray-600 text-sm"
4528
+ };
4529
+ var _hoisted_5$2 = {
4530
+ key: 1,
4531
+ class: "text-gray-500 text-sm"
4532
+ };
4533
+ //#endregion
4534
+ //#region src/vue/chat/Preview.vue
4535
+ var Preview_default = /* @__PURE__ */ (0, vue.defineComponent)({
4536
+ __name: "Preview",
4537
+ props: { result: {} },
4538
+ setup(__props) {
4539
+ const { t } = useCollectionI18n();
4540
+ const props = __props;
4541
+ const data = (0, vue.computed)(() => props.result?.data ?? props.result?.jsonData ?? null);
4542
+ const collectionSlug = (0, vue.computed)(() => data.value?.collectionSlug ?? "");
4543
+ const itemId = (0, vue.computed)(() => data.value?.itemId);
4544
+ return (_ctx, _cache) => {
4545
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$2, [(0, vue.createElementVNode)("div", _hoisted_2$2, [
4546
+ _cache[0] || (_cache[0] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-indigo-600 mb-2" }, "collections_bookmark", -1)),
4547
+ (0, vue.createElementVNode)("h3", _hoisted_3$2, (0, vue.toDisplayString)(collectionSlug.value || (0, vue.unref)(t)("pluginPresentCollection.fallbackTitle")), 1),
4548
+ itemId.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_4$2, (0, vue.toDisplayString)((0, vue.unref)(t)("pluginPresentCollection.itemLabel", { id: itemId.value })), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_5$2, (0, vue.toDisplayString)((0, vue.unref)(t)("pluginPresentCollection.listLabel")), 1))
4549
+ ])]);
4550
+ };
4551
+ }
4552
+ });
4553
+ //#endregion
4395
4554
  //#region src/vue/components/CollectionsIndexView.vue?vue&type=script&setup=true&lang.ts
4396
4555
  var _hoisted_1$1 = {
4397
4556
  class: "h-full overflow-y-auto bg-slate-50/50 px-6 py-6",
@@ -4699,6 +4858,16 @@ var FeedsView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4699
4858
  }
4700
4859
  });
4701
4860
  //#endregion
4861
+ //#region src/vue/index.ts
4862
+ var plugin = {
4863
+ toolDefinition: require_promptSafety.TOOL_DEFINITION,
4864
+ execute: require_promptSafety.executePresentCollection,
4865
+ generatingMessage: "Loading collection...",
4866
+ isEnabled: () => true,
4867
+ viewComponent: View_default,
4868
+ previewComponent: Preview_default
4869
+ };
4870
+ //#endregion
4702
4871
  exports.CollectionCalendarView = CollectionCalendarView_default;
4703
4872
  exports.CollectionCustomView = CollectionCustomView_default;
4704
4873
  exports.CollectionDayView = CollectionDayView_default;
@@ -4710,8 +4879,11 @@ exports.CollectionView = CollectionView_default;
4710
4879
  exports.CollectionViewConfigModal = CollectionViewConfigModal_default;
4711
4880
  exports.CollectionsIndexView = CollectionsIndexView_default;
4712
4881
  exports.FeedsView = FeedsView_default;
4882
+ exports.PresentCollectionPreview = Preview_default;
4883
+ exports.PresentCollectionView = View_default;
4713
4884
  exports.collectionUi = collectionUi;
4714
4885
  exports.configureCollectionUi = configureCollectionUi;
4886
+ exports.plugin = plugin;
4715
4887
  exports.readCollectionSort = readCollectionSort;
4716
4888
  exports.readCollectionViewMode = readCollectionViewMode;
4717
4889
  exports.useCollectionRendering = useCollectionRendering;