@mulmoclaude/collection-plugin 0.5.17 → 0.5.18

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
@@ -1904,82 +1904,90 @@ var _hoisted_13$4 = {
1904
1904
  key: 0,
1905
1905
  class: "text-rose-500 font-bold"
1906
1906
  };
1907
- var _hoisted_14$4 = {
1907
+ var _hoisted_14$4 = [
1908
+ "id",
1909
+ "onUpdate:modelValue",
1910
+ "required",
1911
+ "data-testid"
1912
+ ];
1913
+ var _hoisted_15$4 = { value: "" };
1914
+ var _hoisted_16$4 = ["value"];
1915
+ var _hoisted_17$4 = {
1908
1916
  key: 0,
1909
1917
  class: "inline-flex items-center gap-2.5 text-sm text-slate-700 cursor-pointer select-none"
1910
1918
  };
1911
- var _hoisted_15$4 = [
1919
+ var _hoisted_18$4 = [
1912
1920
  "id",
1913
1921
  "onUpdate:modelValue",
1914
1922
  "data-testid",
1915
1923
  "onChange"
1916
1924
  ];
1917
- var _hoisted_16$4 = [
1925
+ var _hoisted_19$3 = [
1918
1926
  "id",
1919
1927
  "onUpdate:modelValue",
1920
1928
  "required",
1921
1929
  "data-testid"
1922
1930
  ];
1923
- var _hoisted_17$4 = { value: "" };
1924
- var _hoisted_18$4 = ["value"];
1925
- var _hoisted_19$3 = [
1931
+ var _hoisted_20$3 = { value: "" };
1932
+ var _hoisted_21$3 = ["value"];
1933
+ var _hoisted_22$3 = [
1926
1934
  "id",
1927
1935
  "onUpdate:modelValue",
1928
1936
  "required",
1929
1937
  "data-testid"
1930
1938
  ];
1931
- var _hoisted_20$3 = { value: "" };
1932
- var _hoisted_21$3 = ["value"];
1933
- var _hoisted_22$3 = ["data-testid"];
1934
- var _hoisted_23$3 = {
1939
+ var _hoisted_23$3 = { value: "" };
1940
+ var _hoisted_24$2 = ["value"];
1941
+ var _hoisted_25$2 = ["data-testid"];
1942
+ var _hoisted_26$2 = {
1935
1943
  key: 0,
1936
1944
  class: "overflow-hidden border border-slate-200 rounded-lg shadow-sm"
1937
1945
  };
1938
- var _hoisted_24$2 = { class: "w-full text-xs text-slate-600 bg-white" };
1939
- var _hoisted_25$2 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
1940
- var _hoisted_26$2 = { class: "divide-y divide-slate-100" };
1941
- var _hoisted_27$2 = ["onUpdate:modelValue", "onChange"];
1942
- var _hoisted_28$1 = ["onUpdate:modelValue", "required"];
1943
- var _hoisted_29$1 = { value: "" };
1944
- var _hoisted_30$1 = ["value"];
1946
+ var _hoisted_27$2 = { class: "w-full text-xs text-slate-600 bg-white" };
1947
+ var _hoisted_28$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
1948
+ var _hoisted_29$1 = { class: "divide-y divide-slate-100" };
1949
+ var _hoisted_30$1 = ["onUpdate:modelValue", "onChange"];
1945
1950
  var _hoisted_31$1 = ["onUpdate:modelValue", "required"];
1946
1951
  var _hoisted_32$1 = { value: "" };
1947
1952
  var _hoisted_33$1 = ["value"];
1948
- var _hoisted_34$1 = {
1953
+ var _hoisted_34$1 = ["onUpdate:modelValue", "required"];
1954
+ var _hoisted_35$1 = { value: "" };
1955
+ var _hoisted_36$1 = ["value"];
1956
+ var _hoisted_37$1 = {
1949
1957
  key: 3,
1950
1958
  class: "relative flex items-center"
1951
1959
  };
1952
- var _hoisted_35$1 = { class: "absolute left-1.5 text-[10px] text-slate-400 font-bold pr-1 border-r border-slate-200" };
1953
- var _hoisted_36$1 = ["onUpdate:modelValue", "required"];
1954
- var _hoisted_37$1 = [
1960
+ var _hoisted_38$1 = { class: "absolute left-1.5 text-[10px] text-slate-400 font-bold pr-1 border-r border-slate-200" };
1961
+ var _hoisted_39$1 = ["onUpdate:modelValue", "required"];
1962
+ var _hoisted_40$1 = [
1955
1963
  "onUpdate:modelValue",
1956
1964
  "type",
1957
1965
  "step",
1958
1966
  "required"
1959
1967
  ];
1960
- var _hoisted_38$1 = { class: "text-center px-1" };
1961
- var _hoisted_39$1 = [
1968
+ var _hoisted_41$1 = { class: "text-center px-1" };
1969
+ var _hoisted_42$1 = [
1962
1970
  "aria-label",
1963
1971
  "data-testid",
1964
1972
  "onClick"
1965
1973
  ];
1966
- var _hoisted_40$1 = {
1974
+ var _hoisted_43$1 = {
1967
1975
  key: 1,
1968
1976
  class: "text-xs text-slate-400 italic"
1969
1977
  };
1970
- var _hoisted_41$1 = ["data-testid", "onClick"];
1971
- var _hoisted_42$1 = {
1978
+ var _hoisted_44$1 = ["data-testid", "onClick"];
1979
+ var _hoisted_45$1 = {
1972
1980
  key: 4,
1973
1981
  class: "relative flex items-center"
1974
1982
  };
1975
- var _hoisted_43$1 = { class: "absolute left-3 text-slate-400 font-bold text-xs select-none pr-1.5 border-r border-slate-200" };
1976
- var _hoisted_44$1 = [
1983
+ var _hoisted_46$1 = { class: "absolute left-3 text-slate-400 font-bold text-xs select-none pr-1.5 border-r border-slate-200" };
1984
+ var _hoisted_47$1 = [
1977
1985
  "id",
1978
1986
  "onUpdate:modelValue",
1979
1987
  "required",
1980
1988
  "data-testid"
1981
1989
  ];
1982
- var _hoisted_45$1 = [
1990
+ var _hoisted_48$1 = [
1983
1991
  "id",
1984
1992
  "onUpdate:modelValue",
1985
1993
  "type",
@@ -1988,104 +1996,104 @@ var _hoisted_45$1 = [
1988
1996
  "disabled",
1989
1997
  "data-testid"
1990
1998
  ];
1991
- var _hoisted_46$1 = [
1999
+ var _hoisted_49$1 = [
1992
2000
  "id",
1993
2001
  "onUpdate:modelValue",
1994
2002
  "rows",
1995
2003
  "required",
1996
2004
  "data-testid"
1997
2005
  ];
1998
- var _hoisted_47$1 = ["data-testid"];
1999
- var _hoisted_48$1 = {
2006
+ var _hoisted_50$1 = ["data-testid"];
2007
+ var _hoisted_51$1 = {
2000
2008
  key: 0,
2001
2009
  class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-50 text-emerald-700 border border-emerald-200/40"
2002
2010
  };
2003
- var _hoisted_49$1 = {
2011
+ var _hoisted_52$1 = {
2004
2012
  key: 1,
2005
2013
  class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-semibold bg-slate-50 text-slate-400 border border-slate-200/20"
2006
2014
  };
2007
- var _hoisted_50$1 = {
2015
+ var _hoisted_53$1 = {
2008
2016
  key: 0,
2009
2017
  class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-semibold bg-emerald-50 text-emerald-700 border border-emerald-200/40"
2010
2018
  };
2011
- var _hoisted_51$1 = {
2019
+ var _hoisted_54$1 = {
2012
2020
  key: 1,
2013
2021
  class: "inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[10px] font-semibold bg-slate-50 text-slate-400 border border-slate-200/20"
2014
2022
  };
2015
- var _hoisted_52$1 = {
2023
+ var _hoisted_55$1 = {
2016
2024
  key: 2,
2017
2025
  class: "text-slate-300"
2018
2026
  };
2019
- var _hoisted_53$1 = [
2027
+ var _hoisted_56$1 = [
2020
2028
  "href",
2021
2029
  "tabindex",
2022
2030
  "data-testid",
2023
2031
  "onClick",
2024
2032
  "onKeydown"
2025
2033
  ];
2026
- var _hoisted_54$1 = {
2034
+ var _hoisted_57$1 = {
2027
2035
  key: 3,
2028
2036
  class: "font-semibold text-slate-900 tabular-nums text-sm"
2029
2037
  };
2030
- var _hoisted_55$1 = {
2038
+ var _hoisted_58$1 = {
2031
2039
  key: 4,
2032
2040
  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"
2033
2041
  };
2034
- var _hoisted_56$1 = {
2042
+ var _hoisted_59$1 = {
2035
2043
  key: 5,
2036
2044
  class: "border border-slate-200/80 rounded-xl overflow-hidden shadow-sm mt-1"
2037
2045
  };
2038
- var _hoisted_57$1 = { class: "w-full text-[11px] text-slate-600 bg-white" };
2039
- var _hoisted_58$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
2040
- var _hoisted_59$1 = { class: "divide-y divide-slate-100" };
2041
- var _hoisted_60$1 = {
2046
+ var _hoisted_60$1 = { class: "w-full text-[11px] text-slate-600 bg-white" };
2047
+ var _hoisted_61$1 = { class: "bg-slate-50 border-b border-slate-200 text-slate-500 font-bold uppercase tracking-wider" };
2048
+ var _hoisted_62$1 = { class: "divide-y divide-slate-100" };
2049
+ var _hoisted_63$1 = {
2042
2050
  key: 0,
2043
2051
  class: "material-icons text-emerald-600 text-base"
2044
2052
  };
2045
- var _hoisted_61$1 = {
2053
+ var _hoisted_64$1 = {
2046
2054
  key: 1,
2047
2055
  class: "text-slate-300"
2048
2056
  };
2049
- var _hoisted_62$1 = {
2057
+ var _hoisted_65$1 = {
2050
2058
  key: 6,
2051
2059
  class: "text-slate-400 italic"
2052
2060
  };
2053
- var _hoisted_63$1 = {
2061
+ var _hoisted_66$1 = {
2054
2062
  key: 7,
2055
2063
  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"
2056
2064
  };
2057
- var _hoisted_64$1 = [
2065
+ var _hoisted_67$1 = [
2058
2066
  "src",
2059
2067
  "alt",
2060
2068
  "data-testid"
2061
2069
  ];
2062
- var _hoisted_65$1 = ["href", "data-testid"];
2063
- var _hoisted_66$1 = ["href", "data-testid"];
2064
- var _hoisted_67$1 = [
2070
+ var _hoisted_68$1 = ["href", "data-testid"];
2071
+ var _hoisted_69$1 = ["href", "data-testid"];
2072
+ var _hoisted_70$1 = [
2065
2073
  "href",
2066
2074
  "data-testid",
2067
2075
  "onClick"
2068
2076
  ];
2069
- var _hoisted_68$1 = {
2077
+ var _hoisted_71$1 = {
2070
2078
  key: 13,
2071
2079
  class: "text-slate-800 font-semibold"
2072
2080
  };
2073
- var _hoisted_69$1 = {
2081
+ var _hoisted_72$1 = {
2074
2082
  key: 0,
2075
2083
  class: "col-span-full text-xs font-semibold text-red-600 bg-red-50 border border-red-100 p-2.5 rounded-xl"
2076
2084
  };
2077
- var _hoisted_70$1 = {
2085
+ var _hoisted_73$1 = {
2078
2086
  key: 1,
2079
2087
  class: "mt-5 pt-4 border-t border-slate-200/60",
2080
2088
  "data-testid": "collections-detail-chat"
2081
2089
  };
2082
- var _hoisted_71$1 = {
2090
+ var _hoisted_74$1 = {
2083
2091
  class: "block text-[10px] font-bold text-slate-400 uppercase tracking-wider mb-1.5",
2084
2092
  for: "collections-detail-chat-input"
2085
2093
  };
2086
- var _hoisted_72$1 = { class: "flex items-end gap-2" };
2087
- var _hoisted_73$1 = ["placeholder", "onKeydown"];
2088
- var _hoisted_74$1 = ["disabled"];
2094
+ var _hoisted_75$1 = { class: "flex items-end gap-2" };
2095
+ var _hoisted_76$1 = ["placeholder", "onKeydown"];
2096
+ var _hoisted_77$1 = ["disabled"];
2089
2097
  //#endregion
2090
2098
  //#region src/vue/components/CollectionRecordPanel.vue
2091
2099
  var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
@@ -2134,12 +2142,17 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2134
2142
  emit("itemChat", message);
2135
2143
  chatMessage.value = "";
2136
2144
  }
2137
- const embedViews = (0, vue.computed)(() => props.render.embedViews.value);
2138
2145
  /** The record the read-only displays render from: the live draft while
2139
2146
  * editing (so non-editable cells like derived/embed preview the in-flight
2140
2147
  * values), else the open record. Always an object so `[key]` lookups are
2141
2148
  * safe in the template. */
2142
2149
  const detailRecord = (0, vue.computed)(() => editing.value ? props.liveDerived ?? props.liveRecord ?? {} : props.viewing ?? {});
2150
+ const embedViews = (0, vue.computed)(() => props.render.embedViewsFor(detailRecord.value));
2151
+ const embedOwnedKeys = (0, vue.computed)(() => {
2152
+ const keys = /* @__PURE__ */ new Set();
2153
+ for (const field of Object.values(props.collection.schema.fields)) if (field.type === "embed" && field.idField) keys.add(field.idField);
2154
+ return keys;
2155
+ });
2143
2156
  /** Title for the header: the create label, the edited record's id, or the
2144
2157
  * open record's title — same h2 slot in every mode. */
2145
2158
  const headerTitle = (0, vue.computed)(() => {
@@ -2172,8 +2185,9 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2172
2185
  * cell appears or disappears on toggle: respect the `when` predicate
2173
2186
  * (against the active record) and hide the primary key except while
2174
2187
  * creating. */
2175
- function cellVisible(field) {
2188
+ function cellVisible(field, key) {
2176
2189
  if (field.primary && editing.value?.mode !== "create") return false;
2190
+ if (embedOwnedKeys.value.has(key)) return false;
2177
2191
  return (0, _mulmoclaude_core_collection.fieldVisible)(field, detailRecord.value);
2178
2192
  }
2179
2193
  /** Mirror of the create-mode primary-key carve-out: drop the HTML5
@@ -2184,6 +2198,12 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2184
2198
  if (editing.value?.mode === "create" && field.primary === true) return false;
2185
2199
  return true;
2186
2200
  }
2201
+ /** Required flag for an embed's per-record picker — read off the storage
2202
+ * field it writes (`idField`), since the embed itself stores nothing. */
2203
+ function embedPickerRequired(field) {
2204
+ const target = field.idField ? props.collection.schema.fields[field.idField] : void 0;
2205
+ return target ? isFieldRequiredInUi(target) : false;
2206
+ }
2187
2207
  /** Tailwind fill/text/border classes tinting an enum `<select>` by its current
2188
2208
  * value's colour (palette, or notification red/amber/grey when the field is
2189
2209
  * the schema's notifyWhen target). */
@@ -2261,53 +2281,65 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2261
2281
  ]))]),
2262
2282
  !editing.value && __props.actionError ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_10$5, (0, vue.toDisplayString)(__props.actionError), 1)) : (0, vue.createCommentVNode)("", true),
2263
2283
  (0, vue.createElementVNode)("div", _hoisted_11$5, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.collection.schema.fields, (field, key) => {
2264
- return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key }, [cellVisible(field) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2284
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key }, [cellVisible(field, String(key)) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2265
2285
  key: 0,
2266
2286
  class: (0, vue.normalizeClass)(["flex flex-col gap-1.5", colSpanClass(field)])
2267
2287
  }, [(0, vue.createElementVNode)("label", {
2268
2288
  class: "text-[10px] font-bold text-slate-400 uppercase tracking-wider flex items-center gap-1",
2269
2289
  for: `collections-field-${key}`
2270
- }, [(0, vue.createTextVNode)((0, vue.toDisplayString)(field.label) + " ", 1), editing.value && field.required ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_13$4, "*")) : (0, vue.createCommentVNode)("", true)], 8, _hoisted_12$5), editing.value && isEditableType(field.type) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("label", _hoisted_14$4, [(0, vue.withDirectives)((0, vue.createElementVNode)("input", {
2290
+ }, [(0, vue.createTextVNode)((0, vue.toDisplayString)(field.label) + " ", 1), editing.value && field.required ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_13$4, "*")) : (0, vue.createCommentVNode)("", true)], 8, _hoisted_12$5), editing.value && field.type === "embed" && field.idField ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2291
+ key: 0,
2292
+ id: `collections-field-${key}`,
2293
+ "onUpdate:modelValue": ($event) => editing.value.text[field.idField] = $event,
2294
+ required: embedPickerRequired(field),
2295
+ class: "w-full rounded-xl border border-slate-200 px-3 py-2 text-xs bg-slate-50 hover:bg-slate-50/50 focus:bg-white focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none transition-all cursor-pointer font-medium text-slate-700",
2296
+ "data-testid": `collections-input-${key}`
2297
+ }, [(0, vue.createElementVNode)("option", _hoisted_15$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.embedOptions(field.to ?? ""), (opt) => {
2298
+ return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
2299
+ key: opt.slug,
2300
+ value: opt.slug
2301
+ }, (0, vue.toDisplayString)(opt.display), 9, _hoisted_16$4);
2302
+ }), 128))], 8, _hoisted_14$4)), [[vue.vModelSelect, editing.value.text[field.idField]]]) : editing.value && isEditableType(field.type) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("label", _hoisted_17$4, [(0, vue.withDirectives)((0, vue.createElementVNode)("input", {
2271
2303
  id: `collections-field-${key}`,
2272
2304
  "onUpdate:modelValue": ($event) => editing.value.bool[key] = $event,
2273
2305
  type: "checkbox",
2274
2306
  class: "h-5 w-5 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer",
2275
2307
  "data-testid": `collections-input-${key}`,
2276
2308
  onChange: ($event) => markBoolTouched(String(key))
2277
- }, null, 40, _hoisted_15$4), [[vue.vModelCheckbox, editing.value.bool[key]]]), (0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["text-xs font-semibold", editing.value.bool[key] ? "text-indigo-600" : "text-slate-500"]) }, (0, vue.toDisplayString)(editing.value.bool[key] ? (0, vue.unref)(t)("common.yes") : (0, vue.unref)(t)("common.no")), 3)])) : field.type === "ref" && field.to && __props.render.refOptions(field.to).length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2309
+ }, null, 40, _hoisted_18$4), [[vue.vModelCheckbox, editing.value.bool[key]]]), (0, vue.createElementVNode)("span", { class: (0, vue.normalizeClass)(["text-xs font-semibold", editing.value.bool[key] ? "text-indigo-600" : "text-slate-500"]) }, (0, vue.toDisplayString)(editing.value.bool[key] ? (0, vue.unref)(t)("common.yes") : (0, vue.unref)(t)("common.no")), 3)])) : field.type === "ref" && field.to && __props.render.refOptions(field.to).length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2278
2310
  key: 1,
2279
2311
  id: `collections-field-${key}`,
2280
2312
  "onUpdate:modelValue": ($event) => editing.value.text[key] = $event,
2281
2313
  required: isFieldRequiredInUi(field),
2282
2314
  class: "w-full rounded-xl border border-slate-200 px-3 py-2 text-xs bg-slate-50 hover:bg-slate-50/50 focus:bg-white focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none transition-all cursor-pointer font-medium text-slate-700",
2283
2315
  "data-testid": `collections-input-${key}`
2284
- }, [(0, vue.createElementVNode)("option", _hoisted_17$4, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.refOptions(field.to), (opt) => {
2316
+ }, [(0, vue.createElementVNode)("option", _hoisted_20$3, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.refOptions(field.to), (opt) => {
2285
2317
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
2286
2318
  key: opt.slug,
2287
2319
  value: opt.slug
2288
- }, (0, vue.toDisplayString)(opt.display), 9, _hoisted_18$4);
2289
- }), 128))], 8, _hoisted_16$4)), [[vue.vModelSelect, editing.value.text[key]]]) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2320
+ }, (0, vue.toDisplayString)(opt.display), 9, _hoisted_21$3);
2321
+ }), 128))], 8, _hoisted_19$3)), [[vue.vModelSelect, editing.value.text[key]]]) : field.type === "enum" && Array.isArray(field.values) && field.values.length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2290
2322
  key: 2,
