@ai-matrx/alchemy 0.8.13 → 0.8.17
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/CHANGELOG.md +32 -1
- package/dist/actions.cjs +1 -373
- package/dist/actions.cjs.map +1 -1
- package/dist/actions.js +1 -350
- package/dist/actions.js.map +1 -1
- package/dist/checks.cjs +9 -314
- package/dist/checks.cjs.map +1 -1
- package/dist/checks.js +8 -290
- package/dist/checks.js.map +1 -1
- package/dist/core.cjs +9 -1728
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +9 -1702
- package/dist/core.js.map +1 -1
- package/dist/declare-host.cjs +1 -18
- package/dist/declare-host.cjs.map +1 -1
- package/dist/declare.cjs +1 -638
- package/dist/declare.cjs.map +1 -1
- package/dist/declare.js +1 -615
- package/dist/declare.js.map +1 -1
- package/dist/index.cjs +9 -1728
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +9 -1702
- package/dist/index.js.map +1 -1
- package/dist/menu.cjs +1 -529
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +1 -506
- package/dist/menu.js.map +1 -1
- package/dist/operate-document.cjs +4 -162
- package/dist/operate-document.cjs.map +1 -1
- package/dist/operate-document.js +4 -129
- package/dist/operate-document.js.map +1 -1
- package/dist/operate-rich-html.cjs +1 -128
- package/dist/operate-rich-html.cjs.map +1 -1
- package/dist/operate-rich-html.js +1 -95
- package/dist/operate-rich-html.js.map +1 -1
- package/dist/operate-server.cjs +1 -156
- package/dist/operate-server.cjs.map +1 -1
- package/dist/operate-server.js +1 -133
- package/dist/operate-server.js.map +1 -1
- package/dist/operate-xlsx.cjs +143 -34230
- package/dist/operate-xlsx.cjs.map +1 -1
- package/dist/operate-xlsx.js +143 -34214
- package/dist/operate-xlsx.js.map +1 -1
- package/dist/operate.cjs +9 -1726
- package/dist/operate.cjs.map +1 -1
- package/dist/operate.d.cts +2 -0
- package/dist/operate.d.ts +2 -0
- package/dist/operate.js +9 -1702
- package/dist/operate.js.map +1 -1
- package/dist/ports.cjs +1 -18
- package/dist/ports.cjs.map +1 -1
- package/dist/react-bar.cjs +2 -795
- package/dist/react-bar.cjs.map +1 -1
- package/dist/react-bar.js +2 -774
- package/dist/react-bar.js.map +1 -1
- package/dist/react-host.cjs +1 -299
- package/dist/react-host.cjs.map +1 -1
- package/dist/react-host.js +1 -267
- package/dist/react-host.js.map +1 -1
- package/dist/react-menu.cjs +2 -848
- package/dist/react-menu.cjs.map +1 -1
- package/dist/react-menu.js +2 -835
- package/dist/react-menu.js.map +1 -1
- package/dist/react-overflow.cjs +2 -695
- package/dist/react-overflow.cjs.map +1 -1
- package/dist/react-overflow.js +2 -674
- package/dist/react-overflow.js.map +1 -1
- package/dist/react-palette.cjs +2 -586
- package/dist/react-palette.cjs.map +1 -1
- package/dist/react-palette.js +2 -554
- package/dist/react-palette.js.map +1 -1
- package/dist/react-selection.cjs +2 -726
- package/dist/react-selection.cjs.map +1 -1
- package/dist/react-selection.js +2 -705
- package/dist/react-selection.js.map +1 -1
- package/dist/react-sheet.cjs +2 -734
- package/dist/react-sheet.cjs.map +1 -1
- package/dist/react-sheet.js +2 -713
- package/dist/react-sheet.js.map +1 -1
- package/dist/react.cjs +1 -29
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +1 -8
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/menu.js
CHANGED
|
@@ -1,507 +1,2 @@
|
|
|
1
|
-
// src/menu/headings.ts
|
|
2
|
-
var APPROVED_HEADINGS = /* @__PURE__ */ new Set([
|
|
3
|
-
"Save",
|
|
4
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
5
|
-
"Copy as",
|
|
6
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
7
|
-
"Export",
|
|
8
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
9
|
-
"Edit",
|
|
10
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
11
|
-
"Creator tools",
|
|
12
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
13
|
-
"Admin",
|
|
14
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
15
|
-
"App",
|
|
16
|
-
// rich-document menuStructure, e5b8d01229 (2026-05-19)
|
|
17
|
-
"Compare",
|
|
18
|
-
// rich-document menuStructure, 2e3a8a9fdf (2026-06-11)
|
|
19
|
-
"History"
|
|
20
|
-
// context-menu-v3 tiered layout — the one grouping Arman approved, 2026-08-22
|
|
21
|
-
]);
|
|
22
|
-
var CONTAINER_WORD = /^(more|more…|more\.\.\.|other|others|misc|miscellaneous|additional|extra|extras|actions|options|advanced|stuff|everything else)$/i;
|
|
23
|
-
var TARGET_SHAPE = /^(Cell|Row|Column|Table|Range|Selection|Record) · .+$/;
|
|
24
|
-
function isContainerWord(label) {
|
|
25
|
-
return CONTAINER_WORD.test(label.trim());
|
|
26
|
-
}
|
|
27
|
-
function isApprovedHeading(label, kind = "group", approved = APPROVED_HEADINGS) {
|
|
28
|
-
const l = label.trim();
|
|
29
|
-
if (!l || isContainerWord(l)) return false;
|
|
30
|
-
if (approved.has(l)) return true;
|
|
31
|
-
return kind === "target" || TARGET_SHAPE.test(l);
|
|
32
|
-
}
|
|
33
|
-
function defaultHeadingPolicy() {
|
|
34
|
-
const env = globalThis.process?.env?.NODE_ENV;
|
|
35
|
-
return env === "production" ? "inline" : "throw";
|
|
36
|
-
}
|
|
37
|
-
var UnapprovedHeadingError = class extends Error {
|
|
38
|
-
heading;
|
|
39
|
-
constructor(heading, where) {
|
|
40
|
-
super(
|
|
41
|
-
`Menu heading "${heading}" (${where}) is not an approved heading. Remedy: show these rows inline \u2014 declare no section (or kind "target" for the clicked thing's own label) \u2014 or ask Arman to add the name to APPROVED_HEADINGS in @ai-matrx/alchemy/menu. A generic container word ("More\u2026", "Other") is never approved.`
|
|
42
|
-
);
|
|
43
|
-
this.name = "UnapprovedHeadingError";
|
|
44
|
-
this.heading = heading;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
function menuHeadings(model) {
|
|
48
|
-
const out = [];
|
|
49
|
-
const walk = (nodes) => {
|
|
50
|
-
for (const n of nodes) {
|
|
51
|
-
if (n.kind === "submenu") {
|
|
52
|
-
out.push(n.label);
|
|
53
|
-
walk(n.children);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
for (const s of model.sections) {
|
|
58
|
-
if (s.label) out.push(s.label);
|
|
59
|
-
walk(s.nodes);
|
|
60
|
-
}
|
|
61
|
-
return [...new Set(out)];
|
|
62
|
-
}
|
|
63
|
-
function unapprovedHeadings(model, approved = APPROVED_HEADINGS) {
|
|
64
|
-
const out = /* @__PURE__ */ new Set();
|
|
65
|
-
for (const s of model.sections) {
|
|
66
|
-
if (s.label && !isApprovedHeading(s.label, s.primary || s.target ? "target" : "group", approved)) out.add(s.label);
|
|
67
|
-
const walk = (nodes) => {
|
|
68
|
-
for (const n of nodes) if (n.kind === "submenu") {
|
|
69
|
-
if (isContainerWord(n.label)) out.add(n.label);
|
|
70
|
-
walk(n.children);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
walk(s.nodes);
|
|
74
|
-
}
|
|
75
|
-
return [...out];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/menu/model.ts
|
|
79
|
-
var CATEGORY_GROUP = {
|
|
80
|
-
clipboard: "clipboard",
|
|
81
|
-
copy: "transfer",
|
|
82
|
-
export: "transfer",
|
|
83
|
-
save: "document",
|
|
84
|
-
share: "document",
|
|
85
|
-
edit: "editable",
|
|
86
|
-
history: "history",
|
|
87
|
-
ai: "ai",
|
|
88
|
-
ask: "ai",
|
|
89
|
-
listen: "tools",
|
|
90
|
-
study: "tools",
|
|
91
|
-
app: "tools",
|
|
92
|
-
feedback: "quick",
|
|
93
|
-
creator: "admin",
|
|
94
|
-
admin: "admin",
|
|
95
|
-
"surface-info": "surface-info"
|
|
96
|
-
};
|
|
97
|
-
var GROUP_ORDER = [
|
|
98
|
-
"clipboard",
|
|
99
|
-
"editable",
|
|
100
|
-
"transfer",
|
|
101
|
-
"document",
|
|
102
|
-
"history",
|
|
103
|
-
"ai",
|
|
104
|
-
"tools",
|
|
105
|
-
"quick",
|
|
106
|
-
"surface",
|
|
107
|
-
"admin",
|
|
108
|
-
"surface-info"
|
|
109
|
-
];
|
|
110
|
-
var CATEGORY_ORDER = [
|
|
111
|
-
"clipboard",
|
|
112
|
-
"edit",
|
|
113
|
-
"copy",
|
|
114
|
-
"export",
|
|
115
|
-
"save",
|
|
116
|
-
"share",
|
|
117
|
-
"history",
|
|
118
|
-
"ask",
|
|
119
|
-
"ai",
|
|
120
|
-
"listen",
|
|
121
|
-
"study",
|
|
122
|
-
"app",
|
|
123
|
-
"feedback",
|
|
124
|
-
"creator",
|
|
125
|
-
"admin",
|
|
126
|
-
"surface-info"
|
|
127
|
-
];
|
|
128
|
-
function translate(i18n, key, fallback) {
|
|
129
|
-
if (!i18n) return fallback;
|
|
130
|
-
try {
|
|
131
|
-
return i18n.t(key, fallback);
|
|
132
|
-
} catch {
|
|
133
|
-
return fallback;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function actionToNode(resolved, target, options = {}) {
|
|
137
|
-
const { action, eligibility } = resolved;
|
|
138
|
-
const rawLabel = typeof action.label === "function" ? action.label(target) : action.label;
|
|
139
|
-
const label = translate(options.i18n, action.labelKey ?? action.id, rawLabel);
|
|
140
|
-
const base = {
|
|
141
|
-
id: action.id,
|
|
142
|
-
actionId: action.id,
|
|
143
|
-
...action.icon !== void 0 ? { icon: action.icon } : {},
|
|
144
|
-
...action.placement !== void 0 ? { placement: action.placement } : {}
|
|
145
|
-
};
|
|
146
|
-
if (action.expand) {
|
|
147
|
-
const node = {
|
|
148
|
-
...base,
|
|
149
|
-
kind: "submenu",
|
|
150
|
-
label,
|
|
151
|
-
lazy: true,
|
|
152
|
-
loading: false,
|
|
153
|
-
...action.pending?.(target) ? { pending: true } : {},
|
|
154
|
-
children: [],
|
|
155
|
-
...action.emptyLabel !== void 0 ? { emptyLabel: action.emptyLabel } : {}
|
|
156
|
-
};
|
|
157
|
-
return node;
|
|
158
|
-
}
|
|
159
|
-
if (action.href !== void 0) {
|
|
160
|
-
const href = typeof action.href === "function" ? action.href(target) : action.href;
|
|
161
|
-
return { ...base, kind: "link", label, href };
|
|
162
|
-
}
|
|
163
|
-
if (action.toggle) {
|
|
164
|
-
return {
|
|
165
|
-
...base,
|
|
166
|
-
kind: "checkbox",
|
|
167
|
-
label,
|
|
168
|
-
checked: action.pressed?.(target) ?? false,
|
|
169
|
-
...action.description !== void 0 ? { description: action.description } : {}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
const state = action.stateIcon?.(target) ?? null;
|
|
173
|
-
const pressed = action.pressed?.(target);
|
|
174
|
-
const hint = action.hint !== void 0 ? translate(options.i18n, action.hintKey ?? `${action.id}.hint`, action.hint) : void 0;
|
|
175
|
-
return {
|
|
176
|
-
...base,
|
|
177
|
-
kind: "item",
|
|
178
|
-
label,
|
|
179
|
-
...action.iconTone !== void 0 ? { iconTone: action.iconTone } : {},
|
|
180
|
-
...state ? { stateIcon: state } : {},
|
|
181
|
-
...action.description !== void 0 ? { description: action.description } : {},
|
|
182
|
-
...hint !== void 0 ? { hint } : {},
|
|
183
|
-
...pressed !== void 0 ? { pressed } : {},
|
|
184
|
-
...action.destructive ? { destructive: true } : {},
|
|
185
|
-
...action.preserveSelection ? { preserveSelection: true } : {},
|
|
186
|
-
...eligibility.status === "unavailable-verb" ? { unavailable: { sentence: eligibility.sentence } } : {}
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
function buildMenuModel(target, resolved, options = {}) {
|
|
190
|
-
const policy = options.headingPolicy ?? defaultHeadingPolicy();
|
|
191
|
-
resolved = resolved.map((r) => {
|
|
192
|
-
const sec = r.action.section;
|
|
193
|
-
if (r.action.expand && isContainerWord(typeof r.action.label === "string" ? r.action.label : "") && policy === "throw") {
|
|
194
|
-
throw new UnapprovedHeadingError(r.action.label, `submenu of ${r.action.id}`);
|
|
195
|
-
}
|
|
196
|
-
if (!sec) return r;
|
|
197
|
-
const kind = sec.kind ?? (sec.primary ? "target" : "group");
|
|
198
|
-
if (isApprovedHeading(sec.label, kind)) return r;
|
|
199
|
-
if (policy === "throw") throw new UnapprovedHeadingError(sec.label, `section of ${r.action.id}`);
|
|
200
|
-
const { section: _dropped, ...rest } = r.action;
|
|
201
|
-
return { ...r, action: rest };
|
|
202
|
-
});
|
|
203
|
-
const ordered = resolved.map((r, index) => ({ r, index })).sort((a, b) => {
|
|
204
|
-
const ca = CATEGORY_ORDER.indexOf(a.r.action.category);
|
|
205
|
-
const cb = CATEGORY_ORDER.indexOf(b.r.action.category);
|
|
206
|
-
if (ca !== cb) return ca - cb;
|
|
207
|
-
const oa = a.r.action.order ?? 0;
|
|
208
|
-
const ob = b.r.action.order ?? 0;
|
|
209
|
-
if (oa !== ob) return oa - ob;
|
|
210
|
-
return a.index - b.index;
|
|
211
|
-
}).map((x) => x.r);
|
|
212
|
-
const sections = /* @__PURE__ */ new Map();
|
|
213
|
-
const roles = {};
|
|
214
|
-
const explicit = /* @__PURE__ */ new Map();
|
|
215
|
-
resolved.forEach((r, index) => {
|
|
216
|
-
const sec = r.action.section;
|
|
217
|
-
if (!sec) return;
|
|
218
|
-
const entry = explicit.get(sec.id) ?? { group: CATEGORY_GROUP[r.action.category], rows: [] };
|
|
219
|
-
entry.rows.push({ r, index });
|
|
220
|
-
explicit.set(sec.id, entry);
|
|
221
|
-
});
|
|
222
|
-
const placed = /* @__PURE__ */ new Set();
|
|
223
|
-
for (const r of ordered) {
|
|
224
|
-
const sec = r.action.section;
|
|
225
|
-
if (sec && placed.has(sec.id)) continue;
|
|
226
|
-
const group = sec ? explicit.get(sec.id)?.group ?? CATEGORY_GROUP[r.action.category] : CATEGORY_GROUP[r.action.category];
|
|
227
|
-
const sectionId = sec ? `section:${sec.id}` : group;
|
|
228
|
-
let section = sections.get(sectionId);
|
|
229
|
-
if (!section) {
|
|
230
|
-
section = {
|
|
231
|
-
id: sectionId,
|
|
232
|
-
group,
|
|
233
|
-
nodes: [],
|
|
234
|
-
...sec ? { label: translate(options.i18n, sec.labelKey ?? `section.${sec.id}`, sec.label) } : {},
|
|
235
|
-
...sec?.icon !== void 0 ? { icon: sec.icon } : {},
|
|
236
|
-
...sec?.primary ? { primary: true } : {},
|
|
237
|
-
...sec && (sec.kind ?? (sec.primary ? "target" : "group")) === "target" ? { target: true } : {}
|
|
238
|
-
};
|
|
239
|
-
sections.set(sectionId, section);
|
|
240
|
-
}
|
|
241
|
-
const rows = sec ? (explicit.get(sec.id)?.rows ?? []).sort((x, y) => (x.r.action.order ?? 0) - (y.r.action.order ?? 0) || x.index - y.index).map((x) => x.r) : [r];
|
|
242
|
-
if (sec) placed.add(sec.id);
|
|
243
|
-
for (const row of rows) {
|
|
244
|
-
section.nodes.push(actionToNode(row, target, options));
|
|
245
|
-
if (row.action.verb) roles[row.action.verb] = row.action.id;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
const list = [...sections.values()].sort((a, b) => {
|
|
249
|
-
if (Boolean(a.primary) !== Boolean(b.primary)) return a.primary ? -1 : 1;
|
|
250
|
-
return GROUP_ORDER.indexOf(a.group) - GROUP_ORDER.indexOf(b.group);
|
|
251
|
-
});
|
|
252
|
-
const selected = target.selection?.text?.trim();
|
|
253
|
-
const content = options.content?.trim();
|
|
254
|
-
const header = selected ? { label: "Selected", text: target.selection?.text ?? "" } : content ? { label: "Content", text: options.content ?? "" } : null;
|
|
255
|
-
return { header, sections: list, roles };
|
|
256
|
-
}
|
|
257
|
-
function withExpandedChildren(model, nodeId, children) {
|
|
258
|
-
const visit = (nodes) => nodes.map((n) => {
|
|
259
|
-
if (n.kind !== "submenu") return n;
|
|
260
|
-
if (n.id === nodeId) {
|
|
261
|
-
return children === "loading" ? { ...n, loading: true } : { ...n, lazy: false, loading: false, pending: false, children };
|
|
262
|
-
}
|
|
263
|
-
return { ...n, children: visit(n.children) };
|
|
264
|
-
});
|
|
265
|
-
return { ...model, sections: model.sections.map((s) => ({ ...s, nodes: visit(s.nodes) })) };
|
|
266
|
-
}
|
|
267
|
-
function findNode(nodes, id) {
|
|
268
|
-
for (const n of nodes) {
|
|
269
|
-
if (n.id === id) return n;
|
|
270
|
-
if (n.kind === "submenu") {
|
|
271
|
-
const hit = findNode(n.children, id);
|
|
272
|
-
if (hit) return hit;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// src/menu/layouts.ts
|
|
279
|
-
var INLINE_SECTION_MAX = 3;
|
|
280
|
-
function sectionLabel(section) {
|
|
281
|
-
return section.label ?? null;
|
|
282
|
-
}
|
|
283
|
-
function isLeaf(node) {
|
|
284
|
-
return node.kind === "item" || node.kind === "checkbox" || node.kind === "link";
|
|
285
|
-
}
|
|
286
|
-
function collect(nodes, out) {
|
|
287
|
-
for (const n of nodes) {
|
|
288
|
-
if (isLeaf(n)) out.push(n.id);
|
|
289
|
-
else if (n.kind === "submenu") {
|
|
290
|
-
if (n.lazy) out.push(n.id);
|
|
291
|
-
else collect(n.children, out);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
function leafIds(arranged) {
|
|
296
|
-
const out = [];
|
|
297
|
-
if (Array.isArray(arranged)) {
|
|
298
|
-
for (const s of arranged) collect(s.nodes, out);
|
|
299
|
-
} else if ("leaves" in arranged) {
|
|
300
|
-
for (const l of arranged.leaves) out.push(l.node.id);
|
|
301
|
-
} else if ("primary" in arranged && "overflow" in arranged) {
|
|
302
|
-
collect(arranged.primary, out);
|
|
303
|
-
for (const s of arranged.overflow) collect(s.nodes, out);
|
|
304
|
-
} else if ("root" in arranged) {
|
|
305
|
-
collect(arranged.strip, out);
|
|
306
|
-
collect(arranged.root, out);
|
|
307
|
-
} else if ("more" in arranged) {
|
|
308
|
-
collect(arranged.strip, out);
|
|
309
|
-
for (const s of arranged.more) collect(s.nodes, out);
|
|
310
|
-
} else if ("filterable" in arranged) {
|
|
311
|
-
collect(arranged.strip, out);
|
|
312
|
-
for (const s of arranged.sections) collect(s.nodes, out);
|
|
313
|
-
} else {
|
|
314
|
-
for (const s of arranged.sections) collect(s.nodes, out);
|
|
315
|
-
}
|
|
316
|
-
return [...new Set(out)].sort();
|
|
317
|
-
}
|
|
318
|
-
function fold(section) {
|
|
319
|
-
return {
|
|
320
|
-
kind: "submenu",
|
|
321
|
-
id: `${section.id}:fold`,
|
|
322
|
-
label: section.label,
|
|
323
|
-
...section.icon !== void 0 ? { icon: section.icon } : {},
|
|
324
|
-
children: section.nodes
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
function rowCount(nodes) {
|
|
328
|
-
return nodes.filter((n) => n.kind !== "separator" && n.kind !== "label").length;
|
|
329
|
-
}
|
|
330
|
-
function stripOf(model) {
|
|
331
|
-
const clipboard = model.sections.find((s) => s.group === "clipboard" && !s.primary);
|
|
332
|
-
return clipboard ? clipboard.nodes.filter(isLeaf) : [];
|
|
333
|
-
}
|
|
334
|
-
function clipboardRest(model) {
|
|
335
|
-
const clipboard = model.sections.find((s) => s.group === "clipboard" && !s.primary);
|
|
336
|
-
return clipboard ? clipboard.nodes.filter((n) => !isLeaf(n) && n.kind === "submenu") : [];
|
|
337
|
-
}
|
|
338
|
-
function placementOf(node) {
|
|
339
|
-
return "placement" in node && node.placement ? node.placement : "overflow";
|
|
340
|
-
}
|
|
341
|
-
function classic(model) {
|
|
342
|
-
return { strip: [], sections: model.sections, filterable: false };
|
|
343
|
-
}
|
|
344
|
-
function tiered(model, filterable) {
|
|
345
|
-
const strip = stripOf(model);
|
|
346
|
-
const sections = [];
|
|
347
|
-
const rest = clipboardRest(model);
|
|
348
|
-
for (const s of model.sections) {
|
|
349
|
-
if (s.group === "clipboard" && !s.primary) {
|
|
350
|
-
if (rest.length) sections.push({ ...s, nodes: rest });
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
if (s.primary) {
|
|
354
|
-
sections.push(s);
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
if (!s.label || s.group === "surface-info" || rowCount(s.nodes) <= INLINE_SECTION_MAX) {
|
|
358
|
-
sections.push(s);
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
sections.push({ id: s.id, group: s.group, nodes: [fold({ ...s, label: s.label })] });
|
|
362
|
-
}
|
|
363
|
-
return { strip, sections, filterable };
|
|
364
|
-
}
|
|
365
|
-
function context(model, arrangement) {
|
|
366
|
-
if (arrangement === "classic") return classic(model);
|
|
367
|
-
return tiered(model, arrangement === "command");
|
|
368
|
-
}
|
|
369
|
-
function foldNamed(sections) {
|
|
370
|
-
return sections.map(
|
|
371
|
-
(s) => s.label && !s.primary && rowCount(s.nodes) > INLINE_SECTION_MAX ? { id: s.id, group: s.group, nodes: [fold({ ...s, label: s.label })] } : s
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
function overflow(model) {
|
|
375
|
-
return foldNamed(model.sections);
|
|
376
|
-
}
|
|
377
|
-
function bar(model, _options = {}) {
|
|
378
|
-
const primary = [];
|
|
379
|
-
const overflowSections = [];
|
|
380
|
-
for (const s of model.sections) {
|
|
381
|
-
const rest = [];
|
|
382
|
-
for (const n of s.nodes) {
|
|
383
|
-
if (n.kind === "separator" || n.kind === "label") {
|
|
384
|
-
rest.push(n);
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
const p = placementOf(n);
|
|
388
|
-
if (p === "primary" || p === "both") primary.push(n);
|
|
389
|
-
if (p === "overflow" || p === "both") rest.push(n);
|
|
390
|
-
}
|
|
391
|
-
if (rowCount(rest) > 0) overflowSections.push({ ...s, nodes: rest });
|
|
392
|
-
}
|
|
393
|
-
return { primary, overflow: foldNamed(overflowSections) };
|
|
394
|
-
}
|
|
395
|
-
function selectionToolbar(model) {
|
|
396
|
-
const strip = [];
|
|
397
|
-
const more = [];
|
|
398
|
-
for (const s of model.sections) {
|
|
399
|
-
const rest = [];
|
|
400
|
-
for (const n of s.nodes) {
|
|
401
|
-
const inStrip = s.group === "clipboard" && !s.primary && isLeaf(n) || placementOf(n) !== "overflow";
|
|
402
|
-
if (inStrip) strip.push(n);
|
|
403
|
-
else rest.push(n);
|
|
404
|
-
}
|
|
405
|
-
if (rowCount(rest) > 0) more.push({ ...s, nodes: rest });
|
|
406
|
-
}
|
|
407
|
-
return { strip, more };
|
|
408
|
-
}
|
|
409
|
-
function sheet(model) {
|
|
410
|
-
const strip = stripOf(model);
|
|
411
|
-
const root = [];
|
|
412
|
-
const rest = clipboardRest(model);
|
|
413
|
-
for (const s of model.sections) {
|
|
414
|
-
const nodes = s.group === "clipboard" && !s.primary ? rest : s.nodes;
|
|
415
|
-
if (rowCount(nodes) === 0) continue;
|
|
416
|
-
if (root.length) root.push({ kind: "separator", id: `${s.id}:sep` });
|
|
417
|
-
if (s.primary || !s.label || rowCount(nodes) <= INLINE_SECTION_MAX) {
|
|
418
|
-
if (s.label) root.push({ kind: "label", id: `${s.id}:label`, label: s.label });
|
|
419
|
-
root.push(...nodes);
|
|
420
|
-
} else {
|
|
421
|
-
root.push(fold({ ...s, label: s.label, nodes }));
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return { strip, root };
|
|
425
|
-
}
|
|
426
|
-
function palette(model) {
|
|
427
|
-
const leaves = [];
|
|
428
|
-
const walk = (nodes, trail) => {
|
|
429
|
-
for (const n of nodes) {
|
|
430
|
-
if (isLeaf(n)) leaves.push({ node: n, breadcrumb: trail });
|
|
431
|
-
else if (n.kind === "submenu") {
|
|
432
|
-
if (n.lazy) leaves.push({ node: n, breadcrumb: trail });
|
|
433
|
-
else walk(n.children, [...trail, n.label]);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
for (const s of model.sections) walk(s.nodes, s.label ? [s.label] : []);
|
|
438
|
-
return { leaves };
|
|
439
|
-
}
|
|
440
|
-
function filterPalette(arranged, query) {
|
|
441
|
-
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
442
|
-
if (!words.length) return arranged;
|
|
443
|
-
return {
|
|
444
|
-
leaves: arranged.leaves.filter((l) => {
|
|
445
|
-
const hay = `${l.node.label} ${l.breadcrumb.join(" ")}`.toLowerCase();
|
|
446
|
-
return words.every((w) => hay.includes(w));
|
|
447
|
-
})
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
var layouts = { bar, overflow, context, selectionToolbar, sheet, palette };
|
|
451
|
-
|
|
452
|
-
// src/menu/knobs.ts
|
|
453
|
-
var PLATFORM_MENU_LAYOUT = {
|
|
454
|
-
context: "command",
|
|
455
|
-
density: "comfortable"
|
|
456
|
-
};
|
|
457
|
-
var MENU_LAYOUT_SETTING = "alchemy.menu.layout";
|
|
458
|
-
var ARRANGEMENTS = /* @__PURE__ */ new Set(["classic", "tiered", "command"]);
|
|
459
|
-
var DENSITIES = /* @__PURE__ */ new Set(["comfortable", "compact"]);
|
|
460
|
-
async function resolveMenuLayout(ports, organizationId) {
|
|
461
|
-
const org = ports.knobs?.layout(organizationId) ?? null;
|
|
462
|
-
const base = { ...PLATFORM_MENU_LAYOUT, ...org ?? {} };
|
|
463
|
-
let own = null;
|
|
464
|
-
try {
|
|
465
|
-
own = await ports.persistence?.readSetting(MENU_LAYOUT_SETTING) ?? null;
|
|
466
|
-
} catch {
|
|
467
|
-
own = null;
|
|
468
|
-
}
|
|
469
|
-
if (own && typeof own === "object" && !Array.isArray(own)) {
|
|
470
|
-
const o = own;
|
|
471
|
-
return {
|
|
472
|
-
context: typeof o.context === "string" && ARRANGEMENTS.has(o.context) ? o.context : base.context,
|
|
473
|
-
density: typeof o.density === "string" && DENSITIES.has(o.density) ? o.density : base.density
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
return base;
|
|
477
|
-
}
|
|
478
|
-
export {
|
|
479
|
-
APPROVED_HEADINGS,
|
|
480
|
-
CATEGORY_GROUP,
|
|
481
|
-
GROUP_ORDER,
|
|
482
|
-
INLINE_SECTION_MAX,
|
|
483
|
-
MENU_LAYOUT_SETTING,
|
|
484
|
-
PLATFORM_MENU_LAYOUT,
|
|
485
|
-
UnapprovedHeadingError,
|
|
486
|
-
actionToNode,
|
|
487
|
-
bar,
|
|
488
|
-
buildMenuModel,
|
|
489
|
-
context,
|
|
490
|
-
defaultHeadingPolicy,
|
|
491
|
-
filterPalette,
|
|
492
|
-
findNode,
|
|
493
|
-
isApprovedHeading,
|
|
494
|
-
isContainerWord,
|
|
495
|
-
layouts,
|
|
496
|
-
leafIds,
|
|
497
|
-
menuHeadings,
|
|
498
|
-
overflow,
|
|
499
|
-
palette,
|
|
500
|
-
resolveMenuLayout,
|
|
501
|
-
sectionLabel,
|
|
502
|
-
selectionToolbar,
|
|
503
|
-
sheet,
|
|
504
|
-
unapprovedHeadings,
|
|
505
|
-
withExpandedChildren
|
|
506
|
-
};
|
|
1
|
+
var APPROVED_HEADINGS=new Set(["Save","Copy as","Export","Edit","Creator tools","Admin","App","Compare","History"]),CONTAINER_WORD=/^(more|more…|more\.\.\.|other|others|misc|miscellaneous|additional|extra|extras|actions|options|advanced|stuff|everything else)$/i,TARGET_SHAPE=/^(Cell|Row|Column|Table|Range|Selection|Record) · .+$/;function isContainerWord(label){return CONTAINER_WORD.test(label.trim())}function isApprovedHeading(label,kind="group",approved=APPROVED_HEADINGS){let l=label.trim();return!l||isContainerWord(l)?!1:approved.has(l)?!0:kind==="target"||TARGET_SHAPE.test(l)}function defaultHeadingPolicy(){return globalThis.process?.env?.NODE_ENV==="production"?"inline":"throw"}var UnapprovedHeadingError=class extends Error{heading;constructor(heading,where){super(`Menu heading "${heading}" (${where}) is not an approved heading. Remedy: show these rows inline \u2014 declare no section (or kind "target" for the clicked thing's own label) \u2014 or ask Arman to add the name to APPROVED_HEADINGS in @ai-matrx/alchemy/menu. A generic container word ("More\u2026", "Other") is never approved.`),this.name="UnapprovedHeadingError",this.heading=heading}};function menuHeadings(model){let out=[],walk=nodes=>{for(let n of nodes)n.kind==="submenu"&&(out.push(n.label),walk(n.children))};for(let s of model.sections)s.label&&out.push(s.label),walk(s.nodes);return[...new Set(out)]}function unapprovedHeadings(model,approved=APPROVED_HEADINGS){let out=new Set;for(let s of model.sections){s.label&&!isApprovedHeading(s.label,s.primary||s.target?"target":"group",approved)&&out.add(s.label);let walk=nodes=>{for(let n of nodes)n.kind==="submenu"&&(isContainerWord(n.label)&&out.add(n.label),walk(n.children))};walk(s.nodes)}return[...out]}var CATEGORY_GROUP={clipboard:"clipboard",copy:"transfer",export:"transfer",save:"document",share:"document",edit:"editable",history:"history",ai:"ai",ask:"ai",listen:"tools",study:"tools",app:"tools",feedback:"quick",creator:"admin",admin:"admin","surface-info":"surface-info"},GROUP_ORDER=["clipboard","editable","transfer","document","history","ai","tools","quick","surface","admin","surface-info"],CATEGORY_ORDER=["clipboard","edit","copy","export","save","share","history","ask","ai","listen","study","app","feedback","creator","admin","surface-info"];function translate(i18n,key,fallback){if(!i18n)return fallback;try{return i18n.t(key,fallback)}catch{return fallback}}function actionToNode(resolved,target,options={}){let{action,eligibility}=resolved,rawLabel=typeof action.label=="function"?action.label(target):action.label,label=translate(options.i18n,action.labelKey??action.id,rawLabel),base={id:action.id,actionId:action.id,...action.icon!==void 0?{icon:action.icon}:{},...action.placement!==void 0?{placement:action.placement}:{}};if(action.expand)return{...base,kind:"submenu",label,lazy:!0,loading:!1,...action.pending?.(target)?{pending:!0}:{},children:[],...action.emptyLabel!==void 0?{emptyLabel:action.emptyLabel}:{}};if(action.href!==void 0){let href=typeof action.href=="function"?action.href(target):action.href;return{...base,kind:"link",label,href}}if(action.toggle)return{...base,kind:"checkbox",label,checked:action.pressed?.(target)??!1,...action.description!==void 0?{description:action.description}:{}};let state=action.stateIcon?.(target)??null,pressed=action.pressed?.(target),hint=action.hint!==void 0?translate(options.i18n,action.hintKey??`${action.id}.hint`,action.hint):void 0;return{...base,kind:"item",label,...action.iconTone!==void 0?{iconTone:action.iconTone}:{},...state?{stateIcon:state}:{},...action.description!==void 0?{description:action.description}:{},...hint!==void 0?{hint}:{},...pressed!==void 0?{pressed}:{},...action.destructive?{destructive:!0}:{},...action.preserveSelection?{preserveSelection:!0}:{},...eligibility.status==="unavailable-verb"?{unavailable:{sentence:eligibility.sentence}}:{}}}function buildMenuModel(target,resolved,options={}){let policy=options.headingPolicy??defaultHeadingPolicy();resolved=resolved.map(r=>{let sec=r.action.section;if(r.action.expand&&isContainerWord(typeof r.action.label=="string"?r.action.label:"")&&policy==="throw")throw new UnapprovedHeadingError(r.action.label,`submenu of ${r.action.id}`);if(!sec)return r;let kind=sec.kind??(sec.primary?"target":"group");if(isApprovedHeading(sec.label,kind))return r;if(policy==="throw")throw new UnapprovedHeadingError(sec.label,`section of ${r.action.id}`);let{section:_dropped,...rest}=r.action;return{...r,action:rest}});let ordered=resolved.map((r,index)=>({r,index})).sort((a,b)=>{let ca=CATEGORY_ORDER.indexOf(a.r.action.category),cb=CATEGORY_ORDER.indexOf(b.r.action.category);if(ca!==cb)return ca-cb;let oa=a.r.action.order??0,ob=b.r.action.order??0;return oa!==ob?oa-ob:a.index-b.index}).map(x=>x.r),sections=new Map,roles={},explicit=new Map;resolved.forEach((r,index)=>{let sec=r.action.section;if(!sec)return;let entry=explicit.get(sec.id)??{group:CATEGORY_GROUP[r.action.category],rows:[]};entry.rows.push({r,index}),explicit.set(sec.id,entry)});let placed=new Set;for(let r of ordered){let sec=r.action.section;if(sec&&placed.has(sec.id))continue;let group=sec?explicit.get(sec.id)?.group??CATEGORY_GROUP[r.action.category]:CATEGORY_GROUP[r.action.category],sectionId=sec?`section:${sec.id}`:group,section=sections.get(sectionId);section||(section={id:sectionId,group,nodes:[],...sec?{label:translate(options.i18n,sec.labelKey??`section.${sec.id}`,sec.label)}:{},...sec?.icon!==void 0?{icon:sec.icon}:{},...sec?.primary?{primary:!0}:{},...sec&&(sec.kind??(sec.primary?"target":"group"))==="target"?{target:!0}:{}},sections.set(sectionId,section));let rows=sec?(explicit.get(sec.id)?.rows??[]).sort((x,y)=>(x.r.action.order??0)-(y.r.action.order??0)||x.index-y.index).map(x=>x.r):[r];sec&&placed.add(sec.id);for(let row of rows)section.nodes.push(actionToNode(row,target,options)),row.action.verb&&(roles[row.action.verb]=row.action.id)}let list=[...sections.values()].sort((a,b)=>!!a.primary!=!!b.primary?a.primary?-1:1:GROUP_ORDER.indexOf(a.group)-GROUP_ORDER.indexOf(b.group)),selected=target.selection?.text?.trim(),content=options.content?.trim();return{header:selected?{label:"Selected",text:target.selection?.text??""}:content?{label:"Content",text:options.content??""}:null,sections:list,roles}}function withExpandedChildren(model,nodeId,children){let visit=nodes=>nodes.map(n=>n.kind!=="submenu"?n:n.id===nodeId?children==="loading"?{...n,loading:!0}:{...n,lazy:!1,loading:!1,pending:!1,children}:{...n,children:visit(n.children)});return{...model,sections:model.sections.map(s=>({...s,nodes:visit(s.nodes)}))}}function findNode(nodes,id){for(let n of nodes){if(n.id===id)return n;if(n.kind==="submenu"){let hit=findNode(n.children,id);if(hit)return hit}}return null}var INLINE_SECTION_MAX=3;function sectionLabel(section){return section.label??null}function isLeaf(node){return node.kind==="item"||node.kind==="checkbox"||node.kind==="link"}function collect(nodes,out){for(let n of nodes)isLeaf(n)?out.push(n.id):n.kind==="submenu"&&(n.lazy?out.push(n.id):collect(n.children,out))}function leafIds(arranged){let out=[];if(Array.isArray(arranged))for(let s of arranged)collect(s.nodes,out);else if("leaves"in arranged)for(let l of arranged.leaves)out.push(l.node.id);else if("primary"in arranged&&"overflow"in arranged){collect(arranged.primary,out);for(let s of arranged.overflow)collect(s.nodes,out)}else if("root"in arranged)collect(arranged.strip,out),collect(arranged.root,out);else if("more"in arranged){collect(arranged.strip,out);for(let s of arranged.more)collect(s.nodes,out)}else if("filterable"in arranged){collect(arranged.strip,out);for(let s of arranged.sections)collect(s.nodes,out)}else for(let s of arranged.sections)collect(s.nodes,out);return[...new Set(out)].sort()}function fold(section){return{kind:"submenu",id:`${section.id}:fold`,label:section.label,...section.icon!==void 0?{icon:section.icon}:{},children:section.nodes}}function rowCount(nodes){return nodes.filter(n=>n.kind!=="separator"&&n.kind!=="label").length}function stripOf(model){let clipboard=model.sections.find(s=>s.group==="clipboard"&&!s.primary);return clipboard?clipboard.nodes.filter(isLeaf):[]}function clipboardRest(model){let clipboard=model.sections.find(s=>s.group==="clipboard"&&!s.primary);return clipboard?clipboard.nodes.filter(n=>!isLeaf(n)&&n.kind==="submenu"):[]}function placementOf(node){return"placement"in node&&node.placement?node.placement:"overflow"}function classic(model){return{strip:[],sections:model.sections,filterable:!1}}function tiered(model,filterable){let strip=stripOf(model),sections=[],rest=clipboardRest(model);for(let s of model.sections){if(s.group==="clipboard"&&!s.primary){rest.length&§ions.push({...s,nodes:rest});continue}if(s.primary){sections.push(s);continue}if(!s.label||s.group==="surface-info"||rowCount(s.nodes)<=3){sections.push(s);continue}sections.push({id:s.id,group:s.group,nodes:[fold({...s,label:s.label})]})}return{strip,sections,filterable}}function context(model,arrangement){return arrangement==="classic"?classic(model):tiered(model,arrangement==="command")}function foldNamed(sections){return sections.map(s=>s.label&&!s.primary&&rowCount(s.nodes)>3?{id:s.id,group:s.group,nodes:[fold({...s,label:s.label})]}:s)}function overflow(model){return foldNamed(model.sections)}function bar(model,_options={}){let primary=[],overflowSections=[];for(let s of model.sections){let rest=[];for(let n of s.nodes){if(n.kind==="separator"||n.kind==="label"){rest.push(n);continue}let p=placementOf(n);(p==="primary"||p==="both")&&primary.push(n),(p==="overflow"||p==="both")&&rest.push(n)}rowCount(rest)>0&&overflowSections.push({...s,nodes:rest})}return{primary,overflow:foldNamed(overflowSections)}}function selectionToolbar(model){let strip=[],more=[];for(let s of model.sections){let rest=[];for(let n of s.nodes)s.group==="clipboard"&&!s.primary&&isLeaf(n)||placementOf(n)!=="overflow"?strip.push(n):rest.push(n);rowCount(rest)>0&&more.push({...s,nodes:rest})}return{strip,more}}function sheet(model){let strip=stripOf(model),root=[],rest=clipboardRest(model);for(let s of model.sections){let nodes=s.group==="clipboard"&&!s.primary?rest:s.nodes;rowCount(nodes)!==0&&(root.length&&root.push({kind:"separator",id:`${s.id}:sep`}),s.primary||!s.label||rowCount(nodes)<=3?(s.label&&root.push({kind:"label",id:`${s.id}:label`,label:s.label}),root.push(...nodes)):root.push(fold({...s,label:s.label,nodes})))}return{strip,root}}function palette(model){let leaves=[],walk=(nodes,trail)=>{for(let n of nodes)isLeaf(n)?leaves.push({node:n,breadcrumb:trail}):n.kind==="submenu"&&(n.lazy?leaves.push({node:n,breadcrumb:trail}):walk(n.children,[...trail,n.label]))};for(let s of model.sections)walk(s.nodes,s.label?[s.label]:[]);return{leaves}}function filterPalette(arranged,query){let words=query.toLowerCase().split(/\s+/).filter(Boolean);return words.length?{leaves:arranged.leaves.filter(l=>{let hay=`${l.node.label} ${l.breadcrumb.join(" ")}`.toLowerCase();return words.every(w=>hay.includes(w))})}:arranged}var layouts={bar,overflow,context,selectionToolbar,sheet,palette};var PLATFORM_MENU_LAYOUT={context:"command",density:"comfortable"},MENU_LAYOUT_SETTING="alchemy.menu.layout",ARRANGEMENTS=new Set(["classic","tiered","command"]),DENSITIES=new Set(["comfortable","compact"]);async function resolveMenuLayout(ports,organizationId){let org=ports.knobs?.layout(organizationId)??null,base={...PLATFORM_MENU_LAYOUT,...org??{}},own=null;try{own=await ports.persistence?.readSetting(MENU_LAYOUT_SETTING)??null}catch{own=null}if(own&&typeof own=="object"&&!Array.isArray(own)){let o=own;return{context:typeof o.context=="string"&&ARRANGEMENTS.has(o.context)?o.context:base.context,density:typeof o.density=="string"&&DENSITIES.has(o.density)?o.density:base.density}}return base}export{APPROVED_HEADINGS,CATEGORY_GROUP,GROUP_ORDER,INLINE_SECTION_MAX,MENU_LAYOUT_SETTING,PLATFORM_MENU_LAYOUT,UnapprovedHeadingError,actionToNode,bar,buildMenuModel,context,defaultHeadingPolicy,filterPalette,findNode,isApprovedHeading,isContainerWord,layouts,leafIds,menuHeadings,overflow,palette,resolveMenuLayout,sectionLabel,selectionToolbar,sheet,unapprovedHeadings,withExpandedChildren};
|
|
507
2
|
//# sourceMappingURL=menu.js.map
|