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