2291
2323
  id: `collections-field-${key}`,
2292
2324
  "onUpdate:modelValue": ($event) => editing.value.text[key] = $event,
2293
2325
  required: isFieldRequiredInUi(field),
2294
2326
  class: (0, vue.normalizeClass)(["w-full rounded-xl border px-3 py-2 text-xs focus:bg-white focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none transition-all cursor-pointer font-medium", enumControlClass(String(key), editing.value.text[key])]),
2295
2327
  "data-testid": `collections-input-${key}`
2296
- }, [(0, vue.createElementVNode)("option", _hoisted_20$3, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
2328
+ }, [(0, vue.createElementVNode)("option", _hoisted_23$3, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.values, (value) => {
2297
2329
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
2298
2330
  key: value,
2299
2331
  value
2300
- }, (0, vue.toDisplayString)(value), 9, _hoisted_21$3);
2301
- }), 128))], 10, _hoisted_19$3)), [[vue.vModelSelect, editing.value.text[key]]]) : field.type === "table" && field.of ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2332
+ }, (0, vue.toDisplayString)(value), 9, _hoisted_24$2);
2333
+ }), 128))], 10, _hoisted_22$3)), [[vue.vModelSelect, editing.value.text[key]]]) : field.type === "table" && field.of ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2302
2334
  key: 3,
