@mulmoclaude/collection-plugin 0.7.4 → 0.7.5

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.
package/dist/vue.cjs CHANGED
@@ -142,6 +142,8 @@ var i18n = (0, vue_i18n.createI18n)({
142
142
  kanbanUncategorized: "Uncategorized",
143
143
  kanbanOpenCard: "Open {label}",
144
144
  addView: "Add view",
145
+ addViewDesktop: "Custom view",
146
+ addViewMobile: "Phone view",
145
147
  config: {
146
148
  open: "Collection settings",
147
149
  title: "{title} · settings",
@@ -153,6 +155,7 @@ var i18n = (0, vue_i18n.createI18n)({
153
155
  customViewLoading: "Loading view…",
154
156
  customViewError: "Couldn't load this view: {error}",
155
157
  addViewPrompt: "I want to add a custom view to the \"{title}\" collection. Ask me what I want to see or edit, then author the HTML view file at {base}/views/your-view.html and register it in {base}/schema.json under `views[]` (capabilities [\"read\"] for a read-only view, [\"read\",\"write\"] if it edits records). Follow the custom-view help for the data contract.",
158
+ addMobileViewPrompt: "I want to add a phone (remote app) custom view to the \"{title}\" collection. Ask me what I want to see or edit, then author the HTML view file at {base}/views/your-view.html and register it in {base}/schema.json under `views[]` with `target: \"mobile\"` (read-only by default; declare `editableFields` / `allowDelete` only if it edits records). Follow the custom-view-remote help for the postMessage data contract — no fetch of any kind in the view.",
156
159
  repair: "Repair",
157
160
  dataIssuesDetected: "{count} record file(s) have data problems and may be missing from this view.",
158
161
  repairPrompt: "The \"{title}\" collection has {count} record file(s) with data problems that keep them from appearing. Fix each one — Read the file, correct it, then Write it back:\n{issues}\n\nWhen you're done, call presentCollection to confirm the records load.",
@@ -275,6 +278,8 @@ var i18n = (0, vue_i18n.createI18n)({
275
278
  kanbanUncategorized: "未分類",
276
279
  kanbanOpenCard: "{label} を開く",
277
280
  addView: "ビューを追加",
281
+ addViewDesktop: "カスタムビュー",
282
+ addViewMobile: "スマホビュー",
278
283
  config: {
279
284
  open: "コレクション設定",
280
285
  title: "{title} · 設定",
@@ -286,6 +291,7 @@ var i18n = (0, vue_i18n.createI18n)({
286
291
  customViewLoading: "ビューを読み込み中…",
287
292
  customViewError: "このビューを読み込めませんでした: {error}",
288
293
  addViewPrompt: "「{title}」コレクションにカスタムビューを追加したい。何を見たい/編集したいか質問してから、HTML ビューファイルを {base}/views/your-view.html に作成し、{base}/schema.json の `views[]` に登録して(読み取り専用なら capabilities [\"read\"]、レコードを編集するなら [\"read\",\"write\"])。データ契約は custom-view ヘルプに従ってください。",
294
+ addMobileViewPrompt: "「{title}」コレクションにスマホ(リモートアプリ)用カスタムビューを追加したい。何を見たい/編集したいか質問してから、HTML ビューファイルを {base}/views/your-view.html に作成し、{base}/schema.json の `views[]` に `target: \"mobile\"` 付きで登録して(デフォルトは読み取り専用。レコードを編集する場合のみ `editableFields` / `allowDelete` を宣言)。データ契約は custom-view-remote ヘルプに従ってください — ビュー内での fetch は一切禁止です。",
289
295
  repair: "修復",
290
296
  dataIssuesDetected: "{count} 件のレコードファイルにデータの問題があり、この表示に出てこない可能性があります。",
291
297
  repairPrompt: "コレクション「{title}」に、表示されない原因となるデータの問題を持つレコードファイルが {count} 件あります。それぞれを修正してください — ファイルを Read し、修正してから Write し直します:\n{issues}\n\n完了したら presentCollection を呼び出して、レコードが読み込めることを確認してください。",
@@ -408,6 +414,8 @@ var i18n = (0, vue_i18n.createI18n)({
408
414
  kanbanUncategorized: "未分类",
409
415
  kanbanOpenCard: "打开 {label}",
410
416
  addView: "添加视图",
417
+ addViewDesktop: "自定义视图",
418
+ addViewMobile: "手机视图",
411
419
  config: {
412
420
  open: "集合设置",
413
421
  title: "{title} · 设置",
@@ -419,6 +427,7 @@ var i18n = (0, vue_i18n.createI18n)({
419
427
  customViewLoading: "正在加载视图…",
420
428
  customViewError: "无法加载此视图:{error}",
421
429
  addViewPrompt: "我想为“{title}”集合添加一个自定义视图。先问我想查看或编辑什么,然后在 {base}/views/your-view.html 创建 HTML 视图文件,并在 {base}/schema.json 的 `views[]` 中注册(只读视图用 capabilities [\"read\"],需要编辑记录则用 [\"read\",\"write\"])。数据契约请遵循 custom-view 帮助文档。",
430
+ addMobileViewPrompt: "我想为“{title}”集合添加一个手机(远程应用)自定义视图。先问我想查看或编辑什么,然后在 {base}/views/your-view.html 创建 HTML 视图文件,并在 {base}/schema.json 的 `views[]` 中以 `target: \"mobile\"` 注册(默认只读;仅在需要编辑记录时声明 `editableFields` / `allowDelete`)。数据契约请遵循 custom-view-remote 帮助文档 — 视图内禁止任何 fetch。",
422
431
  repair: "修复",
423
432
  dataIssuesDetected: "有 {count} 个记录文件存在数据问题,可能未显示在此视图中。",
424
433
  repairPrompt: "集合 {title} 有 {count} 个记录文件存在导致无法显示的数据问题。请逐一修复——用 Read 读取该文件,更正后再用 Write 写回:\n{issues}\n\n完成后,调用 presentCollection 确认记录可以正常加载。",
@@ -541,6 +550,8 @@ var i18n = (0, vue_i18n.createI18n)({
541
550
  kanbanUncategorized: "미분류",
542
551
  kanbanOpenCard: "{label} 열기",
543
552
  addView: "보기 추가",
553
+ addViewDesktop: "사용자 지정 보기",
554
+ addViewMobile: "스마트폰 보기",
544
555
  config: {
545
556
  open: "컬렉션 설정",
546
557
  title: "{title} · 설정",
@@ -552,6 +563,7 @@ var i18n = (0, vue_i18n.createI18n)({
552
563
  customViewLoading: "보기를 불러오는 중…",
553
564
  customViewError: "이 보기를 불러오지 못했습니다: {error}",
554
565
  addViewPrompt: "{title} 컬렉션에 사용자 지정 보기를 추가하고 싶어요. 무엇을 보거나 편집하고 싶은지 먼저 물어본 뒤, {base}/views/your-view.html 에 HTML 보기 파일을 만들고 {base}/schema.json의 `views[]`에 등록해 주세요(읽기 전용 보기는 capabilities [\"read\"], 레코드를 편집하면 [\"read\",\"write\"]). 데이터 계약은 custom-view 도움말을 따르세요.",
566
+ addMobileViewPrompt: "{title} 컬렉션에 스마트폰(리모트 앱)용 사용자 지정 보기를 추가하고 싶어요. 무엇을 보거나 편집하고 싶은지 먼저 물어본 뒤, {base}/views/your-view.html 에 HTML 보기 파일을 만들고 {base}/schema.json의 `views[]`에 `target: \"mobile\"`과 함께 등록해 주세요(기본은 읽기 전용, 레코드를 편집할 때만 `editableFields` / `allowDelete` 선언). 데이터 계약은 custom-view-remote 도움말을 따르세요 — 보기 안에서 fetch는 일절 금지입니다.",
555
567
  repair: "복구",
556
568
  dataIssuesDetected: "{count}개의 레코드 파일에 데이터 문제가 있어 이 보기에 표시되지 않을 수 있습니다.",
557
569
  repairPrompt: "{title} 컬렉션에 표시되지 않는 원인이 되는 데이터 문제가 있는 레코드 파일이 {count}개 있습니다. 각 파일을 수정하세요 — Read로 파일을 읽고 수정한 뒤 Write로 다시 저장하세요:\n{issues}\n\n완료되면 presentCollection을 호출하여 레코드가 로드되는지 확인하세요.",
@@ -674,6 +686,8 @@ var i18n = (0, vue_i18n.createI18n)({
674
686
  kanbanUncategorized: "Sin categoría",
675
687
  kanbanOpenCard: "Abrir {label}",
676
688
  addView: "Añadir vista",
689
+ addViewDesktop: "Vista personalizada",
690
+ addViewMobile: "Vista móvil",
677
691
  config: {
678
692
  open: "Ajustes de la colección",
679
693
  title: "{title} · ajustes",
@@ -685,6 +699,7 @@ var i18n = (0, vue_i18n.createI18n)({
685
699
  customViewLoading: "Cargando vista…",
686
700
  customViewError: "No se pudo cargar esta vista: {error}",
687
701
  addViewPrompt: "Quiero añadir una vista personalizada a la colección «{title}». Pregúntame qué quiero ver o editar, luego crea el archivo HTML de la vista en {base}/views/your-view.html y regístralo en {base}/schema.json dentro de `views[]` (capabilities [\"read\"] para una vista de solo lectura, [\"read\",\"write\"] si edita registros). Sigue la ayuda custom-view para el contrato de datos.",
702
+ addMobileViewPrompt: "Quiero añadir una vista personalizada para el móvil (aplicación remota) a la colección «{title}». Pregúntame qué quiero ver o editar, luego crea el archivo HTML de la vista en {base}/views/your-view.html y regístralo en {base}/schema.json dentro de `views[]` con `target: \"mobile\"` (de solo lectura por defecto; declara `editableFields` / `allowDelete` solo si edita registros). Sigue la ayuda custom-view-remote para el contrato de datos por postMessage — nada de fetch en la vista.",
688
703
  repair: "Reparar",
689
704
  dataIssuesDetected: "{count} archivo(s) de registro tienen problemas de datos y podrían no aparecer en esta vista.",
690
705
  repairPrompt: "La colección {title} tiene {count} archivo(s) de registro con problemas de datos que impiden que aparezcan. Corrige cada uno: lee el archivo con Read, corrígelo y vuelve a escribirlo con Write:\n{issues}\n\nCuando termines, llama a presentCollection para confirmar que los registros se cargan.",
@@ -807,6 +822,8 @@ var i18n = (0, vue_i18n.createI18n)({
807
822
  kanbanUncategorized: "Sem categoria",
808
823
  kanbanOpenCard: "Abrir {label}",
809
824
  addView: "Adicionar visualização",
825
+ addViewDesktop: "Visualização personalizada",
826
+ addViewMobile: "Visualização para celular",
810
827
  config: {
811
828
  open: "Configurações da coleção",
812
829
  title: "{title} · configurações",
@@ -818,6 +835,7 @@ var i18n = (0, vue_i18n.createI18n)({
818
835
  customViewLoading: "Carregando visualização…",
819
836
  customViewError: "Não foi possível carregar esta visualização: {error}",
820
837
  addViewPrompt: "Quero adicionar uma visualização personalizada à coleção \"{title}\". Pergunte o que eu quero ver ou editar, depois crie o arquivo HTML da visualização em {base}/views/your-view.html e registre-o em {base}/schema.json em `views[]` (capabilities [\"read\"] para uma visualização somente leitura, [\"read\",\"write\"] se editar registros). Siga a ajuda custom-view para o contrato de dados.",
838
+ addMobileViewPrompt: "Quero adicionar uma visualização personalizada para o celular (app remoto) à coleção \"{title}\". Pergunte o que eu quero ver ou editar, depois crie o arquivo HTML da visualização em {base}/views/your-view.html e registre-o em {base}/schema.json em `views[]` com `target: \"mobile\"` (somente leitura por padrão; declare `editableFields` / `allowDelete` apenas se editar registros). Siga a ajuda custom-view-remote para o contrato de dados via postMessage — nenhum fetch na visualização.",
821
839
  repair: "Reparar",
822
840
  dataIssuesDetected: "{count} arquivo(s) de registro têm problemas de dados e podem não aparecer nesta visualização.",
823
841
  repairPrompt: "A coleção {title} tem {count} arquivo(s) de registro com problemas de dados que impedem que apareçam. Corrija cada um: leia o arquivo com Read, corrija-o e grave-o novamente com Write:\n{issues}\n\nQuando terminar, chame presentCollection para confirmar que os registros carregam.",
@@ -940,6 +958,8 @@ var i18n = (0, vue_i18n.createI18n)({
940
958
  kanbanUncategorized: "Non classé",
941
959
  kanbanOpenCard: "Ouvrir {label}",
942
960
  addView: "Ajouter une vue",
961
+ addViewDesktop: "Vue personnalisée",
962
+ addViewMobile: "Vue mobile",
943
963
  config: {
944
964
  open: "Paramètres de la collection",
945
965
  title: "{title} · paramètres",
@@ -951,6 +971,7 @@ var i18n = (0, vue_i18n.createI18n)({
951
971
  customViewLoading: "Chargement de la vue…",
952
972
  customViewError: "Impossible de charger cette vue : {error}",
953
973
  addViewPrompt: "Je veux ajouter une vue personnalisée à la collection « {title} ». Demande-moi ce que je veux voir ou modifier, puis crée le fichier HTML de la vue dans {base}/views/your-view.html et enregistre-le dans {base}/schema.json sous `views[]` (capabilities [\"read\"] pour une vue en lecture seule, [\"read\",\"write\"] si elle modifie des enregistrements). Suis l'aide custom-view pour le contrat de données.",
974
+ addMobileViewPrompt: "Je veux ajouter une vue personnalisée pour le mobile (application distante) à la collection « {title} ». Demande-moi ce que je veux voir ou modifier, puis crée le fichier HTML de la vue dans {base}/views/your-view.html et enregistre-le dans {base}/schema.json sous `views[]` avec `target: \"mobile\"` (en lecture seule par défaut ; déclare `editableFields` / `allowDelete` uniquement si elle modifie des enregistrements). Suis l'aide custom-view-remote pour le contrat de données par postMessage — aucun fetch dans la vue.",
954
975
  repair: "Réparer",
955
976
  dataIssuesDetected: "{count} fichier(s) d'enregistrement présentent des problèmes de données et peuvent être absents de cette vue.",
956
977
  repairPrompt: "La collection « {title} » comporte {count} fichier(s) d'enregistrement présentant des problèmes de données qui les empêchent d'apparaître. Corrigez chacun : lisez le fichier avec Read, corrigez-le, puis réécrivez-le avec Write :\n{issues}\n\nUne fois terminé, appelez presentCollection pour confirmer que les enregistrements se chargent.",
@@ -1073,6 +1094,8 @@ var i18n = (0, vue_i18n.createI18n)({
1073
1094
  kanbanUncategorized: "Nicht kategorisiert",
1074
1095
  kanbanOpenCard: "{label} öffnen",
1075
1096
  addView: "Ansicht hinzufügen",
1097
+ addViewDesktop: "Benutzerdefinierte Ansicht",
1098
+ addViewMobile: "Smartphone-Ansicht",
1076
1099
  config: {
1077
1100
  open: "Sammlungseinstellungen",
1078
1101
  title: "{title} · Einstellungen",
@@ -1084,6 +1107,7 @@ var i18n = (0, vue_i18n.createI18n)({
1084
1107
  customViewLoading: "Ansicht wird geladen…",
1085
1108
  customViewError: "Diese Ansicht konnte nicht geladen werden: {error}",
1086
1109
  addViewPrompt: "Ich möchte der Sammlung {title} eine benutzerdefinierte Ansicht hinzufügen. Frag mich, was ich sehen oder bearbeiten möchte, erstelle dann die HTML-Ansichtsdatei unter {base}/views/your-view.html und registriere sie in {base}/schema.json unter `views[]` (capabilities [\"read\"] für eine schreibgeschützte Ansicht, [\"read\",\"write\"] wenn sie Datensätze bearbeitet). Folge der custom-view-Hilfe für den Datenvertrag.",
1110
+ addMobileViewPrompt: "Ich möchte der Sammlung {title} eine benutzerdefinierte Ansicht für das Smartphone (Remote-App) hinzufügen. Frag mich, was ich sehen oder bearbeiten möchte, erstelle dann die HTML-Ansichtsdatei unter {base}/views/your-view.html und registriere sie in {base}/schema.json unter `views[]` mit `target: \"mobile\"` (standardmäßig schreibgeschützt; deklariere `editableFields` / `allowDelete` nur, wenn sie Datensätze bearbeitet). Folge der custom-view-remote-Hilfe für den postMessage-Datenvertrag — kein fetch jeglicher Art in der Ansicht.",
1087
1111
  repair: "Reparieren",
1088
1112
  dataIssuesDetected: "{count} Datensatzdatei(en) haben Datenprobleme und fehlen möglicherweise in dieser Ansicht.",
1089
1113
  repairPrompt: "Die Sammlung {title} hat {count} Datensatzdatei(en) mit Datenproblemen, die ihr Erscheinen verhindern. Korrigiere jede — die Datei mit Read lesen, korrigieren und mit Write zurückschreiben:\n{issues}\n\nRufe anschließend presentCollection auf, um zu bestätigen, dass die Datensätze geladen werden.",
@@ -3012,11 +3036,144 @@ var CollectionRemoteViewPreview_default = /*#__PURE__*/ _plugin_vue_export_helpe
3012
3036
  }
3013
3037
  }), [["__scopeId", "data-v-965ade6c"]]);
3014
3038
  //#endregion
3015
- //#region src/vue/useCollectionRendering.ts
3039
+ //#region src/vue/useCollectionRendering.helpers.ts
3040
+ var EM_DASH = "—";
3041
+ var DEFAULT_CURRENCY = "USD";
3042
+ var MARKDOWN_CELL_PREVIEW_MAX = 80;
3016
3043
  function stepForFieldType(type) {
3017
3044
  if (type === "money") return "0.01";
3018
3045
  if (type === "number") return "any";
3019
3046
  }
3047
+ function inputTypeFor(type) {
3048
+ if (type === "email") return "email";
3049
+ if (type === "number") return "number";
3050
+ if (type === "money") return "number";
3051
+ if (type === "date") return "date";
3052
+ if (type === "datetime") return "datetime-local";
3053
+ return "text";
3054
+ }
3055
+ function isExternalUrl(value) {
3056
+ return typeof value === "string" && /^https?:\/\//i.test(value);
3057
+ }
3058
+ function detailText(value) {
3059
+ if (value === void 0 || value === null || value === "") return EM_DASH;
3060
+ return String(value);
3061
+ }
3062
+ function formatCell(value, type) {
3063
+ if (value === void 0 || value === null || value === "") return EM_DASH;
3064
+ if (type === "markdown" && typeof value === "string") return value.length > MARKDOWN_CELL_PREVIEW_MAX ? `${value.slice(0, MARKDOWN_CELL_PREVIEW_MAX)}…` : value;
3065
+ if (typeof value === "string" || typeof value === "number") return String(value);
3066
+ return JSON.stringify(value);
3067
+ }
3068
+ /** Resolve the ISO 4217 code for a money field: a per-record
3069
+ * `currencyField` (when present and non-blank) wins over the field's
3070
+ * literal `currency`. */
3071
+ function resolveCurrency(field, record) {
3072
+ if (field.currencyField && record) {
3073
+ const code = record[field.currencyField];
3074
+ if (typeof code === "string" && code.trim().length > 0) return code;
3075
+ }
3076
+ return field.currency;
3077
+ }
3078
+ function formatMoney(value, currency, displayLocale) {
3079
+ if (value === void 0 || value === "") return EM_DASH;
3080
+ const amount = typeof value === "number" ? value : Number(value);
3081
+ if (!Number.isFinite(amount)) return String(value);
3082
+ const currencyCode = currency && currency.length > 0 ? currency : DEFAULT_CURRENCY;
3083
+ try {
3084
+ return new Intl.NumberFormat(displayLocale, {
3085
+ style: "currency",
3086
+ currency: currencyCode
3087
+ }).format(amount);
3088
+ } catch {
3089
+ return String(amount);
3090
+ }
3091
+ }
3092
+ function currencySymbolForLocale(currency, locale) {
3093
+ const code = currency && currency.length > 0 ? currency : DEFAULT_CURRENCY;
3094
+ try {
3095
+ return new Intl.NumberFormat(locale, {
3096
+ style: "currency",
3097
+ currency: code
3098
+ }).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
3099
+ } catch {
3100
+ return code;
3101
+ }
3102
+ }
3103
+ function tableRows(value) {
3104
+ if (!Array.isArray(value)) return [];
3105
+ return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
3106
+ }
3107
+ function hasTableRows(value) {
3108
+ return tableRows(value).length > 0;
3109
+ }
3110
+ /** Pick the field used to label a referenced/embedded record: prefer a
3111
+ * `name` field, then `title`, else fall back to the primary key. */
3112
+ function displayFieldFor(fields, primaryKey) {
3113
+ if ("name" in fields) return "name";
3114
+ if ("title" in fields) return "title";
3115
+ return primaryKey;
3116
+ }
3117
+ function uniqueRefTargets(schema) {
3118
+ const targets = /* @__PURE__ */ new Set();
3119
+ const walk = (fields) => {
3120
+ for (const field of Object.values(fields)) {
3121
+ if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
3122
+ if (field.type === "table" && field.of) walk(field.of);
3123
+ }
3124
+ };
3125
+ walk(schema.fields);
3126
+ return [...targets];
3127
+ }
3128
+ function uniqueEmbedTargets(schema) {
3129
+ const targets = /* @__PURE__ */ new Set();
3130
+ for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
3131
+ return [...targets];
3132
+ }
3133
+ function buildRefDisplayMap(detail) {
3134
+ const { fields, primaryKey } = detail.collection.schema;
3135
+ const displayField = displayFieldFor(fields, primaryKey);
3136
+ const map = {};
3137
+ for (const item of detail.items) {
3138
+ const slugRaw = item[primaryKey];
3139
+ if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
3140
+ const displayRaw = item[displayField];
3141
+ map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
3142
+ }
3143
+ return map;
3144
+ }
3145
+ function buildRefRecordMap(detail) {
3146
+ const { schema } = detail.collection;
3147
+ const map = {};
3148
+ for (const item of detail.items) {
3149
+ const slugRaw = item[schema.primaryKey];
3150
+ if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = (0, _mulmoclaude_core_collection.deriveAll)(schema, item, {});
3151
+ }
3152
+ return map;
3153
+ }
3154
+ function sortedRefOptions(map) {
3155
+ return Object.entries(map).map(([slug, display]) => ({
3156
+ slug,
3157
+ display
3158
+ })).sort((left, right) => left.display.localeCompare(right.display));
3159
+ }
3160
+ /** Dropdown options for an `embed` field's per-record picker: every
3161
+ * record in the target collection, labelled by its name/title (or
3162
+ * primary key), skipping records without a slug and sorted by label. */
3163
+ function buildEmbedOptions(schema, items) {
3164
+ const { fields, primaryKey } = schema;
3165
+ const displayField = displayFieldFor(fields, primaryKey);
3166
+ return items.map((item) => {
3167
+ const slug = String(item[primaryKey] ?? "");
3168
+ const labelRaw = item[displayField];
3169
+ return {
3170
+ slug,
3171
+ display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
3172
+ };
3173
+ }).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
3174
+ }
3175
+ //#endregion
3176
+ //#region src/vue/useCollectionRendering.ts
3020
3177
  function useCollectionRendering(collection, locale) {
3021
3178
  const refCache = (0, vue.ref)({});
3022
3179
  const refRecordCache = (0, vue.ref)({});
@@ -3026,43 +3183,6 @@ function useCollectionRendering(collection, locale) {
3026
3183
  refRecordCache.value = {};
3027
3184
  embedCache.value = {};
3028
3185
  }
3029
- function uniqueRefTargets(schema) {
3030
- const targets = /* @__PURE__ */ new Set();
3031
- const walk = (fields) => {
3032
- for (const field of Object.values(fields)) {
3033
- if (field.type === "ref" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
3034
- if (field.type === "table" && field.of) walk(field.of);
3035
- }
3036
- };
3037
- walk(schema.fields);
3038
- return [...targets];
3039
- }
3040
- function uniqueEmbedTargets(schema) {
3041
- const targets = /* @__PURE__ */ new Set();
3042
- for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
3043
- return [...targets];
3044
- }
3045
- function buildRefDisplayMap(detail) {
3046
- const { fields, primaryKey } = detail.collection.schema;
3047
- const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
3048
- const map = {};
3049
- for (const item of detail.items) {
3050
- const slugRaw = item[primaryKey];
3051
- if (typeof slugRaw !== "string" || slugRaw.length === 0) continue;
3052
- const displayRaw = item[displayField];
3053
- map[slugRaw] = typeof displayRaw === "string" && displayRaw.length > 0 ? displayRaw : slugRaw;
3054
- }
3055
- return map;
3056
- }
3057
- function buildRefRecordMap(detail) {
3058
- const { schema } = detail.collection;
3059
- const map = {};
3060
- for (const item of detail.items) {
3061
- const slugRaw = item[schema.primaryKey];
3062
- if (typeof slugRaw === "string" && slugRaw.length > 0) map[slugRaw] = (0, _mulmoclaude_core_collection.deriveAll)(schema, item, {});
3063
- }
3064
- return map;
3065
- }
3066
3186
  async function loadLinkedCollections(schema, expectedSlug) {
3067
3187
  const refTargets = new Set(uniqueRefTargets(schema));
3068
3188
  const embedTargets = new Set(uniqueEmbedTargets(schema));
@@ -3107,11 +3227,7 @@ function useCollectionRendering(collection, locale) {
3107
3227
  }
3108
3228
  function refOptions(targetSlug) {
3109
3229
  const map = refCache.value[targetSlug];
3110
- if (!map) return [];
3111
- return Object.entries(map).map(([slug, display]) => ({
3112
- slug,
3113
- display
3114
- })).sort((left, right) => left.display.localeCompare(right.display));
3230
+ return map ? sortedRefOptions(map) : [];
3115
3231
  }
3116
3232
  /** Dropdown options for an `embed` field's per-record picker (`idField`):
3117
3233
  * every record in the target collection, labelled by its name/title (or
@@ -3119,17 +3235,7 @@ function useCollectionRendering(collection, locale) {
3119
3235
  * that aren't also `ref` targets (the profile collection, say). */
3120
3236
  function embedOptions(targetSlug) {
3121
3237
  const data = embedCache.value[targetSlug];
3122
- if (!data) return [];
3123
- const { fields, primaryKey } = data.schema;
3124
- const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
3125
- return data.items.map((item) => {
3126
- const slug = String(item[primaryKey] ?? "");
3127
- const labelRaw = item[displayField];
3128
- return {
3129
- slug,
3130
- display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
3131
- };
3132
- }).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
3238
+ return data ? buildEmbedOptions(data.schema, data.items) : [];
3133
3239
  }
3134
3240
  function resolveEmbed(field, record) {
3135
3241
  if (field.type !== "embed" || !field.to) return {
@@ -3182,46 +3288,8 @@ function useCollectionRendering(collection, locale) {
3182
3288
  }
3183
3289
  return out;
3184
3290
  }
3185
- function resolveCurrency(field, record) {
3186
- if (field.currencyField && record) {
3187
- const code = record[field.currencyField];
3188
- if (typeof code === "string" && code.trim().length > 0) return code;
3189
- }
3190
- return field.currency;
3191
- }
3192
3291
  function currencySymbol(currency) {
3193
- const code = currency && currency.length > 0 ? currency : "USD";
3194
- try {
3195
- return new Intl.NumberFormat(locale.value, {
3196
- style: "currency",
3197
- currency: code
3198
- }).formatToParts(0).find((entry) => entry.type === "currency")?.value ?? code;
3199
- } catch {
3200
- return code;
3201
- }
3202
- }
3203
- function formatMoney(value, currency, displayLocale) {
3204
- if (value === void 0 || value === "") return "—";
3205
- const amount = typeof value === "number" ? value : Number(value);
3206
- if (!Number.isFinite(amount)) return String(value);
3207
- const currencyCode = currency && currency.length > 0 ? currency : "USD";
3208
- try {
3209
- return new Intl.NumberFormat(displayLocale, {
3210
- style: "currency",
3211
- currency: currencyCode
3212
- }).format(amount);
3213
- } catch {
3214
- return String(amount);
3215
- }
3216
- }
3217
- function formatCell(value, type) {
3218
- if (value === void 0 || value === null || value === "") return "—";
3219
- if (type === "markdown" && typeof value === "string") return value.length > 80 ? `${value.slice(0, 80)}…` : value;
3220
- if (typeof value === "string" || typeof value === "number") return String(value);
3221
- return JSON.stringify(value);
3222
- }
3223
- function isExternalUrl(value) {
3224
- return typeof value === "string" && /^https?:\/\//i.test(value);
3292
+ return currencySymbolForLocale(currency, locale.value);
3225
3293
  }
3226
3294
  function artifactUrl(value) {
3227
3295
  return collectionUi().fileAssetUrl(value);
@@ -3229,30 +3297,11 @@ function useCollectionRendering(collection, locale) {
3229
3297
  function fileRoutePath(value) {
3230
3298
  return collectionUi().fileRoutePath(value);
3231
3299
  }
3232
- function detailText(value) {
3233
- if (value === void 0 || value === null || value === "") return "—";
3234
- return String(value);
3235
- }
3236
- function tableRows(value) {
3237
- if (!Array.isArray(value)) return [];
3238
- return value.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
3239
- }
3240
- function hasTableRows(value) {
3241
- return tableRows(value).length > 0;
3242
- }
3243
3300
  function formatSubCell(subField, value, record) {
3244
3301
  if (subField.type === "money") return formatMoney(value, resolveCurrency(subField, record), locale.value);
3245
3302
  if (subField.type === "ref" && subField.to && typeof value === "string" && value.length > 0) return refDisplay(subField.to, value);
3246
3303
  return formatCell(value, subField.type);
3247
3304
  }
3248
- function inputTypeFor(type) {
3249
- if (type === "email") return "email";
3250
- if (type === "number") return "number";
3251
- if (type === "money") return "number";
3252
- if (type === "date") return "date";
3253
- if (type === "datetime") return "datetime-local";
3254
- return "text";
3255
- }
3256
3305
  const stepFor = stepForFieldType;
3257
3306
  function evaluateDerivedAgainstItem(field, fieldKey, item) {
3258
3307
  if (!field.formula || !collection.value) return null;
@@ -3436,166 +3485,175 @@ var _hoisted_26$1 = [
3436
3485
  "onClick"
3437
3486
  ];
3438
3487
  var _hoisted_27$1 = { class: "material-icons text-sm" };
3439
- var _hoisted_28 = ["title", "aria-label"];
3440
- var _hoisted_29 = ["title", "aria-label"];
3441
- var _hoisted_30 = ["value", "aria-label"];
3442
- var _hoisted_31 = ["value"];
3443
- var _hoisted_32 = ["value", "aria-label"];
3444
- var _hoisted_33 = ["value"];
3445
- var _hoisted_34 = {
3488
+ var _hoisted_28 = [
3489
+ "title",
3490
+ "aria-label",
3491
+ "aria-expanded"
3492
+ ];
3493
+ var _hoisted_29 = {
3494
+ key: 0,
3495
+ class: "absolute left-0 top-full mt-1 z-20 min-w-max rounded border border-slate-200 bg-white shadow-lg py-1",
3496
+ "data-testid": "collection-view-add-menu"
3497
+ };
3498
+ var _hoisted_30 = ["title", "aria-label"];
3499
+ var _hoisted_31 = ["value", "aria-label"];
3500
+ var _hoisted_32 = ["value"];
3501
+ var _hoisted_33 = ["value", "aria-label"];
3502
+ var _hoisted_34 = ["value"];
3503
+ var _hoisted_35 = {
3446
3504
  key: 3,
3447
3505
  class: "text-[10px] text-slate-400 font-bold uppercase tracking-wider select-none"
3448
3506
  };
3449
- var _hoisted_35 = {
3507
+ var _hoisted_36 = {
3450
3508
  key: 3,
3451
3509
  class: "mx-6 mt-4 rounded-xl border border-amber-200 bg-amber-50/60 p-4 text-sm text-amber-900 shadow-sm flex items-center gap-3",
3452
3510
  "data-testid": "collections-data-issues"
3453
3511
  };
3454
- var _hoisted_36 = { class: "flex-1" };
3455
- var _hoisted_37 = { class: "flex-1 overflow-auto" };
3456
- var _hoisted_38 = {
3512
+ var _hoisted_37 = { class: "flex-1" };
3513
+ var _hoisted_38 = { class: "flex-1 overflow-auto" };
3514
+ var _hoisted_39 = {
3457
3515
  key: 0,
3458
3516
  class: "flex flex-col items-center justify-center py-20 text-sm text-slate-500 gap-3"
3459
3517
  };
3460
- var _hoisted_39 = {
3518
+ var _hoisted_40 = {
3461
3519
  key: 1,
3462
3520
  class: "m-6 rounded-xl border border-red-200 bg-red-50/50 p-4 text-sm text-red-800 shadow-sm flex items-center gap-3"
3463
3521
  };
3464
- var _hoisted_40 = { key: 2 };
3465
- var _hoisted_41 = {
3522
+ var _hoisted_41 = { key: 2 };
3523
+ var _hoisted_42 = {
3466
3524
  key: 3,
3467
3525
  class: "p-4"
3468
3526
  };
3469
- var _hoisted_42 = {
3527
+ var _hoisted_43 = {
3470
3528
  key: 4,
3471
3529
  class: "h-full flex flex-col"
3472
3530
  };
3473
- var _hoisted_43 = {
3531
+ var _hoisted_44 = {
3474
3532
  key: 0,
3475
3533
  class: "m-3 mb-0 rounded-xl border border-red-200 bg-red-50/50 p-4 text-sm text-red-800 shadow-sm flex items-center gap-3",
3476
3534
  "data-testid": "collections-inline-error"
3477
3535
  };
3478
- var _hoisted_44 = { class: "flex-1" };
3479
- var _hoisted_45 = ["aria-label"];
3480
- var _hoisted_46 = { class: "flex-1 min-h-0 px-3 py-2" };
3481
- var _hoisted_47 = {
3536
+ var _hoisted_45 = { class: "flex-1" };
3537
+ var _hoisted_46 = ["aria-label"];
3538
+ var _hoisted_47 = { class: "flex-1 min-h-0 px-3 py-2" };
3539
+ var _hoisted_48 = {
3482
3540
  key: 5,
3483
3541
  class: "h-full",
3484
3542
  "data-testid": "collection-custom-view-body"
3485
3543
  };
3486
- var _hoisted_48 = {
3544
+ var _hoisted_49 = {
3487
3545
  key: 6,
3488
3546
  class: "flex flex-col items-center justify-center py-20 text-sm text-slate-400 gap-2"
3489
3547
  };
3490
- var _hoisted_49 = { class: "font-semibold text-slate-600" };
3491
- var _hoisted_50 = {
3548
+ var _hoisted_50 = { class: "font-semibold text-slate-600" };
3549
+ var _hoisted_51 = {
3492
3550
  key: 7,
3493
3551
  class: "flex flex-col items-center justify-center py-20 text-sm text-slate-400 gap-2"
3494
3552
  };
3495
- var _hoisted_51 = { class: "font-semibold text-slate-600" };
3496
- var _hoisted_52 = {
3553
+ var _hoisted_52 = { class: "font-semibold text-slate-600" };
3554
+ var _hoisted_53 = {
3497
3555
  key: 8,
3498
3556
  class: "overflow-x-auto [container-type:inline-size]"
3499
3557
  };
3500
- var _hoisted_53 = {
3558
+ var _hoisted_54 = {
3501
3559
  key: 0,
3502
3560
  class: "m-4 rounded-xl border border-red-200 bg-red-50/50 p-4 text-sm text-red-800 shadow-sm flex items-center gap-3",
3503
3561
  "data-testid": "collections-inline-error"
3504
3562
  };
3505
- var _hoisted_54 = { class: "flex-1" };
3506
- var _hoisted_55 = ["aria-label"];
3507
- var _hoisted_56 = { class: "min-w-full text-xs" };
3508
- var _hoisted_57 = { class: "bg-slate-50 border-b border-slate-200" };
3509
- var _hoisted_58 = ["aria-sort"];
3510
- var _hoisted_59 = { class: "flex items-center gap-1" };
3511
- var _hoisted_60 = ["title"];
3512
- var _hoisted_61 = [
3563
+ var _hoisted_55 = { class: "flex-1" };
3564
+ var _hoisted_56 = ["aria-label"];
3565
+ var _hoisted_57 = { class: "min-w-full text-xs" };
3566
+ var _hoisted_58 = { class: "bg-slate-50 border-b border-slate-200" };
3567
+ var _hoisted_59 = ["aria-sort"];
3568
+ var _hoisted_60 = { class: "flex items-center gap-1" };
3569
+ var _hoisted_61 = ["title"];
3570
+ var _hoisted_62 = [
3513
3571
  "data-testid",
3514
3572
  "aria-label",
3515
3573
  "onClick",
3516
3574
  "onPointerenter"
3517
3575
  ];
3518
- var _hoisted_62 = { class: "material-icons text-base align-middle" };
3519
- var _hoisted_63 = { class: "divide-y divide-slate-100 bg-white" };
3520
- var _hoisted_64 = [
3576
+ var _hoisted_63 = { class: "material-icons text-base align-middle" };
3577
+ var _hoisted_64 = { class: "divide-y divide-slate-100 bg-white" };
3578
+ var _hoisted_65 = [
3521
3579
  "aria-label",
3522
3580
  "data-testid",
3523
3581
  "onClick",
3524
3582
  "onKeydown"
3525
3583
  ];
3526
- var _hoisted_65 = [
3584
+ var _hoisted_66 = [
3527
3585
  "checked",
3528
3586
  "disabled",
3529
3587
  "data-testid",
3530
3588
  "aria-label",
3531
3589
  "onChange"
3532
3590
  ];
3533
- var _hoisted_66 = [
3591
+ var _hoisted_67 = [
3534
3592
  "checked",
3535
3593
  "disabled",
3536
3594
  "data-testid",
3537
3595
  "aria-label",
3538
3596
  "onChange"
3539
3597
  ];
3540
- var _hoisted_67 = {
3598
+ var _hoisted_68 = {
3541
3599
  key: 2,
3542
3600
  class: "block truncate"
3543
3601
  };
3544
- var _hoisted_68 = [
3602
+ var _hoisted_69 = [
3545
3603
  "href",
3546
3604
  "tabindex",
3547
3605
  "data-testid",
3548
3606
  "onClick",
3549
3607
  "onKeydown"
3550
3608
  ];
3551
- var _hoisted_69 = [
3609
+ var _hoisted_70 = [
3552
3610
  "value",
3553
3611
  "disabled",
3554
3612
  "data-testid",
3555
3613
  "aria-label",
3556
3614
  "onChange"
3557
3615
  ];
3558
- var _hoisted_70 = {
3616
+ var _hoisted_71 = {
3559
3617
  key: 0,
3560
3618
  value: ""
3561
3619
  };
3562
- var _hoisted_71 = ["value"];
3563
- var _hoisted_72 = {
3620
+ var _hoisted_72 = ["value"];
3621
+ var _hoisted_73 = {
3564
3622
  key: 4,
3565
3623
  class: "block truncate tabular-nums font-semibold text-slate-900"
3566
3624
  };
3567
- var _hoisted_73 = {
3625
+ var _hoisted_74 = {
3568
3626
  key: 5,
3569
3627
  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"
3570
3628
  };
3571
- var _hoisted_74 = {
3629
+ var _hoisted_75 = {
3572
3630
  key: 6,
3573
3631
  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"
3574
3632
  };
3575
- var _hoisted_75 = ["href", "data-testid"];
3576
3633
  var _hoisted_76 = ["href", "data-testid"];
3577
- var _hoisted_77 = [
3634
+ var _hoisted_77 = ["href", "data-testid"];
3635
+ var _hoisted_78 = [
3578
3636
  "href",
3579
3637
  "data-testid",
3580
3638
  "onClick"
3581
3639
  ];
3582
- var _hoisted_78 = {
3640
+ var _hoisted_79 = {
3583
3641
  key: 10,
3584
3642
  class: "block truncate text-slate-600"
3585
3643
  };
3586
- var _hoisted_79 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
3587
- var _hoisted_80 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
3588
- var _hoisted_81 = { class: "flex-1" };
3589
- var _hoisted_82 = {
3644
+ var _hoisted_80 = { class: "bg-white rounded-2xl shadow-2xl w-full max-w-xl flex flex-col border border-slate-200 overflow-hidden" };
3645
+ var _hoisted_81 = { class: "px-6 py-4 border-b border-slate-100 flex items-center gap-3 bg-slate-50/50" };
3646
+ var _hoisted_82 = { class: "flex-1" };
3647
+ var _hoisted_83 = {
3590
3648
  id: "collections-chat-title",
3591
3649
  class: "text-sm font-bold text-slate-800 uppercase tracking-wide"
3592
3650
  };
3593
- var _hoisted_83 = { class: "text-xs text-slate-400 font-semibold" };
3594
- var _hoisted_84 = ["aria-label"];
3595
- var _hoisted_85 = { class: "px-6 py-5" };
3596
- var _hoisted_86 = ["placeholder", "onKeydown"];
3597
- var _hoisted_87 = { class: "px-6 py-3.5 border-t border-slate-100 flex items-center justify-end gap-2 bg-slate-50/50" };
3598
- var _hoisted_88 = ["disabled"];
3651
+ var _hoisted_84 = { class: "text-xs text-slate-400 font-semibold" };
3652
+ var _hoisted_85 = ["aria-label"];
3653
+ var _hoisted_86 = { class: "px-6 py-5" };
3654
+ var _hoisted_87 = ["placeholder", "onKeydown"];
3655
+ var _hoisted_88 = { class: "px-6 py-3.5 border-t border-slate-100 flex items-center justify-end gap-2 bg-slate-50/50" };
3656
+ var _hoisted_89 = ["disabled"];
3599
3657
  /** `slug` / `selected` are supplied only in EMBEDDED mode (the
3600
3658
  * `presentCollection` chat card mounts this component and drives both
3601
3659
  * from the tool result). In standalone route mode (the
@@ -4121,16 +4179,41 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4121
4179
  * page only (the seed starts a chat). Feeds qualify too — their views are
4122
4180
  * authored under feeds/<slug>/ and the seed prompt points there. */
4123
4181
  const canAddCustomView = (0, vue.computed)(() => Boolean(collection.value) && !embedded.value);
4182
+ const addMenuOpen = (0, vue.ref)(false);
4183
+ const addMenuRef = (0, vue.ref)(null);
4184
+ /** Whether authoring a phone (remote app) view is worth offering — mirrors
4185
+ * the selector filter above: without the host's `fetchRemoteView` binding a
4186
+ * mobile view could be authored but never shown here. */
4187
+ const canAddMobileView = (0, vue.computed)(() => Boolean(cui.fetchRemoteView));
4188
+ /** "+" click: open the target chooser, or skip the one-item menu and seed
4189
+ * the desktop prompt directly when mobile views aren't available. */
4190
+ function onAddViewClick() {
4191
+ if (!canAddMobileView.value) {
4192
+ addCustomView("desktop");
4193
+ return;
4194
+ }
4195
+ addMenuOpen.value = !addMenuOpen.value;
4196
+ }
4197
+ function closeAddMenuOnOutsideClick(event) {
4198
+ if (!addMenuRef.value?.contains(event.target)) addMenuOpen.value = false;
4199
+ }
4200
+ (0, vue.watch)(addMenuOpen, (open) => {
4201
+ if (open) document.addEventListener("mousedown", closeAddMenuOnOutsideClick);
4202
+ else document.removeEventListener("mousedown", closeAddMenuOnOutsideClick);
4203
+ });
4124
4204
  /** Seed a chat asking Claude to author a new custom view for this collection.
4125
4205
  * Reuses the same chat-seed path as collection actions — the host injects a
4126
4206
  * templated prompt; Claude asks, authors the HTML, and registers it. The
4127
4207
  * authoring base is source-aware: a feed lives under `feeds/<slug>/`, every
4128
- * other collection under the `data/skills/<slug>/` staging dir. */
4129
- function addCustomView() {
4208
+ * other collection under the `data/skills/<slug>/` staging dir. The prompt
4209
+ * is target-aware: phone views follow the custom-view-remote contract and
4210
+ * register with `target: "mobile"`. */
4211
+ function addCustomView(target) {
4212
+ addMenuOpen.value = false;
4130
4213
  const current = collection.value;
4131
4214
  if (!current) return;
4132
4215
  const base = current.source === "feed" ? `feeds/${current.slug}` : `data/skills/${current.slug}`;
4133
- const prompt = t("collectionsView.addViewPrompt", {
4216
+ const prompt = t(target === "mobile" ? "collectionsView.addMobileViewPrompt" : "collectionsView.addViewPrompt", {
4134
4217
  title: current.title,
4135
4218
  base
4136
4219
  });
@@ -4606,6 +4689,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4606
4689
  view.value = slug && readCollectionViewMode(slug) || "table";
4607
4690
  anchorOverride.value = null;
4608
4691
  kanbanOverride.value = null;
4692
+ addMenuOpen.value = false;
4609
4693
  sortState.value = storedSortFor(slug);
4610
4694
  }
4611
4695
  if (slug) loadCollection(slug);
@@ -4654,6 +4738,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4654
4738
  changeUnsub = null;
4655
4739
  clearLiveRefreshTimer();
4656
4740
  if (refreshNoteTimer !== void 0) clearTimeout(refreshNoteTimer);
4741
+ document.removeEventListener("mousedown", closeAddMenuOnOutsideClick);
4657
4742
  });
4658
4743
  (0, vue.watch)([
4659
4744
  activeView,
@@ -4688,7 +4773,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4688
4773
  "aria-label": (0, vue.unref)(t)("collectionsView.backToIndex"),
4689
4774
  "data-testid": "collections-back",
4690
4775
  onClick: goBack
4691
- }, [..._cache[23] || (_cache[23] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$5)) : (0, vue.createCommentVNode)("", true),
4776
+ }, [..._cache[25] || (_cache[25] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "arrow_back", -1)])], 8, _hoisted_3$5)) : (0, vue.createCommentVNode)("", true),
4692
4777
  collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$5, [(0, vue.createElementVNode)("span", _hoisted_5$5, (0, vue.toDisplayString)(collection.value.icon), 1)])) : (0, vue.createCommentVNode)("", true),
4693
4778
  (0, vue.createElementVNode)("div", _hoisted_6$4, [(0, vue.createElementVNode)("h1", _hoisted_7$4, (0, vue.toDisplayString)(collection.value?.title ?? (0, vue.unref)(t)("collectionsView.title")), 1), collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_8$4, (0, vue.toDisplayString)(collection.value.slug), 1)) : (0, vue.createCommentVNode)("", true)]),
4694
4779
  collection.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(pinToggle)), {
@@ -4717,7 +4802,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4717
4802
  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",
4718
4803
  "data-testid": "collections-chat",
4719
4804
  onClick: openChat
4720
- }, [_cache[24] || (_cache[24] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "forum", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chat")), 1)])) : (0, vue.createCommentVNode)("", true),
4805
+ }, [_cache[26] || (_cache[26] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "forum", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chat")), 1)])) : (0, vue.createCommentVNode)("", true),
4721
4806
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(collectionActions.value, (action) => {
4722
4807
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4723
4808
  key: action.id,
@@ -4734,7 +4819,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4734
4819
  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",
4735
4820
  "data-testid": "collections-add-item",
4736
4821
  onClick: openCreate
4737
- }, [_cache[25] || (_cache[25] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.add")), 1)])) : (0, vue.createCommentVNode)("", true),
4822
+ }, [_cache[27] || (_cache[27] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.add")), 1)])) : (0, vue.createCommentVNode)("", true),
4738
4823
  canDeleteCollection.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4739
4824
  key: 6,
4740
4825
  type: "button",
@@ -4743,7 +4828,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4743
4828
  "aria-label": (0, vue.unref)(t)("collectionsView.deleteCollection"),
4744
4829
  "data-testid": "collections-delete",
4745
4830
  onClick: confirmCollectionDelete
4746
- }, [..._cache[26] || (_cache[26] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$4)) : (0, vue.createCommentVNode)("", true),
4831
+ }, [..._cache[28] || (_cache[28] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_13$4)) : (0, vue.createCommentVNode)("", true),
4747
4832
  canDeleteFeed.value && !embedded.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4748
4833
  key: 7,
4749
4834
  type: "button",
@@ -4752,11 +4837,11 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4752
4837
  "aria-label": (0, vue.unref)(t)("collectionsView.deleteFeed"),
4753
4838
  "data-testid": "feeds-delete",
4754
4839
  onClick: confirmFeedDelete
4755
- }, [..._cache[27] || (_cache[27] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$4)) : (0, vue.createCommentVNode)("", true)
4840
+ }, [..._cache[29] || (_cache[29] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "delete_forever", -1)])], 8, _hoisted_14$4)) : (0, vue.createCommentVNode)("", true)
4756
4841
  ])) : (0, vue.createCommentVNode)("", true),
4757
- refreshNote.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$4, [_cache[28] || (_cache[28] = (0, vue.createElementVNode)("span", { class: "material-icons text-base text-indigo-600" }, "hourglass_top", -1)), (0, vue.createElementVNode)("span", _hoisted_16$4, (0, vue.toDisplayString)(refreshNote.value), 1)])) : (0, vue.createCommentVNode)("", true),
4842
+ refreshNote.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_15$4, [_cache[30] || (_cache[30] = (0, vue.createElementVNode)("span", { class: "material-icons text-base text-indigo-600" }, "hourglass_top", -1)), (0, vue.createElementVNode)("span", _hoisted_16$4, (0, vue.toDisplayString)(refreshNote.value), 1)])) : (0, vue.createCommentVNode)("", true),
4758
4843
  collection.value && (!__props.hideSearch && items.value.length > 0 || !__props.hideViewToggle && (hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_17$4, [!__props.hideSearch && items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_18$4, [
4759
- _cache[30] || (_cache[30] = (0, vue.createElementVNode)("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "search")], -1)),
4844
+ _cache[32] || (_cache[32] = (0, vue.createElementVNode)("span", { class: "absolute inset-y-0 left-0 flex items-center pl-3 text-slate-400 pointer-events-none" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "search")], -1)),
4760
4845
  (0, vue.withDirectives)((0, vue.createElementVNode)("input", {
4761
4846
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchQuery.value = $event),
4762
4847
  type: "text",
@@ -4770,7 +4855,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4770
4855
  "aria-label": (0, vue.unref)(t)("collectionsView.clearSearch"),
4771
4856
  class: "absolute inset-y-0 right-0 flex items-center pr-2.5 text-slate-400 hover:text-slate-600",
4772
4857
  onClick: _cache[1] || (_cache[1] = ($event) => searchQuery.value = "")
4773
- }, [..._cache[29] || (_cache[29] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_20$2)) : (0, vue.createCommentVNode)("", true)
4858
+ }, [..._cache[31] || (_cache[31] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "close", -1)])], 8, _hoisted_20$2)) : (0, vue.createCommentVNode)("", true)
4774
4859
  ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_21$2, [
4775
4860
  !__props.hideViewToggle && (hasCalendar.value || hasKanban.value || hasCustomViews.value || canAddCustomView.value) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
4776
4861
  key: 0,
@@ -4784,7 +4869,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4784
4869
  "aria-pressed": activeView.value === "table",
4785
4870
  "data-testid": "collection-view-toggle-table",
4786
4871
  onClick: _cache[2] || (_cache[2] = ($event) => setView("table"))
4787
- }, [_cache[31] || (_cache[31] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "table_rows", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewTable")), 1)], 10, _hoisted_23$2),
4872
+ }, [_cache[33] || (_cache[33] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "table_rows", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewTable")), 1)], 10, _hoisted_23$2),
4788
4873
  hasCalendar.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4789
4874
  key: 0,
4790
4875
  type: "button",
@@ -4792,7 +4877,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4792
4877
  "aria-pressed": activeView.value === "calendar",
4793
4878
  "data-testid": "collection-view-toggle-calendar",
4794
4879
  onClick: _cache[3] || (_cache[3] = ($event) => setView("calendar"))
4795
- }, [_cache[32] || (_cache[32] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "calendar_month", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_24$1)) : (0, vue.createCommentVNode)("", true),
4880
+ }, [_cache[34] || (_cache[34] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "calendar_month", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewCalendar")), 1)], 10, _hoisted_24$1)) : (0, vue.createCommentVNode)("", true),
4796
4881
  hasKanban.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4797
4882
  key: 1,
4798
4883
  type: "button",
@@ -4800,7 +4885,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4800
4885
  "aria-pressed": activeView.value === "kanban",
4801
4886
  "data-testid": "collection-view-toggle-kanban",
4802
4887
  onClick: _cache[4] || (_cache[4] = ($event) => setView("kanban"))
4803
- }, [_cache[33] || (_cache[33] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "view_kanban", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_25$1)) : (0, vue.createCommentVNode)("", true),
4888
+ }, [_cache[35] || (_cache[35] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "view_kanban", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.viewKanban")), 1)], 10, _hoisted_25$1)) : (0, vue.createCommentVNode)("", true),
4804
4889
  ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(customViews.value, (cv) => {
4805
4890
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4806
4891
  key: cv.id,
@@ -4811,15 +4896,30 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4811
4896
  onClick: ($event) => setCustomView(cv.id)
4812
4897
  }, [(0, vue.createElementVNode)("span", _hoisted_27$1, (0, vue.toDisplayString)(cv.icon || (cv.target === "mobile" ? "smartphone" : "dashboard_customize")), 1), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(cv.label), 1)], 10, _hoisted_26$1);
4813
4898
  }), 128)),
4814
- canAddCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4899
+ canAddCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
4815
4900
  key: 2,
4901
+ ref_key: "addMenuRef",
4902
+ ref: addMenuRef,
4903
+ class: "relative"
4904
+ }, [(0, vue.createElementVNode)("button", {
4816
4905
  type: "button",
4817
4906
  class: "h-8 w-8 flex items-center justify-center rounded bg-white text-slate-500 border border-slate-200 hover:bg-slate-50",
4818
4907
  title: (0, vue.unref)(t)("collectionsView.addView"),
4819
4908
  "aria-label": (0, vue.unref)(t)("collectionsView.addView"),
4909
+ "aria-expanded": addMenuOpen.value,
4820
4910
  "data-testid": "collection-view-add",
4821
- onClick: addCustomView
4822
- }, [..._cache[34] || (_cache[34] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_28)) : (0, vue.createCommentVNode)("", true),
4911
+ onClick: onAddViewClick
4912
+ }, [..._cache[36] || (_cache[36] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "add", -1)])], 8, _hoisted_28), addMenuOpen.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_29, [(0, vue.createElementVNode)("button", {
4913
+ type: "button",
4914
+ class: "w-full h-8 px-3 flex items-center gap-2 text-xs font-bold text-slate-600 hover:bg-slate-50",
4915
+ "data-testid": "collection-view-add-desktop",
4916
+ onClick: _cache[5] || (_cache[5] = ($event) => addCustomView("desktop"))
4917
+ }, [_cache[37] || (_cache[37] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "dashboard_customize", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.addViewDesktop")), 1)]), (0, vue.createElementVNode)("button", {
4918
+ type: "button",
4919
+ class: "w-full h-8 px-3 flex items-center gap-2 text-xs font-bold text-slate-600 hover:bg-slate-50",
4920
+ "data-testid": "collection-view-add-mobile",
4921
+ onClick: _cache[6] || (_cache[6] = ($event) => addCustomView("mobile"))
4922
+ }, [_cache[38] || (_cache[38] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "smartphone", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.addViewMobile")), 1)])])) : (0, vue.createCommentVNode)("", true)], 512)) : (0, vue.createCommentVNode)("", true),
4823
4923
  canConfigureViews.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
4824
4924
  key: 3,
4825
4925
  type: "button",
@@ -4827,8 +4927,8 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4827
4927
  title: (0, vue.unref)(t)("collectionsView.config.open"),
4828
4928
  "aria-label": (0, vue.unref)(t)("collectionsView.config.open"),
4829
4929
  "data-testid": "collection-config-open",
4830
- onClick: _cache[5] || (_cache[5] = ($event) => configOpen.value = true)
4831
- }, [..._cache[35] || (_cache[35] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_29)) : (0, vue.createCommentVNode)("", true)
4930
+ onClick: _cache[7] || (_cache[7] = ($event) => configOpen.value = true)
4931
+ }, [..._cache[39] || (_cache[39] = [(0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "settings", -1)])], 8, _hoisted_30)) : (0, vue.createCommentVNode)("", true)
4832
4932
  ], 8, _hoisted_22$2)) : (0, vue.createCommentVNode)("", true),
4833
4933
  calendarActive.value && dateFields.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
4834
4934
  key: 1,
@@ -4836,42 +4936,42 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4836
4936
  class: "h-8 px-2 rounded border border-slate-200 bg-white text-xs font-semibold text-slate-600 focus:outline-none focus:border-indigo-500 cursor-pointer",
4837
4937
  "aria-label": (0, vue.unref)(t)("collectionsView.calendarFieldLabel"),
4838
4938
  "data-testid": "collection-calendar-field",
4839
- onChange: _cache[6] || (_cache[6] = ($event) => anchorOverride.value = $event.target.value)
4939
+ onChange: _cache[8] || (_cache[8] = ($event) => anchorOverride.value = $event.target.value)
4840
4940
  }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(dateFields.value, (key) => {
4841
4941
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
4842
4942
  key,
4843
4943
  value: key
4844
- }, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_31);
4845
- }), 128))], 40, _hoisted_30)) : (0, vue.createCommentVNode)("", true),
4944
+ }, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_32);
4945
+ }), 128))], 40, _hoisted_31)) : (0, vue.createCommentVNode)("", true),
4846
4946
  kanbanActive.value && enumFields.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
4847
4947
  key: 2,
4848
4948
  value: kanbanGroupField.value,
4849
4949
  class: "h-8 px-2 rounded border border-slate-200 bg-white text-xs font-semibold text-slate-600 focus:outline-none focus:border-indigo-500 cursor-pointer",
4850
4950
  "aria-label": (0, vue.unref)(t)("collectionsView.kanbanFieldLabel"),
4851
4951
  "data-testid": "collection-kanban-field",
4852
- onChange: _cache[7] || (_cache[7] = ($event) => kanbanOverride.value = $event.target.value)
4952
+ onChange: _cache[9] || (_cache[9] = ($event) => kanbanOverride.value = $event.target.value)
4853
4953
  }, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(enumFields.value, (key) => {
4854
4954
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
4855
4955
  key,
4856
4956
  value: key
4857
- }, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_33);
4858
- }), 128))], 40, _hoisted_32)) : (0, vue.createCommentVNode)("", true),
4859
- items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_34, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.searchSummary", {
4957
+ }, (0, vue.toDisplayString)(collection.value?.schema.fields[key]?.label ?? key), 9, _hoisted_34);
4958
+ }), 128))], 40, _hoisted_33)) : (0, vue.createCommentVNode)("", true),
4959
+ items.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_35, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.searchSummary", {
4860
4960
  shown: filteredItems.value.length,
4861
4961
  total: items.value.length
4862
4962
  })), 1)) : (0, vue.createCommentVNode)("", true)
4863
4963
  ])])) : (0, vue.createCommentVNode)("", true),
4864
- collection.value && dataIssues.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_35, [
4865
- _cache[37] || (_cache[37] = (0, vue.createElementVNode)("span", { class: "material-icons text-amber-600" }, "warning", -1)),
4866
- (0, vue.createElementVNode)("span", _hoisted_36, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dataIssuesDetected", { count: dataIssues.value.length })), 1),
4964
+ collection.value && dataIssues.value.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_36, [
4965
+ _cache[41] || (_cache[41] = (0, vue.createElementVNode)("span", { class: "material-icons text-amber-600" }, "warning", -1)),
4966
+ (0, vue.createElementVNode)("span", _hoisted_37, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.dataIssuesDetected", { count: dataIssues.value.length })), 1),
4867
4967
  (0, vue.createElementVNode)("button", {
4868
4968
  type: "button",
4869
4969
  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",
4870
4970
  "data-testid": "collections-repair",
4871
4971
  onClick: repairCollection
4872
- }, [_cache[36] || (_cache[36] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "build", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.repair")), 1)])
4972
+ }, [_cache[40] || (_cache[40] = (0, vue.createElementVNode)("span", { class: "material-icons text-sm" }, "build", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.repair")), 1)])
4873
4973
  ])) : (0, vue.createCommentVNode)("", true),
4874
- (0, vue.createElementVNode)("div", _hoisted_37, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_38, [_cache[38] || (_cache[38] = (0, vue.createElementVNode)("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.loading")), 1)])) : loadError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_39, [_cache[39] || (_cache[39] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(loadError.value === "not-found" ? (0, vue.unref)(t)("collectionsView.notFound") : `${(0, vue.unref)(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_40)) : calendarActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_41, [(0, vue.createVNode)(CollectionCalendarView_default, {
4974
+ (0, vue.createElementVNode)("div", _hoisted_38, [loading.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_39, [_cache[42] || (_cache[42] = (0, vue.createElementVNode)("div", { class: "h-8 w-8 border-2 border-indigo-600/20 border-t-indigo-600 rounded-full animate-spin" }, null, -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("common.loading")), 1)])) : loadError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_40, [_cache[43] || (_cache[43] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(loadError.value === "not-found" ? (0, vue.unref)(t)("collectionsView.notFound") : `${(0, vue.unref)(t)("collectionsView.loadFailed")}: ${loadError.value}`), 1)])) : !collection.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_41)) : calendarActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_42, [(0, vue.createVNode)(CollectionCalendarView_default, {
4875
4975
  schema: collection.value.schema,
4876
4976
  items: filteredItems.value,
4877
4977
  "anchor-field": calendarAnchorField.value,
@@ -4907,7 +5007,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4907
5007
  }, {
4908
5008
  detail: (0, vue.withCtx)(() => [(0, vue.createVNode)(CollectionRecordPanel_default, {
4909
5009
  editing: editing.value,
4910
- "onUpdate:editing": _cache[8] || (_cache[8] = ($event) => editing.value = $event),
5010
+ "onUpdate:editing": _cache[10] || (_cache[10] = ($event) => editing.value = $event),
4911
5011
  collection: collection.value,
4912
5012
  viewing: viewing.value,
4913
5013
  saving: saving.value,
@@ -4925,7 +5025,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4925
5025
  onCancel: cancelEditor,
4926
5026
  onEdit: editFromView,
4927
5027
  onClose: onDayClose,
4928
- onDelete: _cache[9] || (_cache[9] = ($event) => viewing.value && confirmDelete(viewing.value)),
5028
+ onDelete: _cache[11] || (_cache[11] = ($event) => viewing.value && confirmDelete(viewing.value)),
4929
5029
  onRunAction: runAction,
4930
5030
  onItemChat
4931
5031
  }, null, 8, [
@@ -4956,16 +5056,16 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4956
5056
  "selected",
4957
5057
  "can-create",
4958
5058
  "show-detail"
4959
- ])) : (0, vue.createCommentVNode)("", true)])) : kanbanActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_42, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_43, [
4960
- _cache[41] || (_cache[41] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
4961
- (0, vue.createElementVNode)("span", _hoisted_44, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
5059
+ ])) : (0, vue.createCommentVNode)("", true)])) : kanbanActive.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_43, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_44, [
5060
+ _cache[45] || (_cache[45] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
5061
+ (0, vue.createElementVNode)("span", _hoisted_45, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
4962
5062
  (0, vue.createElementVNode)("button", {
4963
5063
  type: "button",
4964
5064
  class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
4965
5065
  "aria-label": (0, vue.unref)(t)("common.close"),
4966
- onClick: _cache[10] || (_cache[10] = ($event) => inlineError.value = null)
4967
- }, [..._cache[40] || (_cache[40] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_45)
4968
- ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_46, [(0, vue.createVNode)(CollectionKanbanView_default, {
5066
+ onClick: _cache[12] || (_cache[12] = ($event) => inlineError.value = null)
5067
+ }, [..._cache[44] || (_cache[44] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_46)
5068
+ ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("div", _hoisted_47, [(0, vue.createVNode)(CollectionKanbanView_default, {
4969
5069
  schema: collection.value.schema,
4970
5070
  items: filteredItems.value,
4971
5071
  "group-field": kanbanGroupField.value,
@@ -4979,7 +5079,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4979
5079
  "group-field",
4980
5080
  "selected",
4981
5081
  "notified"
4982
- ])])])) : activeCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_47, [activeCustomView.value.target === "mobile" ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionRemoteViewPreview_default, {
5082
+ ])])])) : activeCustomView.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [activeCustomView.value.target === "mobile" ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionRemoteViewPreview_default, {
4983
5083
  key: 0,
4984
5084
  slug: collection.value.slug,
4985
5085
  view: activeCustomView.value,
@@ -4990,32 +5090,32 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
4990
5090
  view: activeCustomView.value,
4991
5091
  onOpenItem: onCustomViewOpenItem,
4992
5092
  onStartChat: onCustomViewStartChat
4993
- }, null, 8, ["slug", "view"]))])) : items.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_48, [_cache[42] || (_cache[42] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), (0, vue.createElementVNode)("p", _hoisted_49, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_50, [
4994
- _cache[43] || (_cache[43] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
4995
- (0, vue.createElementVNode)("p", _hoisted_51, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noMatchingItems")), 1),
5093
+ }, null, 8, ["slug", "view"]))])) : items.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_49, [_cache[46] || (_cache[46] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "folder_open", -1)), (0, vue.createElementVNode)("p", _hoisted_50, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemsEmpty")), 1)])) : filteredItems.value.length === 0 && editing.value?.mode !== "create" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_51, [
5094
+ _cache[47] || (_cache[47] = (0, vue.createElementVNode)("span", { class: "material-icons text-4xl text-slate-300" }, "search_off", -1)),
5095
+ (0, vue.createElementVNode)("p", _hoisted_52, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noMatchingItems")), 1),
4996
5096
  (0, vue.createElementVNode)("button", {
4997
5097
  type: "button",
4998
5098
  class: "text-xs text-indigo-600 font-semibold hover:underline",
4999
- onClick: _cache[11] || (_cache[11] = ($event) => searchQuery.value = "")
5099
+ onClick: _cache[13] || (_cache[13] = ($event) => searchQuery.value = "")
5000
5100
  }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.clearSearch")), 1)
5001
- ])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_52, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_53, [
5002
- _cache[45] || (_cache[45] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
5003
- (0, vue.createElementVNode)("span", _hoisted_54, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
5101
+ ])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_53, [inlineError.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_54, [
5102
+ _cache[49] || (_cache[49] = (0, vue.createElementVNode)("span", { class: "material-icons text-red-600" }, "error", -1)),
5103
+ (0, vue.createElementVNode)("span", _hoisted_55, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.inlineSaveFailed", { error: inlineError.value })), 1),
5004
5104
  (0, vue.createElementVNode)("button", {
5005
5105
  type: "button",
5006
5106
  class: "h-8 w-8 flex items-center justify-center rounded text-red-600 hover:bg-red-100",
5007
5107
  "aria-label": (0, vue.unref)(t)("common.close"),
5008
- onClick: _cache[12] || (_cache[12] = ($event) => inlineError.value = null)
5009
- }, [..._cache[44] || (_cache[44] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_55)
5010
- ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("table", _hoisted_56, [(0, vue.createElementVNode)("thead", null, [(0, vue.createElementVNode)("tr", _hoisted_57, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(listColumnFields.value, ([key, field]) => {
5108
+ onClick: _cache[14] || (_cache[14] = ($event) => inlineError.value = null)
5109
+ }, [..._cache[48] || (_cache[48] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_56)
5110
+ ])) : (0, vue.createCommentVNode)("", true), (0, vue.createElementVNode)("table", _hoisted_57, [(0, vue.createElementVNode)("thead", null, [(0, vue.createElementVNode)("tr", _hoisted_58, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(listColumnFields.value, ([key, field]) => {
5011
5111
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
5012
5112
  key,
5013
5113
  "aria-sort": (0, vue.unref)(_mulmoclaude_core_collection.isSortableField)(field) ? sortAriaValue(key) : void 0,
5014
5114
  class: "px-5 py-3 font-bold text-slate-500 text-left uppercase tracking-wider whitespace-nowrap"
5015
- }, [(0, vue.createElementVNode)("div", _hoisted_59, [(0, vue.createElementVNode)("span", {
5115
+ }, [(0, vue.createElementVNode)("div", _hoisted_60, [(0, vue.createElementVNode)("span", {
5016
5116
  class: "truncate max-w-[14rem]",
5017
5117
  title: field.label
5018
- }, (0, vue.toDisplayString)(field.label), 9, _hoisted_60), (0, vue.unref)(_mulmoclaude_core_collection.isSortableField)(field) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
5118
+ }, (0, vue.toDisplayString)(field.label), 9, _hoisted_61), (0, vue.unref)(_mulmoclaude_core_collection.isSortableField)(field) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
5019
5119
  key: 0,
5020
5120
  type: "button",
5021
5121
  class: (0, vue.normalizeClass)(["inline-flex items-center justify-center rounded p-0.5 -my-1 leading-none transition-colors", sortButtonClass(key)]),
@@ -5023,9 +5123,9 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5023
5123
  "aria-label": (0, vue.unref)(t)("collectionsView.sortBy", { field: field.label }),
5024
5124
  onClick: (0, vue.withModifiers)(($event) => cycleSort(key), ["stop"]),
5025
5125
  onPointerenter: ($event) => hoveredSortKey.value = key,
5026
- onPointerleave: _cache[13] || (_cache[13] = ($event) => hoveredSortKey.value = null)
5027
- }, [(0, vue.createElementVNode)("span", _hoisted_62, (0, vue.toDisplayString)(sortIconName(key)), 1)], 42, _hoisted_61)) : (0, vue.createCommentVNode)("", true)])], 8, _hoisted_58);
5028
- }), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_63, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(sortedItems.value, (item) => {
5126
+ onPointerleave: _cache[15] || (_cache[15] = ($event) => hoveredSortKey.value = null)
5127
+ }, [(0, vue.createElementVNode)("span", _hoisted_63, (0, vue.toDisplayString)(sortIconName(key)), 1)], 42, _hoisted_62)) : (0, vue.createCommentVNode)("", true)])], 8, _hoisted_59);
5128
+ }), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_64, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(sortedItems.value, (item) => {
5029
5129
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("tr", {
5030
5130
  key: String(item[collection.value.schema.primaryKey] ?? ""),
5031
5131
  class: (0, vue.normalizeClass)(["hover:bg-slate-50/70 cursor-pointer transition-colors focus:outline-none focus:bg-indigo-50/30", isRowOpen(item) || isEditingRow(item) ? "bg-indigo-50/40" : ""]),
@@ -5047,9 +5147,9 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5047
5147
  class: "h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer align-middle disabled:opacity-50 disabled:cursor-not-allowed",
5048
5148
  "data-testid": `collections-inline-toggle-${key}-${item[collection.value.schema.primaryKey]}`,
5049
5149
  "aria-label": field.label,
5050
- onClick: _cache[14] || (_cache[14] = (0, vue.withModifiers)(() => {}, ["stop"])),
5150
+ onClick: _cache[16] || (_cache[16] = (0, vue.withModifiers)(() => {}, ["stop"])),
5051
5151
  onChange: ($event) => commitToggle(item, field)
5052
- }, null, 40, _hoisted_65)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
5152
+ }, null, 40, _hoisted_66)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
5053
5153
  key: 1,
5054
5154
  type: "checkbox",
5055
5155
  checked: item[key] === true,
@@ -5057,9 +5157,9 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5057
5157
  class: "h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer align-middle disabled:opacity-50 disabled:cursor-not-allowed",
5058
5158
  "data-testid": `collections-inline-bool-${key}-${item[collection.value.schema.primaryKey]}`,
5059
5159
  "aria-label": field.label,
5060
- onClick: _cache[15] || (_cache[15] = (0, vue.withModifiers)(() => {}, ["stop"])),
5160
+ onClick: _cache[17] || (_cache[17] = (0, vue.withModifiers)(() => {}, ["stop"])),
5061
5161
  onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.checked)
5062
- }, null, 40, _hoisted_66)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_67, [(0, vue.createElementVNode)("a", {
5162
+ }, null, 40, _hoisted_67)) : field.type === "ref" && field.to && typeof item[key] === "string" && item[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_68, [(0, vue.createElementVNode)("a", {
5063
5163
  href: (0, vue.unref)(cui).recordHref?.(field.to, String(item[key])),
5064
5164
  tabindex: (0, vue.unref)(cui).recordHref?.(field.to, String(item[key])) ? void 0 : 0,
5065
5165
  role: "link",
@@ -5067,44 +5167,44 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5067
5167
  "data-testid": `collections-ref-link-${key}-${item[key]}`,
5068
5168
  onClick: ($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(item[key]), true),
5069
5169
  onKeydown: [(0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(item[key]), true), ["enter"]), (0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(item[key]), true), ["space"])]
5070
- }, (0, vue.toDisplayString)((0, vue.unref)(refDisplay)(field.to, String(item[key]))), 41, _hoisted_68)])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
5170
+ }, (0, vue.toDisplayString)((0, vue.unref)(refDisplay)(field.to, String(item[key]))), 41, _hoisted_69)])) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
5071
5171
  key: 3,
5072
5172
  value: item[key] == null ? "" : String(item[key]),
5073
5173
  disabled: isRowInlineSaving(item),
5074
5174
  class: (0, vue.normalizeClass)(["rounded-lg border px-2 py-0.5 text-[11px] font-semibold focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed", enumControlClass(String(key), item[key])]),
5075
5175
  "data-testid": `collections-inline-enum-${key}-${item[collection.value.schema.primaryKey]}`,
5076
5176
  "aria-label": field.label,
5077
- onClick: _cache[16] || (_cache[16] = (0, vue.withModifiers)(() => {}, ["stop"])),
5177
+ onClick: _cache[18] || (_cache[18] = (0, vue.withModifiers)(() => {}, ["stop"])),
5078
5178
  onChange: ($event) => commitInlineEdit(item, String(key), field, $event.target.value)
5079
- }, [showEnumPlaceholder(item, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option", _hoisted_70, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1)) : (0, vue.createCommentVNode)("", true), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
5179
+ }, [showEnumPlaceholder(item, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("option", _hoisted_71, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1)) : (0, vue.createCommentVNode)("", true), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
5080
5180
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
5081
5181
  key: value,
5082
5182
  value
5083
- }, (0, vue.toDisplayString)(value), 9, _hoisted_71);
5084
- }), 128))], 42, _hoisted_69)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_72, (0, vue.toDisplayString)((0, vue.unref)(formatMoney)(item[key], (0, vue.unref)(resolveCurrency)(field, item), (0, vue.unref)(locale))), 1)) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_73, [_cache[46] || (_cache[46] = (0, vue.createElementVNode)("span", { class: "material-icons text-[11px]" }, "list", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(tableSummary(item[key])), 1)])) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_74, (0, vue.toDisplayString)((0, vue.unref)(derivedDisplay)(field, (0, vue.unref)(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && (0, vue.unref)(isExternalUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
5183
+ }, (0, vue.toDisplayString)(value), 9, _hoisted_72);
5184
+ }), 128))], 42, _hoisted_70)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_73, (0, vue.toDisplayString)((0, vue.unref)(formatMoney)(item[key], (0, vue.unref)(resolveCurrency)(field, item), (0, vue.unref)(locale))), 1)) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_74, [_cache[50] || (_cache[50] = (0, vue.createElementVNode)("span", { class: "material-icons text-[11px]" }, "list", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)(tableSummary(item[key])), 1)])) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_75, (0, vue.toDisplayString)((0, vue.unref)(derivedDisplay)(field, (0, vue.unref)(evaluateDerivedAgainstItem)(field, String(key), item), item)), 1)) : field.type !== "file" && (0, vue.unref)(isExternalUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
5085
5185
  key: 7,
5086
5186
  href: String(item[key]),
5087
5187
  target: "_blank",
5088
5188
  rel: "noopener noreferrer",
5089
5189
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
5090
5190
  "data-testid": `collections-url-link-${key}-${item[collection.value.schema.primaryKey]}`,
5091
- onClick: _cache[17] || (_cache[17] = (0, vue.withModifiers)(() => {}, ["stop"]))
5092
- }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_75)) : field.type === "file" && (0, vue.unref)(artifactUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
5191
+ onClick: _cache[19] || (_cache[19] = (0, vue.withModifiers)(() => {}, ["stop"]))
5192
+ }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_76)) : field.type === "file" && (0, vue.unref)(artifactUrl)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
5093
5193
  key: 8,
5094
5194
  href: (0, vue.unref)(artifactUrl)(item[key]) ?? void 0,
5095
5195
  target: "_blank",
5096
5196
  rel: "noopener noreferrer",
5097
5197
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
5098
5198
  "data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
5099
- onClick: _cache[18] || (_cache[18] = (0, vue.withModifiers)(() => {}, ["stop"]))
5100
- }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_76)) : field.type === "file" && (0, vue.unref)(fileRoutePath)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
5199
+ onClick: _cache[20] || (_cache[20] = (0, vue.withModifiers)(() => {}, ["stop"]))
5200
+ }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_77)) : field.type === "file" && (0, vue.unref)(fileRoutePath)(item[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
5101
5201
  key: 9,
5102
5202
  href: (0, vue.unref)(fileRoutePath)(item[key]) ?? void 0,
5103
5203
  class: "block truncate text-blue-600 hover:text-blue-800 hover:underline font-semibold",
5104
5204
  "data-testid": `collections-file-link-${key}-${item[collection.value.schema.primaryKey]}`,
5105
5205
  onClick: ($event) => (0, vue.unref)(activatePathLink)($event, (0, vue.unref)(fileRoutePath)(item[key]) ?? "", true)
5106
- }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_77)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_78, (0, vue.toDisplayString)((0, vue.unref)(formatCell)(item[key], field.type)), 1))], 64)) : (0, vue.createCommentVNode)("", true)]);
5107
- }), 128))], 42, _hoisted_64);
5206
+ }, (0, vue.toDisplayString)(String(item[key])), 9, _hoisted_78)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_79, (0, vue.toDisplayString)((0, vue.unref)(formatCell)(item[key], field.type)), 1))], 64)) : (0, vue.createCommentVNode)("", true)]);
5207
+ }), 128))], 42, _hoisted_65);
5108
5208
  }), 128))])])]))]),
5109
5209
  collection.value && (viewing.value || editing.value) && !(calendarActive.value && openDay.value) ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionRecordModal_default, {
5110
5210
  key: 4,
@@ -5112,7 +5212,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5112
5212
  }, {
5113
5213
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)(CollectionRecordPanel_default, {
5114
5214
  editing: editing.value,
5115
- "onUpdate:editing": _cache[19] || (_cache[19] = ($event) => editing.value = $event),
5215
+ "onUpdate:editing": _cache[21] || (_cache[21] = ($event) => editing.value = $event),
5116
5216
  collection: collection.value,
5117
5217
  viewing: viewing.value,
5118
5218
  saving: saving.value,
@@ -5130,7 +5230,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5130
5230
  onCancel: cancelEditor,
5131
5231
  onEdit: editFromView,
5132
5232
  onClose: closeView,
5133
- onDelete: _cache[20] || (_cache[20] = ($event) => viewing.value && confirmDelete(viewing.value)),
5233
+ onDelete: _cache[22] || (_cache[22] = ($event) => viewing.value && confirmDelete(viewing.value)),
5134
5234
  onRunAction: runAction,
5135
5235
  onItemChat
5136
5236
  }, null, 8, [
@@ -5157,7 +5257,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5157
5257
  title: collection.value.title,
5158
5258
  views: customViews.value,
5159
5259
  onChanged: onViewsChanged,
5160
- onClose: _cache[21] || (_cache[21] = ($event) => configOpen.value = false)
5260
+ onClose: _cache[23] || (_cache[23] = ($event) => configOpen.value = false)
5161
5261
  }, null, 8, [
5162
5262
  "slug",
5163
5263
  "title",
@@ -5172,29 +5272,29 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5172
5272
  "data-testid": "collections-chat-modal",
5173
5273
  onClick: (0, vue.withModifiers)(closeChat, ["self"]),
5174
5274
  onKeydown: (0, vue.withKeys)(closeChat, ["esc"])
5175
- }, [(0, vue.createElementVNode)("div", _hoisted_79, [
5176
- (0, vue.createElementVNode)("header", _hoisted_80, [
5177
- _cache[48] || (_cache[48] = (0, vue.createElementVNode)("div", { class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100/50" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "forum")], -1)),
5178
- (0, vue.createElementVNode)("div", _hoisted_81, [(0, vue.createElementVNode)("h2", _hoisted_82, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatTitle")), 1), (0, vue.createElementVNode)("span", _hoisted_83, (0, vue.toDisplayString)(collection.value.title), 1)]),
5275
+ }, [(0, vue.createElementVNode)("div", _hoisted_80, [
5276
+ (0, vue.createElementVNode)("header", _hoisted_81, [
5277
+ _cache[52] || (_cache[52] = (0, vue.createElementVNode)("div", { class: "h-9 w-9 flex items-center justify-center rounded-xl bg-indigo-50 text-indigo-600 border border-indigo-100/50" }, [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "forum")], -1)),
5278
+ (0, vue.createElementVNode)("div", _hoisted_82, [(0, vue.createElementVNode)("h2", _hoisted_83, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatTitle")), 1), (0, vue.createElementVNode)("span", _hoisted_84, (0, vue.toDisplayString)(collection.value.title), 1)]),
5179
5279
  (0, vue.createElementVNode)("button", {
5180
5280
  type: "button",
5181
5281
  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",
5182
5282
  "aria-label": (0, vue.unref)(t)("common.close"),
5183
5283
  "data-testid": "collections-chat-close",
5184
5284
  onClick: closeChat
5185
- }, [..._cache[47] || (_cache[47] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_84)
5285
+ }, [..._cache[51] || (_cache[51] = [(0, vue.createElementVNode)("span", { class: "material-icons text-lg" }, "close", -1)])], 8, _hoisted_85)
5186
5286
  ]),
5187
- (0, vue.createElementVNode)("div", _hoisted_85, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
5287
+ (0, vue.createElementVNode)("div", _hoisted_86, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
5188
5288
  ref_key: "chatInputEl",
5189
5289
  ref: chatInputEl,
5190
- "onUpdate:modelValue": _cache[22] || (_cache[22] = ($event) => chatMessage.value = $event),
5290
+ "onUpdate:modelValue": _cache[24] || (_cache[24] = ($event) => chatMessage.value = $event),
5191
5291
  rows: "4",
5192
5292
  placeholder: (0, vue.unref)(t)("collectionsView.chatPlaceholder"),
5193
5293
  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",
5194
5294
  "data-testid": "collections-chat-input",
5195
5295
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(submitChat, ["meta"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(submitChat, ["ctrl"]), ["enter"])]
5196
- }, null, 40, _hoisted_86), [[vue.vModelText, chatMessage.value]])]),
5197
- (0, vue.createElementVNode)("footer", _hoisted_87, [(0, vue.createElementVNode)("button", {
5296
+ }, null, 40, _hoisted_87), [[vue.vModelText, chatMessage.value]])]),
5297
+ (0, vue.createElementVNode)("footer", _hoisted_88, [(0, vue.createElementVNode)("button", {
5198
5298
  type: "button",
5199
5299
  class: "h-8 px-2.5 rounded text-xs font-bold text-slate-500 hover:bg-slate-200/50 transition-colors",
5200
5300
  "data-testid": "collections-chat-cancel",
@@ -5205,7 +5305,7 @@ var CollectionView_default = /* @__PURE__ */ (0, vue.defineComponent)({
5205
5305
  disabled: !chatMessage.value.trim(),
5206
5306
  "data-testid": "collections-chat-send",
5207
5307
  onClick: submitChat
5208
- }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatStart")), 9, _hoisted_88)])
5308
+ }, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.chatStart")), 9, _hoisted_89)])
5209
5309
  ])], 32)) : (0, vue.createCommentVNode)("", true)
5210
5310
  ]);
5211
5311
  };