@stonecrop/desktop 0.25.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/desktop.js CHANGED
@@ -1,6 +1,6 @@
1
- import { defineComponent as Q, ref as M, onMounted as de, openBlock as h, createElementBlock as g, normalizeClass as O, createElementVNode as c, Fragment as W, renderList as q, toDisplayString as V, createCommentVNode as K, withDirectives as G, vShow as oe, useTemplateRef as ve, computed as $, watch as H, nextTick as pe, createBlock as fe, Teleport as Ve, createVNode as L, Transition as Ee, withCtx as z, withModifiers as me, vModelText as he, renderSlot as Y, createTextVNode as J, resolveComponent as Be, withKeys as j, normalizeStyle as Z, unref as ee, provide as te, onUnmounted as Fe } from "vue";
2
- import { useStonecrop as Oe, useValidationStore as Le, isDraftRecordId as Pe, DRAFT_RECORD_ID as je } from "@stonecrop/stonecrop";
3
- import { AForm as Ke } from "@stonecrop/aform";
1
+ import { defineComponent as Q, ref as M, onMounted as ue, openBlock as y, createElementBlock as g, normalizeClass as O, createElementVNode as c, Fragment as W, renderList as q, toDisplayString as V, createCommentVNode as K, withDirectives as G, vShow as oe, useTemplateRef as de, computed as $, watch as H, nextTick as ve, createBlock as pe, Teleport as Te, createVNode as L, Transition as Ve, withCtx as z, withModifiers as fe, vModelText as me, renderSlot as Y, createTextVNode as J, resolveComponent as Ee, withKeys as j, normalizeStyle as Z, unref as ee, provide as te, onUnmounted as Be } from "vue";
2
+ import { useStonecrop as Fe, useValidationStore as Oe, isDraftRecordId as Le, DRAFT_RECORD_ID as Pe } from "@stonecrop/stonecrop";
3
+ import { AForm as je, resolvedFieldsToColumns as Ke } from "@stonecrop/aform";
4
4
  import { flattenFields as He, componentLinkExpansion as ze } from "@stonecrop/schema";
5
5
  const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header" }, qe = ["disabled", "onClick"], Ge = { key: 0 }, Je = { class: "dropdown-header" }, Qe = ["onClick"], Xe = ["onClick"], Ye = { class: "dropdown-container" }, Ze = { class: "dropdown" }, et = ["onClick"], tt = ["href"], ot = { class: "dropdown-item" }, at = /* @__PURE__ */ Q({
6
6
  __name: "ActionSet",
@@ -10,7 +10,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
10
10
  emits: ["actionClick"],