2303
2335
  class: "border border-slate-200 bg-slate-50/30 rounded-xl p-4 space-y-3",
2304
2336
  "data-testid": `collections-table-${key}`
2305
- }, [editing.value.table[key] && editing.value.table[key].length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_23$3, [(0, vue.createElementVNode)("table", _hoisted_24$2, [(0, vue.createElementVNode)("thead", _hoisted_25$2, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.of, (subField, subKey) => {
2337
+ }, [editing.value.table[key] && editing.value.table[key].length > 0 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_26$2, [(0, vue.createElementVNode)("table", _hoisted_27$2, [(0, vue.createElementVNode)("thead", _hoisted_28$1, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.of, (subField, subKey) => {
2306
2338
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
2307
2339
  key: subKey,
2308
2340
  class: "text-left px-3 py-2 font-bold"
2309
2341
  }, (0, vue.toDisplayString)(subField.label), 1);
2310
- }), 128)), _cache[9] || (_cache[9] = (0, vue.createElementVNode)("th", { class: "w-9" }, null, -1))])]), (0, vue.createElementVNode)("tbody", _hoisted_26$2, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(editing.value.table[key], (row, rowIdx) => {
2342
+ }), 128)), _cache[9] || (_cache[9] = (0, vue.createElementVNode)("th", { class: "w-9" }, null, -1))])]), (0, vue.createElementVNode)("tbody", _hoisted_29$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(editing.value.table[key], (row, rowIdx) => {
2311
2343
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("tr", {
2312
2344
  key: rowIdx,
2313
2345
  class: "hover:bg-slate-50/50"
@@ -2321,53 +2353,53 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2321
2353
  type: "checkbox",
2322
2354
  class: "h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500/20 cursor-pointer",
2323
2355
  onChange: ($event) => markRowBoolTouched(row, String(subKey))
2324
- }, null, 40, _hoisted_27$2)), [[vue.vModelCheckbox, row.bool[subKey]]]) : subField.type === "enum" && Array.isArray(subField.values) && subField.values.length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2356
+ }, null, 40, _hoisted_30$1)), [[vue.vModelCheckbox, row.bool[subKey]]]) : subField.type === "enum" && Array.isArray(subField.values) && subField.values.length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2325
2357
  key: 1,
