@stonecrop/desktop 0.25.0 → 0.26.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 +114 -119
- package/dist/desktop.js.map +1 -1
- package/package.json +6 -6
- package/src/components/Desktop.vue +12 -21
package/dist/desktop.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineComponent as Q, ref as M, onMounted as
|
|
2
|
-
import { useStonecrop as
|
|
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
|
-
|
|
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) => (
|
|
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
|
-
(
|
|
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" ? (
|
|
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" ? (
|
|
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
|
-
(
|
|
62
|
+
(y(!0), g(W, null, q(s.actions, (u) => (y(), g("div", {
|
|
63
63
|
key: u.label
|
|
64
64
|
}, [
|
|
65
|
-
u.action != null ? (
|
|
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 ? (
|
|
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
|
-
},
|
|
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
|
-
},
|
|
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 =
|
|
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
|
|
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) => (
|
|
135
|
-
L(
|
|
134
|
+
return (i, f) => (y(), pe(Te, { to: "body" }, [
|
|
135
|
+
L(Ve, { name: "fade" }, {
|
|
136
136
|
default: z(() => [
|
|
137
|
-
l.isOpen ? (
|
|
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] =
|
|
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
|
-
[
|
|
157
|
+
[me, w.value]
|
|
158
158
|
])
|
|
159
159
|
]),
|
|
160
|
-
p.value.length ? (
|
|
161
|
-
(
|
|
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 ? (
|
|
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 =
|
|
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
|
|
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 =
|
|
206
|
-
return
|
|
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((
|
|
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] =
|
|
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
|
-
[
|
|
285
|
+
[me, w.value]
|
|
286
286
|
])
|
|
287
287
|
])
|
|
288
288
|
], 6),
|
|
289
|
-
(
|
|
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
|
-
}),
|
|
307
|
+
}), we = /* @__PURE__ */ ye(ft, [["__scopeId", "data-v-0072260b"]]), mt = {
|
|
308
308
|
key: 1,
|
|
309
309
|
class: "loading"
|
|
310
|
-
},
|
|
310
|
+
}, yt = {
|
|
311
311
|
key: 2,
|
|
312
312
|
class: "loading"
|
|
313
|
-
},
|
|
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 } =
|
|
330
|
+
const d = S, { stonecrop: o } = Fe();
|
|
331
331
|
let p = null;
|
|
332
332
|
try {
|
|
333
|
-
p =
|
|
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:
|
|
352
|
+
records_table: le().map(
|
|
353
353
|
(e) => Object.assign({}, e, {
|
|
354
|
-
id:
|
|
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 || !
|
|
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,
|
|
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 || !
|
|
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}.${
|
|
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 || !
|
|
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,
|
|
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
|
-
}),
|
|
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 = $(() =>
|
|
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
|
-
}),
|
|
467
|
-
if (!o.value || !n.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,
|
|
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:
|
|
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
|
-
},
|
|
490
|
-
if (!o.value || !n.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,
|
|
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:
|
|
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
|
-
},
|
|
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 = [...
|
|
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
|
-
}),
|
|
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 =
|
|
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
|
-
}),
|
|
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
|
-
},
|
|
587
|
+
}, Re = (e) => {
|
|
588
588
|
e.action(), C.value = !1;
|
|
589
|
-
},
|
|
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:
|
|
603
|
-
},
|
|
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
|
-
] : [],
|
|
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
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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:
|
|
659
|
+
...v ? { getRecords: Se, sourceKey: m } : {}
|
|
665
660
|
}
|
|
666
661
|
];
|
|
667
|
-
},
|
|
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
|
-
},
|
|
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
|
-
},
|
|
676
|
+
}, se = $(() => {
|
|
682
677
|
switch (x.value) {
|
|
683
678
|
case "doctypes":
|
|
684
|
-
return
|
|
679
|
+
return $e();
|
|
685
680
|
case "records":
|
|
686
|
-
return
|
|
681
|
+
return Ie();
|
|
687
682
|
case "record":
|
|
688
|
-
return
|
|
683
|
+
return re();
|
|
689
684
|
default:
|
|
690
685
|
return [];
|
|
691
686
|
}
|
|
692
|
-
}),
|
|
687
|
+
}), _e = (e, t) => {
|
|
693
688
|
t && t();
|
|
694
|
-
},
|
|
689
|
+
}, ce = (e) => {
|
|
695
690
|
if (!(!o.value || !n.value))
|
|
696
691
|
return o.value.registry.registry[n.value]?.getRecordId(e);
|
|
697
|
-
},
|
|
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 =
|
|
705
|
-
return A ?
|
|
706
|
-
},
|
|
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 =
|
|
714
|
+
const I = xe(A);
|
|
720
715
|
I && await ne(I);
|
|
721
716
|
}
|
|
722
717
|
}
|
|
723
|
-
},
|
|
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,
|
|
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,
|
|
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 &&
|
|
735
|
+
else if (x.value === "record" && n.value && h.value) {
|
|
741
736
|
if (B.value) return;
|
|
742
|
-
d("load-record", { doctype: n.value, recordId:
|
|
737
|
+
d("load-record", { doctype: n.value, recordId: h.value }), Me();
|
|
743
738
|
}
|
|
744
739
|
},
|
|
745
740
|
{ immediate: !0 }
|
|
746
|
-
), H([n,
|
|
741
|
+
), H([n, h], () => {
|
|
747
742
|
p?.clearAll();
|
|
748
743
|
}), H(
|
|
749
|
-
[n,
|
|
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(
|
|
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:
|
|
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
|
|
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
|
|
801
|
-
document.addEventListener("keydown",
|
|
802
|
-
}),
|
|
803
|
-
document.removeEventListener("keydown",
|
|
804
|
-
}), (e, t) => (
|
|
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:
|
|
801
|
+
onClick: Ne
|
|
807
802
|
}, [
|
|
808
|
-
L(
|
|
809
|
-
elements:
|
|
810
|
-
onActionClick:
|
|
803
|
+
L(he, {
|
|
804
|
+
elements: Ce.value,
|
|
805
|
+
onActionClick: _e
|
|
811
806
|
}, null, 8, ["elements"]),
|
|
812
|
-
|
|
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:
|
|
811
|
+
schema: se.value,
|
|
817
812
|
errors: _.value
|
|
818
|
-
}, null, 8, ["data", "schema", "errors"])) : ee(o) ? (
|
|
813
|
+
}, null, 8, ["data", "schema", "errors"])) : ee(o) ? (y(), g("div", yt, [
|
|
819
814
|
c("p", null, "Loading " + V(x.value) + " data...", 1)
|
|
820
|
-
])) : (
|
|
815
|
+
])) : (y(), g("div", mt, [...t[2] || (t[2] = [
|
|
821
816
|
c("p", null, "Initializing Stonecrop...", -1)
|
|
822
817
|
])])),
|
|
823
|
-
L(
|
|
824
|
-
L(
|
|
818
|
+
L(we, { breadcrumbs: Ae.value }, null, 8, ["breadcrumbs"]),
|
|
819
|
+
L(ge, {
|
|
825
820
|
"is-open": C.value,
|
|
826
|
-
search:
|
|
821
|
+
search: De,
|
|
827
822
|
placeholder: "Type a command or search...",
|
|
828
|
-
onSelect:
|
|
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",
|
|
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
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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
|
package/dist/desktop.js.map
CHANGED
|
@@ -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":";;;;;;;;;;;AA8CA,UAAMA,IAAOC,GAKPC,IAAiBC,EAA6B,EAAE,GAEhDC,IAASD,EAAI,EAAI,GACjBE,IAAeF,EAAe,EAAE;AAEtC,IAAAG,GAAU,MAAM;AACf,MAAAC,EAAA;AAAA,IACD,CAAC;AAED,UAAMA,IAAiB,MAAM;AAC5B,MAAAL,EAAe,QAAQ,CAAA,GACvBG,EAAa,QAAQ,CAAA;AAAA,IACtB,GAEMG,IAAU,MAAM;AACrB,MAAAJ,EAAO,QAAQ,CAACA,EAAO;AAAA,IACxB,GAEMK,IAAiB,CAACC,MAAkB;AACzC,YAAMC,IAAe,CAACT,EAAe,MAAMQ,CAAK;AAChD,MAAAH,EAAA,GACII,MACHT,EAAe,MAAMQ,CAAK,IAAI,IAC9BL,EAAa,MAAMK,CAAK,IAAI;AAAA,IAE9B,GAEME,IAAc,CAACC,GAAkDC,MAAkB;AAExF,MAAAd,EAAK,eAAec,GAAOD,CAAM;AAAA,IAClC;2BAhFCE,EAoCM,OAAA;AAAA,MApCA,OAAKC,EAAA,CAAA,EAAA,WAAA,CAAgBZ,EAAA,MAAA,GAAgB,YAAY,CAAA;AAAA,IAAA;MACtDa,EAEM,OAFNC,IAEM;AAAA,QADLD,EAAqE,OAAA;AAAA,UAAhE,IAAG;AAAA,UAAS,oBAAkBb,EAAA,OAAM;AAAA,UAAK,SAAAI;AAAA,QAAA,GAAgB,KAAC,CAAA;AAAA,MAAA;sBAEhES,EAAsC,OAAA,EAAjC,OAAA,EAAA,gBAAA,OAAA,EAAA,GAA0B,MAAA,EAAA;AAAA,OAC/BE,EAAA,EAAA,GAAAJ,EA8BMK,GAAA,MAAAC,EA9BqBC,EAAA,UAAQ,CAAtBC,GAAIb,YAAjBK,EA8BM,OAAA;AAAA,QA9BgC,KAAKQ,EAAG;AAAA,QAAO,OAAM;AAAA,MAAA;QAC1DN,EAQM,OARNO,IAQM;AAAA,UANED,EAAG,QAAI,iBADdR,EAMS,UAAA;AAAA;YAJP,UAAUQ,EAAG;AAAA,YACd,OAAM;AAAA,YACL,SAAK,CAAAE,MAAEb,EAAYW,EAAG,QAAQA,EAAG,KAAK;AAAA,UAAA,GACpCG,EAAAH,EAAG,KAAK,GAAA,GAAAI,EAAA;;QAGFJ,EAAG,QAAI,mBAAlBR,EAmBM,OAAAa,IAAA;AAAA,UAlBLX,EAKM,OALNY,IAKM;AAAA,YAJLZ,EAAmG,OAAA;AAAA,cAA9F,OAAKD,EAAA,CAAC,SAAO,EAAA,SAAoBX,EAAA,MAAaK,CAAK,EAAA,CAAA,CAAA;AAAA,cAAM,SAAK,CAAAe,MAAEhB,EAAeC,CAAK;AAAA,YAAA,GAAG,KAAC,IAAAoB,EAAA;AAAA,YAC7Fb,EAES,UAAA;AAAA,cAFD,OAAM;AAAA,cAAiC,SAAK,CAAAQ,MAAEhB,EAAeC,CAAK;AAAA,YAAA,GACtEgB,EAAAH,EAAG,KAAK,GAAA,GAAAQ,EAAA;AAAA,UAAA;UAGbC,EAAAf,EAWM,OAXNgB,IAWM;AAAA,YAVLhB,EASM,OATNiB,IASM;AAAA,eARLf,EAAA,EAAA,GAAAJ,EAOMK,GAAA,MAAAC,EAPcE,EAAG,UAAXY,YAAZpB,EAOM,OAAA;AAAA,gBAP2B,KAAKoB,EAAK;AAAA,cAAA;gBAC5BA,EAAK,UAAM,aAAzBpB,EAES,UAAA;AAAA;kBAF0B,OAAM;AAAA,kBAAiB,SAAK,CAAAU,MAAEb,EAAYuB,EAAK,QAAQA,EAAK,KAAK;AAAA,gBAAA,GAChGT,EAAAS,EAAK,KAAK,GAAA,GAAAC,EAAA,KAEAD,EAAK,QAAI,aAAvBpB,EAEC,KAAA;AAAA;kBAFiC,MAAMoB,EAAK;AAAA,gBAAA;kBAC3ClB,EAAuD,UAAvDoB,IAAuDX,EAAtBS,EAAK,KAAK,GAAA,CAAA;AAAA,gBAAA;;;;YAPnC,CAAAG,IAAApC,EAAA,MAAeQ,CAAK,CAAA;AAAA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;ACuCrC,UAAMV,IAAOC,GAKPsC,IAAQpC,EAAI,EAAE,GACdqC,IAAgBrC,EAAI,CAAC,GACrBsC,IAAWC,GAAe,OAAO,GAEjCC,IAAUC,EAAS,MACnBL,EAAM,QACWjB,EAAA,OAAOiB,EAAM,KAAK,EACnB,MAAM,GAAGjB,EAAA,UAAU,IAFf,CAAA,CAGzB;AAGD,IAAAuB;AAAA,MACC,MAAMvB,EAAA;AAAA,MACN,OAAMwB,MAAQ;AACb,QAAIA,MACHP,EAAM,QAAQ,IACdC,EAAc,QAAQ,GACtB,MAAMO,GAAA,GACJN,EAAS,OAA4B,MAAA;AAAA,MAEzC;AAAA,IAAA,GAIDI,EAAMF,GAAS,MAAM;AACpB,MAAAH,EAAc,QAAQ;AAAA,IACvB,CAAC;AAED,UAAMQ,IAAa,MAAM;AACxB,MAAAhD,EAAK,OAAO;AAAA,IACb,GAEMiD,IAAgB,CAACC,MAAqB;AAC3C,cAAQA,EAAE,KAAA;AAAA,QACT,KAAK;AACJ,UAAAF,EAAA;AACA;AAAA,QACD,KAAK;AACJ,UAAAE,EAAE,eAAA,GACEP,EAAQ,MAAM,WACjBH,EAAc,SAASA,EAAc,QAAQ,KAAKG,EAAQ,MAAM;AAEjE;AAAA,QACD,KAAK;AACJ,UAAAO,EAAE,eAAA,GACEP,EAAQ,MAAM,WACjBH,EAAc,SAASA,EAAc,QAAQ,IAAIG,EAAQ,MAAM,UAAUA,EAAQ,MAAM;AAExF;AAAA,QACD,KAAK;AACJ,UAAIA,EAAQ,MAAM,UAAUH,EAAc,SAAS,KAClDW,EAAaR,EAAQ,MAAMH,EAAc,KAAK,CAAC;AAEhD;AAAA,MAAA;AAAA,IAEH,GAEMW,IAAe,CAACC,MAAc;AACnC,MAAApD,EAAK,UAAUoD,CAAM,GACrBJ,EAAA;AAAA,IACD;2BA9HCK,GAqCWC,IAAA,EArCD,IAAG,UAAM;AAAA,MAClBC,EAmCaC,IAAA,EAnCD,MAAK,UAAM;AAAA,mBACtB,MAiCM;AAAA,UAjCKlC,EAAA,eAAXP,EAiCM,OAAA;AAAA;YAjCa,OAAM;AAAA,YAA2B,SAAOiC;AAAA,UAAA;YAC1D/B,EA+BM,OAAA;AAAA,cA/BD,OAAM;AAAA,cAAmB,4BAAD,MAAA;AAAA,cAAA,GAAW,CAAA,MAAA,CAAA;AAAA,YAAA;cACvCA,EASM,OATNC,IASM;AAAA,kBARLD,EAO4B,SAAA;AAAA,kBAN3B,KAAI;AAAA,gEACKsB,EAAK,QAAAd;AAAA,kBACd,MAAK;AAAA,kBACL,OAAM;AAAA,kBACL,aAAaH,EAAA;AAAA,kBACd,WAAA;AAAA,kBACC,WAAS2B;AAAA,gBAAA;uBALDV,EAAA,KAAK;AAAA,gBAAA;;cAQLI,EAAA,MAAQ,UAAnBxB,KAAAJ,EAeM,OAfNY,IAeM;AAAA,iBAdLR,EAAA,EAAA,GAAAJ,EAaMK,GAAA,MAAAC,EAZqBsB,EAAA,OAAO,CAAzBS,GAAQ1C,YADjBK,EAaM,OAAA;AAAA,kBAXJ,KAAKL;AAAA,kBACN,OAAKM,EAAA,CAAC,0BAAwB,EAAA,UACVN,MAAU8B,EAAA,MAAA,CAAa,CAAA;AAAA,kBAC1C,SAAK,CAAAf,MAAE0B,EAAaC,CAAM;AAAA,kBAC1B,aAAS,CAAA3B,MAAEe,EAAA,QAAgB9B;AAAA,gBAAA;kBAC5BO,EAEM,OAFNY,IAEM;AAAA,oBADL4B,EAAsCC,EAAA,QAAA,SAAA,EAAlB,QAAAN,GAAc;AAAA,kBAAA;kBAEnCnC,EAEM,OAFNa,IAEM;AAAA,oBADL2B,EAAwCC,EAAA,QAAA,WAAA,EAAlB,QAAAN,GAAc;AAAA,kBAAA;;oBAIvBb,EAAA,SAAK,CAAKI,EAAA,MAAQ,UAAlCxB,EAAA,GAAAJ,EAEM,OAFNgB,IAEM;AAAA,gBADL0B,EAA8DC,uBAA9D,MAA8D;AAAA,oBAA3C,4BAAuBhC,EAAGa,EAAA,KAAK,IAAG,MAAE,CAAA;AAAA,gBAAA;;;;;;;;;;;;;;;AC+B7D,UAAMoB,IAAsBxD,EAAI,EAAI,GAC9ByD,IAAgBzD,EAAI,EAAK,GACzB0D,IAAa1D,EAAI,EAAE,GACnBsC,IAAWC,GAAiC,aAAa,GAEzDoB,IAAoBlB,EAAS,MAC3Be,EAAoB,QAAQ,cAAc,SACjD,GAEKI,IAAoB,MAAM;AAC/B,MAAAJ,EAAoB,QAAQ,CAACA,EAAoB;AAAA,IAClD,GAEMK,IAAe,YAAY;AAChC,MAAAJ,EAAc,QAAQ,CAACA,EAAc,OACrC,MAAMb,GAAS,MAAM;AACpB,QAAAN,EAAS,OAAO,MAAA;AAAA,MACjB,CAAC;AAAA,IACF,GAEMwB,IAAoB,CAACC,MAA8B;AACxD,MAAAA,EAAM,eAAA,GACNA,EAAM,gBAAA;AAAA,IACP,GAEMC,IAAe,OAAOD,MAAsC;AACjE,MAAAA,EAAM,eAAA,GACNA,EAAM,gBAAA,GACN,MAAMF,EAAA;AAAA,IACP,GAEMI,IAAe,MAA6C;AAAA,IAElE;;;kBAhGCrD,EAuDS,UAAA,MAAA;AAAA,QAtDRE,EAqDK,MArDLC,IAqDK;AAAA,UApDJD,EAEK,MAAA;AAAA,YAFD,OAAM;AAAA,YAAmB,SAAO8C;AAAA,YAAoB,aAAeA,GAAiB,CAAA,OAAA,CAAA;AAAA,UAAA;YACvF9C,EAA2D,KAA3DO,IAA2D;AAAA,cAA3CP,EAAuC,OAAA;AAAA,gBAAjC,SAAO6C,EAAA,KAAiB;AAAA,cAAA,GAAE,KAAC,CAAA;AAAA,YAAA;;UAElD7C,EAWK,MAAA;AAAA,YAVJ,OAAM;AAAA,YACL,oBAAkB0C,EAAA,QAAmB,UAAA,QAAA;AAAA,YACrC,SAAOS;AAAA,YACP,aAAeA,GAAY,CAAA,OAAA,CAAA;AAAA,UAAA;YAC5Bb,EAKcc,GAAA;AAAA,cALD,IAAG;AAAA,cAAI,UAAS;AAAA,YAAA;yBAC5B,MAGM,CAAA,GAAAC,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA;AAAA,gBAHNrD,EAGM,OAAA;AAAA,kBAHD,OAAM;AAAA,kBAAO,cAAW;AAAA,kBAAO,SAAQ;AAAA,kBAAY,MAAK;AAAA,kBAAO,QAAO;AAAA,kBAAe,gBAAa;AAAA,gBAAA;kBACtGA,EAA0B,QAAA,EAApB,GAAE,iBAAe;AAAA,kBACvBA,EAAyB,QAAA,EAAnB,GAAE,gBAAc;AAAA,gBAAA;;;;;UAIzBA,EA8BK,MAAA;AAAA,YA7BH,0CAAwC2C,EAAA,MAAA,CAAa,CAAA;AAAA,YACrD,oBAAkBD,EAAA,QAAmB,UAAA,QAAA;AAAA,UAAA;YACtC1C,EA0BI,KA1BJU,IA0BI;AAAA,sBAzBHZ,EAaM,OAAA;AAAA,gBAXL,OAAM;AAAA,gBACN,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,gBAAa;AAAA,gBACZ,SAAOiD;AAAA,gBACP,aAAeA,GAAY,CAAA,OAAA,CAAA;AAAA,cAAA;gBAC5B/C,EAAgC,UAAA;AAAA,kBAAxB,IAAG;AAAA,kBAAK,IAAG;AAAA,kBAAK,GAAE;AAAA,gBAAA;gBAC1BA,EAA8B,QAAA,EAAxB,GAAE,oBAAA,GAAmB,MAAA,EAAA;AAAA,cAAA;sBAXlB2C,EAAA,KAAa;AAAA,cAAA;gBAavB3C,EAUkC,SAAA;AAAA,gBARjC,KAAI;AAAA,8DACK4C,EAAU,QAAApC;AAAA,gBACnB,MAAK;AAAA,gBACL,aAAY;AAAA,gBACX,4BAAD,MAAA;AAAA,gBAAA,GAAW,CAAA,MAAA,CAAA;AAAA,gBACV,SAAK6C,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAA7C,MAAEwC,EAAkBxC,CAAM;AAAA,gBAC/B,QAAI6C,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAA7C,MAAE0C,EAAa1C,CAAM;AAAA,gBACzB,WAAO;AAAA,kBAAQ6C,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAAC,EAAA,CAAA9C,MAAA0C,EAAa1C,CAAM,GAAA,CAAA,OAAA,CAAA;AAAA,oBAClBuC,GAAY,CAAA,QAAA,CAAA;AAAA,gBAAA;AAAA;qBATrBJ,EAAA,KAAa;AAAA,qBAEZC,EAAA,KAAU;AAAA,cAAA;;;kBAUtB9C,EAKKK,GAAA,MAAAC,EAJiBC,EAAA,aAAW,CAAzBkD,YADRzD,EAKK,MAAA;AAAA,YAHH,KAAKyD,EAAW;AAAA,YAChB,oBAAkBb,EAAA,QAAmB,UAAA,QAAA;AAAA,UAAA;YACtCJ,EAAoFc,GAAA;AAAA,cAAvE,UAAS;AAAA,cAAK,IAAIG,EAAW;AAAA,YAAA;yBAAK,MAAsB;AAAA,gBAAnBC,EAAA/C,EAAA8C,EAAW,KAAK,GAAA,CAAA;AAAA,cAAA;;;;;;;;;;;;;;;;;;;;;;ACgBtE,aAASE,EAAqBC,GAAqC;AAClE,YAAMC,IAAQC,GAAcF,CAAM,EAAE,OAAO,CAAAG,MAASA,EAAM,SAAS,WAAW,EAAQA,EAAM,OAAQ;AAEpG,aAAO,IAAI;AAAA,QACVF,EAAM,OAAO,CAAAE,MAASC,GAAuBD,EAAM,SAAS,MAAM,QAAQ,EAAE,IAAI,CAAAA,MAASA,EAAM,SAAS;AAAA,MAAA;AAAA,IAE1G;AAOA,aAASE,EAAgBC,GAAyB;AACjD,aAAIA,MAAU,QAAQ,OAAOA,KAAU,YAAY,CAAC,MAAM,QAAQA,CAAK,KAAK,QAAQA,IAC3EA,EAA0B,KAE5BA;AAAA,IACR;AAYA,UAAMjF,IAAOC,GA4BP,EAAE,WAAAiF,EAAA,IAAcC,GAAA;AAOtB,QAAIC,IAAgE;AACpE,QAAI;AACH,MAAAA,IAAkBC,GAAA;AAAA,IACnB,QAAQ;AACP,MAAAD,IAAkB;AAAA,IACnB;AAIA,UAAME,IAAc1C;AAAA,MAAmC,MACtD2C,EAAY,UAAU,WAAYH,GAAiB,iBAAiB,CAAA,IAAM,CAAA;AAAA,IAAC,GAItEI,IAAUrF,EAAI,EAAK,GACnBsF,IAAqBtF,EAAI,EAAK,GAI9BuF,IAAcvF,EAAyB,EAAE,GAMzCwF,IAAkB/C,EAA8B;AAAA,MACrD,MAAM;AAEL,YAAI2C,EAAY,UAAU;AACzB,iBAAO;AAAA,YACN,gBACCjE,qBAAmB,IAAI,CAAAsE,OAAY;AAAA,cAClC,IAAIA;AAAA,cACJ,SAAAA;AAAA,cACA,cAAcC,EAAkBD,CAAO;AAAA,cACvC,SAAS;AAAA,YAAA,EACR,KAAK,CAAA;AAAA,UAAC;AAKX,YAAIL,EAAY,UAAU;AACzB,iBAAO;AAAA,YACN,eAAeO,KAAa;AAAA,cAAI,CAAAC,MAC/B,OAAO,OAAO,CAAA,GAAIA,GAAQ;AAAA,gBACzB,IAAIC,GAAgBD,CAAM,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAO/B,SAAS;AAAA,cAAA,CACT;AAAA,YAAA;AAAA,UACF;AAKF,YAAI,CAACb,EAAU,SAAS,CAACe,EAAe,SAAS,CAACC,EAAgB;AACjE,iBAAO,CAAA;AAGR,YAAI;AAQH,gBAAMC,IAA4B,EAAE,GAPrBC,EAAY,QACxBV,EAAY,QACXR,EAAU,MAAM,cAAce,EAAe,OAAOC,EAAgB,KAAK,GAAG,IAAI,EAAE,EAK/C,GAKjCN,IAAUV,EAAU,MAAM,SAAS,SAASe,EAAe,KAAK;AACtE,cAAIL;AACH,uBAAWd,KAASc,EAAQ;AAC3B,kBAAId,EAAM,SAAS,YAAY;AAC9B,sBAAMuB,IAA8B,CAAA;AACpC,2BAAWC,KAASxB,EAAM;AACzB,kBAAIwB,EAAM,cAAWD,EAAOC,EAAM,SAAS,IAAIH,EAAKG,EAAM,SAAS;AAEpE,gBAAAH,EAAKrB,EAAM,SAAS,IAAIuB;AAAA,cACzB;AAAA;AAGF,iBAAOF;AAAA,QACR,QAAQ;AACP,iBAAO,CAAA;AAAA,QACR;AAAA,MACD;AAAA,MACA,IAAII,GAA8B;AAEjC,YAAIhB,EAAY,UAAU,YACtB,GAACL,EAAU,SAAS,CAACe,EAAe,SAAS,CAACC,EAAgB;AAIlE,cAAI;AAGH,kBAAMN,IAAUV,EAAU,MAAM,SAAS,SAASe,EAAe,KAAK,GAChEO,wBAAoB,IAAA;AAC1B,gBAAIZ;AACH,yBAAWd,KAASc,EAAQ;AAC3B,gBAAId,EAAM,SAAS,cAClB0B,EAAc,IAAI1B,EAAM,SAAS;AAIpC,kBAAM2B,IAAc/B,EAAqBkB,IAAUA,EAAQ,eAAA,IAAmB,EAAE,GAM1Ec,IAAgC,CAAA,GAChCC,IAAwC,CAAA;AAC9C,uBAAW,CAACC,GAAK3B,CAAK,KAAK,OAAO,QAAQsB,CAAO;AAChD,cAAIC,EAAc,IAAII,CAAG,KAAK3B,KAAS,OAAOA,KAAU,YAAY,CAAC,MAAM,QAAQA,CAAK,IACvF0B,EAAe,KAAK1B,CAAK,IAEzByB,EAASE,CAAG,IAAI3B;AAGlB,uBAAW4B,KAAeF;AACzB,qBAAO,OAAOD,GAAUG,CAAW;AAMpC,kBAAMC,IAA0B,CAAA;AAChC,gBAAIV,EAAY,OAAO;AAGtB,oBAAMW,IAAO,EAAE,GAAGrB,EAAY,MAAA;AAC9B,yBAAW,CAACsB,GAAW/B,CAAK,KAAK,OAAO,QAAQyB,CAAQ,GAAG;AAC1D,oBAAIzB,MAAU,OAAW;AACzB,sBAAMgC,IAAaR,EAAY,IAAIO,CAAS,IAAIhC,EAAgBC,CAAK,IAAIA;AACzE,gBAAI8B,EAAKC,CAAS,MAAMC,MACvBF,EAAKC,CAAS,IAAIC,GAClBH,EAAc,KAAKE,CAAS;AAAA,cAE9B;AACA,cAAAtB,EAAY,QAAQqB;AAAA,YACrB,OAAO;AACN,oBAAMG,IAAWhC,EAAU,MAAM,SAAA;AACjC,yBAAW,CAAC8B,GAAW/B,CAAK,KAAK,OAAO,QAAQyB,CAAQ,GAAG;AAC1D,oBAAIzB,MAAU,OAAW;AACzB,sBAAMgC,IAAaR,EAAY,IAAIO,CAAS,IAAIhC,EAAgBC,CAAK,IAAIA,GACnEkC,IAAY,GAAGlB,EAAe,KAAK,IAAIC,EAAgB,KAAK,IAAIc,CAAS;AAE/E,iBADqBE,EAAS,IAAIC,CAAS,IAAID,EAAS,IAAIC,CAAS,IAAI,YACpDF,MACpBC,EAAS,IAAIC,GAAWF,CAAU,GAClCH,EAAc,KAAKE,CAAS;AAAA,cAE9B;AAAA,YACD;AAIA,YAAIF,EAAc,SAAS,KAC1BM,EAAqBN,CAAa;AAAA,UAEpC,SAASO,GAAO;AACf,oBAAQ,KAAK,sBAAsBA,CAAK;AAAA,UACzC;AAAA,MACD;AAAA,IAAA,CACA;AAKD,aAASD,EAAqBN,GAAyB;AACtD,UAAI,CAAC1B,KAAmB,CAACF,EAAU,SAAS,CAACe,EAAe,SAAS,CAACC,EAAgB,MAAO;AAG7F,YAAMoB,IADUpC,EAAU,MAAM,SAAS,WAAWe,EAAe,KAAK,GAC9C,YAAA;AAC1B,UAAI,CAACqB,KAAY,OAAO,KAAKA,CAAQ,EAAE,WAAW,EAAG;AAGrD,YAAMvB,IAASK,EAAY,QACxB,EAAE,GAAGV,EAAY,UACjB;AAAA,QACA,GAAIR,EAAU,MAAM,cAAce,EAAe,OAAOC,EAAgB,KAAK,GAAG,IAAI,EAAE;AAAA,MAAA;AAMzF,iBAAWpB,KAASgC;AACnB,QAAK1B,EAAgB,cAAckC,GAAUxC,GAAOiB,CAAM;AAAA,IAE5D;AAIA,UAAMwB,IAAQ3E,EAAS,MAAOtB,EAAA,eAAe,OAAOkG,GAAMtC,EAAU,OAAO,SAAS,QAAQ,YAAY,CAAE,GACpGuC,IAAS7E,EAAS,MAAOtB,EAAA,eAAe,OAAO4D,EAAU,OAAO,SAAS,MAAO,GAChFe,IAAiBrD,EAAS,MAAM;AACrC,UAAItB,eAAc,QAAOA,EAAA,aAAa,kBAAA;AACtC,UAAI,CAACiG,EAAM,MAAO,QAAO;AAGzB,UAAIA,EAAM,MAAM,MAAM;AACrB,eAAOA,EAAM,MAAM,KAAK;AAIzB,UAAIA,EAAM,MAAM,OAAO;AACtB,eAAOA,EAAM,MAAM,OAAO,QAAQ,SAAA;AAInC,YAAMG,IAAYH,EAAM,MAAM,OAAO;AACrC,aAAIG,KAAaA,EAAU,SAAS,IAC5BA,EAAU,CAAC,IAGZ;AAAA,IACR,CAAC,GAGKC,IAAe/E,EAAS,MAAM;AACnC,UAAItB,eAAc,QAAOA,EAAA,aAAa,kBAAA;AACtC,UAAI,CAACiG,EAAM,MAAO,QAAO;AAGzB,UAAIA,EAAM,MAAM,MAAM;AACrB,eAAOA,EAAM,MAAM,KAAK;AAIzB,UAAIA,EAAM,MAAM,OAAO;AACtB,eAAOA,EAAM,MAAM,OAAO,QAAQ,SAAA;AAInC,YAAMG,IAAYH,EAAM,MAAM,OAAO;AACrC,aAAIG,KAAaA,EAAU,SAAS,IAC5BA,EAAU,CAAC,IAGZ;AAAA,IACR,CAAC,GAEKxB,IAAkBtD,EAAS,MAAM;AACtC,UAAItB,eAAc,QAAOA,eAAa,mBAAA;AACtC,UAAI,CAACiG,EAAM,MAAO,QAAO;AAGzB,UAAIA,EAAM,MAAM,OAAO;AACtB,eAAOA,EAAM,MAAM,OAAO,SAAS,SAAA;AAIpC,YAAMG,IAAYH,EAAM,MAAM,OAAO;AACrC,aAAIG,KAAaA,EAAU,SAAS,IAC5BA,EAAU,CAAC,IAGZ;AAAA,IACR,CAAC,GACKtB,IAAcxD,EAAS,MAAMgF,GAAgB1B,EAAgB,KAAK,CAAC,GAGnEX,IAAc3C,EAAS,MAAM;AAClC,UAAItB,EAAA,aAAc,QAAOA,EAAA,aAAa,eAAA;AAMtC,UALI,CAACiG,EAAM,SAKPA,EAAM,MAAM,SAAS,UAAUA,EAAM,MAAM,SAAS;AACvD,eAAO;AAIR,UAAIA,EAAM,MAAM,QAAQA,EAAM,MAAM,SAAS,aAAa;AACzD,cAAMM,IAAYN,EAAM,MAAM;AAC9B,YAAIM,EAAU,SAAS,MAAM,KAAKN,EAAM,MAAM,OAAO;AACpD,iBAAO;AACR,YAAWM,EAAU,SAAS,MAAM,KAAKN,EAAM,MAAM,OAAO;AAC3D,iBAAO;AAAA,MAET;AAGA,YAAMG,IAAYH,EAAM,MAAM,OAAO;AACrC,aAAIG,KAAaA,EAAU,SAAS,IACtBA,EAAU,WAAW,IAAI,YAAY,WAI5C;AAAA,IACR,CAAC,GAMKI,KAA0B,MAAM;AACrC,UAAI,CAAC5C,EAAU,SAAS,CAACe,EAAe,SAAS,CAACC,EAAgB;AACjE,eAAO,CAAA;AAGR,UAAI;AACH,cAAMN,IAAUV,EAAU,MAAM,SAAS,WAAWe,EAAe,KAAK;AACxE,YAAI,CAACL,GAAS,SAAU,QAAO,CAAA;AAG/B,cAAMmC,IAAe7C,EAAU,MAAM,eAAee,EAAe,OAAOC,EAAgB,KAAK,GAGzF8B,IAAcpC,EAAQ,wBAAwBmC,CAAY,GAE1DE,IAAatC,EAAgB,SAAS,CAAA;AAI5C,eAAOqC,EAAY,IAAI,CAAC,EAAE,MAAAE,SAAY;AAAA;AAAA;AAAA,UAGrC,OAAOtC,EAAQ,cAAcsC,CAAI,GAAG,SAASA;AAAA,UAC7C,QAAQ,MAAM;AACb,YAAAlI,EAAK,UAAU;AAAA,cACd,MAAAkI;AAAA,cACA,SAASjC,EAAe;AAAA,cACxB,UAAUC,EAAgB;AAAA,cAC1B,MAAM+B;AAAA,YAAA,CACN;AAAA,UACF;AAAA,QAAA,EACC;AAAA,MACH,SAASZ,GAAO;AACf,uBAAQ,KAAK,wCAAwCA,CAAK,GACnD,CAAA;AAAA,MACR;AAAA,IACD,GAKMc,KAAuB,MAAM;AAClC,UAAI,CAACjD,EAAU,SAAS,CAACe,EAAe,SAAS,CAACC,EAAgB;AACjE,eAAO,CAAA;AAGR,UAAI;AACH,cAAMN,IAAUV,EAAU,MAAM,SAAS,WAAWe,EAAe,KAAK;AACxE,YAAI,CAACL,GAAS,SAAU,QAAO,CAAA;AAE/B,cAAMmC,IAAe7C,EAAU,MAAM,eAAee,EAAe,OAAOC,EAAgB,KAAK,GACzFkC,IAAWxC,EAAQ,qBAAqBmC,CAAY,GACpDE,IAAatC,EAAgB,SAAS,CAAA;AAE5C,eAAOyC,EAAS,IAAI,CAAC,EAAE,MAAAF,SAAY;AAAA,UAClC,OAAOtC,EAAQ,cAAcsC,CAAI,GAAG,SAASA;AAAA,UAC7C,QAAQ,MAAM;AACb,YAAAlI,EAAK,UAAU;AAAA,cACd,MAAAkI;AAAA,cACA,SAASjC,EAAe;AAAA,cACxB,UAAUC,EAAgB;AAAA,cAC1B,MAAM+B;AAAA,YAAA,CACN;AAAA,UACF;AAAA,QAAA,EACC;AAAA,MACH,SAASZ,GAAO;AACf,uBAAQ,KAAK,qCAAqCA,CAAK,GAChD,CAAA;AAAA,MACR;AAAA,IACD,GAEMgB,KAAiBzF,EAAS,MAAM;AACrC,YAAM0F,IAA6B,CAAA;AAEnC,cAAQ/C,EAAY,OAAA;AAAA,QACnB,KAAK;AACJ,UAAA+C,EAAS,KAAK;AAAA,YACb,MAAM;AAAA,YACN,OAAO;AAAA,YACP,QAAQ,MAAA;AAAM,cAAKC,EAAA;AAAA;AAAA,UAAgB,CACnC;AACD;AAAA,QACD,KAAK,UAAU;AAGd,gBAAMC,IAAgB,CAAC,GAAGV,MAA2B,GAAGK,IAAsB;AAC9E,UAAIK,EAAc,SAAS,KAC1BF,EAAS,KAAK;AAAA,YACb,MAAM;AAAA,YACN,OAAO;AAAA,YACP,SAASE;AAAA,UAAA,CACT;AAEF;AAAA,QACD;AAAA,MAAA;AAGD,aAAOF;AAAA,IACR,CAAC,GAEKG,KAAwB7F,EAAS,MAAM;AAC5C,YAAM8F,IAA+C,CAAA;AAErD,UAAInD,EAAY,UAAU,aAAaoC,EAAa;AACnD,QAAAe,EAAY;AAAA,UACX,EAAE,OAAO,QAAQ,IAAI,IAAA;AAAA,UACrB,EAAE,OAAO7C,EAAkB8B,EAAa,KAAK,GAAG,IAAI,IAAIA,EAAa,KAAK,GAAA;AAAA,QAAG;AAAA,eAEpEpC,EAAY,UAAU,YAAYoC,EAAa,OAAO;AAChE,cAAMgB,IAAazC,EAAgB,QAChC,IAAIyB,EAAa,KAAK,IAAIzB,EAAgB,KAAK,KAC9CqB,EAAM,OAAO,YAAY;AAC7B,QAAAmB,EAAY;AAAA,UACX,EAAE,OAAO,QAAQ,IAAI,IAAA;AAAA,UACrB,EAAE,OAAO7C,EAAkB8B,EAAa,KAAK,GAAG,IAAI,IAAIA,EAAa,KAAK,GAAA;AAAA,UAC1E,EAAE,OAAOvB,EAAY,QAAQ,eAAe,eAAe,IAAIuC,EAAA;AAAA,QAAW;AAAA,MAE5E;AAEA,aAAOD;AAAA,IACR,CAAC,GASKE,KAAiB,CAACrG,MAA6B;AACpD,YAAM6F,IAAsB;AAAA,QAC3B;AAAA,UACC,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ,MAAA;AAAM,YAAKS,EAAW,EAAE,MAAM,YAAY;AAAA;AAAA,QAAA;AAAA,QAEnD;AAAA,UACC,OAAO;AAAA,UACP,aAAa;AAAA,UACb,QAAQ,MAAOpD,EAAmB,QAAQ,CAACA,EAAmB;AAAA,QAAA;AAAA,MAC/D;AA4BD,aAxBIkC,EAAa,UAChBS,EAAS,KAAK;AAAA,QACb,OAAO,QAAQvC,EAAkB8B,EAAa,KAAK,CAAC;AAAA,QACpD,aAAa,eAAeA,EAAa,KAAK;AAAA,QAC9C,QAAQ,MAAA;AAAM,UAAKkB,EAAW,EAAE,MAAM,WAAW,SAASlB,EAAa,MAAA,CAAO;AAAA;AAAA,MAAA,CAC9E,GAEDS,EAAS,KAAK;AAAA,QACb,OAAO,cAAcvC,EAAkB8B,EAAa,KAAK,CAAC;AAAA,QAC1D,aAAa,gBAAgBA,EAAa,KAAK;AAAA,QAC/C,QAAQ,MAAA;AAAM,UAAKY,EAAA;AAAA;AAAA,MAAgB,CACnC,IAIFjH,EAAA,kBAAkB,QAAQ,CAAAsE,MAAW;AACpC,QAAAwC,EAAS,KAAK;AAAA,UACb,OAAO,QAAQvC,EAAkBD,CAAO,CAAC;AAAA,UACzC,aAAa,eAAeA,CAAO;AAAA,UACnC,QAAQ,MAAA;AAAM,YAAKiD,EAAW,EAAE,MAAM,WAAW,SAAAjD,GAAS;AAAA;AAAA,QAAA,CAC1D;AAAA,MACF,CAAC,GAGIrD,IAEE6F,EAAS;AAAA,QACf,OACCU,EAAI,MAAM,YAAA,EAAc,SAASvG,EAAM,YAAA,CAAa,KACpDuG,EAAI,YAAY,YAAA,EAAc,SAASvG,EAAM,aAAa;AAAA,MAAA,IALzC6F;AAAA,IAOpB,GAEMW,KAAiB,CAACC,MAAqB;AAC5C,MAAAA,EAAQ,OAAA,GACRvD,EAAmB,QAAQ;AAAA,IAC5B,GAGMwD,KAAqB,CAACC,MAA4D;AACvF,UAAI,CAAChE,EAAU,SAAS,CAACe,EAAe;AACvC,eAAO,QAAQ,QAAQ,EAAE,MAAM,CAAA,GAAI,SAAS,IAAO;AAEpD,YAAML,IAAUV,EAAU,MAAM,SAAS,WAAWe,EAAe,KAAK;AACxE,aAAKL,IAGEV,EAAU,MAAM,WAAWU,GAASsD,CAAO,IAF1C,QAAQ,QAAQ,EAAE,MAAM,CAAA,GAAI,SAAS,IAAO;AAAA,IAGrD,GAGMrD,IAAoB,CAACD,MACnBA,EACL,MAAM,GAAG,EACT,IAAI,CAAAuD,MAAQA,EAAK,OAAO,CAAC,EAAE,YAAA,IAAgBA,EAAK,MAAM,CAAC,CAAC,EACxD,KAAK,GAAG,GAKLN,IAAa,OAAOO,MAA6B;AACtD,MAAApJ,EAAK,YAAYoJ,CAAM,GACnB9H,EAAA,eACH,MAAMA,EAAA,aAAa,SAAS8H,CAAM,IAE9BA,EAAO,SAAS,aACnB,MAAM3B,EAAO,OAAO,KAAK,GAAG,IAClB2B,EAAO,SAAS,aAAaA,EAAO,UAC9C,MAAM3B,EAAO,OAAO,KAAK,IAAI2B,EAAO,OAAO,EAAE,IACnCA,EAAO,SAAS,YAAYA,EAAO,WAAWA,EAAO,YAC/D,MAAM3B,EAAO,OAAO,KAAK,IAAI2B,EAAO,OAAO,IAAIA,EAAO,QAAQ,EAAE;AAAA,IAGnE,GAEMC,KAAoB,OAAOzD,MAAoB;AACpD,YAAMiD,EAAW,EAAE,MAAM,WAAW,SAAAjD,GAAS;AAAA,IAC9C,GAEM0D,KAAa,OAAOC,MAAqB;AAC9C,YAAM3D,IAAU+B,EAAa;AAC7B,MAAA3H,EAAK,eAAe,EAAE,SAAA4F,GAAS,UAAA2D,EAAA,CAAU,GACzC,MAAMV,EAAW,EAAE,MAAM,UAAU,SAAAjD,GAAS,UAAA2D,GAAU;AAAA,IACvD,GAEMhB,IAAkB,YAAY;AACnC,YAAMM,EAAW,EAAE,MAAM,UAAU,SAASlB,EAAa,OAAO,UAAU6B,IAAiB;AAAA,IAC5F,GAKMC,KAAmB,CAAC9E,MAA6C;AACtE,YAAMvB,IAA0B,CAAA;AAChC,iBAAW0B,KAASH;AACnB,QAAIG,EAAM,SAAS,aAClB1B,EAAO,KAAK,GAAGqG,GAAiB3E,EAAM,MAAM,CAAC,IAE7C1B,EAAO,KAAK0B,CAAK;AAGnB,aAAO1B;AAAA,IACR,GAGMsG,KAAoB,MACpBpI,EAAA,mBAAmB,SAEjB;AAAA,MACN;AAAA,QACC,MAAM;AAAA,QACN,WAAW;AAAA,QACX,WAAW;AAAA,QACX,OAAO;AAAA,QACP,QAAQ;AAAA,UACP;AAAA,YACC,WAAW;AAAA,YACX,OAAO;AAAA,YACP,WAAW;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,YACN,OAAO;AAAA,UAAA;AAAA,UAER;AAAA,YACC,WAAW;AAAA,YACX,OAAO;AAAA,YACP,WAAW;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,YACN,OAAO;AAAA,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMR;AAAA,YACC,WAAW;AAAA,YACX,OAAO;AAAA,YACP,WAAW;AAAA,YACX,OAAO;AAAA,YACP,MAAM;AAAA,YACN,OAAO;AAAA,UAAA;AAAA,QACR;AAAA,QAED,QAAQ,EAAE,MAAM,QAAiB,WAAW,GAAA;AAAA,MAAK;AAAA,IAClD,IAvCsC,CAAA,GA2ClCqI,KAAmB,MAAuB;AAC/C,UAAI,CAAC1D,EAAe,MAAO,QAAO,CAAA;AAClC,UAAI,CAACf,EAAU,MAAO,QAAO,CAAA;AAE7B,YAAM0E,IAAW1E,EAAU,MAAM,UAC3BU,IAAUgE,EAAS,SAAS3D,EAAe,KAAK;AAEtD,UAAI,CAACL,EAAS,QAAO,CAAA;AAErB,YAAMiE,IAASD,EAAS,cAAchE,CAAO;AAG7C,UAAIiE,EAAO,WAAW,EAAG,QAAO,CAAA;AAEhC,YAAMC,IAAc7D,EAAe,OAC7B8D,IAAmB,EAAQ7E,EAAU,MAAM;AAEjD,aAAO;AAAA,QACN;AAAA,UACC,MAAM;AAAA,UACN,WAAW;AAAA,UACX,WAAW;AAAA,UACX,QAAQ;AAAA,YACP,GAAIuE,GAAiBI,CAAM;AAAA,YAC3B,EAAE,WAAW,WAAW,OAAO,WAAW,WAAW,aAAA;AAAA,UAAa;AAAA,UAEnE,QAAQ,EAAE,MAAM,QAAiB,WAAW,GAAA;AAAA,UAC5C,GAAIE,IAAmB,EAAE,YAAYd,IAAoB,WAAWa,EAAA,IAAgB,CAAA;AAAA,QAAC;AAAA,MACtF;AAAA,IAEF,GAEME,KAAsB,MAAuB;AAClD,UAAI,CAAC/D,EAAe,MAAO,QAAO,CAAA;AAClC,UAAI,CAACf,EAAU,MAAO,QAAO,CAAA;AAE7B,UAAI;AACH,cAAM0E,IAAW1E,EAAU,OAAO,UAC5BU,IAAUgE,GAAU,SAAS3D,EAAe,KAAK;AAEvD,eAAKL,GAAS,SAKPgE,EAAS,cAAchE,CAAO,IAH7B,CAAA;AAAA,MAIT,QAAQ;AACP,eAAO,CAAA;AAAA,MACR;AAAA,IACD,GAGME,KAAa,MAAM;AACxB,UAAI,CAACZ,EAAU,SAAS,CAACe,EAAe;AACvC,eAAO,CAAA;AAIR,YAAMgE,IADc/E,EAAU,MAAM,QAAQe,EAAe,KAAK,GAC/B,IAAI,EAAE;AAEvC,aAAIgE,KAAe,OAAOA,KAAgB,YAAY,CAAC,MAAM,QAAQA,CAAW,IACxE,OAAO,OAAOA,CAAkC,IAGjD,CAAA;AAAA,IACR,GAGMC,KAAoBtH,EAAS,MAAM;AACxC,cAAQ2C,EAAY,OAAA;AAAA,QACnB,KAAK;AACJ,iBAAOmE,GAAA;AAAA,QACR,KAAK;AACJ,iBAAOC,GAAA;AAAA,QACR,KAAK;AACJ,iBAAOK,GAAA;AAAA,QACR;AACC,iBAAO,CAAA;AAAA,MAAC;AAAA,IAEX,CAAC,GAEKG,KAAoB,CAACC,GAAgBvJ,MAAqD;AAC/F,MAAIA,KACEA,EAAA;AAAA,IAEP,GAcMmF,KAAkB,CAACD,MAAwD;AAChF,UAAI,GAACb,EAAU,SAAS,CAACe,EAAe;AACxC,eAAOf,EAAU,MAAM,SAAS,SAASe,EAAe,KAAK,GAAG,YAAYF,CAAM;AAAA,IACnF,GAGMsE,KAAqB,CAACC,MAAmD;AAC9E,YAAMC,IAAOD,EAAW,cAAc,mBAAmB;AACzD,UAAI,CAACC,EAAM,QAAO;AAElB,YAAMC,IAAeD,EAAK,aAAa,eAAe;AACtD,UAAIC,MAAiB,KAAM,QAAO;AAElC,YAAMC,IAAW,SAASD,GAAc,EAAE;AAC1C,UAAI,MAAMC,CAAQ,EAAG,QAAO;AAG5B,YAAM1E,IADUD,GAAA,EACO2E,CAAQ;AAC/B,aAAK1E,IAEEC,GAAgBD,CAAM,KAAK,OAFd;AAAA,IAGrB,GAEMnF,KAAc,OAAOsD,MAAiB;AAC3C,YAAMkF,IAASlF,EAAM;AAGrB,MAFekF,EAAO,aAAa,aAAa,MAEjC,YACd,MAAMb,EAAA;AAGP,YAAMgC,IAAOnB,EAAO,QAAQ,QAAQ;AACpC,UAAImB,GAAM;AACT,cAAMG,IAAWH,EAAK,aAAa,KAAA,GAC7BI,IAAMJ,EAAK,QAAQ,IAAI;AAE7B,YAAIG,MAAa,kBAAkBC,GAAK;AAEvC,gBAAMC,IAAQD,EAAI,iBAAiB,IAAI;AACvC,cAAIC,EAAM,SAAS,GAAG;AAErB,kBAAMhF,IADcgF,EAAM,CAAC,EACC,aAAa,KAAA;AACzC,YAAIhF,KACH,MAAMyD,GAAkBzD,CAAO;AAAA,UAEjC;AAAA,QACD,WAAW8E,MAAa,UAAUC,GAAK;AAItC,gBAAMpB,IAAWc,GAAmBM,CAAG;AACvC,UAAIpB,KACH,MAAMD,GAAWC,CAAQ;AAAA,QAE3B;AAAA,MACD;AAAA,IACD,GAOMsB,KAAiB,YAAY;AAClC,UAAI,GAAC3F,EAAU,SAAS,CAACe,EAAe,SAAS,CAACf,EAAU,MAAM,cAElE;AAAA,QAAAM,EAAQ,QAAQ;AAChB,YAAI;AACH,gBAAMN,EAAU,MAAM,UAAUe,EAAe,OAAOC,EAAgB,KAAK;AAAA,QAC5E,SAASmB,GAAO;AACf,kBAAQ,KAAK,0BAA0BA,CAAK;AAAA,QAC7C,UAAA;AACC,UAAA7B,EAAQ,QAAQ;AAAA,QACjB;AAAA;AAAA,IACD;AAGA,IAAA3C;AAAA,MACC,CAAC0C,GAAaU,GAAgBC,CAAe;AAAA,MAC7C,MAAM;AAGL,YAAIX,EAAY,UAAU,aAAaU,EAAe;AACrD,UAAAjG,EAAK,gBAAgB,EAAE,SAASiG,EAAe,OAAO;AAAA,iBAC5CV,EAAY,UAAU,YAAYU,EAAe,SAASC,EAAgB,OAAO;AAK3F,cAAIE,EAAY,MAAO;AAEvB,UAAApG,EAAK,eAAe,EAAE,SAASiG,EAAe,OAAO,UAAUC,EAAgB,OAAO,GACjF2E,GAAA;AAAA,QACN;AAAA,MACD;AAAA,MACA,EAAE,WAAW,GAAA;AAAA,IAAK,GAKnBhI,EAAM,CAACoD,GAAgBC,CAAe,GAAG,MAAM;AAC9C,MAAAd,GAAiB,SAAA;AAAA,IAClB,CAAC,GAKDvC;AAAA,MACC,CAACoD,GAAgBC,CAAe;AAAA,MAChC,MAAM;AACL,YAAI,CAACE,EAAY,OAAO;AACvB,UAAAV,EAAY,QAAQ,CAAA;AACpB;AAAA,QACD;AACA,cAAMkE,IAAW1E,EAAU,OAAO;AAClC,QAAAQ,EAAY,QAAQkE,IAAWA,EAAS,iBAAiBI,GAAA,CAAqB,IAAI,CAAA;AAAA,MACnF;AAAA,MACA,EAAE,WAAW,GAAA;AAAA,IAAK,GAyBnBc,GAAQ,kBAlBe;AAAA,MACtB,mBAAAzB;AAAA,MACA,YAAAC;AAAA,MACA,iBAAAf;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,YAAY,CAACL,GAAc6C,MAA+B;AACzD,QAAA/K,EAAK,UAAU;AAAA,UACd,MAAAkI;AAAA,UACA,SAASjC,EAAe;AAAA,UACxB,UAAUC,EAAgB;AAAA,UAC1B,MAAM6E,KAAQpF,EAAgB,SAAS,CAAA;AAAA,QAAC,CACxC;AAAA,MACF;AAAA,IAAA,CAGuC,GAGxCmF,GAAQ,sBAAsB;AAAA,MAC7B,UAAU,CAAClF,GAAiBoF,MAAwB;AACnD,QAAKnC,EAAW,EAAE,MAAM,UAAU,SAAAjD,GAAS,UAAU,OAAOoF,CAAE,GAAG;AAAA,MAClE;AAAA,IAAA,CAC6B,GAK9BF,GAAQ,qBAAqB,OAAOhB,GAAqBkB,MAA4C;AACpG,UAAK9F,EAAU;AACf,YAAI;AAEH,gBAAM+F,KADO,MAAM/F,EAAU,MAAM,QAAQ,EAAE,MAAM,IAAI4E,CAAW,IAAI,UAAU,CAACA,CAAW,GAAG,IACpE;AAC3B,cAAI,CAACmB,EAAc;AAEnB,gBAAMC,IAAc,CAACC,MAAiE;AACrF,gBAAI,CAACA,EAAK;AACV,kBAAMC,IAAMD,EAAIF,CAAY;AAC5B,mBAAO,OAAOG,KAAQ,YAAY,OAAOA,KAAQ,WAAW,OAAOA,CAAG,IAAI;AAAA,UAC3E,GAEMC,IAASnG,EAAU,MAAM,cAAc4E,GAAakB,CAAE,GAAG,IAAI,EAAE,GAC/DM,IAAgBJ,EAAYG,CAAM;AACxC,cAAIC,KAAiB,KAAM,QAAOA;AAElC,gBAAMpG,EAAU,MAAM,UAAU4E,GAAakB,CAAE;AAC/C,gBAAMO,IAAUrG,EAAU,MAAM,cAAc4E,GAAakB,CAAE,GAAG,IAAI,EAAE;AACtE,iBAAOE,EAAYK,CAAO;AAAA,QAC3B,QAAQ;AACP;AAAA,QACD;AAAA,IACD,CAAC;AAED,UAAMtI,KAAgB,CAACiB,MAAyB;AAC/C,OAAKA,EAAM,WAAWA,EAAM,YAAYA,EAAM,QAAQ,QACrDA,EAAM,eAAA,GACNuB,EAAmB,QAAQ,KAExBvB,EAAM,QAAQ,YAAYuB,EAAmB,UAChDA,EAAmB,QAAQ;AAAA,IAE7B;AAEA,WAAAnF,GAAU,MAAM;AACf,eAAS,iBAAiB,WAAW2C,EAAa;AAAA,IACnD,CAAC,GAEDuI,GAAY,MAAM;AACjB,eAAS,oBAAoB,WAAWvI,EAAa;AAAA,IACtD,CAAC,mBAhgCAlC,EAgCM,OAAA;AAAA,MAhCD,OAAM;AAAA,MAAW,SAAOH;AAAA,IAAA;MAE5B2C,EAA0EkI,IAAA;AAAA,QAA9D,UAAUpD,GAAA;AAAA,QAAiB,eAAc8B;AAAA,MAAA;MAI9CD,GAAA,MAAkB,SAAM,UAD/B7G,GAIyBqI,GAAAC,EAAA,GAAA;AAAA;QAFhB,MAAMhG,EAAA;AAAA,gDAAAA,EAAe,QAAAlE;AAAA,QAC5B,QAAQyI,GAAA;AAAA,QACR,QAAQ5E,EAAA;AAAA,MAAA,6CACOoG,GAAAxG,CAAA,KACjB/D,KAAAJ,EAEM,OAFNS,IAEM;AAAA,QADLP,EAAwC,KAAA,MAArC,aAAQS,EAAG6D,EAAA,KAAW,IAAG,YAAQ,CAAA;AAAA,MAAA,OAFrCpE,EAAA,GAAAJ,EAAkF,OAAlFG,IAAkF,CAAA,GAAAoD,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA;AAAA,QAAtCrD,EAAgC,WAA7B,6BAAyB,EAAA;AAAA,MAAA;MAMxEsC,EAAiDqI,IAAA,EAAtC,aAAanD,GAAA,MAAA,GAAqB,MAAA,GAAA,CAAA,aAAA,CAAA;AAAA,MAG7ClF,EAYiBsI,IAAA;AAAA,QAXf,WAASpG,EAAA;AAAA,QACT,QAAQmD;AAAA,QACT,aAAY;AAAA,QACX,UAAQG;AAAA,QACR,gCAAOtD,EAAA,QAAkB;AAAA,MAAA;QACf,OAAKqG,EACf,CAAkB,EADC,QAAA1I,QAAM;AAAA,UACtBqB,EAAA/C,EAAA0B,EAAO,KAAK,GAAA,CAAA;AAAA,QAAA;QAEL,SAAO0I,EACjB,CAAwB,EADH,QAAA1I,QAAM;AAAA,UACxBqB,EAAA/C,EAAA0B,EAAO,WAAW,GAAA,CAAA;AAAA,QAAA;;;;;ICnBnB2I,KAAiB;AAAA,EACtB,SAAS,CAACC,MAAa;AACtB,IAAAA,EAAI,UAAU,aAAaP,EAAS,GACpCO,EAAI,UAAU,kBAAkBH,EAAc,GAC9CG,EAAI,UAAU,WAAWC,EAAO,GAChCD,EAAI,UAAU,YAAYJ,EAAQ;AAAA,EACnC;AACD;"}
|
|
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.
|
|
3
|
+
"version": "0.26.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.
|
|
37
|
-
"@stonecrop/
|
|
38
|
-
"@stonecrop/
|
|
39
|
-
"@stonecrop/stonecrop": "0.
|
|
40
|
-
"@stonecrop/themes": "0.
|
|
36
|
+
"@stonecrop/aform": "0.26.0",
|
|
37
|
+
"@stonecrop/schema": "0.26.0",
|
|
38
|
+
"@stonecrop/atable": "0.26.0",
|
|
39
|
+
"@stonecrop/stonecrop": "0.26.0",
|
|
40
|
+
"@stonecrop/themes": "0.26.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 {
|
|
43
|
-
|
|
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
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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,
|