11
11
  setup(l, { emit: S }) {
12
12
  const D = S, w = M({}), d = M(!0), o = M([]);
13
- de(() => {
13
+ ue(() => {
14
14
  p();
15
15
  });
16
16
  const p = () => {
@@ -23,7 +23,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
23
23
  }, C = (i, f) => {
24
24
  D("actionClick", f, i);
25
25
  };
26
- return (i, f) => (h(), g("div", {
26
+ return (i, f) => (y(), g("div", {
27
27
  class: O([{ collapsed: !d.value }, "action-set"])
28
28
  }, [
29
29
  c("div", Ue, [
@@ -34,19 +34,19 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
34
34
  }, "×", 2)
35
35
  ]),
36
36
  f[0] || (f[0] = c("div", { style: { "margin-right": "30px" } }, null, -1)),
37
- (h(!0), g(W, null, q(l.elements, (s, r) => (h(), g("div", {
37
+ (y(!0), g(W, null, q(l.elements, (s, r) => (y(), g("div", {
38
38
  key: s.label,
39
39
  class: "action-element"
40
40
  }, [
41
41
  c("div", We, [
42
- s.type == "button" ? (h(), g("button", {
42
+ s.type == "button" ? (y(), g("button", {
43
43
  key: 0,
44
44
  disabled: s.disabled,
45
45
  class: "button-default",
46
46
  onClick: (u) => C(s.action, s.label)
47
47
  }, V(s.label), 9, qe)) : K("", !0)
48
48
  ]),
49
- s.type == "dropdown" ? (h(), g("div", Ge, [
49
+ s.type == "dropdown" ? (y(), g("div", Ge, [
50
50
  c("div", Je, [
51
51
  c("div", {
52
52
  class: O(["cross", { rotated: o.value[r] }]),
@@ -59,14 +59,14 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
59
59
  ]),
60
60
  G(c("div", Ye, [
61
61
  c("div", Ze, [
62
- (h(!0), g(W, null, q(s.actions, (u) => (h(), g("div", {
62
+ (y(!0), g(W, null, q(s.actions, (u) => (y(), g("div", {
63
63
  key: u.label
64
64
  }, [
65
- u.action != null ? (h(), g("button", {
65
+ u.action != null ? (y(), g("button", {
66
66
  key: 0,
67
67
  class: "dropdown-item",
68
68
  onClick: (n) => C(u.action, u.label)
69
- }, V(u.label), 9, et)) : u.link != null ? (h(), g("a", {
69
+ }, V(u.label), 9, et)) : u.link != null ? (y(), g("a", {
70
70
  key: 1,
71
71
  href: u.link
72
72
  }, [
@@ -86,13 +86,13 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
86
86
  for (const [w, d] of S)
87
87
  D[w] = d;
88
88
  return D;
89
- }, ge = /* @__PURE__ */ ye(at, [["__scopeId", "data-v-54797d4b"]]), nt = { class: "command-palette-header" }, rt = ["placeholder"], lt = {
89
+ }, he = /* @__PURE__ */ ye(at, [["__scopeId", "data-v-54797d4b"]]), nt = { class: "command-palette-header" }, rt = ["placeholder"], lt = {
90
90
  key: 0,
91
91
  class: "command-palette-results"
92
92
  }, st = ["onClick", "onMouseover"], ct = { class: "result-title" }, it = { class: "result-content" }, ut = {
93
93
  key: 1,
94
94
  class: "command-palette-no-results"
95
- }, we = /* @__PURE__ */ Q({
95
+ }, ge = /* @__PURE__ */ Q({
96
96
  __name: "CommandPalette",
97
97
  props: {
98
98
  search: { type: Function },
@@ -102,11 +102,11 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
102
102
  },
103
103
  emits: ["select", "close"],
104
104
  setup(l, { emit: S }) {
105
- const D = S, w = M(""), d = M(0), o = ve("input"), p = $(() => w.value ? l.search(w.value).slice(0, l.maxResults) : []);
105
+ const D = S, w = M(""), d = M(0), o = de("input"), p = $(() => w.value ? l.search(w.value).slice(0, l.maxResults) : []);
106
106
  H(
107
107
  () => l.isOpen,
108
108
  async (i) => {
109
- i && (w.value = "", d.value = 0, await pe(), o.value?.focus());
109
+ i && (w.value = "", d.value = 0, await ve(), o.value?.focus());
110
110
  }
111
111
  ), H(p, () => {
112
112
  d.value = 0;
@@ -131,17 +131,17 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
131
131
  }, C = (i) => {
132
132
  D("select", i), _();
133
133
  };
134
- return (i, f) => (h(), fe(Ve, { to: "body" }, [
135
- L(Ee, { name: "fade" }, {
134
+ return (i, f) => (y(), pe(Te, { to: "body" }, [
135
+ L(Ve, { name: "fade" }, {
136
136
  default: z(() => [
137
- l.isOpen ? (h(), g("div", {
137
+ l.isOpen ? (y(), g("div", {
138
138
  key: 0,
139
139
  class: "command-palette-overlay",
140
140
  onClick: _
141
141
  }, [
142
142
  c("div", {
143
143
  class: "command-palette",
144
- onClick: f[1] || (f[1] = me(() => {
144
+ onClick: f[1] || (f[1] = fe(() => {
145
145
  }, ["stop"]))
146
146
  }, [
147
147
  c("div", nt, [
@@ -154,11 +154,11 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
154
154
  autofocus: "",
155
155
  onKeydown: T
156
156
  }, null, 40, rt), [
157
- [he, w.value]
157
+ [me, w.value]
158
158
  ])
159
159
  ]),
160
- p.value.length ? (h(), g("div", lt, [
161
- (h(!0), g(W, null, q(p.value, (s, r) => (h(), g("div", {
160
+ p.value.length ? (y(), g("div", lt, [
161
+ (y(!0), g(W, null, q(p.value, (s, r) => (y(), g("div", {
162
162
  key: r,
163
163
  class: O(["command-palette-result", { selected: r === d.value }]),
164
164
  onClick: (u) => C(s),
@@ -171,7 +171,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
171
171
  Y(i.$slots, "content", { result: s })
172
172
  ])
173
173
  ], 42, st))), 128))
174
- ])) : w.value && !p.value.length ? (h(), g("div", ut, [
174
+ ])) : w.value && !p.value.length ? (y(), g("div", ut, [
175
175
  Y(i.$slots, "empty", {}, () => [
176
176
  J(' No results found for "' + V(w.value) + '" ', 1)
177
177
  ])
@@ -189,10 +189,10 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
189
189
  breadcrumbs: { default: () => [] }
190
190
  },
191
191
  setup(l) {
192
- const S = M(!0), D = M(!1), w = M(""), d = ve("searchinput"), o = $(() => S.value ? "unrotated" : "rotated"), p = () => {
192
+ const S = M(!0), D = M(!1), w = M(""), d = de("searchinput"), o = $(() => S.value ? "unrotated" : "rotated"), p = () => {
193
193
  S.value = !S.value;
194
194
  }, _ = async () => {
195
- D.value = !D.value, await pe(() => {
195
+ D.value = !D.value, await ve(() => {
196
196
  d.value?.focus();
197
197
  });
198
198
  }, T = (f) => {
@@ -202,8 +202,8 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
202
202
  }, i = () => {
203
203
  };
204
204
  return (f, s) => {
205
- const r = Be("router-link");
206
- return h(), g("footer", null, [
205
+ const r = Ee("router-link");
206
+ return y(), g("footer", null, [
207
207
  c("ul", dt, [
208
208
  c("li", {
209
209
  class: "hidebreadcrumbs",
@@ -247,7 +247,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
247
247
  style: Z({ display: S.value ? "block" : "none" })
248
248
  }, [
249
249
  c("a", pt, [
250
- G((h(), g("svg", {
250
+ G((y(), g("svg", {
251
251
  class: "icon search-icon",
252
252
  role: "button",
253
253
  "aria-label": "Search",
@@ -272,7 +272,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
272
272
  "onUpdate:modelValue": s[0] || (s[0] = (u) => w.value = u),
273
273
  type: "text",
274
274
  placeholder: "Search...",
275
- onClick: s[1] || (s[1] = me(() => {
275
+ onClick: s[1] || (s[1] = fe(() => {
276
276
  }, ["stop"])),
277
277
  onInput: s[2] || (s[2] = (u) => T(u)),
278
278
  onBlur: s[3] || (s[3] = (u) => C(u)),
@@ -282,11 +282,11 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
282
282
  ]
283
283
  }, null, 544), [
284
284
  [oe, D.value],
285
- [he, w.value]
285
+ [me, w.value]
286
286
  ])
287
287
  ])
288
288
  ], 6),
289
- (h(!0), g(W, null, q(l.breadcrumbs, (u) => (h(), g("li", {
289
+ (y(!0), g(W, null, q(l.breadcrumbs, (u) => (y(), g("li", {
290
290
  key: u.title,
291
291
  style: Z({ display: S.value ? "block" : "none" })
292
292
  }, [
@@ -304,13 +304,13 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
304
304
  ]);
305
305
  };
306
306
  }
307
- }), be = /* @__PURE__ */ ye(ft, [["__scopeId", "data-v-0072260b"]]), mt = {
307
+ }), we = /* @__PURE__ */ ye(ft, [["__scopeId", "data-v-0072260b"]]), mt = {
308
308
  key: 1,
309
309
  class: "loading"
310
- }, ht = {
310
+ }, yt = {
311
311
  key: 2,
312
312
  class: "loading"
313
- }, yt = /* @__PURE__ */ Q({
313
+ }, ht = /* @__PURE__ */ Q({
314
314
  __name: "Desktop",
315
315
  props: {
316
316
  availableDoctypes: { default: () => [] },
@@ -327,10 +327,10 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
327
327
  function w(e) {
328
328
  return e !== null && typeof e == "object" && !Array.isArray(e) && "id" in e ? e.id : e;
329
329
  }
330
- const d = S, { stonecrop: o } = Oe();
330
+ const d = S, { stonecrop: o } = Fe();
331
331
  let p = null;
332
332
  try {
333
- p = Le();
333
+ p = Oe();
334
334
  } catch {
335
335
  p = null;
336
336
  }
@@ -349,9 +349,9 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
349
349
  };
350
350
  if (x.value === "records")
351
351
  return {
352
- records_table: se().map(
352
+ records_table: le().map(
353
353
  (e) => Object.assign({}, e, {
354
- id: ie(e) ?? "",
354
+ id: ce(e) ?? "",
355
355
  // A list row is navigation. Actions live on the record view, where the Actions
356
356
  // dropdown is built from what the doctype declares and what the record's
357
357
  // current state allows. This cell used to also offer Delete, which dispatched
@@ -362,10 +362,10 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
362
362
  })
363
363
  )
364
364
  };
365
- if (!o.value || !n.value || !y.value)
365
+ if (!o.value || !n.value || !h.value)
366
366
  return {};
367
367
  try {
368
- const t = { ...B.value ? i.value : o.value.getRecordById(n.value, y.value)?.get("") }, a = o.value.registry.registry[n.value];
368
+ const t = { ...B.value ? i.value : o.value.getRecordById(n.value, h.value)?.get("") }, a = o.value.registry.registry[n.value];
369
369
  if (a) {
370
370
  for (const m of a.getSchemaArray())
371
371
  if (m.kind === "fieldset") {
@@ -381,7 +381,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
381
381
  }
382
382
  },
383
383
  set(e) {
384
- if (x.value === "record" && !(!o.value || !n.value || !y.value))
384
+ if (x.value === "record" && !(!o.value || !n.value || !h.value))
385
385
  try {
386
386
  const t = o.value.registry.registry[n.value], a = /* @__PURE__ */ new Set();
387
387
  if (t)
@@ -405,7 +405,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
405
405
  const k = o.value.getStore();
406
406
  for (const [b, N] of Object.entries(v)) {
407
407
  if (N === void 0) continue;
408
- const P = m.has(b) ? w(N) : N, X = `${n.value}.${y.value}.${b}`;
408
+ const P = m.has(b) ? w(N) : N, X = `${n.value}.${h.value}.${b}`;
409
409
  (k.has(X) ? k.get(X) : void 0) !== P && (k.set(X, P), I.push(b));
410
410
  }
411
411
  }
@@ -416,11 +416,11 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
416
416
  }
417
417
  });
418
418
  function s(e) {
419
- if (!p || !o.value || !n.value || !y.value) return;
419
+ if (!p || !o.value || !n.value || !h.value) return;
420
420
  const a = o.value.registry.getDoctype(n.value)?.getTriggers();
421
421
  if (!a || Object.keys(a).length === 0) return;
422
422
  const m = B.value ? { ...i.value } : {
423
- ...o.value.getRecordById(n.value, y.value)?.get("")
423
+ ...o.value.getRecordById(n.value, h.value)?.get("")
424
424
  };
425
425
  for (const v of e)
426
426
  p.validateField(a, v, m);
@@ -443,14 +443,14 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
443
443
  return r.value.params.doctype.toString();
444
444
  const e = r.value.params.pathMatch;
445
445
  return e && e.length > 0 ? e[0] : "";
446
- }), y = $(() => {
446
+ }), h = $(() => {
447
447
  if (l.routeAdapter) return l.routeAdapter.getCurrentRecordId();
448
448
  if (!r.value) return "";
449
449
  if (r.value.params.recordId)
450
450
  return r.value.params.recordId.toString();
451
451
  const e = r.value.params.pathMatch;
452
452
  return e && e.length > 1 ? e[1] : "";
453
- }), B = $(() => Pe(y.value)), x = $(() => {
453
+ }), B = $(() => Le(h.value)), x = $(() => {
454
454
  if (l.routeAdapter) return l.routeAdapter.getCurrentView();
455
455
  if (!r.value || r.value.name === "home" || r.value.path === "/")
456
456
  return "doctypes";
@@ -463,13 +463,13 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
463
463
  }
464
464
  const e = r.value.params.pathMatch;
465
465
  return e && e.length > 0 ? e.length === 1 ? "records" : "record" : "doctypes";
466
- }), ke = () => {
467
- if (!o.value || !n.value || !y.value)
466
+ }), be = () => {
467
+ if (!o.value || !n.value || !h.value)
468
468
  return [];
469
469
  try {
470
470
  const e = o.value.registry.getDoctype(n.value);
471
471
  if (!e?.workflow) return [];
472
- const t = o.value.getRecordState(n.value, y.value), a = e.getAvailableTransitions(t), m = f.value || {};
472
+ const t = o.value.getRecordState(n.value, h.value), a = e.getAvailableTransitions(t), m = f.value || {};
473
473
  return a.map(({ name: v }) => ({
474
474
  // Prefer the workflow action's human-readable label (WorkflowMeta format);
475
475
  // fall back to the raw transition name for XState workflows with no action meta.
@@ -478,7 +478,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
478
478
  d("action", {
479
479
  name: v,
480
480
  doctype: n.value,
481
- recordId: y.value,
481
+ recordId: h.value,
482
482
  data: m
483
483
  });
484
484
  }
@@ -486,20 +486,20 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
486
486
  } catch (e) {
487
487
  return console.warn("Error getting available transitions:", e), [];
488
488
  }
489
- }, Ce = () => {
490
- if (!o.value || !n.value || !y.value)
489
+ }, ke = () => {
490
+ if (!o.value || !n.value || !h.value)
491
491
  return [];
492
492
  try {
493
493
  const e = o.value.registry.getDoctype(n.value);
494
494
  if (!e?.workflow) return [];
495
- const t = o.value.getRecordState(n.value, y.value), a = e.getAvailableCommands(t), m = f.value || {};
495
+ const t = o.value.getRecordState(n.value, h.value), a = e.getAvailableCommands(t), m = f.value || {};
496
496
  return a.map(({ name: v }) => ({
497
497
  label: e.getActionMeta(v)?.label ?? v,
498
498
  action: () => {
499
499
  d("action", {
500
500
  name: v,
501
501
  doctype: n.value,
502
- recordId: y.value,
502
+ recordId: h.value,
503
503
  data: m
504
504
  });
505
505
  }
@@ -507,7 +507,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
507
507
  } catch (e) {
508
508
  return console.warn("Error getting available commands:", e), [];
509
509
  }
510
- }, Ae = $(() => {
510
+ }, Ce = $(() => {
511
511
  const e = [];
512
512
  switch (x.value) {
513
513
  case "records":
@@ -520,7 +520,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
520
520
  });
521
521
  break;
522
522
  case "record": {
523
- const t = [...ke(), ...Ce()];
523
+ const t = [...be(), ...ke()];
524
524
  t.length > 0 && e.push({
525
525
  type: "dropdown",
526
526
  label: "Actions",
@@ -530,7 +530,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
530
530
  }
531
531
  }
532
532
  return e;
533
- }), De = $(() => {
533
+ }), Ae = $(() => {
534
534
  const e = [];
535
535
  if (x.value === "records" && R.value)
536
536
  e.push(
@@ -538,7 +538,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
538
538
  { title: F(R.value), to: `/${R.value}` }
539
539
  );
540
540
  else if (x.value === "record" && R.value) {
541
- const t = y.value ? `/${R.value}/${y.value}` : r.value?.fullPath ?? "";
541
+ const t = h.value ? `/${R.value}/${h.value}` : r.value?.fullPath ?? "";
542
542
  e.push(
543
543
  { title: "Home", to: "/" },
544
544
  { title: F(R.value), to: `/${R.value}` },
@@ -546,7 +546,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
546
546
  );
547
547
  }
548
548
  return e;
549
- }), Re = (e) => {
549
+ }), De = (e) => {
550
550
  const t = [
551
551
  {
552
552
  title: "Go Home",
@@ -584,9 +584,9 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
584
584
  }), e ? t.filter(
585
585
  (a) => a.title.toLowerCase().includes(e.toLowerCase()) || a.description.toLowerCase().includes(e.toLowerCase())
586
586
  ) : t;
587
- }, Se = (e) => {
587
+ }, Re = (e) => {
588
588
  e.action(), C.value = !1;
589
- }, $e = (e) => {
589
+ }, Se = (e) => {
590
590
  if (!o.value || !n.value)
591
591
  return Promise.resolve({ data: [], hasMore: !1 });
592
592
  const t = o.value.registry.getDoctype(n.value);
@@ -599,13 +599,8 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
599
599
  const t = R.value;
600
600
  d("record:open", { doctype: t, recordId: e }), await E({ view: "record", doctype: t, recordId: e });
601
601
  }, U = async () => {
602
- await E({ view: "record", doctype: R.value, recordId: je });
603
- }, re = (e) => {
604
- const t = [];
605
- for (const a of e)
606
- a.kind === "fieldset" ? t.push(...re(a.schema)) : t.push(a);
607
- return t;
608
- }, Ie = () => l.availableDoctypes?.length ? [
602
+ await E({ view: "record", doctype: R.value, recordId: Pe });
603
+ }, $e = () => l.availableDoctypes?.length ? [
609
604
  {
610
605
  kind: "table",
611
606
  fieldname: "doctypes_table",
@@ -643,7 +638,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
643
638
  ],
644
639
  config: { view: "list", fullWidth: !0 }
645
640
  }
646
- ] : [], _e = () => {
641
+ ] : [], Ie = () => {
647
642
  if (!n.value) return [];
648
643
  if (!o.value) return [];
649
644
  const e = o.value.registry, t = e.registry[n.value];
@@ -656,15 +651,15 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
656
651
  kind: "table",
657
652
  fieldname: "records_table",
658
653
  component: "ATable",
659
- schema: [
660
- ...re(a),
661
- { fieldname: "actions", label: "Actions", component: "ATextInput" }
662
- ],
654
+ // Which resolved fields a cell can render is answered once, in @stonecrop/aform, and
655
+ // called by the child-table builder too. Flattening alone is not the rule: it kept the
656
+ // expanding links, whose value is a nested record or an array of them.
657
+ schema: [...Ke(a), { fieldname: "actions", label: "Actions", component: "ATextInput" }],
663
658
  config: { view: "list", fullWidth: !0 },
664
- ...v ? { getRecords: $e, sourceKey: m } : {}
659
+ ...v ? { getRecords: Se, sourceKey: m } : {}
665
660
  }
666
661
  ];
667
- }, le = () => {
662
+ }, re = () => {
668
663
  if (!n.value) return [];
669
664
  if (!o.value) return [];
670
665
  try {
@@ -673,37 +668,37 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
673
668
  } catch {
674
669
  return [];
675
670
  }
676
- }, se = () => {
671
+ }, le = () => {
677
672
  if (!o.value || !n.value)
678
673
  return [];
679
674
  const t = o.value.records(n.value)?.get("");
680
675
  return t && typeof t == "object" && !Array.isArray(t) ? Object.values(t) : [];
681
- }, ce = $(() => {
676
+ }, se = $(() => {
682
677
  switch (x.value) {
683
678
  case "doctypes":
684
- return Ie();
679
+ return $e();
685
680
  case "records":
686
- return _e();
681
+ return Ie();
687
682
  case "record":
688
- return le();
683
+ return re();
689
684
  default:
690
685
  return [];
691
686
  }
692
- }), xe = (e, t) => {
687
+ }), _e = (e, t) => {
693
688
  t && t();
694
- }, ie = (e) => {
689
+ }, ce = (e) => {
695
690
  if (!(!o.value || !n.value))
696
691
  return o.value.registry.registry[n.value]?.getRecordId(e);
697
- }, Ne = (e) => {
692
+ }, xe = (e) => {
698
693
  const t = e.querySelector("td[data-rowindex]");
699
694
  if (!t) return null;
700
695
  const a = t.getAttribute("data-rowindex");
701
696
  if (a === null) return null;
702
697
  const m = parseInt(a, 10);
703
698
  if (isNaN(m)) return null;
704
- const A = se()[m];
705
- return A ? ie(A) ?? null : null;
706
- }, Me = async (e) => {
699
+ const A = le()[m];
700
+ return A ? ce(A) ?? null : null;
701
+ }, Ne = async (e) => {
707
702
  const t = e.target;
708
703
  t.getAttribute("data-action") === "create" && await U();
709
704
  const m = t.closest("td, th");
@@ -716,15 +711,15 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
716
711
  b && await ae(b);
717
712
  }
718
713
  } else if (v === "Edit" && A) {
719
- const I = Ne(A);
714
+ const I = xe(A);
720
715
  I && await ne(I);
721
716
  }
722
717
  }
723
- }, Te = async () => {
718
+ }, Me = async () => {
724
719
  if (!(!o.value || !n.value || !o.value.getClient())) {
725
720
  T.value = !0;
726
721
  try {
727
- await o.value.getRecord(n.value, y.value);
722
+ await o.value.getRecord(n.value, h.value);
728
723
  } catch (e) {
729
724
  console.warn("Error fetching record:", e);
730
725
  } finally {
@@ -733,27 +728,27 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
733
728
  }
734
729
  };
735
730
  H(
736
- [x, n, y],
731
+ [x, n, h],
737
732
  () => {
738
733
  if (x.value === "records" && n.value)
739
734
  d("load-records", { doctype: n.value });
740
- else if (x.value === "record" && n.value && y.value) {
735
+ else if (x.value === "record" && n.value && h.value) {
741
736
  if (B.value) return;
742
- d("load-record", { doctype: n.value, recordId: y.value }), Te();
737
+ d("load-record", { doctype: n.value, recordId: h.value }), Me();
743
738
  }
744
739
  },
745
740
  { immediate: !0 }
746
- ), H([n, y], () => {
741
+ ), H([n, h], () => {
747
742
  p?.clearAll();
748
743
  }), H(
749
- [n, y],
744
+ [n, h],
750
745
  () => {
751
746
  if (!B.value) {
752
747
  i.value = {};
753
748
  return;
754
749
  }
755
750
  const e = o.value?.registry;
756
- i.value = e ? e.initializeRecord(le()) : {};
751
+ i.value = e ? e.initializeRecord(re()) : {};
757
752
  },
758
753
  { immediate: !0 }
759
754
  ), te("desktopMethods", {
@@ -768,7 +763,7 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
768
763
  d("action", {
769
764
  name: e,
770
765
  doctype: n.value,
771
- recordId: y.value,
766
+ recordId: h.value,
772
767
  data: t ?? f.value ?? {}
773
768
  });
774
769
  }
@@ -794,38 +789,38 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
794
789
  return;
795
790
  }
796
791
  });
797
- const ue = (e) => {
792
+ const ie = (e) => {
798
793
  (e.ctrlKey || e.metaKey) && e.key === "k" && (e.preventDefault(), C.value = !0), e.key === "Escape" && C.value && (C.value = !1);
799
794
  };
800
- return de(() => {
801
- document.addEventListener("keydown", ue);
802
- }), Fe(() => {
803
- document.removeEventListener("keydown", ue);
804
- }), (e, t) => (h(), g("div", {
795
+ return ue(() => {
796
+ document.addEventListener("keydown", ie);
797
+ }), Be(() => {
798
+ document.removeEventListener("keydown", ie);
799
+ }), (e, t) => (y(), g("div", {
805
800
  class: "desktop",
806
- onClick: Me
801
+ onClick: Ne
807
802
  }, [
808
- L(ge, {
809
- elements: Ae.value,
810
- onActionClick: xe
803
+ L(he, {
804
+ elements: Ce.value,
805
+ onActionClick: _e
811
806
  }, null, 8, ["elements"]),
812
- ce.value.length > 0 ? (h(), fe(ee(Ke), {
807
+ se.value.length > 0 ? (y(), pe(ee(je), {
813
808
  key: 0,
814
809
  data: f.value,
815
810
  "onUpdate:data": t[0] || (t[0] = (a) => f.value = a),
816
- schema: ce.value,
811
+ schema: se.value,
817
812
  errors: _.value
818
- }, null, 8, ["data", "schema", "errors"])) : ee(o) ? (h(), g("div", ht, [
813
+ }, null, 8, ["data", "schema", "errors"])) : ee(o) ? (y(), g("div", yt, [
819
814
  c("p", null, "Loading " + V(x.value) + " data...", 1)
820
- ])) : (h(), g("div", mt, [...t[2] || (t[2] = [
815
+ ])) : (y(), g("div", mt, [...t[2] || (t[2] = [
821
816
  c("p", null, "Initializing Stonecrop...", -1)
822
817
  ])])),
823
- L(be, { breadcrumbs: De.value }, null, 8, ["breadcrumbs"]),
824
- L(we, {
818
+ L(we, { breadcrumbs: Ae.value }, null, 8, ["breadcrumbs"]),
819
+ L(ge, {
825
820
  "is-open": C.value,
826
- search: Re,
821
+ search: De,
827
822
  placeholder: "Type a command or search...",
828
- onSelect: Se,
823
+ onSelect: Re,
829
824
  onClose: t[1] || (t[1] = (a) => C.value = !1)
830
825
  }, {
831
826
  title: z(({ result: a }) => [
@@ -840,14 +835,14 @@ const Ue = { class: "action-menu-icon" }, We = { class: "action-element-header"
840
835
  }
841
836
  }), Dt = {
842
837
  install: (l) => {
843
- l.component("ActionSet", ge), l.component("CommandPalette", we), l.component("Desktop", yt), l.component("SheetNav", be);
838
+ l.component("ActionSet", he), l.component("CommandPalette", ge), l.component("Desktop", ht), l.component("SheetNav", we);
844
839
  }
845
840
  };
846
841
  export {
847
- ge as ActionSet,
848
- we as CommandPalette,
849
- yt as Desktop,
850
- be as SheetNav,
842
+ he as ActionSet,
843
+ ge as CommandPalette,
844
+ ht as Desktop,
845
+ we as SheetNav,
851
846
  Dt as StonecropDesktop
852
847
  };
853
848
  //# sourceMappingURL=desktop.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"desktop.js","sources":["../src/components/ActionSet.vue","../src/components/CommandPalette.vue","../src/components/SheetNav.vue","../src/components/Desktop.vue","../src/plugins/index.ts"],"sourcesContent":["<template>\n\t<div :class=\"{ collapsed: !isOpen }\" class=\"action-set\">\n\t\t<div class=\"action-menu-icon\">\n\t\t\t<div id=\"cross\" :class=\"{ rotated: isOpen }\" @click=\"onClick\">×</div>\n\t\t</div>\n\t\t<div style=\"margin-right: 30px\"></div>\n\t\t<div v-for=\"(el, index) in elements\" :key=\"el.label\" class=\"action-element\">\n\t\t\t<div class=\"action-element-header\">\n\t\t\t\t<button\n\t\t\t\t\tv-if=\"el.type == 'button'\"\n\t\t\t\t\t:disabled=\"el.disabled\"\n\t\t\t\t\tclass=\"button-default\"\n\t\t\t\t\t@click=\"handleClick(el.action, el.label)\">\n\t\t\t\t\t{{ el.label }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div v-if=\"el.type == 'dropdown'\">\n\t\t\t\t<div class=\"dropdown-header\">\n\t\t\t\t\t<div class=\"cross\" :class=\"{ rotated: dropdownOpen[index] }\" @click=\"toggleDropdown(index)\">×</div>\n\t\t\t\t\t<button class=\"button-default dropdown-title\" @click=\"toggleDropdown(index)\">\n\t\t\t\t\t\t{{ el.label }}\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t\t<div v-show=\"dropdownStates[index]\" class=\"dropdown-container\">\n\t\t\t\t\t<div class=\"dropdown\">\n\t\t\t\t\t\t<div v-for=\"item in el.actions\" :key=\"item.label\">\n\t\t\t\t\t\t\t<button v-if=\"item.action != null\" class=\"dropdown-item\" @click=\"handleClick(item.action, item.label)\">\n\t\t\t\t\t\t\t\t{{ item.label }}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<a v-else-if=\"item.link != null\" :href=\"item.link\"\n\t\t\t\t\t\t\t\t><button class=\"dropdown-item\">{{ item.label }}</button></a\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, ref } from 'vue'\n\nimport type { ActionElements } from '../types'\n\nconst { elements = [] } = defineProps<{ elements?: ActionElements[] }>()\nconst emit = defineEmits<{\n\tactionClick: [label: string, action: (() => void | Promise<void>) | undefined]\n}>()\n\n// Track dropdown open state separately (index -> boolean)\nconst dropdownStates = ref<Record<number, boolean>>({})\n\nconst isOpen = ref(true)\nconst dropdownOpen = ref<boolean[]>([])\n\nonMounted(() => {\n\tcloseDropdowns()\n})\n\nconst closeDropdowns = () => {\n\tdropdownStates.value = {}\n\tdropdownOpen.value = []\n}\n\nconst onClick = () => {\n\tisOpen.value = !isOpen.value\n}\n\nconst toggleDropdown = (index: number) => {\n\tconst showDropdown = !dropdownStates.value[index]\n\tcloseDropdowns()\n\tif (showDropdown) {\n\t\tdropdownStates.value[index] = true\n\t\tdropdownOpen.value[index] = true\n\t}\n}\n\nconst handleClick = (action: (() => void | Promise<void>) | undefined, label: string) => {\n\t// Emit event to parent - parent will handle execution\n\temit('actionClick', label, action)\n}\n</script>\n\n<style scoped>\n#cross {\n\tposition: relative;\n\ttransform: rotate(45deg);\n\tcursor: pointer;\n\ttransition: all 0.2s ease-in-out;\n\tuser-select: none;\n\tline-height: 1rem;\n}\n#cross.rotated,\n.cross.rotated {\n\ttransform: rotate(0deg);\n}\n#cross svg {\n\twidth: 1.5em;\n\theight: 1.5em;\n}\n\n.action-set {\n\tposition: fixed;\n\ttop: 300px;\n\tright: 10px;\n\tpadding: 10px;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-end;\n\tbackground: var(--sc-form-background);\n\tborder: 1px solid var(--sc-gray-20);\n\tborder-left: 4px solid var(--sc-gray-20);\n\tborder-radius: 0;\n\toverflow: hidden;\n\tz-index: 1001; /* Above SheetNav (100) and operation log button (999) */\n\t-webkit-transition: all 0.5s ease-in-out;\n\t-moz-transition: all 0.5s ease-in-out;\n\t-o-transition: all 0.5s ease-in-out;\n\ttransition: all 0.5s ease-in-out;\n}\n.action-menu-icon {\n\tposition: relative;\n\tfont-size: 2rem;\n\tdisplay: inline-block;\n\tcolor: var(--sc-gray-60, #666);\n\ttransition: all 0.2s ease-in-out;\n}\n.action-set.collapsed {\n\tmax-width: 46px;\n\tmax-height: 40px;\n\toverflow: hidden;\n}\n.action-set.collapsed .action-element {\n\topacity: 0;\n\t-webkit-transition: opacity 0.25s ease-in-out;\n\t-moz-transition: opacity 0.25s ease-in-out;\n\t-o-transition: opacity 0.25s ease-in-out;\n\ttransition: opacity 0.25s ease-in-out;\n}\n\n.action-element {\n\twidth: 100%;\n\ttext-align: right;\n\tborder: 1px solid var(--sc-gray-20);\n\tbackground: none;\n\tfont-size: 1.5rem;\n\tfont-family: var(--sc-font-family);\n\tfont-weight: 600;\n\tmargin-top: 10px;\n\tposition: relative; /* Make this the positioning context for absolute children */\n}\n.action-element-header {\n\tdisplay: flex;\n\tjustify-content: end;\n}\nbutton.button-default {\n\tbackground-color: transparent;\n\tpadding: 5px 12px;\n\tborder-radius: 0px;\n\tbox-shadow: none;\n\tborder: none;\n\tcursor: pointer;\n\twhite-space: nowrap;\n\tfont-weight: bold;\n\tfont-size: 1rem;\n\ttext-align: right;\n\tcolor: var(--sc-gray-80);\n\tpadding-left: 50px;\n\tfont-family: var(--sc-font-family);\n}\n.dropdown-header:hover button.button-default,\n.dropdown-header:hover,\n.action-element-header:hover {\n\tbackground-color: #f2f2f2;\n}\n\n.dropdown-title {\n\tposition: relative;\n}\n.dropdown-header {\n\tdisplay: flex;\n\talign-items: center;\n}\n.cross {\n\tpointer-events: all;\n\tmargin-left: 5px;\n\tfont-family: var(--sc-font-family);\n\tcolor: var(--sc-gray-60, #666);\n\tcursor: pointer;\n\ttransform: rotate(45deg);\n\tuser-select: none;\n\ttransition: all 0.2s ease-in-out;\n\tline-height: 1rem;\n}\n\nbutton.dropdown-item {\n\twidth: 100%;\n\tpadding: 5px 12px;\n\ttext-align: right;\n\tborder: none;\n\tbackground-color: #ffffff;\n\tcursor: pointer;\n\tborder-radius: 5px;\n\tfont-size: 1rem;\n}\n\nbutton.dropdown-item:hover {\n\tbackground-color: #f2f2f2;\n}\n</style>\n","<template>\n\t<Teleport to=\"body\">\n\t\t<Transition name=\"fade\">\n\t\t\t<div v-if=\"isOpen\" class=\"command-palette-overlay\" @click=\"closeModal\">\n\t\t\t\t<div class=\"command-palette\" @click.stop>\n\t\t\t\t\t<div class=\"command-palette-header\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref=\"input\"\n\t\t\t\t\t\t\tv-model=\"query\"\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclass=\"command-palette-input\"\n\t\t\t\t\t\t\t:placeholder=\"placeholder\"\n\t\t\t\t\t\t\tautofocus\n\t\t\t\t\t\t\t@keydown=\"handleKeydown\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div v-if=\"results.length\" class=\"command-palette-results\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-for=\"(result, index) in results\"\n\t\t\t\t\t\t\t:key=\"index\"\n\t\t\t\t\t\t\tclass=\"command-palette-result\"\n\t\t\t\t\t\t\t:class=\"{ selected: index === selectedIndex }\"\n\t\t\t\t\t\t\t@click=\"selectResult(result)\"\n\t\t\t\t\t\t\t@mouseover=\"selectedIndex = index\">\n\t\t\t\t\t\t\t<div class=\"result-title\">\n\t\t\t\t\t\t\t\t<slot name=\"title\" :result=\"result\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"result-content\">\n\t\t\t\t\t\t\t\t<slot name=\"content\" :result=\"result\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div v-else-if=\"query && !results.length\" class=\"command-palette-no-results\">\n\t\t\t\t\t\t<slot name=\"empty\"> No results found for \"{{ query }}\" </slot>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</Transition>\n\t</Teleport>\n</template>\n\n<script setup lang=\"ts\" generic=\"T\">\nimport { ref, computed, watch, nextTick, useTemplateRef } from 'vue'\n\ndefineSlots<{\n\ttitle?: { result: T }\n\tcontent?: { result: T }\n\tempty?: null\n}>()\n\nconst {\n\tsearch,\n\tisOpen = false,\n\tplaceholder = 'Type a command or search...',\n\tmaxResults = 10,\n} = defineProps<{\n\tsearch: (query: string) => T[]\n\tisOpen?: boolean\n\tplaceholder?: string\n\tmaxResults?: number\n}>()\n\nconst emit = defineEmits<{\n\tselect: [T]\n\tclose: []\n}>()\n\nconst query = ref('')\nconst selectedIndex = ref(0)\nconst inputRef = useTemplateRef('input')\n\nconst results = computed(() => {\n\tif (!query.value) return []\n\tconst searchResults = search(query.value)\n\treturn searchResults.slice(0, maxResults)\n})\n\n// reset search query when modal opens\nwatch(\n\t() => isOpen,\n\tasync open => {\n\t\tif (open) {\n\t\t\tquery.value = ''\n\t\t\tselectedIndex.value = 0\n\t\t\tawait nextTick()\n\t\t\t;(inputRef.value as HTMLInputElement)?.focus()\n\t\t}\n\t}\n)\n\n// reset selected index when results change\nwatch(results, () => {\n\tselectedIndex.value = 0\n})\n\nconst closeModal = () => {\n\temit('close')\n}\n\nconst handleKeydown = (e: KeyboardEvent) => {\n\tswitch (e.key) {\n\t\tcase 'Escape':\n\t\t\tcloseModal()\n\t\t\tbreak\n\t\tcase 'ArrowDown':\n\t\t\te.preventDefault()\n\t\t\tif (results.value.length) {\n\t\t\t\tselectedIndex.value = (selectedIndex.value + 1) % results.value.length\n\t\t\t}\n\t\t\tbreak\n\t\tcase 'ArrowUp':\n\t\t\te.preventDefault()\n\t\t\tif (results.value.length) {\n\t\t\t\tselectedIndex.value = (selectedIndex.value - 1 + results.value.length) % results.value.length\n\t\t\t}\n\t\t\tbreak\n\t\tcase 'Enter':\n\t\t\tif (results.value.length && selectedIndex.value >= 0) {\n\t\t\t\tselectResult(results.value[selectedIndex.value])\n\t\t\t}\n\t\t\tbreak\n\t}\n}\n\nconst selectResult = (result: T) => {\n\temit('select', result)\n\tcloseModal()\n}\n</script>\n\n<style>\n.fade-enter-active,\n.fade-leave-active {\n\ttransition: opacity 0.2s ease;\n}\n\n.fade-enter-from,\n.fade-leave-to {\n\topacity: 0;\n}\n\n.command-palette-overlay {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground-color: rgba(0, 0, 0, 0.5);\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: center;\n\tz-index: 300;\n\tpadding-top: 100px;\n}\n\n.command-palette {\n\twidth: 600px;\n\tmax-width: 90%;\n\tbackground-color: white;\n\tborder-radius: 8px;\n\tbox-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n\toverflow: hidden;\n\tmax-height: 80vh;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.command-palette-header {\n\tdisplay: flex;\n\tborder-bottom: 1px solid #eaeaea;\n\tpadding: 12px;\n}\n\n.command-palette-input {\n\tflex: 1;\n\tborder: none;\n\toutline: none;\n\tfont-size: 16px;\n\tpadding: 8px 12px;\n\tbackground-color: transparent;\n}\n\n.command-palette-close {\n\tbackground: transparent;\n\tborder: none;\n\tfont-size: 24px;\n\tcursor: pointer;\n\tcolor: #666;\n\tpadding: 0 8px;\n}\n\n.command-palette-close:hover {\n\tcolor: #333;\n}\n\n.command-palette-results {\n\toverflow-y: auto;\n\tmax-height: 60vh;\n}\n\n.command-palette-result {\n\tpadding: 12px 16px;\n\tcursor: pointer;\n\tborder-bottom: 1px solid #f0f0f0;\n}\n\n.command-palette-result:hover,\n.command-palette-result.selected {\n\tbackground-color: #f5f5f5;\n}\n\n.command-palette-result.selected {\n\tbackground-color: rgba(132, 60, 3, 0.1);\n}\n\n.result-title {\n\tfont-weight: 500;\n\tmargin-bottom: 4px;\n\tcolor: #333;\n}\n\n.result-content {\n\tfont-size: 14px;\n\tcolor: #666;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.command-palette-no-results {\n\tpadding: 20px 16px;\n\ttext-align: center;\n\tcolor: #666;\n}\n</style>\n","<template>\n\t<footer>\n\t\t<ul class=\"tabs\">\n\t\t\t<li class=\"hidebreadcrumbs\" @click=\"toggleBreadcrumbs\" @keydown.enter=\"toggleBreadcrumbs\">\n\t\t\t\t<a tabindex=\"0\"><div :class=\"rotateHideTabIcon\">×</div></a>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\tclass=\"hometab\"\n\t\t\t\t:style=\"{ display: breadcrumbsVisibile ? 'block' : 'none' }\"\n\t\t\t\t@click=\"navigateHome\"\n\t\t\t\t@keydown.enter=\"navigateHome\">\n\t\t\t\t<router-link to=\"/\" tabindex=\"0\">\n\t\t\t\t\t<svg class=\"icon\" aria-label=\"Home\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n\t\t\t\t\t\t<path d=\"M3 12l9-9 9 9\" />\n\t\t\t\t\t\t<path d=\"M9 21V12h6v9\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</router-link>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\t:class=\"['searchtab', { 'search-active': searchVisible }]\"\n\t\t\t\t:style=\"{ display: breadcrumbsVisibile ? 'block' : 'none' }\">\n\t\t\t\t<a tabindex=\"0\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\tv-show=\"!searchVisible\"\n\t\t\t\t\t\tclass=\"icon search-icon\"\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\taria-label=\"Search\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t@click=\"toggleSearch\"\n\t\t\t\t\t\t@keydown.enter=\"toggleSearch\">\n\t\t\t\t\t\t<circle cx=\"11\" cy=\"11\" r=\"7\" />\n\t\t\t\t\t\t<path d=\"M21 21l-4.35-4.35\" />\n\t\t\t\t\t</svg>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-show=\"searchVisible\"\n\t\t\t\t\t\tref=\"searchinput\"\n\t\t\t\t\t\tv-model=\"searchText\"\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tplaceholder=\"Search...\"\n\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t@input=\"handleSearchInput($event)\"\n\t\t\t\t\t\t@blur=\"handleSearch($event)\"\n\t\t\t\t\t\t@keydown.enter=\"handleSearch($event)\"\n\t\t\t\t\t\t@keydown.escape=\"toggleSearch\" />\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\tv-for=\"breadcrumb in breadcrumbs\"\n\t\t\t\t:key=\"breadcrumb.title\"\n\t\t\t\t:style=\"{ display: breadcrumbsVisibile ? 'block' : 'none' }\">\n\t\t\t\t<router-link tabindex=\"0\" :to=\"breadcrumb.to\"> {{ breadcrumb.title }} </router-link>\n\t\t\t</li>\n\t\t</ul>\n\t</footer>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useTemplateRef } from 'vue'\n\nconst { breadcrumbs = [] } = defineProps<{ breadcrumbs?: { title: string; to: string }[] }>()\n\nconst breadcrumbsVisibile = ref(true)\nconst searchVisible = ref(false)\nconst searchText = ref('')\nconst inputRef = useTemplateRef<HTMLInputElement>('searchinput')\n\nconst rotateHideTabIcon = computed(() => {\n\treturn breadcrumbsVisibile.value ? 'unrotated' : 'rotated'\n})\n\nconst toggleBreadcrumbs = () => {\n\tbreadcrumbsVisibile.value = !breadcrumbsVisibile.value\n}\n\nconst toggleSearch = async () => {\n\tsearchVisible.value = !searchVisible.value\n\tawait nextTick(() => {\n\t\tinputRef.value?.focus()\n\t})\n}\n\nconst handleSearchInput = (event: Event | MouseEvent) => {\n\tevent.preventDefault()\n\tevent.stopPropagation()\n}\n\nconst handleSearch = async (event: FocusEvent | KeyboardEvent) => {\n\tevent.preventDefault()\n\tevent.stopPropagation()\n\tawait toggleSearch()\n}\n\nconst navigateHome = (/* event: MouseEvent | KeyboardEvent */) => {\n\t// navigate home\n}\n</script>\n\n<style scoped>\nfooter {\n\tposition: fixed;\n\tbottom: 0px;\n\twidth: 100%;\n\tbackground-color: transparent;\n\theight: auto;\n\tmin-height: 2.4rem;\n\tz-index: 100;\n\ttext-align: left;\n\tfont-size: 100%;\n\tdisplay: flex;\n\tjustify-content: right;\n\tpadding: 0 0.75rem 0 0;\n\tbox-sizing: border-box;\n}\nul {\n\tdisplay: flex;\n\tflex-direction: row-reverse;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.tabs li {\n\tfloat: left;\n\tlist-style-type: none;\n\tposition: relative;\n\tmargin-left: -1px;\n}\n\n/* Base tab styling */\n.tabs a {\n\tfloat: left;\n\tpadding: 0.5rem 1rem;\n\theight: 2.4rem;\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\ttext-decoration: none;\n\tcolor: var(--sc-gray-60, #666);\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tfont-size: 0.85rem;\n\tfont-family: var(--sc-font-family, system-ui, sans-serif);\n\ttransition: all 0.15s ease;\n\n\t/* Minimal ornamentation - subtle top radius only */\n\tborder-top-left-radius: 2px;\n\tborder-top-right-radius: 2px;\n}\n\n.tabs a:hover {\n\tbackground: var(--sc-btn-hover, #f2f2f2);\n}\n\n.tabs .router-link-active {\n\tz-index: 3;\n\tbackground: var(--sc-primary-color, #827553) !important;\n\tborder-color: var(--sc-primary-color, #827553) !important;\n\tcolor: var(--sc-primary-text-color, #fff) !important;\n}\n\n/* Pseudo-elements removed - minimal ornamentation style */\n\n/* Hide breadcrumbs tab */\n.hidebreadcrumbs a {\n\tmin-width: 2.4rem;\n\twidth: 2.4rem;\n\theight: 2.4rem;\n\tpadding: 0.5rem;\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tcolor: var(--sc-gray-60, #666);\n}\n\n.hidebreadcrumbs a div {\n\tfont-size: 1.2rem;\n}\n\n.rotated {\n\ttransform: rotate(45deg);\n\t-webkit-transform: rotate(45deg);\n\t-moz-transform: rotate(45deg);\n\t-ms-transform: rotate(45deg);\n\t-o-transform: rotate(45deg);\n\ttransition: transform 250ms ease;\n}\n.unrotated {\n\ttransform: rotate(0deg);\n\t-webkit-transform: rotate(0deg);\n\t-moz-transform: rotate(0deg);\n\t-ms-transform: rotate(0deg);\n\t-o-transform: rotate(0deg);\n\ttransition: transform 250ms ease;\n}\n\nli:active,\nli:hover,\nli:focus,\nli > a:active,\nli > a:hover,\nli > a:focus {\n\tz-index: 3;\n}\n\na:active,\na:hover,\na:focus {\n\toutline: 2px solid var(--sc-input-active-border-color, black);\n\tz-index: 3;\n}\n\n/* Home tab */\n.hometab a {\n\tmin-width: 2.4rem;\n\twidth: 2.4rem;\n\theight: 2.4rem;\n\tpadding: 0.5rem;\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tcolor: var(--sc-gray-60, #666);\n}\n\n/* SVG icon styling */\n.icon {\n\twidth: 1rem;\n\theight: 1rem;\n\tstroke: currentColor;\n\tflex-shrink: 0;\n}\n\n/* Search tab with animation */\n.searchtab {\n\toverflow: hidden;\n}\n\n.searchtab a {\n\tmin-width: 2.4rem;\n\theight: 2.4rem;\n\tpadding: 0.5rem;\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tcolor: var(--sc-gray-60, #666);\n\toverflow: hidden;\n\t/* Animation for smooth expand/collapse */\n\tmax-width: 2.4rem;\n\ttransition:\n\t\tmax-width 0.35s ease-in-out,\n\t\tpadding 0.35s ease-in-out,\n\t\tbackground 0.2s ease;\n}\n\n.searchtab .search-icon {\n\tcursor: pointer;\n}\n\n.searchtab input {\n\toutline: none;\n\tborder: 1px solid var(--sc-input-border-color, #ccc);\n\tborder-radius: 0.25rem;\n\tbackground-color: var(--sc-form-background, #ffffff);\n\tcolor: var(--sc-gray-80, #333);\n\ttext-align: left;\n\tfont-size: 0.875rem;\n\tpadding: 0.25rem 0.5rem;\n\twidth: 180px;\n\theight: 1.5rem;\n\tflex-shrink: 0;\n\topacity: 0;\n\ttransition: opacity 0.2s ease-in-out;\n\ttransition-delay: 0s;\n}\n\n.searchtab input:focus {\n\tborder-color: var(--sc-input-active-border-color, #4f46e5);\n\toutline: none;\n}\n\n.searchtab input::placeholder {\n\tcolor: var(--sc-input-label-color, #999);\n}\n\n/* Search active state - expanded with animation */\n.searchtab.search-active a {\n\tmax-width: 200px;\n\tmin-width: auto;\n\twidth: auto;\n\tpadding: 0.5rem 0.75rem;\n}\n\n.searchtab.search-active input {\n\topacity: 1;\n\ttransition-delay: 0.15s;\n}\n</style>\n","<template>\n\t<div class=\"desktop\" @click=\"handleClick\">\n\t\t<!-- Action Set -->\n\t\t<ActionSet :elements=\"actionElements\" @action-click=\"handleActionClick\" />\n\n\t\t<!-- Main content using AForm -->\n\t\t<AForm\n\t\t\tv-if=\"currentViewSchema.length > 0\"\n\t\t\tv-model:data=\"currentViewData\"\n\t\t\t:schema=\"currentViewSchema\"\n\t\t\t:errors=\"fieldErrors\" />\n\t\t<div v-else-if=\"!stonecrop\" class=\"loading\"><p>Initializing Stonecrop...</p></div>\n\t\t<div v-else class=\"loading\">\n\t\t\t<p>Loading {{ currentView }} data...</p>\n\t\t</div>\n\n\t\t<!-- Sheet Navigation -->\n\t\t<SheetNav :breadcrumbs=\"navigationBreadcrumbs\" />\n\n\t\t<!-- Command Palette -->\n\t\t<CommandPalette\n\t\t\t:is-open=\"commandPaletteOpen\"\n\t\t\t:search=\"searchCommands\"\n\t\t\tplaceholder=\"Type a command or search...\"\n\t\t\t@select=\"executeCommand\"\n\t\t\t@close=\"commandPaletteOpen = false\">\n\t\t\t<template #title=\"{ result }\">\n\t\t\t\t{{ result.title }}\n\t\t\t</template>\n\t\t\t<template #content=\"{ result }\">\n\t\t\t\t{{ result.description }}\n\t\t\t</template>\n\t\t</CommandPalette>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\n// The draft segment comes from @stonecrop/stonecrop rather than being spelled out here: that\n// package guards fetching, field initialization and workflow readiness on the same question, and\n// when the two were written separately they disagreed and every guard over there went dead.\nimport { DRAFT_RECORD_ID, isDraftRecordId, useStonecrop, useValidationStore } from '@stonecrop/stonecrop'\nimport { AForm, type AFormLinkNavigator, type ResolvedField, type ResolvedTable } from '@stonecrop/aform'\nimport { type ColumnSchema, componentLinkExpansion, type DoctypeField, flattenFields } from '@stonecrop/schema'\nimport { computed, onMounted, onUnmounted, provide, ref, unref, watch } from 'vue'\n\nimport ActionSet from './ActionSet.vue'\nimport SheetNav from './SheetNav.vue'\nimport CommandPalette from './CommandPalette.vue'\nimport type {\n\tActionElements,\n\tRouteAdapter,\n\tNavigationTarget,\n\tActionEventPayload,\n\tRecordOpenEventPayload,\n\tLoadRecordsEventPayload,\n\tLoadRecordEventPayload,\n} from '../types'\n\n/**\n * Inline link fields on a doctype — only these may be unwrapped back to a scalar id on write.\n *\n * Both the component and the doctype declaration decide what a field's value holds. An\n * *inline* link's value (`{ id, displayText }`) is indistinguishable by inspection from an\n * *expanded* one (`{ id, ...the whole target record }`), so the component is what tells them\n * apart.\n *\n * `componentLinkExpansion` is the same rule the adapter reads when it decides which links to\n * expand, so the two sides agree on what a field's value contains.\n */\nfunction inlineLinkFieldnames(fields: DoctypeField[]): Set<string> {\n\tconst links = flattenFields(fields).filter(field => field.kind === 'field' && Boolean(field.doctype))\n\n\treturn new Set(\n\t\tlinks.filter(field => componentLinkExpansion(field.component) === 'inline').map(field => field.fieldname)\n\t)\n}\n\n/**\n * Unwrap an inline link's value back to the id that gets persisted. Applied only to fields in\n * `inlineLinks` — the display text is a rendering concern and was never part of the record, while\n * an expanded link's object is the record and must survive the round trip intact.\n */\nfunction unwrapLinkValue(value: unknown): unknown {\n\tif (value !== null && typeof value === 'object' && !Array.isArray(value) && 'id' in value) {\n\t\treturn (value as { id: unknown }).id\n\t}\n\treturn value\n}\n\nconst { availableDoctypes = [], routeAdapter } = defineProps<{\n\tavailableDoctypes?: string[]\n\t/**\n\t * Pluggable router adapter. When provided, Desktop uses these functions for all\n\t * routing instead of reaching into the registry's internal Vue Router instance.\n\t * Nuxt hosts (or any host with custom route conventions) should supply this.\n\t */\n\trouteAdapter?: RouteAdapter\n}>()\n\nconst emit = defineEmits<{\n\t/**\n\t * Fired when the user triggers an FSM transition (action button click).\n\t * The host app is responsible for calling the server, persisting state, etc.\n\t */\n\taction: [payload: ActionEventPayload]\n\t/**\n\t * Fired when Desktop wants to navigate to a different view.\n\t * Also calls routeAdapter.navigate() if an adapter is provided.\n\t */\n\tnavigate: [target: NavigationTarget]\n\t/**\n\t * Fired when the user opens a specific record.\n\t */\n\t'record:open': [payload: RecordOpenEventPayload]\n\t/**\n\t * Fired when Desktop is about to read records for a list view. A notification, not a request:\n\t * Desktop performs the read itself through `Stonecrop.getRecords`. A host that fetches here\n\t * races that read into the same HST key.\n\t */\n\t'load-records': [payload: LoadRecordsEventPayload]\n\t/**\n\t * Fired when Desktop is about to read a single record for a form view. A notification, not a\n\t * request — see `load-records`. Not emitted for a draft, which has nothing to fetch.\n\t */\n\t'load-record': [payload: LoadRecordEventPayload]\n}>()\n\nconst { stonecrop } = useStonecrop()\n\n// Field-validation store (advisory, client-side). Pinia is a declared peerDependency, kept external\n// in this package's build (rollupOptions), so `useValidationStore()` resolves the host app's single\n// active Pinia directly — the old `getCurrentInstance().$pinia` workaround for the bundled-Pinia bug\n// is no longer needed. Still guarded: validation is optional and Desktop predates it, so a host that\n// mounts Desktop without Pinia disables validation gracefully instead of crashing on mount.\nlet validationStore: ReturnType<typeof useValidationStore> | null = null\ntry {\n\tvalidationStore = useValidationStore()\n} catch {\n\tvalidationStore = null\n}\n\n// Inline field errors handed to AForm. Only surfaced in the record form view (currentView is\n// defined below); empty elsewhere so a previous record's errors never bleed into a list view.\nconst fieldErrors = computed<Record<string, string[]>>(() =>\n\tcurrentView.value === 'record' ? (validationStore?.errorsByField ?? {}) : {}\n)\n\n// State\nconst loading = ref(false)\nconst commandPaletteOpen = ref(false)\n\n// The record being composed on a `/{doctype}/new` route. Deliberately not in HST: a draft has no\n// identity to be keyed by, and both ways of faking one fail — see `DRAFT_RECORD_ID`.\nconst draftRecord = ref<Record<string, any>>({})\n\n// Form/list data management — each view produces a different data shape.\n// List views (doctypes, records) return table row data keyed by fieldname.\n// Record view returns the record's fields for two-way binding — from HST, or from `draftRecord`\n// when the route is a draft.\nconst currentViewData = computed<Record<string, any>>({\n\tget() {\n\t\t// Doctypes list — rows come from availableDoctypes prop (reactive via availableDoctypes)\n\t\tif (currentView.value === 'doctypes') {\n\t\t\treturn {\n\t\t\t\tdoctypes_table:\n\t\t\t\t\tavailableDoctypes?.map(doctype => ({\n\t\t\t\t\t\tid: doctype,\n\t\t\t\t\t\tdoctype,\n\t\t\t\t\t\tdisplay_name: formatDoctypeName(doctype),\n\t\t\t\t\t\tactions: 'View Records',\n\t\t\t\t\t})) ?? [],\n\t\t\t}\n\t\t}\n\n\t\t// Records list — rows come from HST store (reactive because HST is Vue reactive())\n\t\tif (currentView.value === 'records') {\n\t\t\treturn {\n\t\t\t\trecords_table: getRecords().map(record =>\n\t\t\t\t\tObject.assign({}, record, {\n\t\t\t\t\t\tid: resolveRecordId(record) ?? '',\n\t\t\t\t\t\t// A list row is navigation. Actions live on the record view, where the Actions\n\t\t\t\t\t\t// dropdown is built from what the doctype declares and what the record's\n\t\t\t\t\t\t// current state allows. This cell used to also offer Delete, which dispatched\n\t\t\t\t\t\t// an action named `DELETE` that Desktop invented — no doctype in a\n\t\t\t\t\t\t// WorkflowMeta app declares it, so it failed on every click. Removal is a\n\t\t\t\t\t\t// workflow outcome (`archive`, `cancel`) and belongs in the doctype.\n\t\t\t\t\t\tactions: 'Edit',\n\t\t\t\t\t})\n\t\t\t\t),\n\t\t\t}\n\t\t}\n\n\t\t// Record form — read single record from HST\n\t\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\t\treturn {}\n\t\t}\n\n\t\ttry {\n\t\t\tconst source = isNewRecord.value\n\t\t\t\t? draftRecord.value\n\t\t\t\t: (stonecrop.value.getRecordById(currentDoctype.value, currentRecordId.value)?.get('') as\n\t\t\t\t\t\tRecord<string, any> | undefined)\n\t\t\t// Return a plain shallow copy so AForm mutations don't propagate directly into\n\t\t\t// the HST reactive object, which would bypass field-trigger diffing and cause\n\t\t\t// setupDeepReactivity to fire triggers for all fields on every keystroke.\n\t\t\tconst flat: Record<string, any> = { ...source }\n\n\t\t\t// AFieldset receives data[fieldsetFieldname] as its data prop, so the fieldset's\n\t\t\t// children must be grouped under the fieldset key. The server returns flat SQL rows,\n\t\t\t// so we nest fieldset children here before AForm renders them.\n\t\t\tconst doctype = stonecrop.value.registry.registry[currentDoctype.value]\n\t\t\tif (doctype) {\n\t\t\t\tfor (const field of doctype.getSchemaArray()) {\n\t\t\t\t\tif (field.kind === 'fieldset') {\n\t\t\t\t\t\tconst nested: Record<string, any> = {}\n\t\t\t\t\t\tfor (const child of field.schema) {\n\t\t\t\t\t\t\tif (child.fieldname) nested[child.fieldname] = flat[child.fieldname]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tflat[field.fieldname] = nested\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn flat\n\t\t} catch {\n\t\t\treturn {}\n\t\t}\n\t},\n\tset(newData: Record<string, any>) {\n\t\t// List views are read-only from AForm's perspective — HST writes only apply to record form\n\t\tif (currentView.value !== 'record') return\n\t\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\t// AForm emits nested data for fieldsets: { fieldsetKey: { childA: val } }.\n\t\t\t// HST and the server both expect flat rows, so flatten fieldset values back before writing.\n\t\t\tconst doctype = stonecrop.value.registry.registry[currentDoctype.value]\n\t\t\tconst fieldsetNames = new Set<string>()\n\t\t\tif (doctype) {\n\t\t\t\tfor (const field of doctype.getSchemaArray()) {\n\t\t\t\t\tif (field.kind === 'fieldset') {\n\t\t\t\t\t\tfieldsetNames.add(field.fieldname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst inlineLinks = inlineLinkFieldnames(doctype ? doctype.getSchemaArray() : [])\n\n\t\t\t// Two-pass flatten: non-fieldset keys first, then fieldset children.\n\t\t\t// Fieldset children must be applied last — AForm may emit stale flat copies\n\t\t\t// of fieldset children alongside the updated nested value, and the nested\n\t\t\t// value must win regardless of key insertion order.\n\t\t\tconst flatData: Record<string, any> = {}\n\t\t\tconst fieldsetValues: Record<string, any>[] = []\n\t\t\tfor (const [key, value] of Object.entries(newData)) {\n\t\t\t\tif (fieldsetNames.has(key) && value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\tfieldsetValues.push(value)\n\t\t\t\t} else {\n\t\t\t\t\tflatData[key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const nestedValue of fieldsetValues) {\n\t\t\t\tObject.assign(flatData, nestedValue)\n\t\t\t}\n\n\t\t\t// Only update fields that actually changed. Never write undefined — AForm may emit\n\t\t\t// schema fields absent from the record as undefined; writing them would silently\n\t\t\t// clear values that exist. Explicit null is allowed (intentional clear).\n\t\t\tconst changedFields: string[] = []\n\t\t\tif (isNewRecord.value) {\n\t\t\t\t// Reassigned, not mutated, so the getter re-runs. Relying on in-place mutation of the\n\t\t\t\t// cached object is what made a draft's edits vanish on any invalidation.\n\t\t\t\tconst next = { ...draftRecord.value }\n\t\t\t\tfor (const [fieldname, value] of Object.entries(flatData)) {\n\t\t\t\t\tif (value === undefined) continue\n\t\t\t\t\tconst normalized = inlineLinks.has(fieldname) ? unwrapLinkValue(value) : value\n\t\t\t\t\tif (next[fieldname] !== normalized) {\n\t\t\t\t\t\tnext[fieldname] = normalized\n\t\t\t\t\t\tchangedFields.push(fieldname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdraftRecord.value = next\n\t\t\t} else {\n\t\t\t\tconst hstStore = stonecrop.value.getStore()\n\t\t\t\tfor (const [fieldname, value] of Object.entries(flatData)) {\n\t\t\t\t\tif (value === undefined) continue\n\t\t\t\t\tconst normalized = inlineLinks.has(fieldname) ? unwrapLinkValue(value) : value\n\t\t\t\t\tconst fieldPath = `${currentDoctype.value}.${currentRecordId.value}.${fieldname}`\n\t\t\t\t\tconst currentValue = hstStore.has(fieldPath) ? hstStore.get(fieldPath) : undefined\n\t\t\t\t\tif (currentValue !== normalized) {\n\t\t\t\t\t\thstStore.set(fieldPath, normalized)\n\t\t\t\t\t\tchangedFields.push(fieldname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Advisory field-validation runs on the fields that actually changed (honors each\n\t\t\t// trigger's `on` set). Driven here, after the HST writes, so HST stays value-only.\n\t\t\tif (changedFields.length > 0) {\n\t\t\t\tdriveFieldValidation(changedFields)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn('HST update failed:', error)\n\t\t}\n\t},\n})\n\n// Advisory field-validation: run the doctype's triggers for the fields that changed on this edit.\n// Snapshots the post-edit record as a plain, flat object so validators read siblings read-only\n// (the core store freezes it). Fire-and-forget — the reactive error store repaints AForm on resolve.\nfunction driveFieldValidation(changedFields: string[]) {\n\tif (!validationStore || !stonecrop.value || !currentDoctype.value || !currentRecordId.value) return\n\n\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\tconst triggers = doctype?.getTriggers()\n\tif (!triggers || Object.keys(triggers).length === 0) return\n\n\t// A draft's siblings come from the buffer; reading HST would hand every validator an empty record.\n\tconst record = isNewRecord.value\n\t\t? { ...draftRecord.value }\n\t\t: {\n\t\t\t\t...(stonecrop.value.getRecordById(currentDoctype.value, currentRecordId.value)?.get('') as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tunknown\n\t\t\t\t>),\n\t\t\t}\n\n\tfor (const field of changedFields) {\n\t\tvoid validationStore.validateField(triggers, field, record)\n\t}\n}\n\n// Computed properties for current route context.\n// When a routeAdapter is provided it takes full precedence over the registry's internal router.\nconst route = computed(() => (routeAdapter ? null : unref(stonecrop.value?.registry.router?.currentRoute)))\nconst router = computed(() => (routeAdapter ? null : stonecrop.value?.registry.router))\nconst currentDoctype = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentDoctype()\n\tif (!route.value) return ''\n\n\t// First check if we have actualDoctype in meta (from registered routes)\n\tif (route.value.meta?.actualDoctype) {\n\t\treturn route.value.meta.actualDoctype as string\n\t}\n\n\t// For named routes, use params.doctype\n\tif (route.value.params.doctype) {\n\t\treturn route.value.params.doctype.toString()\n\t}\n\n\t// For catch-all routes that haven't been registered yet, extract from path\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 0) {\n\t\treturn pathMatch[0]\n\t}\n\n\treturn ''\n})\n\n// The route doctype for display and navigation (e.g., 'todo')\nconst routeDoctype = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentDoctype()\n\tif (!route.value) return ''\n\n\t// Check route meta first\n\tif (route.value.meta?.doctype) {\n\t\treturn route.value.meta.doctype as string\n\t}\n\n\t// For named routes, use params.doctype\n\tif (route.value.params.doctype) {\n\t\treturn route.value.params.doctype.toString()\n\t}\n\n\t// For catch-all routes, extract from path\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 0) {\n\t\treturn pathMatch[0]\n\t}\n\n\treturn ''\n})\n\nconst currentRecordId = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentRecordId()\n\tif (!route.value) return ''\n\n\t// For named routes, use params.recordId\n\tif (route.value.params.recordId) {\n\t\treturn route.value.params.recordId.toString()\n\t}\n\n\t// For catch-all routes that haven't been registered yet, extract from path\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 1) {\n\t\treturn pathMatch[1]\n\t}\n\n\treturn ''\n})\nconst isNewRecord = computed(() => isDraftRecordId(currentRecordId.value))\n\n// Determine current view based on route\nconst currentView = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentView()\n\tif (!route.value) {\n\t\treturn 'doctypes'\n\t}\n\n\t// Home route\n\tif (route.value.name === 'home' || route.value.path === '/') {\n\t\treturn 'doctypes'\n\t}\n\n\t// Named routes from registered doctypes\n\tif (route.value.name && route.value.name !== 'catch-all') {\n\t\tconst routeName = route.value.name as string\n\t\tif (routeName.includes('form') || route.value.params.recordId) {\n\t\t\treturn 'record'\n\t\t} else if (routeName.includes('list') || route.value.params.doctype) {\n\t\t\treturn 'records'\n\t\t}\n\t}\n\n\t// Catch-all route - determine from path structure\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 0) {\n\t\tconst view = pathMatch.length === 1 ? 'records' : 'record'\n\t\treturn view\n\t}\n\n\treturn 'doctypes'\n})\n\n// Computed properties (now that all helper functions are defined)\n// Helper function to get available transitions for current record.\n// Reads the actual FSM state from the record's `status` field (or falls back to the\n// workflow initial state) so the available action buttons always reflect reality.\nconst getAvailableTransitions = () => {\n\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\treturn []\n\t}\n\n\ttry {\n\t\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\t\tif (!doctype?.workflow) return []\n\n\t\t// Delegate state resolution to Stonecrop — reads record 'status', falls back to workflow.initial\n\t\tconst currentState = stonecrop.value.getRecordState(currentDoctype.value, currentRecordId.value)\n\n\t\t// Delegate transition lookup to Doctype — no more manual workflow introspection\n\t\tconst transitions = doctype.getAvailableTransitions(currentState)\n\n\t\tconst recordData = currentViewData.value || {}\n\n\t\t// Each transition emits an 'action' event. The host app decides what to do\n\t\t// (call the server, trigger an FSM actor, update HST, etc.).\n\t\treturn transitions.map(({ name }) => ({\n\t\t\t// Prefer the workflow action's human-readable label (WorkflowMeta format);\n\t\t\t// fall back to the raw transition name for XState workflows with no action meta.\n\t\t\tlabel: doctype.getActionMeta(name)?.label ?? name,\n\t\t\taction: () => {\n\t\t\t\temit('action', {\n\t\t\t\t\tname,\n\t\t\t\t\tdoctype: currentDoctype.value,\n\t\t\t\t\trecordId: currentRecordId.value,\n\t\t\t\t\tdata: recordData,\n\t\t\t\t})\n\t\t\t},\n\t\t}))\n\t} catch (error) {\n\t\tconsole.warn('Error getting available transitions:', error)\n\t\treturn []\n\t}\n}\n\n// Helper: stateless Commands available for the current record — side-effect actions that\n// change no workflow state. Surfaced in the same Actions dropdown as transitions; each emits\n// the same 'action' event, so the host's handler runs a Command's clientHandler identically.\nconst getAvailableCommands = () => {\n\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\treturn []\n\t}\n\n\ttry {\n\t\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\t\tif (!doctype?.workflow) return []\n\n\t\tconst currentState = stonecrop.value.getRecordState(currentDoctype.value, currentRecordId.value)\n\t\tconst commands = doctype.getAvailableCommands(currentState)\n\t\tconst recordData = currentViewData.value || {}\n\n\t\treturn commands.map(({ name }) => ({\n\t\t\tlabel: doctype.getActionMeta(name)?.label ?? name,\n\t\t\taction: () => {\n\t\t\t\temit('action', {\n\t\t\t\t\tname,\n\t\t\t\t\tdoctype: currentDoctype.value,\n\t\t\t\t\trecordId: currentRecordId.value,\n\t\t\t\t\tdata: recordData,\n\t\t\t\t})\n\t\t\t},\n\t\t}))\n\t} catch (error) {\n\t\tconsole.warn('Error getting available commands:', error)\n\t\treturn []\n\t}\n}\n\nconst actionElements = computed(() => {\n\tconst elements: ActionElements[] = []\n\n\tswitch (currentView.value) {\n\t\tcase 'records':\n\t\t\telements.push({\n\t\t\t\ttype: 'button',\n\t\t\t\tlabel: 'New Record',\n\t\t\t\taction: () => void createNewRecord(),\n\t\t\t})\n\t\t\tbreak\n\t\tcase 'record': {\n\t\t\t// Populate the Actions dropdown with every FSM transition AND stateless Command\n\t\t\t// available in the record's current state. Clicking either emits 'action'.\n\t\t\tconst recordActions = [...getAvailableTransitions(), ...getAvailableCommands()]\n\t\t\tif (recordActions.length > 0) {\n\t\t\t\telements.push({\n\t\t\t\t\ttype: 'dropdown',\n\t\t\t\t\tlabel: 'Actions',\n\t\t\t\t\tactions: recordActions,\n\t\t\t\t})\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn elements\n})\n\nconst navigationBreadcrumbs = computed(() => {\n\tconst breadcrumbs: { title: string; to: string }[] = []\n\n\tif (currentView.value === 'records' && routeDoctype.value) {\n\t\tbreadcrumbs.push(\n\t\t\t{ title: 'Home', to: '/' },\n\t\t\t{ title: formatDoctypeName(routeDoctype.value), to: `/${routeDoctype.value}` }\n\t\t)\n\t} else if (currentView.value === 'record' && routeDoctype.value) {\n\t\tconst recordPath = currentRecordId.value\n\t\t\t? `/${routeDoctype.value}/${currentRecordId.value}`\n\t\t\t: (route.value?.fullPath ?? '')\n\t\tbreadcrumbs.push(\n\t\t\t{ title: 'Home', to: '/' },\n\t\t\t{ title: formatDoctypeName(routeDoctype.value), to: `/${routeDoctype.value}` },\n\t\t\t{ title: isNewRecord.value ? 'New Record' : 'Edit Record', to: recordPath }\n\t\t)\n\t}\n\n\treturn breadcrumbs\n})\n\n// Command palette functionality\ntype Command = {\n\ttitle: string\n\tdescription: string\n\taction: () => void\n}\n\nconst searchCommands = (query: string): Command[] => {\n\tconst commands: Command[] = [\n\t\t{\n\t\t\ttitle: 'Go Home',\n\t\t\tdescription: 'Navigate to the home page',\n\t\t\taction: () => void doNavigate({ view: 'doctypes' }),\n\t\t},\n\t\t{\n\t\t\ttitle: 'Toggle Command Palette',\n\t\t\tdescription: 'Open/close the command palette',\n\t\t\taction: () => (commandPaletteOpen.value = !commandPaletteOpen.value),\n\t\t},\n\t]\n\n\t// Add doctype-specific commands\n\tif (routeDoctype.value) {\n\t\tcommands.push({\n\t\t\ttitle: `View ${formatDoctypeName(routeDoctype.value)} Records`,\n\t\t\tdescription: `Navigate to ${routeDoctype.value} list`,\n\t\t\taction: () => void doNavigate({ view: 'records', doctype: routeDoctype.value }),\n\t\t})\n\n\t\tcommands.push({\n\t\t\ttitle: `Create New ${formatDoctypeName(routeDoctype.value)}`,\n\t\t\tdescription: `Create a new ${routeDoctype.value} record`,\n\t\t\taction: () => void createNewRecord(),\n\t\t})\n\t}\n\n\t// Add available doctypes as commands\n\tavailableDoctypes.forEach(doctype => {\n\t\tcommands.push({\n\t\t\ttitle: `View ${formatDoctypeName(doctype)}`,\n\t\t\tdescription: `Navigate to ${doctype} list`,\n\t\t\taction: () => void doNavigate({ view: 'records', doctype }),\n\t\t})\n\t})\n\n\t// Filter commands based on query\n\tif (!query) return commands\n\n\treturn commands.filter(\n\t\tcmd =>\n\t\t\tcmd.title.toLowerCase().includes(query.toLowerCase()) ||\n\t\t\tcmd.description.toLowerCase().includes(query.toLowerCase())\n\t)\n}\n\nconst executeCommand = (command: Command) => {\n\tcommand.action()\n\tcommandPaletteOpen.value = false\n}\n\n// List reads are wired on the records table; ATable owns the fetch via getRecords.\nconst listRecordsFetcher = (options?: import('@stonecrop/schema').GetRecordsOptions) => {\n\tif (!stonecrop.value || !currentDoctype.value) {\n\t\treturn Promise.resolve({ data: [], hasMore: false })\n\t}\n\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\tif (!doctype) {\n\t\treturn Promise.resolve({ data: [], hasMore: false })\n\t}\n\treturn stonecrop.value.getRecords(doctype, options)\n}\n\n// Helper functions - moved here to avoid \"before initialization\" errors\nconst formatDoctypeName = (doctype: string): string => {\n\treturn doctype\n\t\t.split('-')\n\t\t.map(word => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(' ')\n}\n\n// Internal navigation helper: emits 'navigate', then calls the adapter (if any)\n// or falls back to the registry's Vue Router instance.\nconst doNavigate = async (target: NavigationTarget) => {\n\temit('navigate', target)\n\tif (routeAdapter) {\n\t\tawait routeAdapter.navigate(target)\n\t} else {\n\t\tif (target.view === 'doctypes') {\n\t\t\tawait router.value?.push('/')\n\t\t} else if (target.view === 'records' && target.doctype) {\n\t\t\tawait router.value?.push(`/${target.doctype}`)\n\t\t} else if (target.view === 'record' && target.doctype && target.recordId) {\n\t\t\tawait router.value?.push(`/${target.doctype}/${target.recordId}`)\n\t\t}\n\t}\n}\n\nconst navigateToDoctype = async (doctype: string) => {\n\tawait doNavigate({ view: 'records', doctype })\n}\n\nconst openRecord = async (recordId: string) => {\n\tconst doctype = routeDoctype.value\n\temit('record:open', { doctype, recordId })\n\tawait doNavigate({ view: 'record', doctype, recordId })\n}\n\nconst createNewRecord = async () => {\n\tawait doNavigate({ view: 'record', doctype: routeDoctype.value, recordId: DRAFT_RECORD_ID })\n}\n\n// Flatten Fieldset containers into individual columns for list/table views.\n// A Fieldset groups form fields visually but has no DB column; its children are the\n// actual data fields and should appear as flat columns in a list view.\nconst flattenFieldsets = (fields: ResolvedField[]): ResolvedField[] => {\n\tconst result: ResolvedField[] = []\n\tfor (const field of fields) {\n\t\tif (field.kind === 'fieldset') {\n\t\t\tresult.push(...flattenFieldsets(field.schema))\n\t\t} else {\n\t\t\tresult.push(field)\n\t\t}\n\t}\n\treturn result\n}\n\n// Schema generator functions - moved here to be available to computed properties\nconst getDoctypesSchema = (): ResolvedField[] => {\n\tif (!availableDoctypes?.length) return []\n\n\treturn [\n\t\t{\n\t\t\tkind: 'table' as const,\n\t\t\tfieldname: 'doctypes_table',\n\t\t\tcomponent: 'ATable',\n\t\t\tlabel: 'Doctypes',\n\t\t\tschema: [\n\t\t\t\t{\n\t\t\t\t\tfieldname: 'doctype',\n\t\t\t\t\tlabel: 'Doctype',\n\t\t\t\t\tcomponent: 'ATextInput',\n\t\t\t\t\talign: 'left' as const,\n\t\t\t\t\tedit: false,\n\t\t\t\t\twidth: '20ch',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tfieldname: 'display_name',\n\t\t\t\t\tlabel: 'Name',\n\t\t\t\t\tcomponent: 'ATextInput',\n\t\t\t\t\talign: 'left' as const,\n\t\t\t\t\tedit: false,\n\t\t\t\t\twidth: '30ch',\n\t\t\t\t},\n\t\t\t\t// No record count column. It read `getRecordIds(doctype).length`, which is how many\n\t\t\t\t// records HST happens to hold — zero for a doctype never opened, and the page size\n\t\t\t\t// for one that was. The real total belongs to the backend and Desktop never asks\n\t\t\t\t// for it, so this shell cannot answer it. Same reason the `recordIdField` prop went.\n\t\t\t\t{\n\t\t\t\t\tfieldname: 'actions',\n\t\t\t\t\tlabel: 'Actions',\n\t\t\t\t\tcomponent: 'ATextInput',\n\t\t\t\t\talign: 'center' as const,\n\t\t\t\t\tedit: false,\n\t\t\t\t\twidth: '20ch',\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: { view: 'list' as const, fullWidth: true },\n\t\t} satisfies ResolvedTable,\n\t]\n}\n\nconst getRecordsSchema = (): ResolvedField[] => {\n\tif (!currentDoctype.value) return []\n\tif (!stonecrop.value) return []\n\n\tconst registry = stonecrop.value.registry\n\tconst doctype = registry.registry[currentDoctype.value]\n\n\tif (!doctype) return []\n\n\tconst schema = registry.resolveSchema(doctype)\n\n\t// If no schema is available, let the template fallback handle the loading state\n\tif (schema.length === 0) return []\n\n\tconst doctypeSlug = currentDoctype.value\n\tconst clientConfigured = Boolean(stonecrop.value.getClient())\n\n\treturn [\n\t\t{\n\t\t\tkind: 'table' as const,\n\t\t\tfieldname: 'records_table',\n\t\t\tcomponent: 'ATable',\n\t\t\tschema: [\n\t\t\t\t...(flattenFieldsets(schema) as ColumnSchema[]),\n\t\t\t\t{ fieldname: 'actions', label: 'Actions', component: 'ATextInput' },\n\t\t\t],\n\t\t\tconfig: { view: 'list' as const, fullWidth: true },\n\t\t\t...(clientConfigured ? { getRecords: listRecordsFetcher, sourceKey: doctypeSlug } : {}),\n\t\t} satisfies ResolvedTable,\n\t]\n}\n\nconst getRecordFormSchema = (): ResolvedField[] => {\n\tif (!currentDoctype.value) return []\n\tif (!stonecrop.value) return []\n\n\ttry {\n\t\tconst registry = stonecrop.value?.registry\n\t\tconst doctype = registry?.registry[currentDoctype.value]\n\n\t\tif (!doctype?.schema) {\n\t\t\t// Let the template fallback handle the loading state\n\t\t\treturn []\n\t\t}\n\n\t\treturn registry.resolveSchema(doctype)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n// Additional data helper functions\nconst getRecords = () => {\n\tif (!stonecrop.value || !currentDoctype.value) {\n\t\treturn []\n\t}\n\n\tconst recordsNode = stonecrop.value.records(currentDoctype.value)\n\tconst recordsData = recordsNode?.get('')\n\n\tif (recordsData && typeof recordsData === 'object' && !Array.isArray(recordsData)) {\n\t\treturn Object.values(recordsData as Record<string, any>)\n\t}\n\n\treturn []\n}\n\n// Schema for different views - defined here after all helper functions are available\nconst currentViewSchema = computed(() => {\n\tswitch (currentView.value) {\n\t\tcase 'doctypes':\n\t\t\treturn getDoctypesSchema()\n\t\tcase 'records':\n\t\t\treturn getRecordsSchema()\n\t\tcase 'record':\n\t\t\treturn getRecordFormSchema()\n\t\tdefault:\n\t\t\treturn []\n\t}\n})\n\nconst handleActionClick = (_label: string, action: (() => void | Promise<void>) | undefined) => {\n\tif (action) {\n\t\tvoid action()\n\t}\n}\n\n/**\n * Resolve a record's identity for links and navigation.\n *\n * Identity is declared once, on the doctype: `primaryKey`, or `id` when nothing is declared.\n * Delegating to `Doctype.getRecordId` is what guarantees this matches the key\n * `Stonecrop.getRecords` stored the record under — resolving it independently here would let a\n * row render a link to an HST path that does not exist.\n *\n * There is deliberately no per-shell override. Identity is a per-doctype fact and one shell\n * renders many doctypes, so a single prop cannot answer it; a shell that named a field would\n * also be overriding the very declaration the store keyed on, which is the bug above.\n */\nconst resolveRecordId = (record: Record<string, unknown>): string | undefined => {\n\tif (!stonecrop.value || !currentDoctype.value) return undefined\n\treturn stonecrop.value.registry.registry[currentDoctype.value]?.getRecordId(record)\n}\n\n// Event handlers\nconst getRecordIdFromRow = (rowElement: HTMLTableRowElement): string | null => {\n\tconst cell = rowElement.querySelector('td[data-rowindex]')\n\tif (!cell) return null\n\n\tconst rowIndexAttr = cell.getAttribute('data-rowindex')\n\tif (rowIndexAttr === null) return null\n\n\tconst rowIndex = parseInt(rowIndexAttr, 10)\n\tif (isNaN(rowIndex)) return null\n\n\tconst records = getRecords()\n\tconst record = records[rowIndex]\n\tif (!record) return null\n\n\treturn resolveRecordId(record) ?? null\n}\n\nconst handleClick = async (event: Event) => {\n\tconst target = event.target as HTMLElement\n\tconst action = target.getAttribute('data-action')\n\n\tif (action === 'create') {\n\t\tawait createNewRecord()\n\t}\n\n\tconst cell = target.closest('td, th')\n\tif (cell) {\n\t\tconst cellText = cell.textContent?.trim()\n\t\tconst row = cell.closest('tr')\n\n\t\tif (cellText === 'View Records' && row) {\n\t\t\t// Get the doctype from the row data\n\t\t\tconst cells = row.querySelectorAll('td')\n\t\t\tif (cells.length > 0) {\n\t\t\t\tconst doctypeCell = cells[1] // Assuming doctype is in second column (first column is index)\n\t\t\t\tconst doctype = doctypeCell.textContent?.trim()\n\t\t\t\tif (doctype) {\n\t\t\t\t\tawait navigateToDoctype(doctype)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (cellText === 'Edit' && row) {\n\t\t\t// Matched exactly, not by substring. This handler is bound to the whole desktop, so a\n\t\t\t// substring match fired on any cell whose *data* happened to contain the word — a task\n\t\t\t// titled \"Delete old backups\" popped a delete confirmation when you clicked it.\n\t\t\tconst recordId = getRecordIdFromRow(row)\n\t\t\tif (recordId) {\n\t\t\t\tawait openRecord(recordId)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Reads go through Stonecrop, which owns whether to fetch, how to key the result, and where to\n// put it. Desktop asks for data and renders what arrives; it decides none of that itself.\n//\n// Both loaders are no-ops without a client, so a host that populates HST some other way keeps\n// working unchanged rather than taking a thrown error on every navigation.\nconst loadRecordData = async () => {\n\tif (!stonecrop.value || !currentDoctype.value || !stonecrop.value.getClient()) return\n\n\tloading.value = true\n\ttry {\n\t\tawait stonecrop.value.getRecord(currentDoctype.value, currentRecordId.value)\n\t} catch (error) {\n\t\tconsole.warn('Error fetching record:', error)\n\t} finally {\n\t\tloading.value = false\n\t}\n}\n\n// Watch for route changes to load appropriate data\nwatch(\n\t[currentView, currentDoctype, currentRecordId],\n\t() => {\n\t\t// The events are notifications, not fetch requests: they announce what Desktop is about to\n\t\t// read so a host can hang analytics or a prefetch off them. The read itself is Stonecrop's.\n\t\tif (currentView.value === 'records' && currentDoctype.value) {\n\t\t\temit('load-records', { doctype: currentDoctype.value })\n\t\t} else if (currentView.value === 'record' && currentDoctype.value && currentRecordId.value) {\n\t\t\t// A draft has nothing to fetch — the record does not exist on the server yet. Desktop\n\t\t\t// used to emit anyway and leave the host to work it out, which meant every host had to\n\t\t\t// recognise the private draft-id scheme above just to suppress a doomed request; all of\n\t\t\t// them did, identically. `getRecord` declines the same case for the same reason.\n\t\t\tif (isNewRecord.value) return\n\n\t\t\temit('load-record', { doctype: currentDoctype.value, recordId: currentRecordId.value })\n\t\t\tvoid loadRecordData()\n\t\t}\n\t},\n\t{ immediate: true }\n)\n\n// Clear advisory validation errors when the target record changes, so errors from a previously\n// edited record never bleed into a newly opened one.\nwatch([currentDoctype, currentRecordId], () => {\n\tvalidationStore?.clearAll()\n})\n\n// Seeding on entry gives a new record the doctype's declared defaults, which it never used to get.\n// Discarding on exit matters as much: the draft segment is one shared literal, so a stale buffer\n// would open the next New Record pre-filled with the abandoned one's values.\nwatch(\n\t[currentDoctype, currentRecordId],\n\t() => {\n\t\tif (!isNewRecord.value) {\n\t\t\tdraftRecord.value = {}\n\t\t\treturn\n\t\t}\n\t\tconst registry = stonecrop.value?.registry\n\t\tdraftRecord.value = registry ? registry.initializeRecord(getRecordFormSchema()) : {}\n\t},\n\t{ immediate: true }\n)\n\n// Stonecrop reactive computed properties update automatically when the instance\n// becomes available — no manual watcher needed.\n\n// Provide navigation helpers and an emitAction convenience function to child components.\nconst desktopMethods = {\n\tnavigateToDoctype,\n\topenRecord,\n\tcreateNewRecord,\n\t/**\n\t * Convenience wrapper so child components (e.g. slot content) can emit\n\t * an action event without needing a direct reference to the emit function.\n\t */\n\temitAction: (name: string, data?: Record<string, any>) => {\n\t\temit('action', {\n\t\t\tname,\n\t\t\tdoctype: currentDoctype.value,\n\t\t\trecordId: currentRecordId.value,\n\t\t\tdata: data ?? currentViewData.value ?? {},\n\t\t})\n\t},\n}\n\nprovide('desktopMethods', desktopMethods)\n\n// Provide a navigator for AFormLink so the arrow button navigates to the linked record.\nprovide('aformLinkNavigator', {\n\tnavigate: (doctype: string, id: string | number) => {\n\t\tvoid doNavigate({ view: 'record', doctype, recordId: String(id) })\n\t},\n} satisfies AFormLinkNavigator)\n\n// Provide a resolver for AFormLink to look up display text by doctype + id.\n// Checks HST first (sync); falls back to an async client fetch if not cached.\n// Uses the target doctype's declared displayField — no heuristic field guessing.\nprovide('aformLinkResolver', async (doctypeSlug: string, id: string): Promise<string | undefined> => {\n\tif (!stonecrop.value) return undefined\n\ttry {\n\t\tconst meta = await stonecrop.value.getMeta({ path: `/${doctypeSlug}`, segments: [doctypeSlug] })\n\t\tconst displayField = meta?.displayField\n\t\tif (!displayField) return undefined\n\n\t\tconst readDisplay = (rec: Record<string, unknown> | undefined): string | undefined => {\n\t\t\tif (!rec) return undefined\n\t\t\tconst val = rec[displayField]\n\t\t\treturn typeof val === 'string' || typeof val === 'number' ? String(val) : undefined\n\t\t}\n\n\t\tconst cached = stonecrop.value.getRecordById(doctypeSlug, id)?.get('') as Record<string, unknown> | undefined\n\t\tconst cachedDisplay = readDisplay(cached)\n\t\tif (cachedDisplay != null) return cachedDisplay\n\n\t\tawait stonecrop.value.getRecord(doctypeSlug, id)\n\t\tconst fetched = stonecrop.value.getRecordById(doctypeSlug, id)?.get('') as Record<string, unknown> | undefined\n\t\treturn readDisplay(fetched)\n\t} catch {\n\t\treturn undefined\n\t}\n})\n\nconst handleKeydown = (event: KeyboardEvent) => {\n\tif ((event.ctrlKey || event.metaKey) && event.key === 'k') {\n\t\tevent.preventDefault()\n\t\tcommandPaletteOpen.value = true\n\t}\n\tif (event.key === 'Escape' && commandPaletteOpen.value) {\n\t\tcommandPaletteOpen.value = false\n\t}\n}\n\nonMounted(() => {\n\tdocument.addEventListener('keydown', handleKeydown)\n})\n\nonUnmounted(() => {\n\tdocument.removeEventListener('keydown', handleKeydown)\n})\n</script>\n","import { App, type Plugin } from 'vue'\n\nimport ActionSet from '../components/ActionSet.vue'\nimport CommandPalette from '../components/CommandPalette.vue'\nimport Desktop from '../components/Desktop.vue'\nimport SheetNav from '../components/SheetNav.vue'\n\n/**\n * This is the main plugin that will be used to register all the desktop components.\n * @public\n */\nconst plugin: Plugin = {\n\tinstall: (app: App) => {\n\t\tapp.component('ActionSet', ActionSet)\n\t\tapp.component('CommandPalette', CommandPalette)\n\t\tapp.component('Desktop', Desktop)\n\t\tapp.component('SheetNav', SheetNav)\n\t},\n}\n\nexport default plugin\n"],"names":["emit","__emit","dropdownStates","ref","isOpen","dropdownOpen","onMounted","closeDropdowns","onClick","toggleDropdown","index","showDropdown","handleClick","action","label","_createElementBlock","_normalizeClass","_createElementVNode","_hoisted_1","_openBlock","_Fragment","_renderList","__props","el","_hoisted_2","$event","_toDisplayString","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_withDirectives","_hoisted_8","_hoisted_9","item","_hoisted_10","_hoisted_12","_vShow","query","selectedIndex","inputRef","useTemplateRef","results","computed","watch","open","nextTick","closeModal","handleKeydown","e","selectResult","result","_createBlock","_Teleport","_createVNode","_Transition","_renderSlot","_ctx","breadcrumbsVisibile","searchVisible","searchText","rotateHideTabIcon","toggleBreadcrumbs","toggleSearch","handleSearchInput","event","handleSearch","navigateHome","_component_router_link","_cache","_withKeys","breadcrumb","_createTextVNode","inlineLinkFieldnames","fields","links","flattenFields","field","componentLinkExpansion","unwrapLinkValue","value","stonecrop","useStonecrop","validationStore","useValidationStore","fieldErrors","currentView","loading","commandPaletteOpen","draftRecord","currentViewData","doctype","formatDoctypeName","getRecords","record","resolveRecordId","currentDoctype","currentRecordId","flat","isNewRecord","nested","child","newData","fieldsetNames","inlineLinks","flatData","fieldsetValues","key","nestedValue","changedFields","next","fieldname","normalized","hstStore","fieldPath","driveFieldValidation","error","triggers","route","unref","router","pathMatch","routeDoctype","isDraftRecordId","routeName","getAvailableTransitions","currentState","transitions","recordData","name","getAvailableCommands","commands","actionElements","elements","createNewRecord","recordActions","navigationBreadcrumbs","breadcrumbs","recordPath","searchCommands","doNavigate","cmd","executeCommand","command","listRecordsFetcher","options","word","target","navigateToDoctype","openRecord","recordId","DRAFT_RECORD_ID","flattenFieldsets","getDoctypesSchema","getRecordsSchema","registry","schema","doctypeSlug","clientConfigured","getRecordFormSchema","recordsData","currentViewSchema","handleActionClick","_label","getRecordIdFromRow","rowElement","cell","rowIndexAttr","rowIndex","cellText","row","cells","loadRecordData","provide","data","id","displayField","readDisplay","rec","val","cached","cachedDisplay","fetched","onUnmounted","ActionSet","_unref","AForm","SheetNav","CommandPalette","_withCtx","plugin","app","Desktop"],"mappings":"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"}
1
+ {"version":3,"file":"desktop.js","sources":["../src/components/ActionSet.vue","../src/components/CommandPalette.vue","../src/components/SheetNav.vue","../src/components/Desktop.vue","../src/plugins/index.ts"],"sourcesContent":["<template>\n\t<div :class=\"{ collapsed: !isOpen }\" class=\"action-set\">\n\t\t<div class=\"action-menu-icon\">\n\t\t\t<div id=\"cross\" :class=\"{ rotated: isOpen }\" @click=\"onClick\">×</div>\n\t\t</div>\n\t\t<div style=\"margin-right: 30px\"></div>\n\t\t<div v-for=\"(el, index) in elements\" :key=\"el.label\" class=\"action-element\">\n\t\t\t<div class=\"action-element-header\">\n\t\t\t\t<button\n\t\t\t\t\tv-if=\"el.type == 'button'\"\n\t\t\t\t\t:disabled=\"el.disabled\"\n\t\t\t\t\tclass=\"button-default\"\n\t\t\t\t\t@click=\"handleClick(el.action, el.label)\">\n\t\t\t\t\t{{ el.label }}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div v-if=\"el.type == 'dropdown'\">\n\t\t\t\t<div class=\"dropdown-header\">\n\t\t\t\t\t<div class=\"cross\" :class=\"{ rotated: dropdownOpen[index] }\" @click=\"toggleDropdown(index)\">×</div>\n\t\t\t\t\t<button class=\"button-default dropdown-title\" @click=\"toggleDropdown(index)\">\n\t\t\t\t\t\t{{ el.label }}\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t\t<div v-show=\"dropdownStates[index]\" class=\"dropdown-container\">\n\t\t\t\t\t<div class=\"dropdown\">\n\t\t\t\t\t\t<div v-for=\"item in el.actions\" :key=\"item.label\">\n\t\t\t\t\t\t\t<button v-if=\"item.action != null\" class=\"dropdown-item\" @click=\"handleClick(item.action, item.label)\">\n\t\t\t\t\t\t\t\t{{ item.label }}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<a v-else-if=\"item.link != null\" :href=\"item.link\"\n\t\t\t\t\t\t\t\t><button class=\"dropdown-item\">{{ item.label }}</button></a\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\nimport { onMounted, ref } from 'vue'\n\nimport type { ActionElements } from '../types'\n\nconst { elements = [] } = defineProps<{ elements?: ActionElements[] }>()\nconst emit = defineEmits<{\n\tactionClick: [label: string, action: (() => void | Promise<void>) | undefined]\n}>()\n\n// Track dropdown open state separately (index -> boolean)\nconst dropdownStates = ref<Record<number, boolean>>({})\n\nconst isOpen = ref(true)\nconst dropdownOpen = ref<boolean[]>([])\n\nonMounted(() => {\n\tcloseDropdowns()\n})\n\nconst closeDropdowns = () => {\n\tdropdownStates.value = {}\n\tdropdownOpen.value = []\n}\n\nconst onClick = () => {\n\tisOpen.value = !isOpen.value\n}\n\nconst toggleDropdown = (index: number) => {\n\tconst showDropdown = !dropdownStates.value[index]\n\tcloseDropdowns()\n\tif (showDropdown) {\n\t\tdropdownStates.value[index] = true\n\t\tdropdownOpen.value[index] = true\n\t}\n}\n\nconst handleClick = (action: (() => void | Promise<void>) | undefined, label: string) => {\n\t// Emit event to parent - parent will handle execution\n\temit('actionClick', label, action)\n}\n</script>\n\n<style scoped>\n#cross {\n\tposition: relative;\n\ttransform: rotate(45deg);\n\tcursor: pointer;\n\ttransition: all 0.2s ease-in-out;\n\tuser-select: none;\n\tline-height: 1rem;\n}\n#cross.rotated,\n.cross.rotated {\n\ttransform: rotate(0deg);\n}\n#cross svg {\n\twidth: 1.5em;\n\theight: 1.5em;\n}\n\n.action-set {\n\tposition: fixed;\n\ttop: 300px;\n\tright: 10px;\n\tpadding: 10px;\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: flex-end;\n\tbackground: var(--sc-form-background);\n\tborder: 1px solid var(--sc-gray-20);\n\tborder-left: 4px solid var(--sc-gray-20);\n\tborder-radius: 0;\n\toverflow: hidden;\n\tz-index: 1001; /* Above SheetNav (100) and operation log button (999) */\n\t-webkit-transition: all 0.5s ease-in-out;\n\t-moz-transition: all 0.5s ease-in-out;\n\t-o-transition: all 0.5s ease-in-out;\n\ttransition: all 0.5s ease-in-out;\n}\n.action-menu-icon {\n\tposition: relative;\n\tfont-size: 2rem;\n\tdisplay: inline-block;\n\tcolor: var(--sc-gray-60, #666);\n\ttransition: all 0.2s ease-in-out;\n}\n.action-set.collapsed {\n\tmax-width: 46px;\n\tmax-height: 40px;\n\toverflow: hidden;\n}\n.action-set.collapsed .action-element {\n\topacity: 0;\n\t-webkit-transition: opacity 0.25s ease-in-out;\n\t-moz-transition: opacity 0.25s ease-in-out;\n\t-o-transition: opacity 0.25s ease-in-out;\n\ttransition: opacity 0.25s ease-in-out;\n}\n\n.action-element {\n\twidth: 100%;\n\ttext-align: right;\n\tborder: 1px solid var(--sc-gray-20);\n\tbackground: none;\n\tfont-size: 1.5rem;\n\tfont-family: var(--sc-font-family);\n\tfont-weight: 600;\n\tmargin-top: 10px;\n\tposition: relative; /* Make this the positioning context for absolute children */\n}\n.action-element-header {\n\tdisplay: flex;\n\tjustify-content: end;\n}\nbutton.button-default {\n\tbackground-color: transparent;\n\tpadding: 5px 12px;\n\tborder-radius: 0px;\n\tbox-shadow: none;\n\tborder: none;\n\tcursor: pointer;\n\twhite-space: nowrap;\n\tfont-weight: bold;\n\tfont-size: 1rem;\n\ttext-align: right;\n\tcolor: var(--sc-gray-80);\n\tpadding-left: 50px;\n\tfont-family: var(--sc-font-family);\n}\n.dropdown-header:hover button.button-default,\n.dropdown-header:hover,\n.action-element-header:hover {\n\tbackground-color: #f2f2f2;\n}\n\n.dropdown-title {\n\tposition: relative;\n}\n.dropdown-header {\n\tdisplay: flex;\n\talign-items: center;\n}\n.cross {\n\tpointer-events: all;\n\tmargin-left: 5px;\n\tfont-family: var(--sc-font-family);\n\tcolor: var(--sc-gray-60, #666);\n\tcursor: pointer;\n\ttransform: rotate(45deg);\n\tuser-select: none;\n\ttransition: all 0.2s ease-in-out;\n\tline-height: 1rem;\n}\n\nbutton.dropdown-item {\n\twidth: 100%;\n\tpadding: 5px 12px;\n\ttext-align: right;\n\tborder: none;\n\tbackground-color: #ffffff;\n\tcursor: pointer;\n\tborder-radius: 5px;\n\tfont-size: 1rem;\n}\n\nbutton.dropdown-item:hover {\n\tbackground-color: #f2f2f2;\n}\n</style>\n","<template>\n\t<Teleport to=\"body\">\n\t\t<Transition name=\"fade\">\n\t\t\t<div v-if=\"isOpen\" class=\"command-palette-overlay\" @click=\"closeModal\">\n\t\t\t\t<div class=\"command-palette\" @click.stop>\n\t\t\t\t\t<div class=\"command-palette-header\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tref=\"input\"\n\t\t\t\t\t\t\tv-model=\"query\"\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tclass=\"command-palette-input\"\n\t\t\t\t\t\t\t:placeholder=\"placeholder\"\n\t\t\t\t\t\t\tautofocus\n\t\t\t\t\t\t\t@keydown=\"handleKeydown\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div v-if=\"results.length\" class=\"command-palette-results\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tv-for=\"(result, index) in results\"\n\t\t\t\t\t\t\t:key=\"index\"\n\t\t\t\t\t\t\tclass=\"command-palette-result\"\n\t\t\t\t\t\t\t:class=\"{ selected: index === selectedIndex }\"\n\t\t\t\t\t\t\t@click=\"selectResult(result)\"\n\t\t\t\t\t\t\t@mouseover=\"selectedIndex = index\">\n\t\t\t\t\t\t\t<div class=\"result-title\">\n\t\t\t\t\t\t\t\t<slot name=\"title\" :result=\"result\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"result-content\">\n\t\t\t\t\t\t\t\t<slot name=\"content\" :result=\"result\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div v-else-if=\"query && !results.length\" class=\"command-palette-no-results\">\n\t\t\t\t\t\t<slot name=\"empty\"> No results found for \"{{ query }}\" </slot>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</Transition>\n\t</Teleport>\n</template>\n\n<script setup lang=\"ts\" generic=\"T\">\nimport { ref, computed, watch, nextTick, useTemplateRef } from 'vue'\n\ndefineSlots<{\n\ttitle?: { result: T }\n\tcontent?: { result: T }\n\tempty?: null\n}>()\n\nconst {\n\tsearch,\n\tisOpen = false,\n\tplaceholder = 'Type a command or search...',\n\tmaxResults = 10,\n} = defineProps<{\n\tsearch: (query: string) => T[]\n\tisOpen?: boolean\n\tplaceholder?: string\n\tmaxResults?: number\n}>()\n\nconst emit = defineEmits<{\n\tselect: [T]\n\tclose: []\n}>()\n\nconst query = ref('')\nconst selectedIndex = ref(0)\nconst inputRef = useTemplateRef('input')\n\nconst results = computed(() => {\n\tif (!query.value) return []\n\tconst searchResults = search(query.value)\n\treturn searchResults.slice(0, maxResults)\n})\n\n// reset search query when modal opens\nwatch(\n\t() => isOpen,\n\tasync open => {\n\t\tif (open) {\n\t\t\tquery.value = ''\n\t\t\tselectedIndex.value = 0\n\t\t\tawait nextTick()\n\t\t\t;(inputRef.value as HTMLInputElement)?.focus()\n\t\t}\n\t}\n)\n\n// reset selected index when results change\nwatch(results, () => {\n\tselectedIndex.value = 0\n})\n\nconst closeModal = () => {\n\temit('close')\n}\n\nconst handleKeydown = (e: KeyboardEvent) => {\n\tswitch (e.key) {\n\t\tcase 'Escape':\n\t\t\tcloseModal()\n\t\t\tbreak\n\t\tcase 'ArrowDown':\n\t\t\te.preventDefault()\n\t\t\tif (results.value.length) {\n\t\t\t\tselectedIndex.value = (selectedIndex.value + 1) % results.value.length\n\t\t\t}\n\t\t\tbreak\n\t\tcase 'ArrowUp':\n\t\t\te.preventDefault()\n\t\t\tif (results.value.length) {\n\t\t\t\tselectedIndex.value = (selectedIndex.value - 1 + results.value.length) % results.value.length\n\t\t\t}\n\t\t\tbreak\n\t\tcase 'Enter':\n\t\t\tif (results.value.length && selectedIndex.value >= 0) {\n\t\t\t\tselectResult(results.value[selectedIndex.value])\n\t\t\t}\n\t\t\tbreak\n\t}\n}\n\nconst selectResult = (result: T) => {\n\temit('select', result)\n\tcloseModal()\n}\n</script>\n\n<style>\n.fade-enter-active,\n.fade-leave-active {\n\ttransition: opacity 0.2s ease;\n}\n\n.fade-enter-from,\n.fade-leave-to {\n\topacity: 0;\n}\n\n.command-palette-overlay {\n\tposition: fixed;\n\ttop: 0;\n\tleft: 0;\n\twidth: 100%;\n\theight: 100%;\n\tbackground-color: rgba(0, 0, 0, 0.5);\n\tdisplay: flex;\n\talign-items: flex-start;\n\tjustify-content: center;\n\tz-index: 300;\n\tpadding-top: 100px;\n}\n\n.command-palette {\n\twidth: 600px;\n\tmax-width: 90%;\n\tbackground-color: white;\n\tborder-radius: 8px;\n\tbox-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n\toverflow: hidden;\n\tmax-height: 80vh;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.command-palette-header {\n\tdisplay: flex;\n\tborder-bottom: 1px solid #eaeaea;\n\tpadding: 12px;\n}\n\n.command-palette-input {\n\tflex: 1;\n\tborder: none;\n\toutline: none;\n\tfont-size: 16px;\n\tpadding: 8px 12px;\n\tbackground-color: transparent;\n}\n\n.command-palette-close {\n\tbackground: transparent;\n\tborder: none;\n\tfont-size: 24px;\n\tcursor: pointer;\n\tcolor: #666;\n\tpadding: 0 8px;\n}\n\n.command-palette-close:hover {\n\tcolor: #333;\n}\n\n.command-palette-results {\n\toverflow-y: auto;\n\tmax-height: 60vh;\n}\n\n.command-palette-result {\n\tpadding: 12px 16px;\n\tcursor: pointer;\n\tborder-bottom: 1px solid #f0f0f0;\n}\n\n.command-palette-result:hover,\n.command-palette-result.selected {\n\tbackground-color: #f5f5f5;\n}\n\n.command-palette-result.selected {\n\tbackground-color: rgba(132, 60, 3, 0.1);\n}\n\n.result-title {\n\tfont-weight: 500;\n\tmargin-bottom: 4px;\n\tcolor: #333;\n}\n\n.result-content {\n\tfont-size: 14px;\n\tcolor: #666;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.command-palette-no-results {\n\tpadding: 20px 16px;\n\ttext-align: center;\n\tcolor: #666;\n}\n</style>\n","<template>\n\t<footer>\n\t\t<ul class=\"tabs\">\n\t\t\t<li class=\"hidebreadcrumbs\" @click=\"toggleBreadcrumbs\" @keydown.enter=\"toggleBreadcrumbs\">\n\t\t\t\t<a tabindex=\"0\"><div :class=\"rotateHideTabIcon\">×</div></a>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\tclass=\"hometab\"\n\t\t\t\t:style=\"{ display: breadcrumbsVisibile ? 'block' : 'none' }\"\n\t\t\t\t@click=\"navigateHome\"\n\t\t\t\t@keydown.enter=\"navigateHome\">\n\t\t\t\t<router-link to=\"/\" tabindex=\"0\">\n\t\t\t\t\t<svg class=\"icon\" aria-label=\"Home\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n\t\t\t\t\t\t<path d=\"M3 12l9-9 9 9\" />\n\t\t\t\t\t\t<path d=\"M9 21V12h6v9\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</router-link>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\t:class=\"['searchtab', { 'search-active': searchVisible }]\"\n\t\t\t\t:style=\"{ display: breadcrumbsVisibile ? 'block' : 'none' }\">\n\t\t\t\t<a tabindex=\"0\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\tv-show=\"!searchVisible\"\n\t\t\t\t\t\tclass=\"icon search-icon\"\n\t\t\t\t\t\trole=\"button\"\n\t\t\t\t\t\taria-label=\"Search\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t@click=\"toggleSearch\"\n\t\t\t\t\t\t@keydown.enter=\"toggleSearch\">\n\t\t\t\t\t\t<circle cx=\"11\" cy=\"11\" r=\"7\" />\n\t\t\t\t\t\t<path d=\"M21 21l-4.35-4.35\" />\n\t\t\t\t\t</svg>\n\t\t\t\t\t<input\n\t\t\t\t\t\tv-show=\"searchVisible\"\n\t\t\t\t\t\tref=\"searchinput\"\n\t\t\t\t\t\tv-model=\"searchText\"\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tplaceholder=\"Search...\"\n\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t@input=\"handleSearchInput($event)\"\n\t\t\t\t\t\t@blur=\"handleSearch($event)\"\n\t\t\t\t\t\t@keydown.enter=\"handleSearch($event)\"\n\t\t\t\t\t\t@keydown.escape=\"toggleSearch\" />\n\t\t\t\t</a>\n\t\t\t</li>\n\t\t\t<li\n\t\t\t\tv-for=\"breadcrumb in breadcrumbs\"\n\t\t\t\t:key=\"breadcrumb.title\"\n\t\t\t\t:style=\"{ display: breadcrumbsVisibile ? 'block' : 'none' }\">\n\t\t\t\t<router-link tabindex=\"0\" :to=\"breadcrumb.to\"> {{ breadcrumb.title }} </router-link>\n\t\t\t</li>\n\t\t</ul>\n\t</footer>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useTemplateRef } from 'vue'\n\nconst { breadcrumbs = [] } = defineProps<{ breadcrumbs?: { title: string; to: string }[] }>()\n\nconst breadcrumbsVisibile = ref(true)\nconst searchVisible = ref(false)\nconst searchText = ref('')\nconst inputRef = useTemplateRef<HTMLInputElement>('searchinput')\n\nconst rotateHideTabIcon = computed(() => {\n\treturn breadcrumbsVisibile.value ? 'unrotated' : 'rotated'\n})\n\nconst toggleBreadcrumbs = () => {\n\tbreadcrumbsVisibile.value = !breadcrumbsVisibile.value\n}\n\nconst toggleSearch = async () => {\n\tsearchVisible.value = !searchVisible.value\n\tawait nextTick(() => {\n\t\tinputRef.value?.focus()\n\t})\n}\n\nconst handleSearchInput = (event: Event | MouseEvent) => {\n\tevent.preventDefault()\n\tevent.stopPropagation()\n}\n\nconst handleSearch = async (event: FocusEvent | KeyboardEvent) => {\n\tevent.preventDefault()\n\tevent.stopPropagation()\n\tawait toggleSearch()\n}\n\nconst navigateHome = (/* event: MouseEvent | KeyboardEvent */) => {\n\t// navigate home\n}\n</script>\n\n<style scoped>\nfooter {\n\tposition: fixed;\n\tbottom: 0px;\n\twidth: 100%;\n\tbackground-color: transparent;\n\theight: auto;\n\tmin-height: 2.4rem;\n\tz-index: 100;\n\ttext-align: left;\n\tfont-size: 100%;\n\tdisplay: flex;\n\tjustify-content: right;\n\tpadding: 0 0.75rem 0 0;\n\tbox-sizing: border-box;\n}\nul {\n\tdisplay: flex;\n\tflex-direction: row-reverse;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.tabs li {\n\tfloat: left;\n\tlist-style-type: none;\n\tposition: relative;\n\tmargin-left: -1px;\n}\n\n/* Base tab styling */\n.tabs a {\n\tfloat: left;\n\tpadding: 0.5rem 1rem;\n\theight: 2.4rem;\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\ttext-decoration: none;\n\tcolor: var(--sc-gray-60, #666);\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tfont-size: 0.85rem;\n\tfont-family: var(--sc-font-family, system-ui, sans-serif);\n\ttransition: all 0.15s ease;\n\n\t/* Minimal ornamentation - subtle top radius only */\n\tborder-top-left-radius: 2px;\n\tborder-top-right-radius: 2px;\n}\n\n.tabs a:hover {\n\tbackground: var(--sc-btn-hover, #f2f2f2);\n}\n\n.tabs .router-link-active {\n\tz-index: 3;\n\tbackground: var(--sc-primary-color, #827553) !important;\n\tborder-color: var(--sc-primary-color, #827553) !important;\n\tcolor: var(--sc-primary-text-color, #fff) !important;\n}\n\n/* Pseudo-elements removed - minimal ornamentation style */\n\n/* Hide breadcrumbs tab */\n.hidebreadcrumbs a {\n\tmin-width: 2.4rem;\n\twidth: 2.4rem;\n\theight: 2.4rem;\n\tpadding: 0.5rem;\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tcolor: var(--sc-gray-60, #666);\n}\n\n.hidebreadcrumbs a div {\n\tfont-size: 1.2rem;\n}\n\n.rotated {\n\ttransform: rotate(45deg);\n\t-webkit-transform: rotate(45deg);\n\t-moz-transform: rotate(45deg);\n\t-ms-transform: rotate(45deg);\n\t-o-transform: rotate(45deg);\n\ttransition: transform 250ms ease;\n}\n.unrotated {\n\ttransform: rotate(0deg);\n\t-webkit-transform: rotate(0deg);\n\t-moz-transform: rotate(0deg);\n\t-ms-transform: rotate(0deg);\n\t-o-transform: rotate(0deg);\n\ttransition: transform 250ms ease;\n}\n\nli:active,\nli:hover,\nli:focus,\nli > a:active,\nli > a:hover,\nli > a:focus {\n\tz-index: 3;\n}\n\na:active,\na:hover,\na:focus {\n\toutline: 2px solid var(--sc-input-active-border-color, black);\n\tz-index: 3;\n}\n\n/* Home tab */\n.hometab a {\n\tmin-width: 2.4rem;\n\twidth: 2.4rem;\n\theight: 2.4rem;\n\tpadding: 0.5rem;\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tcolor: var(--sc-gray-60, #666);\n}\n\n/* SVG icon styling */\n.icon {\n\twidth: 1rem;\n\theight: 1rem;\n\tstroke: currentColor;\n\tflex-shrink: 0;\n}\n\n/* Search tab with animation */\n.searchtab {\n\toverflow: hidden;\n}\n\n.searchtab a {\n\tmin-width: 2.4rem;\n\theight: 2.4rem;\n\tpadding: 0.5rem;\n\tbackground: var(--sc-btn-color, #ffffff);\n\tborder: 1px solid var(--sc-btn-border, #ccc);\n\tcolor: var(--sc-gray-60, #666);\n\toverflow: hidden;\n\t/* Animation for smooth expand/collapse */\n\tmax-width: 2.4rem;\n\ttransition:\n\t\tmax-width 0.35s ease-in-out,\n\t\tpadding 0.35s ease-in-out,\n\t\tbackground 0.2s ease;\n}\n\n.searchtab .search-icon {\n\tcursor: pointer;\n}\n\n.searchtab input {\n\toutline: none;\n\tborder: 1px solid var(--sc-input-border-color, #ccc);\n\tborder-radius: 0.25rem;\n\tbackground-color: var(--sc-form-background, #ffffff);\n\tcolor: var(--sc-gray-80, #333);\n\ttext-align: left;\n\tfont-size: 0.875rem;\n\tpadding: 0.25rem 0.5rem;\n\twidth: 180px;\n\theight: 1.5rem;\n\tflex-shrink: 0;\n\topacity: 0;\n\ttransition: opacity 0.2s ease-in-out;\n\ttransition-delay: 0s;\n}\n\n.searchtab input:focus {\n\tborder-color: var(--sc-input-active-border-color, #4f46e5);\n\toutline: none;\n}\n\n.searchtab input::placeholder {\n\tcolor: var(--sc-input-label-color, #999);\n}\n\n/* Search active state - expanded with animation */\n.searchtab.search-active a {\n\tmax-width: 200px;\n\tmin-width: auto;\n\twidth: auto;\n\tpadding: 0.5rem 0.75rem;\n}\n\n.searchtab.search-active input {\n\topacity: 1;\n\ttransition-delay: 0.15s;\n}\n</style>\n","<template>\n\t<div class=\"desktop\" @click=\"handleClick\">\n\t\t<!-- Action Set -->\n\t\t<ActionSet :elements=\"actionElements\" @action-click=\"handleActionClick\" />\n\n\t\t<!-- Main content using AForm -->\n\t\t<AForm\n\t\t\tv-if=\"currentViewSchema.length > 0\"\n\t\t\tv-model:data=\"currentViewData\"\n\t\t\t:schema=\"currentViewSchema\"\n\t\t\t:errors=\"fieldErrors\" />\n\t\t<div v-else-if=\"!stonecrop\" class=\"loading\"><p>Initializing Stonecrop...</p></div>\n\t\t<div v-else class=\"loading\">\n\t\t\t<p>Loading {{ currentView }} data...</p>\n\t\t</div>\n\n\t\t<!-- Sheet Navigation -->\n\t\t<SheetNav :breadcrumbs=\"navigationBreadcrumbs\" />\n\n\t\t<!-- Command Palette -->\n\t\t<CommandPalette\n\t\t\t:is-open=\"commandPaletteOpen\"\n\t\t\t:search=\"searchCommands\"\n\t\t\tplaceholder=\"Type a command or search...\"\n\t\t\t@select=\"executeCommand\"\n\t\t\t@close=\"commandPaletteOpen = false\">\n\t\t\t<template #title=\"{ result }\">\n\t\t\t\t{{ result.title }}\n\t\t\t</template>\n\t\t\t<template #content=\"{ result }\">\n\t\t\t\t{{ result.description }}\n\t\t\t</template>\n\t\t</CommandPalette>\n\t</div>\n</template>\n\n<script setup lang=\"ts\">\n// The draft segment comes from @stonecrop/stonecrop rather than being spelled out here: that\n// package guards fetching, field initialization and workflow readiness on the same question, and\n// when the two were written separately they disagreed and every guard over there went dead.\nimport { DRAFT_RECORD_ID, isDraftRecordId, useStonecrop, useValidationStore } from '@stonecrop/stonecrop'\nimport {\n\tAForm,\n\ttype AFormLinkNavigator,\n\tresolvedFieldsToColumns,\n\ttype ResolvedField,\n\ttype ResolvedTable,\n} from '@stonecrop/aform'\nimport { componentLinkExpansion, type DoctypeField, flattenFields } from '@stonecrop/schema'\nimport { computed, onMounted, onUnmounted, provide, ref, unref, watch } from 'vue'\n\nimport ActionSet from './ActionSet.vue'\nimport SheetNav from './SheetNav.vue'\nimport CommandPalette from './CommandPalette.vue'\nimport type {\n\tActionElements,\n\tRouteAdapter,\n\tNavigationTarget,\n\tActionEventPayload,\n\tRecordOpenEventPayload,\n\tLoadRecordsEventPayload,\n\tLoadRecordEventPayload,\n} from '../types'\n\n/**\n * Inline link fields on a doctype — only these may be unwrapped back to a scalar id on write.\n *\n * Both the component and the doctype declaration decide what a field's value holds. An\n * *inline* link's value (`{ id, displayText }`) is indistinguishable by inspection from an\n * *expanded* one (`{ id, ...the whole target record }`), so the component is what tells them\n * apart.\n *\n * `componentLinkExpansion` is the same rule the adapter reads when it decides which links to\n * expand, so the two sides agree on what a field's value contains.\n */\nfunction inlineLinkFieldnames(fields: DoctypeField[]): Set<string> {\n\tconst links = flattenFields(fields).filter(field => field.kind === 'field' && Boolean(field.doctype))\n\n\treturn new Set(\n\t\tlinks.filter(field => componentLinkExpansion(field.component) === 'inline').map(field => field.fieldname)\n\t)\n}\n\n/**\n * Unwrap an inline link's value back to the id that gets persisted. Applied only to fields in\n * `inlineLinks` — the display text is a rendering concern and was never part of the record, while\n * an expanded link's object is the record and must survive the round trip intact.\n */\nfunction unwrapLinkValue(value: unknown): unknown {\n\tif (value !== null && typeof value === 'object' && !Array.isArray(value) && 'id' in value) {\n\t\treturn (value as { id: unknown }).id\n\t}\n\treturn value\n}\n\nconst { availableDoctypes = [], routeAdapter } = defineProps<{\n\tavailableDoctypes?: string[]\n\t/**\n\t * Pluggable router adapter. When provided, Desktop uses these functions for all\n\t * routing instead of reaching into the registry's internal Vue Router instance.\n\t * Nuxt hosts (or any host with custom route conventions) should supply this.\n\t */\n\trouteAdapter?: RouteAdapter\n}>()\n\nconst emit = defineEmits<{\n\t/**\n\t * Fired when the user triggers an FSM transition (action button click).\n\t * The host app is responsible for calling the server, persisting state, etc.\n\t */\n\taction: [payload: ActionEventPayload]\n\t/**\n\t * Fired when Desktop wants to navigate to a different view.\n\t * Also calls routeAdapter.navigate() if an adapter is provided.\n\t */\n\tnavigate: [target: NavigationTarget]\n\t/**\n\t * Fired when the user opens a specific record.\n\t */\n\t'record:open': [payload: RecordOpenEventPayload]\n\t/**\n\t * Fired when Desktop is about to read records for a list view. A notification, not a request:\n\t * Desktop performs the read itself through `Stonecrop.getRecords`. A host that fetches here\n\t * races that read into the same HST key.\n\t */\n\t'load-records': [payload: LoadRecordsEventPayload]\n\t/**\n\t * Fired when Desktop is about to read a single record for a form view. A notification, not a\n\t * request — see `load-records`. Not emitted for a draft, which has nothing to fetch.\n\t */\n\t'load-record': [payload: LoadRecordEventPayload]\n}>()\n\nconst { stonecrop } = useStonecrop()\n\n// Field-validation store (advisory, client-side). Pinia is a declared peerDependency, kept external\n// in this package's build (rollupOptions), so `useValidationStore()` resolves the host app's single\n// active Pinia directly — the old `getCurrentInstance().$pinia` workaround for the bundled-Pinia bug\n// is no longer needed. Still guarded: validation is optional and Desktop predates it, so a host that\n// mounts Desktop without Pinia disables validation gracefully instead of crashing on mount.\nlet validationStore: ReturnType<typeof useValidationStore> | null = null\ntry {\n\tvalidationStore = useValidationStore()\n} catch {\n\tvalidationStore = null\n}\n\n// Inline field errors handed to AForm. Only surfaced in the record form view (currentView is\n// defined below); empty elsewhere so a previous record's errors never bleed into a list view.\nconst fieldErrors = computed<Record<string, string[]>>(() =>\n\tcurrentView.value === 'record' ? (validationStore?.errorsByField ?? {}) : {}\n)\n\n// State\nconst loading = ref(false)\nconst commandPaletteOpen = ref(false)\n\n// The record being composed on a `/{doctype}/new` route. Deliberately not in HST: a draft has no\n// identity to be keyed by, and both ways of faking one fail — see `DRAFT_RECORD_ID`.\nconst draftRecord = ref<Record<string, any>>({})\n\n// Form/list data management — each view produces a different data shape.\n// List views (doctypes, records) return table row data keyed by fieldname.\n// Record view returns the record's fields for two-way binding — from HST, or from `draftRecord`\n// when the route is a draft.\nconst currentViewData = computed<Record<string, any>>({\n\tget() {\n\t\t// Doctypes list — rows come from availableDoctypes prop (reactive via availableDoctypes)\n\t\tif (currentView.value === 'doctypes') {\n\t\t\treturn {\n\t\t\t\tdoctypes_table:\n\t\t\t\t\tavailableDoctypes?.map(doctype => ({\n\t\t\t\t\t\tid: doctype,\n\t\t\t\t\t\tdoctype,\n\t\t\t\t\t\tdisplay_name: formatDoctypeName(doctype),\n\t\t\t\t\t\tactions: 'View Records',\n\t\t\t\t\t})) ?? [],\n\t\t\t}\n\t\t}\n\n\t\t// Records list — rows come from HST store (reactive because HST is Vue reactive())\n\t\tif (currentView.value === 'records') {\n\t\t\treturn {\n\t\t\t\trecords_table: getRecords().map(record =>\n\t\t\t\t\tObject.assign({}, record, {\n\t\t\t\t\t\tid: resolveRecordId(record) ?? '',\n\t\t\t\t\t\t// A list row is navigation. Actions live on the record view, where the Actions\n\t\t\t\t\t\t// dropdown is built from what the doctype declares and what the record's\n\t\t\t\t\t\t// current state allows. This cell used to also offer Delete, which dispatched\n\t\t\t\t\t\t// an action named `DELETE` that Desktop invented — no doctype in a\n\t\t\t\t\t\t// WorkflowMeta app declares it, so it failed on every click. Removal is a\n\t\t\t\t\t\t// workflow outcome (`archive`, `cancel`) and belongs in the doctype.\n\t\t\t\t\t\tactions: 'Edit',\n\t\t\t\t\t})\n\t\t\t\t),\n\t\t\t}\n\t\t}\n\n\t\t// Record form — read single record from HST\n\t\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\t\treturn {}\n\t\t}\n\n\t\ttry {\n\t\t\tconst source = isNewRecord.value\n\t\t\t\t? draftRecord.value\n\t\t\t\t: (stonecrop.value.getRecordById(currentDoctype.value, currentRecordId.value)?.get('') as\n\t\t\t\t\t\tRecord<string, any> | undefined)\n\t\t\t// Return a plain shallow copy so AForm mutations don't propagate directly into\n\t\t\t// the HST reactive object, which would bypass field-trigger diffing and cause\n\t\t\t// setupDeepReactivity to fire triggers for all fields on every keystroke.\n\t\t\tconst flat: Record<string, any> = { ...source }\n\n\t\t\t// AFieldset receives data[fieldsetFieldname] as its data prop, so the fieldset's\n\t\t\t// children must be grouped under the fieldset key. The server returns flat SQL rows,\n\t\t\t// so we nest fieldset children here before AForm renders them.\n\t\t\tconst doctype = stonecrop.value.registry.registry[currentDoctype.value]\n\t\t\tif (doctype) {\n\t\t\t\tfor (const field of doctype.getSchemaArray()) {\n\t\t\t\t\tif (field.kind === 'fieldset') {\n\t\t\t\t\t\tconst nested: Record<string, any> = {}\n\t\t\t\t\t\tfor (const child of field.schema) {\n\t\t\t\t\t\t\tif (child.fieldname) nested[child.fieldname] = flat[child.fieldname]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tflat[field.fieldname] = nested\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn flat\n\t\t} catch {\n\t\t\treturn {}\n\t\t}\n\t},\n\tset(newData: Record<string, any>) {\n\t\t// List views are read-only from AForm's perspective — HST writes only apply to record form\n\t\tif (currentView.value !== 'record') return\n\t\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\t// AForm emits nested data for fieldsets: { fieldsetKey: { childA: val } }.\n\t\t\t// HST and the server both expect flat rows, so flatten fieldset values back before writing.\n\t\t\tconst doctype = stonecrop.value.registry.registry[currentDoctype.value]\n\t\t\tconst fieldsetNames = new Set<string>()\n\t\t\tif (doctype) {\n\t\t\t\tfor (const field of doctype.getSchemaArray()) {\n\t\t\t\t\tif (field.kind === 'fieldset') {\n\t\t\t\t\t\tfieldsetNames.add(field.fieldname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst inlineLinks = inlineLinkFieldnames(doctype ? doctype.getSchemaArray() : [])\n\n\t\t\t// Two-pass flatten: non-fieldset keys first, then fieldset children.\n\t\t\t// Fieldset children must be applied last — AForm may emit stale flat copies\n\t\t\t// of fieldset children alongside the updated nested value, and the nested\n\t\t\t// value must win regardless of key insertion order.\n\t\t\tconst flatData: Record<string, any> = {}\n\t\t\tconst fieldsetValues: Record<string, any>[] = []\n\t\t\tfor (const [key, value] of Object.entries(newData)) {\n\t\t\t\tif (fieldsetNames.has(key) && value && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t\t\tfieldsetValues.push(value)\n\t\t\t\t} else {\n\t\t\t\t\tflatData[key] = value\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (const nestedValue of fieldsetValues) {\n\t\t\t\tObject.assign(flatData, nestedValue)\n\t\t\t}\n\n\t\t\t// Only update fields that actually changed. Never write undefined — AForm may emit\n\t\t\t// schema fields absent from the record as undefined; writing them would silently\n\t\t\t// clear values that exist. Explicit null is allowed (intentional clear).\n\t\t\tconst changedFields: string[] = []\n\t\t\tif (isNewRecord.value) {\n\t\t\t\t// Reassigned, not mutated, so the getter re-runs. Relying on in-place mutation of the\n\t\t\t\t// cached object is what made a draft's edits vanish on any invalidation.\n\t\t\t\tconst next = { ...draftRecord.value }\n\t\t\t\tfor (const [fieldname, value] of Object.entries(flatData)) {\n\t\t\t\t\tif (value === undefined) continue\n\t\t\t\t\tconst normalized = inlineLinks.has(fieldname) ? unwrapLinkValue(value) : value\n\t\t\t\t\tif (next[fieldname] !== normalized) {\n\t\t\t\t\t\tnext[fieldname] = normalized\n\t\t\t\t\t\tchangedFields.push(fieldname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdraftRecord.value = next\n\t\t\t} else {\n\t\t\t\tconst hstStore = stonecrop.value.getStore()\n\t\t\t\tfor (const [fieldname, value] of Object.entries(flatData)) {\n\t\t\t\t\tif (value === undefined) continue\n\t\t\t\t\tconst normalized = inlineLinks.has(fieldname) ? unwrapLinkValue(value) : value\n\t\t\t\t\tconst fieldPath = `${currentDoctype.value}.${currentRecordId.value}.${fieldname}`\n\t\t\t\t\tconst currentValue = hstStore.has(fieldPath) ? hstStore.get(fieldPath) : undefined\n\t\t\t\t\tif (currentValue !== normalized) {\n\t\t\t\t\t\thstStore.set(fieldPath, normalized)\n\t\t\t\t\t\tchangedFields.push(fieldname)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Advisory field-validation runs on the fields that actually changed (honors each\n\t\t\t// trigger's `on` set). Driven here, after the HST writes, so HST stays value-only.\n\t\t\tif (changedFields.length > 0) {\n\t\t\t\tdriveFieldValidation(changedFields)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn('HST update failed:', error)\n\t\t}\n\t},\n})\n\n// Advisory field-validation: run the doctype's triggers for the fields that changed on this edit.\n// Snapshots the post-edit record as a plain, flat object so validators read siblings read-only\n// (the core store freezes it). Fire-and-forget — the reactive error store repaints AForm on resolve.\nfunction driveFieldValidation(changedFields: string[]) {\n\tif (!validationStore || !stonecrop.value || !currentDoctype.value || !currentRecordId.value) return\n\n\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\tconst triggers = doctype?.getTriggers()\n\tif (!triggers || Object.keys(triggers).length === 0) return\n\n\t// A draft's siblings come from the buffer; reading HST would hand every validator an empty record.\n\tconst record = isNewRecord.value\n\t\t? { ...draftRecord.value }\n\t\t: {\n\t\t\t\t...(stonecrop.value.getRecordById(currentDoctype.value, currentRecordId.value)?.get('') as Record<\n\t\t\t\t\tstring,\n\t\t\t\t\tunknown\n\t\t\t\t>),\n\t\t\t}\n\n\tfor (const field of changedFields) {\n\t\tvoid validationStore.validateField(triggers, field, record)\n\t}\n}\n\n// Computed properties for current route context.\n// When a routeAdapter is provided it takes full precedence over the registry's internal router.\nconst route = computed(() => (routeAdapter ? null : unref(stonecrop.value?.registry.router?.currentRoute)))\nconst router = computed(() => (routeAdapter ? null : stonecrop.value?.registry.router))\nconst currentDoctype = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentDoctype()\n\tif (!route.value) return ''\n\n\t// First check if we have actualDoctype in meta (from registered routes)\n\tif (route.value.meta?.actualDoctype) {\n\t\treturn route.value.meta.actualDoctype as string\n\t}\n\n\t// For named routes, use params.doctype\n\tif (route.value.params.doctype) {\n\t\treturn route.value.params.doctype.toString()\n\t}\n\n\t// For catch-all routes that haven't been registered yet, extract from path\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 0) {\n\t\treturn pathMatch[0]\n\t}\n\n\treturn ''\n})\n\n// The route doctype for display and navigation (e.g., 'todo')\nconst routeDoctype = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentDoctype()\n\tif (!route.value) return ''\n\n\t// Check route meta first\n\tif (route.value.meta?.doctype) {\n\t\treturn route.value.meta.doctype as string\n\t}\n\n\t// For named routes, use params.doctype\n\tif (route.value.params.doctype) {\n\t\treturn route.value.params.doctype.toString()\n\t}\n\n\t// For catch-all routes, extract from path\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 0) {\n\t\treturn pathMatch[0]\n\t}\n\n\treturn ''\n})\n\nconst currentRecordId = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentRecordId()\n\tif (!route.value) return ''\n\n\t// For named routes, use params.recordId\n\tif (route.value.params.recordId) {\n\t\treturn route.value.params.recordId.toString()\n\t}\n\n\t// For catch-all routes that haven't been registered yet, extract from path\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 1) {\n\t\treturn pathMatch[1]\n\t}\n\n\treturn ''\n})\nconst isNewRecord = computed(() => isDraftRecordId(currentRecordId.value))\n\n// Determine current view based on route\nconst currentView = computed(() => {\n\tif (routeAdapter) return routeAdapter.getCurrentView()\n\tif (!route.value) {\n\t\treturn 'doctypes'\n\t}\n\n\t// Home route\n\tif (route.value.name === 'home' || route.value.path === '/') {\n\t\treturn 'doctypes'\n\t}\n\n\t// Named routes from registered doctypes\n\tif (route.value.name && route.value.name !== 'catch-all') {\n\t\tconst routeName = route.value.name as string\n\t\tif (routeName.includes('form') || route.value.params.recordId) {\n\t\t\treturn 'record'\n\t\t} else if (routeName.includes('list') || route.value.params.doctype) {\n\t\t\treturn 'records'\n\t\t}\n\t}\n\n\t// Catch-all route - determine from path structure\n\tconst pathMatch = route.value.params.pathMatch as string[] | undefined\n\tif (pathMatch && pathMatch.length > 0) {\n\t\tconst view = pathMatch.length === 1 ? 'records' : 'record'\n\t\treturn view\n\t}\n\n\treturn 'doctypes'\n})\n\n// Computed properties (now that all helper functions are defined)\n// Helper function to get available transitions for current record.\n// Reads the actual FSM state from the record's `status` field (or falls back to the\n// workflow initial state) so the available action buttons always reflect reality.\nconst getAvailableTransitions = () => {\n\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\treturn []\n\t}\n\n\ttry {\n\t\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\t\tif (!doctype?.workflow) return []\n\n\t\t// Delegate state resolution to Stonecrop — reads record 'status', falls back to workflow.initial\n\t\tconst currentState = stonecrop.value.getRecordState(currentDoctype.value, currentRecordId.value)\n\n\t\t// Delegate transition lookup to Doctype — no more manual workflow introspection\n\t\tconst transitions = doctype.getAvailableTransitions(currentState)\n\n\t\tconst recordData = currentViewData.value || {}\n\n\t\t// Each transition emits an 'action' event. The host app decides what to do\n\t\t// (call the server, trigger an FSM actor, update HST, etc.).\n\t\treturn transitions.map(({ name }) => ({\n\t\t\t// Prefer the workflow action's human-readable label (WorkflowMeta format);\n\t\t\t// fall back to the raw transition name for XState workflows with no action meta.\n\t\t\tlabel: doctype.getActionMeta(name)?.label ?? name,\n\t\t\taction: () => {\n\t\t\t\temit('action', {\n\t\t\t\t\tname,\n\t\t\t\t\tdoctype: currentDoctype.value,\n\t\t\t\t\trecordId: currentRecordId.value,\n\t\t\t\t\tdata: recordData,\n\t\t\t\t})\n\t\t\t},\n\t\t}))\n\t} catch (error) {\n\t\tconsole.warn('Error getting available transitions:', error)\n\t\treturn []\n\t}\n}\n\n// Helper: stateless Commands available for the current record — side-effect actions that\n// change no workflow state. Surfaced in the same Actions dropdown as transitions; each emits\n// the same 'action' event, so the host's handler runs a Command's clientHandler identically.\nconst getAvailableCommands = () => {\n\tif (!stonecrop.value || !currentDoctype.value || !currentRecordId.value) {\n\t\treturn []\n\t}\n\n\ttry {\n\t\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\t\tif (!doctype?.workflow) return []\n\n\t\tconst currentState = stonecrop.value.getRecordState(currentDoctype.value, currentRecordId.value)\n\t\tconst commands = doctype.getAvailableCommands(currentState)\n\t\tconst recordData = currentViewData.value || {}\n\n\t\treturn commands.map(({ name }) => ({\n\t\t\tlabel: doctype.getActionMeta(name)?.label ?? name,\n\t\t\taction: () => {\n\t\t\t\temit('action', {\n\t\t\t\t\tname,\n\t\t\t\t\tdoctype: currentDoctype.value,\n\t\t\t\t\trecordId: currentRecordId.value,\n\t\t\t\t\tdata: recordData,\n\t\t\t\t})\n\t\t\t},\n\t\t}))\n\t} catch (error) {\n\t\tconsole.warn('Error getting available commands:', error)\n\t\treturn []\n\t}\n}\n\nconst actionElements = computed(() => {\n\tconst elements: ActionElements[] = []\n\n\tswitch (currentView.value) {\n\t\tcase 'records':\n\t\t\telements.push({\n\t\t\t\ttype: 'button',\n\t\t\t\tlabel: 'New Record',\n\t\t\t\taction: () => void createNewRecord(),\n\t\t\t})\n\t\t\tbreak\n\t\tcase 'record': {\n\t\t\t// Populate the Actions dropdown with every FSM transition AND stateless Command\n\t\t\t// available in the record's current state. Clicking either emits 'action'.\n\t\t\tconst recordActions = [...getAvailableTransitions(), ...getAvailableCommands()]\n\t\t\tif (recordActions.length > 0) {\n\t\t\t\telements.push({\n\t\t\t\t\ttype: 'dropdown',\n\t\t\t\t\tlabel: 'Actions',\n\t\t\t\t\tactions: recordActions,\n\t\t\t\t})\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn elements\n})\n\nconst navigationBreadcrumbs = computed(() => {\n\tconst breadcrumbs: { title: string; to: string }[] = []\n\n\tif (currentView.value === 'records' && routeDoctype.value) {\n\t\tbreadcrumbs.push(\n\t\t\t{ title: 'Home', to: '/' },\n\t\t\t{ title: formatDoctypeName(routeDoctype.value), to: `/${routeDoctype.value}` }\n\t\t)\n\t} else if (currentView.value === 'record' && routeDoctype.value) {\n\t\tconst recordPath = currentRecordId.value\n\t\t\t? `/${routeDoctype.value}/${currentRecordId.value}`\n\t\t\t: (route.value?.fullPath ?? '')\n\t\tbreadcrumbs.push(\n\t\t\t{ title: 'Home', to: '/' },\n\t\t\t{ title: formatDoctypeName(routeDoctype.value), to: `/${routeDoctype.value}` },\n\t\t\t{ title: isNewRecord.value ? 'New Record' : 'Edit Record', to: recordPath }\n\t\t)\n\t}\n\n\treturn breadcrumbs\n})\n\n// Command palette functionality\ntype Command = {\n\ttitle: string\n\tdescription: string\n\taction: () => void\n}\n\nconst searchCommands = (query: string): Command[] => {\n\tconst commands: Command[] = [\n\t\t{\n\t\t\ttitle: 'Go Home',\n\t\t\tdescription: 'Navigate to the home page',\n\t\t\taction: () => void doNavigate({ view: 'doctypes' }),\n\t\t},\n\t\t{\n\t\t\ttitle: 'Toggle Command Palette',\n\t\t\tdescription: 'Open/close the command palette',\n\t\t\taction: () => (commandPaletteOpen.value = !commandPaletteOpen.value),\n\t\t},\n\t]\n\n\t// Add doctype-specific commands\n\tif (routeDoctype.value) {\n\t\tcommands.push({\n\t\t\ttitle: `View ${formatDoctypeName(routeDoctype.value)} Records`,\n\t\t\tdescription: `Navigate to ${routeDoctype.value} list`,\n\t\t\taction: () => void doNavigate({ view: 'records', doctype: routeDoctype.value }),\n\t\t})\n\n\t\tcommands.push({\n\t\t\ttitle: `Create New ${formatDoctypeName(routeDoctype.value)}`,\n\t\t\tdescription: `Create a new ${routeDoctype.value} record`,\n\t\t\taction: () => void createNewRecord(),\n\t\t})\n\t}\n\n\t// Add available doctypes as commands\n\tavailableDoctypes.forEach(doctype => {\n\t\tcommands.push({\n\t\t\ttitle: `View ${formatDoctypeName(doctype)}`,\n\t\t\tdescription: `Navigate to ${doctype} list`,\n\t\t\taction: () => void doNavigate({ view: 'records', doctype }),\n\t\t})\n\t})\n\n\t// Filter commands based on query\n\tif (!query) return commands\n\n\treturn commands.filter(\n\t\tcmd =>\n\t\t\tcmd.title.toLowerCase().includes(query.toLowerCase()) ||\n\t\t\tcmd.description.toLowerCase().includes(query.toLowerCase())\n\t)\n}\n\nconst executeCommand = (command: Command) => {\n\tcommand.action()\n\tcommandPaletteOpen.value = false\n}\n\n// List reads are wired on the records table; ATable owns the fetch via getRecords.\nconst listRecordsFetcher = (options?: import('@stonecrop/schema').GetRecordsOptions) => {\n\tif (!stonecrop.value || !currentDoctype.value) {\n\t\treturn Promise.resolve({ data: [], hasMore: false })\n\t}\n\tconst doctype = stonecrop.value.registry.getDoctype(currentDoctype.value)\n\tif (!doctype) {\n\t\treturn Promise.resolve({ data: [], hasMore: false })\n\t}\n\treturn stonecrop.value.getRecords(doctype, options)\n}\n\n// Helper functions - moved here to avoid \"before initialization\" errors\nconst formatDoctypeName = (doctype: string): string => {\n\treturn doctype\n\t\t.split('-')\n\t\t.map(word => word.charAt(0).toUpperCase() + word.slice(1))\n\t\t.join(' ')\n}\n\n// Internal navigation helper: emits 'navigate', then calls the adapter (if any)\n// or falls back to the registry's Vue Router instance.\nconst doNavigate = async (target: NavigationTarget) => {\n\temit('navigate', target)\n\tif (routeAdapter) {\n\t\tawait routeAdapter.navigate(target)\n\t} else {\n\t\tif (target.view === 'doctypes') {\n\t\t\tawait router.value?.push('/')\n\t\t} else if (target.view === 'records' && target.doctype) {\n\t\t\tawait router.value?.push(`/${target.doctype}`)\n\t\t} else if (target.view === 'record' && target.doctype && target.recordId) {\n\t\t\tawait router.value?.push(`/${target.doctype}/${target.recordId}`)\n\t\t}\n\t}\n}\n\nconst navigateToDoctype = async (doctype: string) => {\n\tawait doNavigate({ view: 'records', doctype })\n}\n\nconst openRecord = async (recordId: string) => {\n\tconst doctype = routeDoctype.value\n\temit('record:open', { doctype, recordId })\n\tawait doNavigate({ view: 'record', doctype, recordId })\n}\n\nconst createNewRecord = async () => {\n\tawait doNavigate({ view: 'record', doctype: routeDoctype.value, recordId: DRAFT_RECORD_ID })\n}\n\n// Schema generator functions - moved here to be available to computed properties\nconst getDoctypesSchema = (): ResolvedField[] => {\n\tif (!availableDoctypes?.length) return []\n\n\treturn [\n\t\t{\n\t\t\tkind: 'table' as const,\n\t\t\tfieldname: 'doctypes_table',\n\t\t\tcomponent: 'ATable',\n\t\t\tlabel: 'Doctypes',\n\t\t\tschema: [\n\t\t\t\t{\n\t\t\t\t\tfieldname: 'doctype',\n\t\t\t\t\tlabel: 'Doctype',\n\t\t\t\t\tcomponent: 'ATextInput',\n\t\t\t\t\talign: 'left' as const,\n\t\t\t\t\tedit: false,\n\t\t\t\t\twidth: '20ch',\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tfieldname: 'display_name',\n\t\t\t\t\tlabel: 'Name',\n\t\t\t\t\tcomponent: 'ATextInput',\n\t\t\t\t\talign: 'left' as const,\n\t\t\t\t\tedit: false,\n\t\t\t\t\twidth: '30ch',\n\t\t\t\t},\n\t\t\t\t// No record count column. It read `getRecordIds(doctype).length`, which is how many\n\t\t\t\t// records HST happens to hold — zero for a doctype never opened, and the page size\n\t\t\t\t// for one that was. The real total belongs to the backend and Desktop never asks\n\t\t\t\t// for it, so this shell cannot answer it. Same reason the `recordIdField` prop went.\n\t\t\t\t{\n\t\t\t\t\tfieldname: 'actions',\n\t\t\t\t\tlabel: 'Actions',\n\t\t\t\t\tcomponent: 'ATextInput',\n\t\t\t\t\talign: 'center' as const,\n\t\t\t\t\tedit: false,\n\t\t\t\t\twidth: '20ch',\n\t\t\t\t},\n\t\t\t],\n\t\t\tconfig: { view: 'list' as const, fullWidth: true },\n\t\t} satisfies ResolvedTable,\n\t]\n}\n\nconst getRecordsSchema = (): ResolvedField[] => {\n\tif (!currentDoctype.value) return []\n\tif (!stonecrop.value) return []\n\n\tconst registry = stonecrop.value.registry\n\tconst doctype = registry.registry[currentDoctype.value]\n\n\tif (!doctype) return []\n\n\tconst schema = registry.resolveSchema(doctype)\n\n\t// If no schema is available, let the template fallback handle the loading state\n\tif (schema.length === 0) return []\n\n\tconst doctypeSlug = currentDoctype.value\n\tconst clientConfigured = Boolean(stonecrop.value.getClient())\n\n\treturn [\n\t\t{\n\t\t\tkind: 'table' as const,\n\t\t\tfieldname: 'records_table',\n\t\t\tcomponent: 'ATable',\n\t\t\t// Which resolved fields a cell can render is answered once, in @stonecrop/aform, and\n\t\t\t// called by the child-table builder too. Flattening alone is not the rule: it kept the\n\t\t\t// expanding links, whose value is a nested record or an array of them.\n\t\t\tschema: [...resolvedFieldsToColumns(schema), { fieldname: 'actions', label: 'Actions', component: 'ATextInput' }],\n\t\t\tconfig: { view: 'list' as const, fullWidth: true },\n\t\t\t...(clientConfigured ? { getRecords: listRecordsFetcher, sourceKey: doctypeSlug } : {}),\n\t\t} satisfies ResolvedTable,\n\t]\n}\n\nconst getRecordFormSchema = (): ResolvedField[] => {\n\tif (!currentDoctype.value) return []\n\tif (!stonecrop.value) return []\n\n\ttry {\n\t\tconst registry = stonecrop.value?.registry\n\t\tconst doctype = registry?.registry[currentDoctype.value]\n\n\t\tif (!doctype?.schema) {\n\t\t\t// Let the template fallback handle the loading state\n\t\t\treturn []\n\t\t}\n\n\t\treturn registry.resolveSchema(doctype)\n\t} catch {\n\t\treturn []\n\t}\n}\n\n// Additional data helper functions\nconst getRecords = () => {\n\tif (!stonecrop.value || !currentDoctype.value) {\n\t\treturn []\n\t}\n\n\tconst recordsNode = stonecrop.value.records(currentDoctype.value)\n\tconst recordsData = recordsNode?.get('')\n\n\tif (recordsData && typeof recordsData === 'object' && !Array.isArray(recordsData)) {\n\t\treturn Object.values(recordsData as Record<string, any>)\n\t}\n\n\treturn []\n}\n\n// Schema for different views - defined here after all helper functions are available\nconst currentViewSchema = computed(() => {\n\tswitch (currentView.value) {\n\t\tcase 'doctypes':\n\t\t\treturn getDoctypesSchema()\n\t\tcase 'records':\n\t\t\treturn getRecordsSchema()\n\t\tcase 'record':\n\t\t\treturn getRecordFormSchema()\n\t\tdefault:\n\t\t\treturn []\n\t}\n})\n\nconst handleActionClick = (_label: string, action: (() => void | Promise<void>) | undefined) => {\n\tif (action) {\n\t\tvoid action()\n\t}\n}\n\n/**\n * Resolve a record's identity for links and navigation.\n *\n * Identity is declared once, on the doctype: `primaryKey`, or `id` when nothing is declared.\n * Delegating to `Doctype.getRecordId` is what guarantees this matches the key\n * `Stonecrop.getRecords` stored the record under — resolving it independently here would let a\n * row render a link to an HST path that does not exist.\n *\n * There is deliberately no per-shell override. Identity is a per-doctype fact and one shell\n * renders many doctypes, so a single prop cannot answer it; a shell that named a field would\n * also be overriding the very declaration the store keyed on, which is the bug above.\n */\nconst resolveRecordId = (record: Record<string, unknown>): string | undefined => {\n\tif (!stonecrop.value || !currentDoctype.value) return undefined\n\treturn stonecrop.value.registry.registry[currentDoctype.value]?.getRecordId(record)\n}\n\n// Event handlers\nconst getRecordIdFromRow = (rowElement: HTMLTableRowElement): string | null => {\n\tconst cell = rowElement.querySelector('td[data-rowindex]')\n\tif (!cell) return null\n\n\tconst rowIndexAttr = cell.getAttribute('data-rowindex')\n\tif (rowIndexAttr === null) return null\n\n\tconst rowIndex = parseInt(rowIndexAttr, 10)\n\tif (isNaN(rowIndex)) return null\n\n\tconst records = getRecords()\n\tconst record = records[rowIndex]\n\tif (!record) return null\n\n\treturn resolveRecordId(record) ?? null\n}\n\nconst handleClick = async (event: Event) => {\n\tconst target = event.target as HTMLElement\n\tconst action = target.getAttribute('data-action')\n\n\tif (action === 'create') {\n\t\tawait createNewRecord()\n\t}\n\n\tconst cell = target.closest('td, th')\n\tif (cell) {\n\t\tconst cellText = cell.textContent?.trim()\n\t\tconst row = cell.closest('tr')\n\n\t\tif (cellText === 'View Records' && row) {\n\t\t\t// Get the doctype from the row data\n\t\t\tconst cells = row.querySelectorAll('td')\n\t\t\tif (cells.length > 0) {\n\t\t\t\tconst doctypeCell = cells[1] // Assuming doctype is in second column (first column is index)\n\t\t\t\tconst doctype = doctypeCell.textContent?.trim()\n\t\t\t\tif (doctype) {\n\t\t\t\t\tawait navigateToDoctype(doctype)\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (cellText === 'Edit' && row) {\n\t\t\t// Matched exactly, not by substring. This handler is bound to the whole desktop, so a\n\t\t\t// substring match fired on any cell whose *data* happened to contain the word — a task\n\t\t\t// titled \"Delete old backups\" popped a delete confirmation when you clicked it.\n\t\t\tconst recordId = getRecordIdFromRow(row)\n\t\t\tif (recordId) {\n\t\t\t\tawait openRecord(recordId)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Reads go through Stonecrop, which owns whether to fetch, how to key the result, and where to\n// put it. Desktop asks for data and renders what arrives; it decides none of that itself.\n//\n// Both loaders are no-ops without a client, so a host that populates HST some other way keeps\n// working unchanged rather than taking a thrown error on every navigation.\nconst loadRecordData = async () => {\n\tif (!stonecrop.value || !currentDoctype.value || !stonecrop.value.getClient()) return\n\n\tloading.value = true\n\ttry {\n\t\tawait stonecrop.value.getRecord(currentDoctype.value, currentRecordId.value)\n\t} catch (error) {\n\t\tconsole.warn('Error fetching record:', error)\n\t} finally {\n\t\tloading.value = false\n\t}\n}\n\n// Watch for route changes to load appropriate data\nwatch(\n\t[currentView, currentDoctype, currentRecordId],\n\t() => {\n\t\t// The events are notifications, not fetch requests: they announce what Desktop is about to\n\t\t// read so a host can hang analytics or a prefetch off them. The read itself is Stonecrop's.\n\t\tif (currentView.value === 'records' && currentDoctype.value) {\n\t\t\temit('load-records', { doctype: currentDoctype.value })\n\t\t} else if (currentView.value === 'record' && currentDoctype.value && currentRecordId.value) {\n\t\t\t// A draft has nothing to fetch — the record does not exist on the server yet. Desktop\n\t\t\t// used to emit anyway and leave the host to work it out, which meant every host had to\n\t\t\t// recognise the private draft-id scheme above just to suppress a doomed request; all of\n\t\t\t// them did, identically. `getRecord` declines the same case for the same reason.\n\t\t\tif (isNewRecord.value) return\n\n\t\t\temit('load-record', { doctype: currentDoctype.value, recordId: currentRecordId.value })\n\t\t\tvoid loadRecordData()\n\t\t}\n\t},\n\t{ immediate: true }\n)\n\n// Clear advisory validation errors when the target record changes, so errors from a previously\n// edited record never bleed into a newly opened one.\nwatch([currentDoctype, currentRecordId], () => {\n\tvalidationStore?.clearAll()\n})\n\n// Seeding on entry gives a new record the doctype's declared defaults, which it never used to get.\n// Discarding on exit matters as much: the draft segment is one shared literal, so a stale buffer\n// would open the next New Record pre-filled with the abandoned one's values.\nwatch(\n\t[currentDoctype, currentRecordId],\n\t() => {\n\t\tif (!isNewRecord.value) {\n\t\t\tdraftRecord.value = {}\n\t\t\treturn\n\t\t}\n\t\tconst registry = stonecrop.value?.registry\n\t\tdraftRecord.value = registry ? registry.initializeRecord(getRecordFormSchema()) : {}\n\t},\n\t{ immediate: true }\n)\n\n// Stonecrop reactive computed properties update automatically when the instance\n// becomes available — no manual watcher needed.\n\n// Provide navigation helpers and an emitAction convenience function to child components.\nconst desktopMethods = {\n\tnavigateToDoctype,\n\topenRecord,\n\tcreateNewRecord,\n\t/**\n\t * Convenience wrapper so child components (e.g. slot content) can emit\n\t * an action event without needing a direct reference to the emit function.\n\t */\n\temitAction: (name: string, data?: Record<string, any>) => {\n\t\temit('action', {\n\t\t\tname,\n\t\t\tdoctype: currentDoctype.value,\n\t\t\trecordId: currentRecordId.value,\n\t\t\tdata: data ?? currentViewData.value ?? {},\n\t\t})\n\t},\n}\n\nprovide('desktopMethods', desktopMethods)\n\n// Provide a navigator for AFormLink so the arrow button navigates to the linked record.\nprovide('aformLinkNavigator', {\n\tnavigate: (doctype: string, id: string | number) => {\n\t\tvoid doNavigate({ view: 'record', doctype, recordId: String(id) })\n\t},\n} satisfies AFormLinkNavigator)\n\n// Provide a resolver for AFormLink to look up display text by doctype + id.\n// Checks HST first (sync); falls back to an async client fetch if not cached.\n// Uses the target doctype's declared displayField — no heuristic field guessing.\nprovide('aformLinkResolver', async (doctypeSlug: string, id: string): Promise<string | undefined> => {\n\tif (!stonecrop.value) return undefined\n\ttry {\n\t\tconst meta = await stonecrop.value.getMeta({ path: `/${doctypeSlug}`, segments: [doctypeSlug] })\n\t\tconst displayField = meta?.displayField\n\t\tif (!displayField) return undefined\n\n\t\tconst readDisplay = (rec: Record<string, unknown> | undefined): string | undefined => {\n\t\t\tif (!rec) return undefined\n\t\t\tconst val = rec[displayField]\n\t\t\treturn typeof val === 'string' || typeof val === 'number' ? String(val) : undefined\n\t\t}\n\n\t\tconst cached = stonecrop.value.getRecordById(doctypeSlug, id)?.get('') as Record<string, unknown> | undefined\n\t\tconst cachedDisplay = readDisplay(cached)\n\t\tif (cachedDisplay != null) return cachedDisplay\n\n\t\tawait stonecrop.value.getRecord(doctypeSlug, id)\n\t\tconst fetched = stonecrop.value.getRecordById(doctypeSlug, id)?.get('') as Record<string, unknown> | undefined\n\t\treturn readDisplay(fetched)\n\t} catch {\n\t\treturn undefined\n\t}\n})\n\nconst handleKeydown = (event: KeyboardEvent) => {\n\tif ((event.ctrlKey || event.metaKey) && event.key === 'k') {\n\t\tevent.preventDefault()\n\t\tcommandPaletteOpen.value = true\n\t}\n\tif (event.key === 'Escape' && commandPaletteOpen.value) {\n\t\tcommandPaletteOpen.value = false\n\t}\n}\n\nonMounted(() => {\n\tdocument.addEventListener('keydown', handleKeydown)\n})\n\nonUnmounted(() => {\n\tdocument.removeEventListener('keydown', handleKeydown)\n})\n</script>\n","import { App, type Plugin } from 'vue'\n\nimport ActionSet from '../components/ActionSet.vue'\nimport CommandPalette from '../components/CommandPalette.vue'\nimport Desktop from '../components/Desktop.vue'\nimport SheetNav from '../components/SheetNav.vue'\n\n/**\n * This is the main plugin that will be used to register all the desktop components.\n * @public\n */\nconst plugin: Plugin = {\n\tinstall: (app: App) => {\n\t\tapp.component('ActionSet', ActionSet)\n\t\tapp.component('CommandPalette', CommandPalette)\n\t\tapp.component('Desktop', Desktop)\n\t\tapp.component('SheetNav', SheetNav)\n\t},\n}\n\nexport default plugin\n"],"names":["emit","__emit","dropdownStates","ref","isOpen","dropdownOpen","onMounted","closeDropdowns","onClick","toggleDropdown","index","showDropdown","handleClick","action","label","_createElementBlock","_normalizeClass","_createElementVNode","_hoisted_1","_openBlock","_Fragment","_renderList","__props","el","_hoisted_2","$event","_toDisplayString","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_withDirectives","_hoisted_8","_hoisted_9","item","_hoisted_10","_hoisted_12","_vShow","query","selectedIndex","inputRef","useTemplateRef","results","computed","watch","open","nextTick","closeModal","handleKeydown","e","selectResult","result","_createBlock","_Teleport","_createVNode","_Transition","_renderSlot","_ctx","breadcrumbsVisibile","searchVisible","searchText","rotateHideTabIcon","toggleBreadcrumbs","toggleSearch","handleSearchInput","event","handleSearch","navigateHome","_component_router_link","_cache","_withKeys","breadcrumb","_createTextVNode","inlineLinkFieldnames","fields","links","flattenFields","field","componentLinkExpansion","unwrapLinkValue","value","stonecrop","useStonecrop","validationStore","useValidationStore","fieldErrors","currentView","loading","commandPaletteOpen","draftRecord","currentViewData","doctype","formatDoctypeName","getRecords","record","resolveRecordId","currentDoctype","currentRecordId","flat","isNewRecord","nested","child","newData","fieldsetNames","inlineLinks","flatData","fieldsetValues","key","nestedValue","changedFields","next","fieldname","normalized","hstStore","fieldPath","driveFieldValidation","error","triggers","route","unref","router","pathMatch","routeDoctype","isDraftRecordId","routeName","getAvailableTransitions","currentState","transitions","recordData","name","getAvailableCommands","commands","actionElements","elements","createNewRecord","recordActions","navigationBreadcrumbs","breadcrumbs","recordPath","searchCommands","doNavigate","cmd","executeCommand","command","listRecordsFetcher","options","word","target","navigateToDoctype","openRecord","recordId","DRAFT_RECORD_ID","getDoctypesSchema","getRecordsSchema","registry","schema","doctypeSlug","clientConfigured","resolvedFieldsToColumns","getRecordFormSchema","recordsData","currentViewSchema","handleActionClick","_label","getRecordIdFromRow","rowElement","cell","rowIndexAttr","rowIndex","cellText","row","cells","loadRecordData","provide","data","id","displayField","readDisplay","rec","val","cached","cachedDisplay","fetched","onUnmounted","ActionSet","_unref","AForm","SheetNav","CommandPalette","_withCtx","plugin","app","Desktop"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stonecrop/desktop",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "Desktop-specific components for Stonecrop UI",
5
5
  "bugs": {
6
6
  "url": "https://github.com/agritheory/stonecrop/issues"
@@ -33,11 +33,11 @@
33
33
  "src/*"
34
34
  ],
35
35
  "dependencies": {
36
- "@stonecrop/aform": "0.25.0",
37
- "@stonecrop/atable": "0.25.0",
38
- "@stonecrop/schema": "0.25.0",
39
- "@stonecrop/stonecrop": "0.25.0",
40
- "@stonecrop/themes": "0.25.0"
36
+ "@stonecrop/atable": "0.27.0",
37
+ "@stonecrop/schema": "0.27.0",
38
+ "@stonecrop/aform": "0.27.0",
39
+ "@stonecrop/stonecrop": "0.27.0",
40
+ "@stonecrop/themes": "0.27.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@microsoft/api-documenter": "^7.30.5",
@@ -39,8 +39,14 @@
39
39
  // package guards fetching, field initialization and workflow readiness on the same question, and
40
40
  // when the two were written separately they disagreed and every guard over there went dead.
41
41
  import { DRAFT_RECORD_ID, isDraftRecordId, useStonecrop, useValidationStore } from '@stonecrop/stonecrop'
42
- import { AForm, type AFormLinkNavigator, type ResolvedField, type ResolvedTable } from '@stonecrop/aform'
43
- import { type ColumnSchema, componentLinkExpansion, type DoctypeField, flattenFields } from '@stonecrop/schema'
42
+ import {
43
+ AForm,
44
+ type AFormLinkNavigator,
45
+ resolvedFieldsToColumns,
46
+ type ResolvedField,
47
+ type ResolvedTable,
48
+ } from '@stonecrop/aform'
49
+ import { componentLinkExpansion, type DoctypeField, flattenFields } from '@stonecrop/schema'
44
50
  import { computed, onMounted, onUnmounted, provide, ref, unref, watch } from 'vue'
45
51
 
46
52
  import ActionSet from './ActionSet.vue'
@@ -669,21 +675,6 @@ const createNewRecord = async () => {
669
675
  await doNavigate({ view: 'record', doctype: routeDoctype.value, recordId: DRAFT_RECORD_ID })
670
676
  }
671
677
 
672
- // Flatten Fieldset containers into individual columns for list/table views.
673
- // A Fieldset groups form fields visually but has no DB column; its children are the
674
- // actual data fields and should appear as flat columns in a list view.
675
- const flattenFieldsets = (fields: ResolvedField[]): ResolvedField[] => {
676
- const result: ResolvedField[] = []
677
- for (const field of fields) {
678
- if (field.kind === 'fieldset') {
679
- result.push(...flattenFieldsets(field.schema))
680
- } else {
681
- result.push(field)
682
- }
683
- }
684
- return result
685
- }
686
-
687
678
  // Schema generator functions - moved here to be available to computed properties
688
679
  const getDoctypesSchema = (): ResolvedField[] => {
689
680
  if (!availableDoctypes?.length) return []
@@ -751,10 +742,10 @@ const getRecordsSchema = (): ResolvedField[] => {
751
742
  kind: 'table' as const,
752
743
  fieldname: 'records_table',
753
744
  component: 'ATable',
754
- schema: [
755
- ...(flattenFieldsets(schema) as ColumnSchema[]),
756
- { fieldname: 'actions', label: 'Actions', component: 'ATextInput' },
757
- ],
745
+ // Which resolved fields a cell can render is answered once, in @stonecrop/aform, and
746
+ // called by the child-table builder too. Flattening alone is not the rule: it kept the
747
+ // expanding links, whose value is a nested record or an array of them.
748
+ schema: [...resolvedFieldsToColumns(schema), { fieldname: 'actions', label: 'Actions', component: 'ATextInput' }],
758
749
  config: { view: 'list' as const, fullWidth: true },
759
750
  ...(clientConfigured ? { getRecords: listRecordsFetcher, sourceKey: doctypeSlug } : {}),
760
751
  } satisfies ResolvedTable,