2326
2358
  "onUpdate:modelValue": ($event) => row.text[subKey] = $event,
2327
2359
  required: subField.required,
2328
2360
  class: "w-full rounded-lg border border-slate-200 px-2 py-1 text-xs focus:border-indigo-500 focus:outline-none cursor-pointer bg-slate-50 font-medium"
2329
- }, [(0, vue.createElementVNode)("option", _hoisted_29$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(subField.values, (value) => {
2361
+ }, [(0, vue.createElementVNode)("option", _hoisted_32$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(subField.values, (value) => {
2330
2362
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
2331
2363
  key: value,
2332
2364
  value
2333
- }, (0, vue.toDisplayString)(value), 9, _hoisted_30$1);
2334
- }), 128))], 8, _hoisted_28$1)), [[vue.vModelSelect, row.text[subKey]]]) : subField.type === "ref" && subField.to && __props.render.refOptions(subField.to).length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2365
+ }, (0, vue.toDisplayString)(value), 9, _hoisted_33$1);
2366
+ }), 128))], 8, _hoisted_31$1)), [[vue.vModelSelect, row.text[subKey]]]) : subField.type === "ref" && subField.to && __props.render.refOptions(subField.to).length > 0 ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("select", {
2335
2367
  key: 2,
2336
2368
  "onUpdate:modelValue": ($event) => row.text[subKey] = $event,
2337
2369
  required: subField.required,
2338
2370
  class: "w-full rounded-lg border border-slate-200 px-2 py-1 text-xs focus:border-indigo-500 focus:outline-none cursor-pointer bg-slate-50 font-medium"
2339
- }, [(0, vue.createElementVNode)("option", _hoisted_32$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.refOptions(subField.to), (opt) => {
2371
+ }, [(0, vue.createElementVNode)("option", _hoisted_35$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.selectPlaceholder")), 1), ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.refOptions(subField.to), (opt) => {
2340
2372
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("option", {
2341
2373
  key: opt.slug,
2342
2374
  value: opt.slug
2343
- }, (0, vue.toDisplayString)(opt.display), 9, _hoisted_33$1);
2344
- }), 128))], 8, _hoisted_31$1)), [[vue.vModelSelect, row.text[subKey]]]) : subField.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_34$1, [(0, vue.createElementVNode)("span", _hoisted_35$1, (0, vue.toDisplayString)(__props.render.currencySymbol(__props.render.resolveCurrency(subField, __props.liveRecord))), 1), (0, vue.withDirectives)((0, vue.createElementVNode)("input", {
2375
+ }, (0, vue.toDisplayString)(opt.display), 9, _hoisted_36$1);
2376
+ }), 128))], 8, _hoisted_34$1)), [[vue.vModelSelect, row.text[subKey]]]) : subField.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_37$1, [(0, vue.createElementVNode)("span", _hoisted_38$1, (0, vue.toDisplayString)(__props.render.currencySymbol(__props.render.resolveCurrency(subField, __props.liveRecord))), 1), (0, vue.withDirectives)((0, vue.createElementVNode)("input", {
2345
2377
  "onUpdate:modelValue": ($event) => row.text[subKey] = $event,
2346
2378
  type: "number",
2347
2379
  step: "0.01",
2348
2380
  required: subField.required,
2349
2381
  class: "w-full rounded-lg border border-slate-200 pl-6 pr-1.5 py-1 text-xs focus:border-indigo-500 focus:outline-none font-semibold text-slate-800"
2350
- }, null, 8, _hoisted_36$1), [[vue.vModelText, row.text[subKey]]])])) : (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
2382
+ }, null, 8, _hoisted_39$1), [[vue.vModelText, row.text[subKey]]])])) : (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("input", {
2351
2383
  key: 4,
2352
2384
  "onUpdate:modelValue": ($event) => row.text[subKey] = $event,
2353
2385
  type: __props.render.inputTypeFor(subField.type),
2354
2386
  step: __props.render.stepFor(subField.type),
2355
2387
  required: subField.required,
2356
2388
  class: "w-full rounded-lg border border-slate-200 px-2 py-1 text-xs focus:border-indigo-500 focus:outline-none font-medium text-slate-700"
2357
- }, null, 8, _hoisted_37$1)), [[vue.vModelDynamic, row.text[subKey]]])]);
2358
- }), 128)), (0, vue.createElementVNode)("td", _hoisted_38$1, [(0, vue.createElementVNode)("button", {
2389
+ }, null, 8, _hoisted_40$1)), [[vue.vModelDynamic, row.text[subKey]]])]);
2390
+ }), 128)), (0, vue.createElementVNode)("td", _hoisted_41$1, [(0, vue.createElementVNode)("button", {
2359
2391
  type: "button",
2360
2392
  class: "h-7 w-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-rose-600 hover:bg-rose-50 transition-colors",
2361
2393
  "aria-label": (0, vue.unref)(t)("collectionsView.removeRow"),
2362
2394
  "data-testid": `collections-table-${key}-remove-${rowIdx}`,
2363
2395
  onClick: ($event) => removeTableRow(String(key), rowIdx)
2364
- }, [..._cache[10] || (_cache[10] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_39$1)])]);
2365
- }), 128))])])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_40$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 1)), (0, vue.createElementVNode)("button", {
2396
+ }, [..._cache[10] || (_cache[10] = [(0, vue.createElementVNode)("span", { class: "material-icons text-base" }, "close", -1)])], 8, _hoisted_42$1)])]);
2397
+ }), 128))])])])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_43$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 1)), (0, vue.createElementVNode)("button", {
2366
2398
  type: "button",
2367
2399
  class: "inline-flex items-center gap-1 text-xs text-indigo-600 hover:text-indigo-800 font-bold hover:underline",
2368
2400
  "data-testid": `collections-table-${key}-add`,
2369
2401
  onClick: ($event) => addTableRow(String(key), field.of)
2370
- }, [_cache[11] || (_cache[11] = (0, vue.createElementVNode)("span", { class: "material-icons text-xs" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.addRow")), 1)], 8, _hoisted_41$1)], 8, _hoisted_22$3)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_42$1, [(0, vue.createElementVNode)("div", _hoisted_43$1, (0, vue.toDisplayString)(__props.render.currencySymbol(__props.render.resolveCurrency(field, __props.liveRecord))), 1), (0, vue.withDirectives)((0, vue.createElementVNode)("input", {
2402
+ }, [_cache[11] || (_cache[11] = (0, vue.createElementVNode)("span", { class: "material-icons text-xs" }, "add", -1)), (0, vue.createElementVNode)("span", null, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.addRow")), 1)], 8, _hoisted_44$1)], 8, _hoisted_25$2)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_45$1, [(0, vue.createElementVNode)("div", _hoisted_46$1, (0, vue.toDisplayString)(__props.render.currencySymbol(__props.render.resolveCurrency(field, __props.liveRecord))), 1), (0, vue.withDirectives)((0, vue.createElementVNode)("input", {
2371
2403
  id: `collections-field-${key}`,
2372
2404
  "onUpdate:modelValue": ($event) => editing.value.text[key] = $event,
2373
2405
  type: "number",
@@ -2375,7 +2407,7 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2375
2407
  required: isFieldRequiredInUi(field),
2376
2408
  class: "w-full rounded-xl border border-slate-200 pl-11 pr-3 py-2 text-xs focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none font-semibold text-slate-800 transition-all",
2377
2409
  "data-testid": `collections-input-${key}`
2378
- }, null, 8, _hoisted_44$1), [[vue.vModelText, editing.value.text[key]]])])) : [
2410
+ }, null, 8, _hoisted_47$1), [[vue.vModelText, editing.value.text[key]]])])) : [
2379
2411
  "string",
2380
2412
  "email",
2381
2413
  "number",
@@ -2394,7 +2426,7 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2394
2426
  disabled: field.primary === true && (editing.value.mode === "edit" || __props.isSingleton),
2395
2427
  class: "w-full rounded-xl border border-slate-200 px-3 py-2 text-xs focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none disabled:bg-slate-100 disabled:text-slate-400 font-medium text-slate-700 transition-all",
2396
2428
  "data-testid": `collections-input-${key}`
2397
- }, null, 8, _hoisted_45$1)), [[vue.vModelDynamic, editing.value.text[key]]]) : (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("textarea", {
2429
+ }, null, 8, _hoisted_48$1)), [[vue.vModelDynamic, editing.value.text[key]]]) : (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createElementBlock)("textarea", {
2398
2430
  key: 6,
2399
2431
  id: `collections-field-${key}`,
2400
2432
  "onUpdate:modelValue": ($event) => editing.value.text[key] = $event,
@@ -2402,11 +2434,11 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2402
2434
  required: isFieldRequiredInUi(field),
2403
2435
  class: "w-full rounded-xl border border-slate-200 px-3 py-2 text-xs focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none font-medium text-slate-700 transition-all",
2404
2436
  "data-testid": `collections-input-${key}`
2405
- }, null, 8, _hoisted_46$1)), [[vue.vModelText, editing.value.text[key]]])], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2406
- key: 1,
2437
+ }, null, 8, _hoisted_49$1)), [[vue.vModelText, editing.value.text[key]]])], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
2438
+ key: 2,
2407
2439
  class: "text-xs font-medium text-slate-700 break-words",
2408
2440
  "data-testid": `collections-detail-value-${key}`
2409
- }, [field.type === "toggle" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [field.field !== void 0 && String(detailRecord.value[field.field] ?? "") === field.onValue ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_48$1, [_cache[12] || (_cache[12] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_49$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1))], 64)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [detailRecord.value[key] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_50$1, [_cache[13] || (_cache[13] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : detailRecord.value[key] === false ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_51$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_52$1, "—"))], 64)) : field.type === "ref" && field.to && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2441
+ }, [field.type === "toggle" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [field.field !== void 0 && String(detailRecord.value[field.field] ?? "") === field.onValue ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_51$1, [_cache[12] || (_cache[12] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_52$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1))], 64)) : field.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [detailRecord.value[key] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_53$1, [_cache[13] || (_cache[13] = (0, vue.createElementVNode)("span", { class: "h-1.5 w-1.5 rounded-full bg-emerald-500" }, null, -1)), (0, vue.createTextVNode)(" " + (0, vue.toDisplayString)((0, vue.unref)(t)("common.yes")), 1)])) : detailRecord.value[key] === false ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_54$1, (0, vue.toDisplayString)((0, vue.unref)(t)("common.no")), 1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_55$1, "—"))], 64)) : field.type === "ref" && field.to && typeof detailRecord.value[key] === "string" && detailRecord.value[key] ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2410
2442
  key: 2,
2411
2443
  href: (0, vue.unref)(cui).recordHref?.(field.to, String(detailRecord.value[key])),
2412
2444
  tabindex: (0, vue.unref)(cui).recordHref?.(field.to, String(detailRecord.value[key])) ? void 0 : 0,
@@ -2415,12 +2447,12 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2415
2447
  "data-testid": `collections-detail-ref-${key}`,
2416
2448
  onClick: ($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(detailRecord.value[key])),
2417
2449
  onKeydown: [(0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(detailRecord.value[key])), ["enter"]), (0, vue.withKeys)(($event) => (0, vue.unref)(activateRefLink)($event, field.to, String(detailRecord.value[key])), ["space"])]
2418
- }, (0, vue.toDisplayString)(__props.render.refDisplay(field.to, String(detailRecord.value[key]))), 41, _hoisted_53$1)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_54$1, (0, vue.toDisplayString)(__props.render.formatMoney(detailRecord.value[key], __props.render.resolveCurrency(field, detailRecord.value), __props.locale)), 1)) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_55$1, (0, vue.toDisplayString)(__props.render.derivedDisplay(field, __props.render.evaluateDerivedAgainstItem(field, String(key), detailRecord.value), detailRecord.value)), 1)) : field.type === "table" && field.of && __props.render.hasTableRows(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_56$1, [(0, vue.createElementVNode)("table", _hoisted_57$1, [(0, vue.createElementVNode)("thead", _hoisted_58$1, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.of, (subField, subKey) => {
2450
+ }, (0, vue.toDisplayString)(__props.render.refDisplay(field.to, String(detailRecord.value[key]))), 41, _hoisted_56$1)) : field.type === "money" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_57$1, (0, vue.toDisplayString)(__props.render.formatMoney(detailRecord.value[key], __props.render.resolveCurrency(field, detailRecord.value), __props.locale)), 1)) : field.type === "derived" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_58$1, (0, vue.toDisplayString)(__props.render.derivedDisplay(field, __props.render.evaluateDerivedAgainstItem(field, String(key), detailRecord.value), detailRecord.value)), 1)) : field.type === "table" && field.of && __props.render.hasTableRows(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_59$1, [(0, vue.createElementVNode)("table", _hoisted_60$1, [(0, vue.createElementVNode)("thead", _hoisted_61$1, [(0, vue.createElementVNode)("tr", null, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(field.of, (subField, subKey) => {
2419
2451
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("th", {
2420
2452
  key: subKey,
2421
2453
  class: "text-left px-4 py-2 font-bold"
2422
2454
  }, (0, vue.toDisplayString)(subField.label), 1);
2423
- }), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_59$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.tableRows(detailRecord.value[key]), (row, rowIdx) => {
2455
+ }), 128))])]), (0, vue.createElementVNode)("tbody", _hoisted_62$1, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(__props.render.tableRows(detailRecord.value[key]), (row, rowIdx) => {
2424
2456
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("tr", {
2425
2457
  key: rowIdx,
2426
2458
  class: "hover:bg-slate-50/50"
@@ -2428,12 +2460,12 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2428
2460
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("td", {
2429
2461
  key: subKey,
2430
2462
  class: "px-4 py-2 align-middle font-medium"
2431
- }, [subField.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [row[subKey] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_60$1, "check_circle")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_61$1, "—"))], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2463
+ }, [subField.type === "boolean" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, [row[subKey] === true ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_63$1, "check_circle")) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_64$1, "—"))], 64)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
2432
2464
  key: 1,
2433
2465
  class: (0, vue.normalizeClass)([subField.type === "money" ? "font-bold text-slate-800 tabular-nums" : ""])
2434
2466
  }, (0, vue.toDisplayString)(__props.render.formatSubCell(subField, row[subKey], detailRecord.value)), 3))]);
2435
2467
  }), 128))]);
2436
- }), 128))])])])) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_62$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 1)) : field.type === "markdown" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_63$1, (0, vue.toDisplayString)(__props.render.detailText(detailRecord.value[key])), 1)) : field.type === "embed" && embedViews.value[key] ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionEmbedView_default, {
2468
+ }), 128))])])])) : field.type === "table" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_65$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.noRows")), 1)) : field.type === "markdown" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_66$1, (0, vue.toDisplayString)(__props.render.detailText(detailRecord.value[key])), 1)) : field.type === "embed" && embedViews.value[key] ? ((0, vue.openBlock)(), (0, vue.createBlock)(CollectionEmbedView_default, {
2437
2469
  key: 8,
2438
2470
  view: embedViews.value[key],
2439
2471
  "field-key": String(key)
@@ -2443,29 +2475,29 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2443
2475
  alt: field.label,
2444
2476
  class: "max-h-64 max-w-full object-contain rounded-lg border border-slate-200 bg-slate-50",
2445
2477
  "data-testid": `collections-detail-image-${key}`
2446
- }, null, 8, _hoisted_64$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2478
+ }, null, 8, _hoisted_67$1)) : field.type !== "file" && __props.render.isExternalUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2447
2479
  key: 10,
2448
2480
  href: String(detailRecord.value[key]),
2449
2481
  target: "_blank",
2450
2482
  rel: "noopener noreferrer",
2451
2483
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2452
2484
  "data-testid": `collections-detail-url-${key}`
2453
- }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_65$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2485
+ }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_68$1)) : field.type === "file" && __props.render.artifactUrl(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2454
2486
  key: 11,
2455
2487
  href: __props.render.artifactUrl(detailRecord.value[key]) ?? void 0,
2456
2488
  target: "_blank",
2457
2489
  rel: "noopener noreferrer",
2458
2490
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2459
2491
  "data-testid": `collections-detail-file-${key}`
2460
- }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_66$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2492
+ }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_69$1)) : field.type === "file" && __props.render.fileRoutePath(detailRecord.value[key]) ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("a", {
2461
2493
  key: 12,
2462
2494
  href: __props.render.fileRoutePath(detailRecord.value[key]) ?? void 0,
2463
2495
  class: "text-blue-600 hover:text-blue-800 font-semibold hover:underline break-all",
2464
2496
  "data-testid": `collections-detail-file-${key}`,
2465
2497
  onClick: ($event) => (0, vue.unref)(activatePathLink)($event, __props.render.fileRoutePath(detailRecord.value[key]) ?? "")
2466
- }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_67$1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_68$1, (0, vue.toDisplayString)(__props.render.formatCell(detailRecord.value[key], field.type)), 1))], 8, _hoisted_47$1))], 2)) : (0, vue.createCommentVNode)("", true)], 64);
2467
- }), 128)), editing.value && __props.saveError ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_69$1, (0, vue.toDisplayString)(__props.saveError), 1)) : (0, vue.createCommentVNode)("", true)]),
2468
- !editing.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_70$1, [(0, vue.createElementVNode)("label", _hoisted_71$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemChatLabel")), 1), (0, vue.createElementVNode)("div", _hoisted_72$1, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
2498
+ }, (0, vue.toDisplayString)(String(detailRecord.value[key])), 9, _hoisted_70$1)) : ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", _hoisted_71$1, (0, vue.toDisplayString)(__props.render.formatCell(detailRecord.value[key], field.type)), 1))], 8, _hoisted_50$1))], 2)) : (0, vue.createCommentVNode)("", true)], 64);
2499
+ }), 128)), editing.value && __props.saveError ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("p", _hoisted_72$1, (0, vue.toDisplayString)(__props.saveError), 1)) : (0, vue.createCommentVNode)("", true)]),
2500
+ !editing.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_73$1, [(0, vue.createElementVNode)("label", _hoisted_74$1, (0, vue.toDisplayString)((0, vue.unref)(t)("collectionsView.itemChatLabel")), 1), (0, vue.createElementVNode)("div", _hoisted_75$1, [(0, vue.withDirectives)((0, vue.createElementVNode)("textarea", {
2469
2501
  id: "collections-detail-chat-input",
2470
2502
  "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => chatMessage.value = $event),
2471
2503
  rows: "2",
@@ -2473,13 +2505,13 @@ var CollectionRecordPanel_default = /* @__PURE__ */ (0, vue.defineComponent)({
2473
2505
  class: "flex-1 bg-slate-50 border border-slate-200/80 rounded-xl px-3 py-2 text-xs 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",
2474
2506
  "data-testid": "collections-detail-chat-input",
2475
2507
  onKeydown: [(0, vue.withKeys)((0, vue.withModifiers)(submitItemChat, ["meta"]), ["enter"]), (0, vue.withKeys)((0, vue.withModifiers)(submitItemChat, ["ctrl"]), ["enter"])]
2476
- }, null, 40, _hoisted_73$1), [[vue.vModelText, chatMessage.value]]), (0, vue.createElementVNode)("button", {
2508
+ }, null, 40, _hoisted_76$1), [[vue.vModelText, chatMessage.value]]), (0, vue.createElementVNode)("button", {
2477
2509
  type: "button",
2478
2510
  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 flex items-center gap-1 shrink-0",
2479
2511
  disabled: !chatMessage.value.trim(),
2480
2512
  "data-testid": "collections-detail-chat-send",
2481
2513
  onClick: submitItemChat
2482
- }, [_cache[14] || (_cache[14] = (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)], 8, _hoisted_74$1)])])) : (0, vue.createCommentVNode)("", true)
2514
+ }, [_cache[14] || (_cache[14] = (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)], 8, _hoisted_77$1)])])) : (0, vue.createCommentVNode)("", true)
2483
2515
  ]),
2484
2516
  _: 1
2485
2517
  }, 40, ["data-testid"]);
@@ -2855,17 +2887,43 @@ function useCollectionRendering(collection, locale) {
2855
2887
  display
2856
2888
  })).sort((left, right) => left.display.localeCompare(right.display));
2857
2889
  }
2858
- function resolveEmbed(field) {
2859
- if (field.type !== "embed" || !field.to || !field.id) return {
2890
+ /** Dropdown options for an `embed` field's per-record picker (`idField`):
2891
+ * every record in the target collection, labelled by its name/title (or
2892
+ * primary key). Built from `embedCache` so it works for embed targets
2893
+ * that aren't also `ref` targets (the profile collection, say). */
2894
+ function embedOptions(targetSlug) {
2895
+ const data = embedCache.value[targetSlug];
2896
+ if (!data) return [];
2897
+ const { fields, primaryKey } = data.schema;
2898
+ const displayField = "name" in fields ? "name" : "title" in fields ? "title" : primaryKey;
2899
+ return data.items.map((item) => {
2900
+ const slug = String(item[primaryKey] ?? "");
2901
+ const labelRaw = item[displayField];
2902
+ return {
2903
+ slug,
2904
+ display: typeof labelRaw === "string" && labelRaw.length > 0 ? labelRaw : slug
2905
+ };
2906
+ }).filter((opt) => opt.slug.length > 0).sort((left, right) => left.display.localeCompare(right.display));
2907
+ }
2908
+ /** The target record id an embed points at: its fixed `id`, or the
2909
+ * value of the sibling `idField` on the open record (per-record). */
2910
+ function embedTargetId(field, record) {
2911
+ if (field.id) return field.id;
2912
+ if (field.idField && record) return String(record[field.idField] ?? "");
2913
+ return "";
2914
+ }
2915
+ function resolveEmbed(field, record) {
2916
+ if (field.type !== "embed" || !field.to) return {
2860
2917
  schema: null,
2861
2918
  item: null
2862
2919
  };
2863
- const data = embedCache.value[field.to];
2920
+ const targetId = embedTargetId(field, record);
2921
+ const data = targetId ? embedCache.value[field.to] : void 0;
2864
2922
  if (!data) return {
2865
2923
  schema: null,
2866
2924
  item: null
2867
2925
  };
2868
- const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === field.id) ?? null;
2926
+ const item = data.items.find((entry) => String(entry[data.schema.primaryKey] ?? "") === targetId) ?? null;
2869
2927
  return {
2870
2928
  schema: data.schema,
2871
2929
  item
@@ -2875,12 +2933,15 @@ function useCollectionRendering(collection, locale) {
2875
2933
  if (field.type === "money") return formatMoney(value, resolveCurrency(field, record), locale.value);
2876
2934
  return detailText(value);
2877
2935
  }
2878
- const embedViews = (0, vue.computed)(() => {
2936
+ /** Build the read-only embed view-models for one record. A function of
2937
+ * the open record (not a bare computed) because a per-record `idField`
2938
+ * embed resolves a different target per row. */
2939
+ function embedViewsFor(record) {
2879
2940
  const out = {};
2880
2941
  if (!collection.value) return out;
2881
2942
  for (const [key, field] of Object.entries(collection.value.schema.fields)) {
2882
2943
  if (field.type !== "embed") continue;
2883
- const { schema, item } = resolveEmbed(field);
2944
+ const { schema, item } = resolveEmbed(field, record);
2884
2945
  const rows = [];
2885
2946
  if (schema && item) for (const [subKey, subField] of Object.entries(schema.fields)) {
2886
2947
  const value = item[subKey];
@@ -2897,11 +2958,11 @@ function useCollectionRendering(collection, locale) {
2897
2958
  found: Boolean(item),
2898
2959
  rows,
2899
2960
  targetSlug: field.to ?? "",
2900
- recordId: field.id ?? ""
2961
+ recordId: embedTargetId(field, record)
2901
2962
  };
2902
2963
  }
2903
2964
  return out;
2904
- });
2965
+ }
2905
2966
  function resolveCurrency(field, record) {
2906
2967
  if (field.currencyField && record) {
2907
2968
  const code = record[field.currencyField];
@@ -2992,7 +3053,8 @@ function useCollectionRendering(collection, locale) {
2992
3053
  loadLinkedCollections,
2993
3054
  refDisplay,
2994
3055
  refOptions,
2995
- embedViews,
3056
+ embedOptions,
3057
+ embedViewsFor,
2996
3058
  resolveCurrency,
2997
3059
  currencySymbol,
2998
3060
  formatMoney,