@ai-matrx/context-menu 0.1.9 → 0.1.11

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.
Files changed (70) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/builder.cjs +17 -18
  3. package/dist/builder.cjs.map +1 -1
  4. package/dist/builder.js +2 -3
  5. package/dist/builder.js.map +1 -1
  6. package/dist/{chunk-ECKA474B.js → chunk-2B37SXQW.js} +21 -12
  7. package/dist/chunk-2B37SXQW.js.map +1 -0
  8. package/dist/{chunk-TSOUI723.cjs → chunk-7TIZAANU.cjs} +26 -11
  9. package/dist/chunk-7TIZAANU.cjs.map +1 -0
  10. package/dist/{chunk-A24SBKSA.js → chunk-EG23Y2KW.js} +14 -13
  11. package/dist/{chunk-A24SBKSA.js.map → chunk-EG23Y2KW.js.map} +1 -1
  12. package/dist/{chunk-7VDSMZWI.cjs → chunk-ERFH2RZC.cjs} +18 -17
  13. package/dist/chunk-ERFH2RZC.cjs.map +1 -0
  14. package/dist/{chunk-EIP5YS4Y.js → chunk-HJU3RU2B.js} +27 -12
  15. package/dist/chunk-HJU3RU2B.js.map +1 -0
  16. package/dist/{chunk-SWEWOUHK.cjs → chunk-IH5WN5JP.cjs} +31 -26
  17. package/dist/chunk-IH5WN5JP.cjs.map +1 -0
  18. package/dist/{chunk-QXHLM7SS.cjs → chunk-LDMYNYFC.cjs} +42 -33
  19. package/dist/chunk-LDMYNYFC.cjs.map +1 -0
  20. package/dist/{chunk-ANJYKQAA.cjs → chunk-MBU4Y22O.cjs} +3 -3
  21. package/dist/{chunk-ANJYKQAA.cjs.map → chunk-MBU4Y22O.cjs.map} +1 -1
  22. package/dist/{chunk-L6DPISFT.js → chunk-NBEXJ25Z.js} +24 -19
  23. package/dist/{chunk-L6DPISFT.js.map → chunk-NBEXJ25Z.js.map} +1 -1
  24. package/dist/{chunk-LE6ZUVCO.js → chunk-PB3PMDKV.js} +2 -2
  25. package/dist/{chunk-NOYMFNSU.js → chunk-SJWKMZEJ.js} +33 -12
  26. package/dist/chunk-SJWKMZEJ.js.map +1 -0
  27. package/dist/{chunk-XERD2YCJ.cjs → chunk-WROBL3DR.cjs} +32 -11
  28. package/dist/chunk-WROBL3DR.cjs.map +1 -0
  29. package/dist/design-store.cjs +0 -1
  30. package/dist/design-store.cjs.map +1 -1
  31. package/dist/design-store.js +0 -1
  32. package/dist/design-store.js.map +1 -1
  33. package/dist/engine.cjs +64 -96
  34. package/dist/engine.cjs.map +1 -1
  35. package/dist/engine.d.cts +7 -7
  36. package/dist/engine.d.ts +7 -7
  37. package/dist/engine.js +72 -104
  38. package/dist/engine.js.map +1 -1
  39. package/dist/index.cjs +3 -4
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.js +2 -3
  42. package/dist/menu-content.cjs +18 -19
  43. package/dist/menu-content.cjs.map +1 -1
  44. package/dist/menu-content.js +4 -5
  45. package/dist/menu-content.js.map +1 -1
  46. package/dist/menu-inspect.cjs +0 -1
  47. package/dist/menu-inspect.cjs.map +1 -1
  48. package/dist/menu-inspect.js +0 -1
  49. package/dist/menu-inspect.js.map +1 -1
  50. package/dist/model.cjs +4 -5
  51. package/dist/model.cjs.map +1 -1
  52. package/dist/model.js +3 -4
  53. package/dist/react.cjs +52 -44
  54. package/dist/react.cjs.map +1 -1
  55. package/dist/react.js +30 -22
  56. package/dist/react.js.map +1 -1
  57. package/package.json +6 -6
  58. package/dist/chunk-7VDSMZWI.cjs.map +0 -1
  59. package/dist/chunk-CUODNUAD.cjs +0 -24
  60. package/dist/chunk-CUODNUAD.cjs.map +0 -1
  61. package/dist/chunk-ECKA474B.js.map +0 -1
  62. package/dist/chunk-EIP5YS4Y.js.map +0 -1
  63. package/dist/chunk-NOYMFNSU.js.map +0 -1
  64. package/dist/chunk-PE3ZHHM5.js +0 -24
  65. package/dist/chunk-PE3ZHHM5.js.map +0 -1
  66. package/dist/chunk-QXHLM7SS.cjs.map +0 -1
  67. package/dist/chunk-SWEWOUHK.cjs.map +0 -1
  68. package/dist/chunk-TSOUI723.cjs.map +0 -1
  69. package/dist/chunk-XERD2YCJ.cjs.map +0 -1
  70. /package/dist/{chunk-LE6ZUVCO.js.map → chunk-PB3PMDKV.js.map} +0 -0
@@ -107,13 +107,15 @@ function MenuPanel({ nodes, env, className, style, rootProps, panelRef }) {
107
107
  const searchAt = nodes.findIndex((n) => n.kind === "search");
108
108
  const hits = q ? env.search(q) : null;
109
109
  const above = searchAt >= 0 ? nodes.slice(0, searchAt) : [];
110
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Env.Provider, { value: env, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { className: `cm-menu ${_nullishCoalesce(className, () => ( ""))}`, style: { width: env.look.width, ...style }, root: true, rootProps, panelRef, children: nodes.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "cm-empty", children: "Empty menu \u2014 drag options in" }) : hits ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
111
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Nodes, { nodes: above }),
112
- searchAt >= 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SearchBox, { node: nodes[searchAt] }),
113
- hits.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "cm-empty", children: "No matches" }) : hits.map(
114
- (h) => h.node.kind === "item" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Item, { node: h.node, path: h.path }, `hit:${h.node.id}`) : null
115
- )
116
- ] }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Nodes, { nodes }) }) });
110
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Env.Provider, { value: env, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Panel, { className: `cm-menu ${_nullishCoalesce(className, () => ( ""))}`, style: { width: env.look.width, ...style }, root: true, rootProps, panelRef, children: nodes.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "cm-empty", children: "Empty menu \u2014 drag options in" }) : (
111
+ // The search box keeps ONE place in the tree whether or not a query is typed, so typing
112
+ // never remounts it (and never drops the caret).
113
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
114
+ searchAt >= 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Nodes, { nodes: above }),
115
+ searchAt >= 0 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, SearchBox, { node: nodes[searchAt] }, "cm-search"),
116
+ hits ? hits.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "cm-empty", children: "No matches" }) : hits.map((h) => h.node.kind === "item" ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Item, { node: h.node, path: h.path, hit: true }, `hit:${h.node.id}`) : null) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Nodes, { nodes: searchAt >= 0 ? nodes.slice(searchAt + 1) : nodes })
117
+ ] })
118
+ ) }) });
117
119
  }
118
120
  function Panel({
119
121
  children,
@@ -138,10 +140,16 @@ function Panel({
138
140
  const at = rows.indexOf(document.activeElement);
139
141
  const focus = (i) => _optionalChain([rows, 'access', _9 => _9[(i + rows.length) % rows.length], 'optionalAccess', _10 => _10.focus, 'call', _11 => _11()]);
140
142
  switch (e.key) {
141
- case "ArrowDown":
143
+ case "ArrowDown": {
142
144
  e.preventDefault();
143
- focus(inSearch || at < 0 ? 0 : at + 1);
145
+ if (inSearch) {
146
+ const below = rows.find((r) => e.target.compareDocumentPosition(r) & Node.DOCUMENT_POSITION_FOLLOWING);
147
+ _optionalChain([(_nullishCoalesce(below, () => ( rows[0]))), 'optionalAccess', _12 => _12.focus, 'call', _13 => _13()]);
148
+ return;
149
+ }
150
+ focus(at < 0 ? 0 : at + 1);
144
151
  return;
152
+ }
145
153
  case "ArrowUp":
146
154
  e.preventDefault();
147
155
  if (inSearch) return;
@@ -160,7 +168,7 @@ function Panel({
160
168
  case "ArrowRight": {
161
169
  if (inSearch) return;
162
170
  const row = document.activeElement;
163
- const sub = _optionalChain([row, 'optionalAccess', _12 => _12.getAttribute, 'call', _13 => _13("data-cm-sub")]);
171
+ const sub = _optionalChain([row, 'optionalAccess', _14 => _14.getAttribute, 'call', _15 => _15("data-cm-sub")]);
164
172
  if (sub) {
165
173
  e.preventDefault();
166
174
  intent.openNow(sub, true);
@@ -178,12 +186,12 @@ function Panel({
178
186
  e.stopPropagation();
179
187
  if (inSearch && env.query) return env.setQuery("");
180
188
  if (onClose) onClose();
181
- else _optionalChain([env, 'access', _14 => _14.onEscape, 'optionalCall', _15 => _15()]);
189
+ else _optionalChain([env, 'access', _16 => _16.onEscape, 'optionalCall', _17 => _17()]);
182
190
  return;
183
191
  case "Enter":
184
192
  if (inSearch) {
185
193
  e.preventDefault();
186
- _optionalChain([rows, 'access', _16 => _16[0], 'optionalAccess', _17 => _17.click, 'call', _18 => _18()]);
194
+ _optionalChain([panel, 'access', _18 => _18.querySelector, 'call', _19 => _19('[data-cm-hit]:not([aria-disabled="true"])'), 'optionalAccess', _20 => _20.click, 'call', _21 => _21()]);
187
195
  }
188
196
  return;
189
197
  default:
@@ -214,7 +222,7 @@ function Panel({
214
222
  {
215
223
  ref: (el) => {
216
224
  ref.current = el;
217
- _optionalChain([panelRef, 'optionalCall', _19 => _19(el)]);
225
+ _optionalChain([panelRef, 'optionalCall', _22 => _22(el)]);
218
226
  },
219
227
  role: "menu",
220
228
  tabIndex: -1,
@@ -243,9 +251,9 @@ function SearchBox({ node }) {
243
251
  const env = useEnv();
244
252
  const ref = _react.useRef.call(void 0, null);
245
253
  _react.useEffect.call(void 0, () => {
246
- if (node.autofocus) _optionalChain([ref, 'access', _20 => _20.current, 'optionalAccess', _21 => _21.focus, 'call', _22 => _22({ preventScroll: true })]);
254
+ if (node.autofocus) _optionalChain([ref, 'access', _23 => _23.current, 'optionalAccess', _24 => _24.focus, 'call', _25 => _25({ preventScroll: true })]);
247
255
  }, [node.autofocus]);
248
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: `cm-search ${env.highlightId === node.designId ? "cm-hl" : ""}`, onClick: () => node.designId && _optionalChain([env, 'access', _23 => _23.onSelectNode, 'optionalCall', _24 => _24(node.designId)]), children: [
256
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: `cm-search ${env.highlightId === node.designId ? "cm-hl" : ""}`, onClick: () => node.designId && _optionalChain([env, 'access', _26 => _26.onSelectNode, 'optionalCall', _27 => _27(node.designId)]), children: [
249
257
  env.renderIcon("Search", "cm-icon"),
250
258
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "input", { ref, value: env.query, onChange: (e) => env.setQuery(e.target.value), placeholder: node.placeholder || "Type to filter\u2026", "aria-label": "Filter menu" })
251
259
  ] });
@@ -263,7 +271,7 @@ function Section({ node }) {
263
271
  tabIndex: -1,
264
272
  className: `cm-section-label ${node.placeholder ? "cm-slot-label" : ""}`,
265
273
  onClick: () => {
266
- if (node.designId) _optionalChain([env, 'access', _25 => _25.onSelectNode, 'optionalCall', _26 => _26(node.designId)]);
274
+ if (node.designId) _optionalChain([env, 'access', _28 => _28.onSelectNode, 'optionalCall', _29 => _29(node.designId)]);
267
275
  if (node.collapsible) setOpen((o) => !o);
268
276
  },
269
277
  children: [
@@ -299,7 +307,7 @@ function Tile({ node }) {
299
307
  ...statusAttrs(node),
300
308
  className: `cm-tile ${node.danger ? "cm-danger" : ""} ${node.ai ? "cm-ai" : ""} ${env.highlightId && env.highlightId === node.designId ? "cm-hl" : ""}`,
301
309
  onClick: () => {
302
- if (node.designId) _optionalChain([env, 'access', _27 => _27.onSelectNode, 'optionalCall', _28 => _28(node.designId)]);
310
+ if (node.designId) _optionalChain([env, 'access', _30 => _30.onSelectNode, 'optionalCall', _31 => _31(node.designId)]);
303
311
  if (!node.disabled) env.onActivate(node);
304
312
  },
305
313
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { icon: node.icon, fallback: "Circle" })
@@ -307,7 +315,7 @@ function Tile({ node }) {
307
315
  )
308
316
  );
309
317
  }
310
- function Item({ node, path }) {
318
+ function Item({ node, path, hit }) {
311
319
  const env = useEnv();
312
320
  const { look } = env;
313
321
  const [checked, setChecked] = _react.useState.call(void 0, Boolean(node.checked));
@@ -326,15 +334,16 @@ function Item({ node, path }) {
326
334
  tabIndex: -1,
327
335
  "data-cm-row": "",
328
336
  "data-cm-label": node.label,
337
+ ...hit ? { "data-cm-hit": "" } : {},
329
338
  "aria-checked": settable ? checked : void 0,
330
339
  "aria-disabled": node.disabled || void 0,
331
340
  ...reason ? { title: reason, "aria-description": reason } : {},
332
341
  ...toneAttrs(node.tone, node.toneLabel),
333
342
  ...statusAttrs(node),
334
343
  className: `cm-item ${node.placeholder ? "cm-slot" : ""} ${node.danger ? "cm-danger" : ""} ${node.ai ? "cm-ai" : ""} ${env.highlightId && env.highlightId === node.designId ? "cm-hl" : ""}`,
335
- onPointerEnter: (e) => _optionalChain([intent, 'optionalAccess', _29 => _29.enter, 'call', _30 => _30(null, e.currentTarget)]),
344
+ onPointerEnter: (e) => _optionalChain([intent, 'optionalAccess', _32 => _32.enter, 'call', _33 => _33(null, e.currentTarget)]),
336
345
  onClick: () => {
337
- if (node.designId) _optionalChain([env, 'access', _31 => _31.onSelectNode, 'optionalCall', _32 => _32(node.designId)]);
346
+ if (node.designId) _optionalChain([env, 'access', _34 => _34.onSelectNode, 'optionalCall', _35 => _35(node.designId)]);
338
347
  if (node.disabled) return;
339
348
  if (settable) setChecked((c) => !c);
340
349
  env.onActivate(node);
@@ -355,7 +364,7 @@ function Item({ node, path }) {
355
364
  }
356
365
  function flyoutSide(row, width) {
357
366
  const r = row.getBoundingClientRect();
358
- const bounds = _nullishCoalesce(_optionalChain([row, 'access', _33 => _33.closest, 'call', _34 => _34("[data-menu-bounds]"), 'optionalAccess', _35 => _35.getBoundingClientRect, 'call', _36 => _36()]), () => ( { left: 0, right: window.innerWidth }));
367
+ const bounds = _nullishCoalesce(_optionalChain([row, 'access', _36 => _36.closest, 'call', _37 => _37("[data-menu-bounds]"), 'optionalAccess', _38 => _38.getBoundingClientRect, 'call', _39 => _39()]), () => ( { left: 0, right: window.innerWidth }));
359
368
  if (r.right + width + 8 <= bounds.right) return "right";
360
369
  if (r.left - width - 8 >= bounds.left) return "left";
361
370
  return "below";
@@ -380,7 +389,7 @@ function Submenu({ node }) {
380
389
  const env = useEnv();
381
390
  const intent = _react.useContext.call(void 0, PanelIntent);
382
391
  const { look } = env;
383
- const open = _optionalChain([intent, 'optionalAccess', _37 => _37.openId]) === node.id;
392
+ const open = _optionalChain([intent, 'optionalAccess', _40 => _40.openId]) === node.id;
384
393
  const [side, setSide] = _react.useState.call(void 0, "right");
385
394
  const [fixed, setFixed] = _react.useState.call(void 0, null);
386
395
  const rowRef = _react.useRef.call(void 0, null);
@@ -397,16 +406,16 @@ function Submenu({ node }) {
397
406
  }, [open, env.flyout, look.width]);
398
407
  _react.useEffect.call(void 0, () => {
399
408
  if (!open) setFixed(null);
400
- if (open && _optionalChain([intent, 'optionalAccess', _38 => _38.viaKey]) && flyRef.current) {
409
+ if (open && _optionalChain([intent, 'optionalAccess', _41 => _41.viaKey]) && flyRef.current) {
401
410
  const first = rowsOf(flyRef.current)[0];
402
411
  (_nullishCoalesce(first, () => ( flyRef.current))).focus();
403
412
  }
404
- }, [open, _optionalChain([intent, 'optionalAccess', _39 => _39.viaKey])]);
413
+ }, [open, _optionalChain([intent, 'optionalAccess', _42 => _42.viaKey])]);
405
414
  const closeFlyout = _react.useCallback.call(void 0, () => {
406
- _optionalChain([intent, 'optionalAccess', _40 => _40.close, 'call', _41 => _41()]);
407
- _optionalChain([rowRef, 'access', _42 => _42.current, 'optionalAccess', _43 => _43.focus, 'call', _44 => _44()]);
415
+ _optionalChain([intent, 'optionalAccess', _43 => _43.close, 'call', _44 => _44()]);
416
+ _optionalChain([rowRef, 'access', _45 => _45.current, 'optionalAccess', _46 => _46.focus, 'call', _47 => _47()]);
408
417
  }, [intent]);
409
- const flyStyle = env.flyout === "fixed" ? { position: "fixed", width: look.width, maxHeight: "calc(100vh - 16px)", overflowY: "auto", left: _nullishCoalesce(_optionalChain([fixed, 'optionalAccess', _45 => _45.left]), () => ( -9999)), top: _nullishCoalesce(_optionalChain([fixed, 'optionalAccess', _46 => _46.top]), () => ( -9999)), visibility: fixed ? "visible" : "hidden" } : { width: look.width };
418
+ const flyStyle = env.flyout === "fixed" ? { position: "fixed", width: look.width, maxHeight: "calc(100vh - 16px)", overflowY: "auto", left: _nullishCoalesce(_optionalChain([fixed, 'optionalAccess', _48 => _48.left]), () => ( -9999)), top: _nullishCoalesce(_optionalChain([fixed, 'optionalAccess', _49 => _49.top]), () => ( -9999)), visibility: fixed ? "visible" : "hidden" } : { width: look.width };
410
419
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "cm-sub", children: [
411
420
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
412
421
  "button",
@@ -427,14 +436,14 @@ function Submenu({ node }) {
427
436
  className: `cm-item ${node.placeholder ? "cm-slot" : ""} ${open ? "cm-open" : ""} ${env.highlightId && env.highlightId === node.designId ? "cm-hl" : ""}`,
428
437
  onPointerEnter: (e) => {
429
438
  if (env.flyout === "absolute") setSide(flyoutSide(e.currentTarget, look.width));
430
- if (greyed) _optionalChain([intent, 'optionalAccess', _47 => _47.enter, 'call', _48 => _48(null, e.currentTarget)]);
431
- else _optionalChain([intent, 'optionalAccess', _49 => _49.enter, 'call', _50 => _50(node.id, e.currentTarget)]);
439
+ if (greyed) _optionalChain([intent, 'optionalAccess', _50 => _50.enter, 'call', _51 => _51(null, e.currentTarget)]);
440
+ else _optionalChain([intent, 'optionalAccess', _52 => _52.enter, 'call', _53 => _53(node.id, e.currentTarget)]);
432
441
  },
433
442
  onClick: (e) => {
434
- if (node.designId) _optionalChain([env, 'access', _51 => _51.onSelectNode, 'optionalCall', _52 => _52(node.designId)]);
443
+ if (node.designId) _optionalChain([env, 'access', _54 => _54.onSelectNode, 'optionalCall', _55 => _55(node.designId)]);
435
444
  if (greyed) return;
436
445
  if (env.flyout === "absolute") setSide(flyoutSide(e.currentTarget, look.width));
437
- _optionalChain([intent, 'optionalAccess', _53 => _53.openNow, 'call', _54 => _54(node.id, e.detail === 0)]);
446
+ _optionalChain([intent, 'optionalAccess', _56 => _56.openNow, 'call', _57 => _57(node.id, e.detail === 0)]);
438
447
  },
439
448
  children: [
440
449
  look.showIcons && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "cm-icon-slot", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Icon, { icon: node.icon }) }),
@@ -454,7 +463,7 @@ function Submenu({ node }) {
454
463
  function FlyoutBody({ node, flyRef, side }) {
455
464
  const ref = _react.useRef.call(void 0, null);
456
465
  _react.useLayoutEffect.call(void 0, () => {
457
- const panel = _nullishCoalesce(_optionalChain([ref, 'access', _55 => _55.current, 'optionalAccess', _56 => _56.closest, 'call', _57 => _57('[role="menu"]')]), () => ( null));
466
+ const panel = _nullishCoalesce(_optionalChain([ref, 'access', _58 => _58.current, 'optionalAccess', _59 => _59.closest, 'call', _60 => _60('[role="menu"]')]), () => ( null));
458
467
  flyRef.current = panel;
459
468
  if (panel && side) panel.setAttribute("data-side", side);
460
469
  });
@@ -467,4 +476,4 @@ function FlyoutBody({ node, flyRef, side }) {
467
476
 
468
477
 
469
478
  exports.MenuPanel = MenuPanel;
470
- //# sourceMappingURL=chunk-QXHLM7SS.cjs.map
479
+ //# sourceMappingURL=chunk-LDMYNYFC.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/context-menu/dist/chunk-LDMYNYFC.cjs","../src/designed/MenuPanel.tsx"],"names":[],"mappings":"AAAA,6rBAAY;AACZ;AACA;ACOA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AAAA,8BAKK;AAyDI,+CAAA;AAjCX,IAAM,IAAA,EAAM,kCAAA,IAAuC,CAAA;AACnD,SAAS,MAAA,CAAA,EAAuB;AAC9B,EAAA,MAAM,IAAA,EAAM,+BAAA,GAAc,CAAA;AAC1B,EAAA,GAAA,CAAI,CAAC,GAAA,EAAK,MAAM,IAAI,KAAA,CAAM,sCAAsC,CAAA;AAChE,EAAA,OAAO,GAAA;AACT;AAIA,IAAM,IAAA,EAAM,eAAA;AAGZ,SAAS,MAAA,CAAO,KAAA,EAAmC;AACjD,EAAA,OAAO,CAAC,GAAG,KAAA,CAAM,gBAAA,CAA8B,GAAG,CAAC,CAAA,CAAE,MAAA,CAAO,CAAC,EAAA,EAAA,GAAO,EAAA,CAAG,OAAA,CAAQ,eAAe,EAAA,IAAM,MAAA,GAAS,EAAA,CAAG,YAAA,CAAa,eAAe,EAAA,IAAM,MAAM,CAAA;AAC1J;AAEA,SAAS,OAAA,CAAQ,EAAA,EAAyB;AACxC,EAAA,OAAA,mCAAQ,EAAA,CAAG,YAAA,CAAa,eAAe,CAAA,UAAK,EAAA,CAAG,aAAA,UAAe,IAAA,CAAA,CAAI,IAAA,CAAK,CAAA,CAAE,WAAA,CAAY,CAAA;AACvF;AAEA,SAAS,QAAA,CAAS,CAAA,EAAuD;AACvE,EAAA,MAAM,MAAA,EAAQ,CAAC,CAAA,CAAE,YAAA,EAAc,CAAA,CAAE,SAAA,EAAW,CAAA,CAAE,eAAA,EAAiB,KAAA,CAAS,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AACxF,EAAA,OAAO,KAAA,CAAM,OAAA,EAAS,KAAA,CAAM,IAAA,CAAK,UAAK,EAAA,EAAI,KAAA,CAAA;AAC5C;AAEA,SAAS,SAAA,CAAU,IAAA,EAA4B,SAAA,EAAgC;AAC7E,EAAA,OAAO,KAAA,GAAQ,KAAA,IAAS,UAAA,EAAY,EAAE,WAAA,EAAa,IAAA,EAAM,GAAI,UAAA,EAAY,EAAE,iBAAA,EAAmB,GAAG,EAAA,EAAI,CAAC,EAAG,EAAA,EAAI,CAAC,CAAA;AAChH;AAEA,SAAS,IAAA,CAAK,EAAE,IAAA,EAAM,SAAS,CAAA,EAAsD;AACnF,EAAA,MAAM,IAAA,EAAM,MAAA,CAAO,CAAA;AACnB,EAAA,GAAA,iBAAI,IAAA,2BAAM,WAAA,EAAW;AACnB,IAAA,MAAM,EAAA,EAAI,IAAA,CAAK,SAAA;AACf,IAAA,uBAAO,6BAAA,CAAC,EAAA,EAAE,SAAA,EAAW,CAAA,QAAA,mBAAW,IAAA,CAAK,SAAA,UAAa,IAAE,CAAA,CAAA;AACtD,EAAA;AAC2B,EAAA;AACW,EAAA;AACxC;AAa6B;AACmF,EAAA;AAC/C,EAAA;AACZ,EAAA;AACL,EAAA;AACjB,EAAA;AACV,EAAA;AAC0D,EAAA;AAE9C,EAAA;AACR,IAAA;AACgB,IAAA;AACV,IAAA;AAEK,IAAA;AACI,IAAA;AAC1B,IAAA;AACuB,IAAA;AACG,IAAA;AAES,IAAA;AACrB,IAAA;AACC,IAAA;AACD,IAAA;AACa,IAAA;AACA,IAAA;AACrB,IAAA;AAClB,EAAA;AAEiD,EAAA;AACF,IAAA;AACZ,IAAA;AACnC,EAAA;AAEO,EAAA;AACS,IAAA;AACA,IAAA;AACA,IAAA;AACqC,MAAA;AACC,MAAA;AACpD,IAAA;AACmB,IAAA;AACgB,MAAA;AACb,MAAA;AAC2B,QAAA;AAC7C,QAAA;AACF,MAAA;AACiF,MAAA;AACzB,MAAA;AACD,MAAA;AAC7B,MAAA;AAET,QAAA;AACgB,UAAA;AACzB,QAAA;AAC6B,MAAA;AACvC,IAAA;AACyB,IAAA;AACsB,MAAA;AACd,MAAA;AACjC,IAAA;AACa,IAAA;AACkC,MAAA;AACL,MAAA;AAC1C,IAAA;AACF,EAAA;AACF;AAEqD;AAgB4C;AACxD,EAAA;AACoB,EAAA;AAC1B,EAAA;AACyB,EAAA;AAGtD,EAAA;AAE0D;AAAA;AAKnD,oBAAA;AAAsC,MAAA;AAC0B,MAAA;AAG9C,MAAA;AAOrB,IAAA;AAGN,EAAA;AAEJ;AAEe;AACb,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAWC;AACkB,EAAA;AACM,EAAA;AACc,EAAA;AACC,EAAA;AAEqB,EAAA;AACzC,IAAA;AACN,IAAA;AAEsD,IAAA;AACX,IAAA;AAC9B,IAAA;AACoC,IAAA;AACa,IAAA;AAC3D,IAAA;AACK,MAAA;AACC,QAAA;AACH,QAAA;AAE0E,UAAA;AAC5D,0BAAA;AAC1B,UAAA;AACF,QAAA;AACyB,QAAA;AACzB,QAAA;AACF,MAAA;AACK,MAAA;AACc,QAAA;AACH,QAAA;AACO,QAAA;AACoD,UAAA;AACvC,UAAA;AAClC,QAAA;AACuC,QAAA;AACvC,QAAA;AACG,MAAA;AACA,MAAA;AACW,QAAA;AACG,QAAA;AAC2B,QAAA;AAC5C,QAAA;AACiB,MAAA;AACH,QAAA;AACO,QAAA;AACsB,QAAA;AAClC,QAAA;AACU,UAAA;AACO,UAAA;AAC1B,QAAA;AACA,QAAA;AACF,MAAA;AACK,MAAA;AACuB,QAAA;AACT,QAAA;AACC,QAAA;AACV,QAAA;AACR,QAAA;AACG,MAAA;AACc,QAAA;AACC,QAAA;AAC+B,QAAA;AAC5B,QAAA;AACD,QAAA;AACpB,QAAA;AACG,MAAA;AACW,QAAA;AACK,UAAA;AAE8D,0BAAA;AACjF,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACJ,IAAA;AAG0E,IAAA;AACQ,IAAA;AACtE,IAAA;AACO,MAAA;AACJ,MAAA;AACiB,MAAA;AAC9B,MAAA;AACF,IAAA;AACqB,IAAA;AACoD,IAAA;AACJ,IAAA;AAC/B,IAAA;AACM,MAAA;AACc,MAAA;AACrC,QAAA;AACP,QAAA;AACV,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAII,EAAA;AAAC,IAAA;AAAA,IAAA;AACc,MAAA;AACG,QAAA;AACD,wBAAA;AACf,MAAA;AACK,MAAA;AACK,MAAA;AACa,MAAA;AAC2B,MAAA;AAClD,MAAA;AACA,MAAA;AACA,MAAA;AACsB,MAAA;AACH,MAAA;AAElB,MAAA;AAAA,IAAA;AAEL,EAAA;AAEJ;AAE8D;AAGtC,EAAA;AACsC,IAAA;AACE,IAAA;AACT,IAAA;AACa,IAAA;AACD,IAAA;AACvB,IAAA;AAExC,EAAA;AAEJ;AAEuD;AAClC,EAAA;AACsB,EAAA;AACzB,EAAA;AACgD,IAAA;AAC7C,EAAA;AAEgB,EAAA;AACI,IAAA;AAEqB,oBAAA;AAC1D,EAAA;AAEJ;AAEsD;AACjC,EAAA;AAC+B,EAAA;AAC9B,EAAA;AAEgB,EAAA;AAC1B,IAAA;AAEJ,oBAAA;AAAC,MAAA;AAAA,MAAA;AACM,QAAA;AACK,QAAA;AAC4D,QAAA;AACvD,QAAA;AACsC,UAAA;AACZ,UAAA;AACzC,QAAA;AAEC,QAAA;AAAK,UAAA;AAC+B,UAAA;AAA6B,QAAA;AAAA,MAAA;AACpE,IAAA;AAIG,IAAA;AAOP,EAAA;AAEJ;AAEwD;AACgB,EAAA;AACzB,EAAA;AAC/C;AAEgD;AAC3B,EAAA;AACS,EAAA;AAC5B,EAAA;AAAA;AAEE,oBAAA;AAAC,MAAA;AAAA,MAAA;AACM,QAAA;AACA,QAAA;AACK,QAAA;AACE,QAAA;AACQ,QAAA;AAC8C,QAAA;AACjD,QAAA;AACe,QAAA;AACO,QAAA;AACnB,QAAA;AAC8C,QAAA;AACnD,QAAA;AACsC,UAAA;AACZ,UAAA;AACzC,QAAA;AAEgC,QAAA;AAAS,MAAA;AAC3C,IAAA;AAAA,EAAA;AAEJ;AAEyF;AACpE,EAAA;AACF,EAAA;AAC2C,EAAA;AAEK,EAAA;AAC5B,EAAA;AACc,EAAA;AACvB,EAAA;AACS,EAAA;AACrC,EAAA;AAAA;AAEE,oBAAA;AAAC,MAAA;AAAA,MAAA;AACM,QAAA;AACiC,QAAA;AAC5B,QAAA;AACE,QAAA;AACQ,QAAA;AACgB,QAAA;AACD,QAAA;AACH,QAAA;AAC+B,QAAA;AACxB,QAAA;AACnB,QAAA;AAC4C,QAAA;AACN,QAAA;AAC3C,QAAA;AACsC,UAAA;AAChC,UAAA;AACe,UAAA;AACf,UAAA;AACrB,QAAA;AAEC,QAAA;AACiB,UAAA;AAGhB,0BAAA;AAA4B,4BAAA;AACQ,YAAA;AACtC,UAAA;AAC2C,UAAA;AACa,UAAA;AACZ,UAAA;AAAsD,QAAA;AAAA,MAAA;AACpG,IAAA;AAAA,EAAA;AAEJ;AAQiE;AAC3B,EAAA;AACsD,EAAA;AAC1C,EAAA;AACF,EAAA;AACvC,EAAA;AACT;AAG4H;AAC9G,EAAA;AACG,EAAA;AACQ,EAAA;AACM,EAAA;AACgB,EAAA;AACL,IAAA;AACf,IAAA;AACd,MAAA;AACA,MAAA;AACsD,IAAA;AACjE,EAAA;AACoB,EAAA;AACsD,EAAA;AACjD,EAAA;AAC3B;AAEsD;AACjC,EAAA;AACkB,EAAA;AACpB,EAAA;AACoB,EAAA;AACe,EAAA;AACyB,EAAA;AAChC,EAAA;AACH,EAAA;AACd,EAAA;AACR,EAAA;AAEE,EAAA;AACuD,IAAA;AAC5B,IAAA;AACA,IAAA;AAC4B,IAAA;AACpC,IAAA;AACxB,IAAA;AACgB,EAAA;AACjB,EAAA;AACU,IAAA;AACsB,IAAA;AACN,MAAA;AACN,MAAA;AAClC,IAAA;AACuB,EAAA;AAEa,EAAA;AACtB,oBAAA;AACQ,oBAAA;AACb,EAAA;AAI4B,EAAA;AAMnC,EAAA;AAAA,oBAAA;AAAC,MAAA;AAAA,MAAA;AACM,QAAA;AACA,QAAA;AACA,QAAA;AACK,QAAA;AACE,QAAA;AACQ,QAAA;AACwB,QAAA;AAC9B,QAAA;AACC,QAAA;AACU,QAAA;AACsC,QAAA;AACxB,QAAA;AACnB,QAAA;AAC8C,QAAA;AAC3C,QAAA;AAC8C,UAAA;AACpB,UAAA;AACJ,UAAA;AAC7C,QAAA;AACgB,QAAA;AACqC,UAAA;AACvC,UAAA;AACuD,UAAA;AAC5B,0BAAA;AACzC,QAAA;AAEC,QAAA;AAAkC,UAAA;AAEjC,0BAAA;AAA4B,4BAAA;AACU,YAAA;AACxC,UAAA;AAC4C,UAAA;AACD,UAAA;AACM,UAAA;AAAA,QAAA;AAAA,MAAA;AACnD,IAAA;AAEuC,IAAA;AAIzC,EAAA;AAEJ;AAGqJ;AAC3G,EAAA;AAClB,EAAA;AACmD,IAAA;AACtD,IAAA;AACsC,IAAA;AACxD,EAAA;AAGG,EAAA;AAAuB,oBAAA;AAEhB,IAAA;AAMT,EAAA;AAEJ;AD9J+E;AACA;AACA;AACA","file":"/home/runner/work/aidream/aidream/apps/shared/context-menu/dist/chunk-LDMYNYFC.cjs","sourcesContent":[null,"// src/designed/MenuPanel.tsx — @ai-matrx/context-menu: THE ONE MENU RENDERER.\n//\n// Draws a resolved menu (ResolvedNode[]) — the builder's preview and the live right-click menu are\n// both this component, so what the designer sees is what opens. It owns the menu's own behaviour:\n// submenus on hover with an intent delay and a safe triangle toward the open flyout, flyouts that\n// flip at the screen's edge, the keyboard (arrows, Enter, Esc / Left closes a submenu, Right opens\n// one, typeahead), the search box, and the status marks (partial / unmatched / needs-arman).\n// This file IS the menu component: its rows are the menu's own rows (hence the ui-exception marks).\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type PointerEvent as ReactPointerEvent,\n type ReactNode,\n} from \"react\";\nimport type { MenuLook, MenuTone } from \"../design/model\";\nimport type { IconRef, ResolvedItem, ResolvedNode, ResolvedSearch, ResolvedSection, ResolvedSubmenu } from \"./types\";\nimport type { ResolvedHit } from \"./bindings\";\n\nexport interface MenuPanelEnv {\n look: MenuLook;\n /** Draws a lucide icon by name (the design's icons). */\n renderIcon: (name: string, className?: string) => ReactNode;\n /** A row was chosen (live: run it and close; builder: the designer's pick). */\n onActivate: (item: ResolvedItem) => void;\n /** Builder: a click on any entry selects its design node. */\n onSelectNode?: ((designId: string) => void) | undefined;\n highlightId?: string | null | undefined;\n /** absolute: flyouts sit beside their row (the builder) · fixed: measured against the screen (live). */\n flyout: \"absolute\" | \"fixed\";\n query: string;\n setQuery: (q: string) => void;\n /** The search, over the bound entries. */\n search: (q: string) => ResolvedHit[];\n /** Esc at the top: close the whole menu. */\n onEscape?: (() => void) | undefined;\n}\n\nconst Env = createContext<MenuPanelEnv | null>(null);\nfunction useEnv(): MenuPanelEnv {\n const env = useContext(Env);\n if (!env) throw new Error(\"MenuPanel: rendered outside its menu\");\n return env;\n}\n\n// ── rows: focus, keyboard ────────────────────────────────────────────────────\n\nconst ROW = \"[data-cm-row]\";\n\n/** The rows of THIS panel (not those of a flyout inside it), in order. */\nfunction rowsOf(panel: HTMLElement): HTMLElement[] {\n return [...panel.querySelectorAll<HTMLElement>(ROW)].filter((el) => el.closest('[role=\"menu\"]') === panel && el.getAttribute(\"aria-disabled\") !== \"true\");\n}\n\nfunction labelOf(el: HTMLElement): string {\n return (el.getAttribute(\"data-cm-label\") ?? el.textContent ?? \"\").trim().toLowerCase();\n}\n\nfunction reasonOf(n: ResolvedItem | ResolvedSubmenu): string | undefined {\n const parts = [n.statusReason, n.disabled ? n.disabledReason : undefined].filter(Boolean);\n return parts.length ? parts.join(\" — \") : undefined;\n}\n\nfunction toneAttrs(tone: MenuTone | undefined, toneLabel: boolean | undefined) {\n return tone && tone !== \"default\" ? { \"data-tone\": tone, ...(toneLabel ? { \"data-tone-label\": \"\" } : {}) } : {};\n}\n\nfunction Icon({ icon, fallback }: { icon?: IconRef | undefined; fallback?: string }) {\n const env = useEnv();\n if (icon?.component) {\n const C = icon.component;\n return <C className={`cm-icon ${icon.className ?? \"\"}`} {...(icon.style ? { style: icon.style } : {})} />;\n }\n const name = icon?.name ?? fallback;\n return name ? <>{env.renderIcon(name, \"cm-icon\")}</> : null;\n}\n\n// ── submenu intent: open on hover after a beat, never steal a flyout the pointer is heading to ──\n\ninterface Intent {\n openId: string | null;\n viaKey: boolean;\n enter: (id: string | null, el: HTMLElement) => void;\n openNow: (id: string, viaKey: boolean) => void;\n close: () => void;\n track: (e: ReactPointerEvent) => void;\n}\n\nfunction useIntent(): Intent {\n const [state, setState] = useState<{ openId: string | null; viaKey: boolean }>({ openId: null, viaKey: false });\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const trail = useRef<{ x: number; y: number }[]>([]);\n const flyoutRect = useRef<DOMRect | null>(null);\n const stateRef = useRef(state);\n stateRef.current = state;\n useEffect(() => () => void (timer.current && clearTimeout(timer.current)), []);\n\n const heading = (): boolean => {\n const r = flyoutRect.current;\n const [a, b] = trail.current.slice(-2);\n if (!r || !a || !b) return false;\n // Is the pointer inside the triangle from where it was to the flyout's near edge?\n const towardRight = r.left >= a.x;\n const ex = towardRight ? r.left : r.right;\n const p = b;\n const t1 = { x: ex, y: r.top - 8 };\n const t2 = { x: ex, y: r.bottom + 8 };\n const sign = (p1: { x: number; y: number }, p2: { x: number; y: number }, p3: { x: number; y: number }) =>\n (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);\n const d1 = sign(p, a, t1);\n const d2 = sign(p, t1, t2);\n const d3 = sign(p, t2, a);\n const neg = d1 < 0 || d2 < 0 || d3 < 0;\n const pos = d1 > 0 || d2 > 0 || d3 > 0;\n return !(neg && pos);\n };\n\n const schedule = (fn: () => void, ms: number) => {\n if (timer.current) clearTimeout(timer.current);\n timer.current = setTimeout(fn, ms);\n };\n\n return {\n openId: state.openId,\n viaKey: state.viaKey,\n track: (e) => {\n trail.current.push({ x: e.clientX, y: e.clientY });\n if (trail.current.length > 3) trail.current.shift();\n },\n enter: (id, el) => {\n const current = stateRef.current.openId;\n if (id === current) {\n if (timer.current) clearTimeout(timer.current);\n return;\n }\n const flyout = current ? el.closest('[role=\"menu\"]')?.querySelector<HTMLElement>(`[data-cm-flyout=\"${CSS.escape(current)}\"]`) : null;\n flyoutRect.current = flyout?.getBoundingClientRect() ?? null;\n const go = () => setState({ openId: id, viaKey: false });\n if (current && heading()) {\n // On the way to the open flyout: wait, then look again.\n schedule(() => {\n if (el.matches(\":hover\")) go();\n }, 300);\n } else schedule(go, current ? 60 : 90);\n },\n openNow: (id, viaKey) => {\n if (timer.current) clearTimeout(timer.current);\n setState({ openId: id, viaKey });\n },\n close: () => {\n if (timer.current) clearTimeout(timer.current);\n setState({ openId: null, viaKey: false });\n },\n };\n}\n\nconst PanelIntent = createContext<Intent | null>(null);\n\n// ── the panel ────────────────────────────────────────────────────────────────\n\nexport interface MenuPanelProps {\n nodes: readonly ResolvedNode[];\n env: MenuPanelEnv;\n className?: string;\n style?: CSSProperties;\n /** Props for the root element (position, data attributes). */\n rootProps?: Record<string, unknown>;\n /** The root element, for the owner's own measuring and outside-click test. */\n panelRef?: (el: HTMLDivElement | null) => void;\n}\n\n/** THE MENU: the root panel (with the search box), drawn from resolved nodes. */\nexport function MenuPanel({ nodes, env, className, style, rootProps, panelRef }: MenuPanelProps) {\n const q = env.query.trim().toLowerCase();\n const searchAt = nodes.findIndex((n) => n.kind === \"search\");\n const hits = q ? env.search(q) : null;\n const above = searchAt >= 0 ? nodes.slice(0, searchAt) : [];\n return (\n <Env.Provider value={env}>\n <Panel className={`cm-menu ${className ?? \"\"}`} style={{ width: env.look.width, ...style }} root rootProps={rootProps} panelRef={panelRef}>\n {nodes.length === 0 ? (\n <div className=\"cm-empty\">Empty menu — drag options in</div>\n ) : (\n // The search box keeps ONE place in the tree whether or not a query is typed, so typing\n // never remounts it (and never drops the caret).\n <>\n {searchAt >= 0 && <Nodes nodes={above} />}\n {searchAt >= 0 && <SearchBox key=\"cm-search\" node={nodes[searchAt] as ResolvedSearch} />}\n {hits ? (\n hits.length === 0 ? (\n <div className=\"cm-empty\">No matches</div>\n ) : (\n hits.map((h) => (h.node.kind === \"item\" ? <Item key={`hit:${h.node.id}`} node={h.node} path={h.path} hit /> : null))\n )\n ) : (\n <Nodes nodes={searchAt >= 0 ? nodes.slice(searchAt + 1) : nodes} />\n )}\n </>\n )}\n </Panel>\n </Env.Provider>\n );\n}\n\nfunction Panel({\n children,\n className,\n style,\n root,\n rootProps,\n onClose,\n flyoutId,\n panelRef,\n}: {\n panelRef?: ((el: HTMLDivElement | null) => void) | undefined;\n children: ReactNode;\n className: string;\n style?: CSSProperties | undefined;\n root?: boolean;\n rootProps?: Record<string, unknown> | undefined;\n /** A flyout: Esc / Left closes it (the parent focuses its row again). */\n onClose?: (() => void) | undefined;\n flyoutId?: string | undefined;\n}) {\n const env = useEnv();\n const intent = useIntent();\n const ref = useRef<HTMLDivElement>(null);\n const typed = useRef({ text: \"\", at: 0 });\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>) => {\n const panel = ref.current;\n if (!panel) return;\n // A key that started in a flyout inside this panel belongs to that flyout.\n if ((e.target as HTMLElement).closest('[role=\"menu\"]') !== panel) return;\n const inSearch = (e.target as HTMLElement).tagName === \"INPUT\";\n const rows = rowsOf(panel);\n const at = rows.indexOf(document.activeElement as HTMLElement);\n const focus = (i: number) => rows[(i + rows.length) % rows.length]?.focus();\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (inSearch) {\n // From the search box: the first row below it.\n const below = rows.find((r) => (e.target as HTMLElement).compareDocumentPosition(r) & Node.DOCUMENT_POSITION_FOLLOWING);\n (below ?? rows[0])?.focus();\n return;\n }\n focus(at < 0 ? 0 : at + 1);\n return;\n }\n case \"ArrowUp\":\n e.preventDefault();\n if (inSearch) return;\n if (at <= 0 && root) {\n const search = panel.querySelector<HTMLInputElement>(\".cm-search input\");\n if (search) return search.focus();\n }\n focus(at < 0 ? rows.length - 1 : at - 1);\n return;\n case \"Home\":\n case \"End\":\n if (inSearch) return;\n e.preventDefault();\n focus(e.key === \"Home\" ? 0 : rows.length - 1);\n return;\n case \"ArrowRight\": {\n if (inSearch) return;\n const row = document.activeElement as HTMLElement | null;\n const sub = row?.getAttribute(\"data-cm-sub\");\n if (sub) {\n e.preventDefault();\n intent.openNow(sub, true);\n }\n return;\n }\n case \"ArrowLeft\":\n if (inSearch || !onClose) return;\n e.preventDefault();\n e.stopPropagation();\n onClose();\n return;\n case \"Escape\":\n e.preventDefault();\n e.stopPropagation();\n if (inSearch && env.query) return env.setQuery(\"\");\n if (onClose) onClose();\n else env.onEscape?.();\n return;\n case \"Enter\":\n if (inSearch) {\n e.preventDefault();\n // The first match runs (the rows above the search box are not matches).\n panel.querySelector<HTMLElement>('[data-cm-hit]:not([aria-disabled=\"true\"])')?.click();\n }\n return;\n default:\n break;\n }\n // Typeahead: a printable key jumps to the next row starting with what was typed. At the top\n // of a menu with a search box, it types into the search instead.\n if (e.key.length !== 1 || e.metaKey || e.ctrlKey || e.altKey || inSearch) return;\n const search = root ? panel.querySelector<HTMLInputElement>(\".cm-search input\") : null;\n if (search) {\n e.preventDefault();\n search.focus();\n env.setQuery(env.query + e.key);\n return;\n }\n const now = Date.now();\n typed.current = { text: now - typed.current.at > 700 ? e.key.toLowerCase() : typed.current.text + e.key.toLowerCase(), at: now };\n const start = at < 0 ? 0 : typed.current.text.length > 1 ? at : at + 1;\n for (let i = 0; i < rows.length; i++) {\n const row = rows[(start + i) % rows.length];\n if (row && labelOf(row).startsWith(typed.current.text)) {\n e.preventDefault();\n row.focus();\n return;\n }\n }\n };\n\n return (\n <PanelIntent.Provider value={intent}>\n <div\n ref={(el) => {\n ref.current = el;\n panelRef?.(el);\n }}\n role=\"menu\"\n tabIndex={-1}\n data-density={env.look.density}\n {...(flyoutId ? { \"data-cm-flyout\": flyoutId } : {})}\n className={className}\n style={style}\n onKeyDown={onKeyDown}\n onPointerMove={intent.track}\n {...(rootProps ?? {})}\n >\n {children}\n </div>\n </PanelIntent.Provider>\n );\n}\n\nfunction Nodes({ nodes }: { nodes: readonly ResolvedNode[] }) {\n return (\n <>\n {nodes.map((n) => {\n if (n.kind === \"divider\") return <div key={n.id} role=\"separator\" className=\"cm-divider\" />;\n if (n.kind === \"item\") return <Item key={n.id} node={n} />;\n if (n.kind === \"submenu\") return <Submenu key={n.id} node={n} />;\n if (n.kind === \"search\") return <SearchBox key={n.id} node={n} />;\n if (n.kind === \"note\") return <div key={n.id} className={n.tone === \"runtime\" ? \"cm-runtime\" : \"cm-empty\"}>{n.text}</div>;\n return <Section key={n.id} node={n} />;\n })}\n </>\n );\n}\n\nfunction SearchBox({ node }: { node: ResolvedSearch }) {\n const env = useEnv();\n const ref = useRef<HTMLInputElement>(null);\n useEffect(() => {\n if (node.autofocus) ref.current?.focus({ preventScroll: true });\n }, [node.autofocus]);\n return (\n <div className={`cm-search ${env.highlightId === node.designId ? \"cm-hl\" : \"\"}`} onClick={() => node.designId && env.onSelectNode?.(node.designId)}>\n {env.renderIcon(\"Search\", \"cm-icon\")}\n {/* ui-exception: the menu's own filter row — this file IS the menu component */}\n <input ref={ref} value={env.query} onChange={(e) => env.setQuery(e.target.value)} placeholder={node.placeholder || \"Type to filter…\"} aria-label=\"Filter menu\" />\n </div>\n );\n}\n\nfunction Section({ node }: { node: ResolvedSection }) {\n const env = useEnv();\n const [open, setOpen] = useState(!node.collapsible);\n const layout = node.layout;\n return (\n <div className={`cm-section ${env.highlightId && env.highlightId === node.designId ? \"cm-hl\" : \"\"}`} data-layout={layout} {...(node.feature ? { \"data-feature\": \"\" } : {})}>\n {node.label && (\n // ui-exception: a menu row of the menu component itself\n <button\n type=\"button\"\n tabIndex={-1}\n className={`cm-section-label ${node.placeholder ? \"cm-slot-label\" : \"\"}`}\n onClick={() => {\n if (node.designId) env.onSelectNode?.(node.designId);\n if (node.collapsible) setOpen((o) => !o);\n }}\n >\n {node.label}\n {node.collapsible && <span className=\"cm-caret\">{open ? \"▾\" : \"▸\"}</span>}\n </button>\n )}\n {open && (\n <div className=\"cm-section-body\">\n {layout === \"list\" ? (\n <Nodes nodes={node.children} />\n ) : (\n node.children.map((c) => (c.kind === \"item\" ? <Tile key={c.id} node={c} /> : <Nodes key={c.id} nodes={[c]} />))\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction statusAttrs(n: ResolvedItem | ResolvedSubmenu) {\n const status = n.missingRequired ? \"required-missing\" : n.status && n.status !== \"works\" ? n.status : undefined;\n return status ? { \"data-status\": status } : {};\n}\n\nfunction Tile({ node }: { node: ResolvedItem }) {\n const env = useEnv();\n const reason = reasonOf(node);\n return (\n // ui-exception: a menu row of the menu component itself\n <button\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n data-cm-row=\"\"\n data-cm-label={node.label}\n title={[node.label + (node.shortcut ? ` ${node.shortcut}` : \"\"), reason].filter(Boolean).join(\"\\n\")}\n aria-label={node.label}\n aria-disabled={node.disabled || undefined}\n {...toneAttrs(node.tone, node.toneLabel)}\n {...statusAttrs(node)}\n className={`cm-tile ${node.danger ? \"cm-danger\" : \"\"} ${node.ai ? \"cm-ai\" : \"\"} ${env.highlightId && env.highlightId === node.designId ? \"cm-hl\" : \"\"}`}\n onClick={() => {\n if (node.designId) env.onSelectNode?.(node.designId);\n if (!node.disabled) env.onActivate(node);\n }}\n >\n <Icon icon={node.icon} fallback=\"Circle\" />\n </button>\n );\n}\n\nfunction Item({ node, path, hit }: { node: ResolvedItem; path?: string; hit?: boolean }) {\n const env = useEnv();\n const { look } = env;\n const [checked, setChecked] = useState(Boolean(node.checked));\n // A changed default shows at once (the designer flips \"On by default\" while looking at the row).\n useEffect(() => setChecked(Boolean(node.checked)), [node.checked]);\n const settable = node.toggle || node.switch;\n const hint = path || (look.showHints ? node.hint : undefined);\n const reason = reasonOf(node);\n const intent = useContext(PanelIntent);\n return (\n // ui-exception: a menu row of the menu component itself\n <button\n type=\"button\"\n role={settable ? \"menuitemcheckbox\" : \"menuitem\"}\n tabIndex={-1}\n data-cm-row=\"\"\n data-cm-label={node.label}\n {...(hit ? { \"data-cm-hit\": \"\" } : {})}\n aria-checked={settable ? checked : undefined}\n aria-disabled={node.disabled || undefined}\n {...(reason ? { title: reason, \"aria-description\": reason } : {})}\n {...toneAttrs(node.tone, node.toneLabel)}\n {...statusAttrs(node)}\n className={`cm-item ${node.placeholder ? \"cm-slot\" : \"\"} ${node.danger ? \"cm-danger\" : \"\"} ${node.ai ? \"cm-ai\" : \"\"} ${env.highlightId && env.highlightId === node.designId ? \"cm-hl\" : \"\"}`}\n onPointerEnter={(e) => intent?.enter(null, e.currentTarget)}\n onClick={() => {\n if (node.designId) env.onSelectNode?.(node.designId);\n if (node.disabled) return;\n if (settable) setChecked((c) => !c);\n env.onActivate(node);\n }}\n >\n {look.showIcons && (\n <span className=\"cm-icon-slot\">{node.toggle ? (checked ? env.renderIcon(\"Check\", \"cm-icon\") : null) : <Icon icon={node.icon} />}</span>\n )}\n <span className=\"cm-text\">\n <span className=\"cm-label\">{node.label}</span>\n {hint && <span className=\"cm-hint\">{hint}</span>}\n </span>\n {node.count && <span className=\"cm-count\">{node.count}</span>}\n {look.showShortcuts && node.shortcut && !node.switch && <kbd className=\"cm-shortcut\">{node.shortcut}</kbd>}\n {node.switch && <span className=\"cm-switch\" data-on={checked ? \"\" : undefined} aria-hidden=\"true\" />}\n </button>\n );\n}\n\ntype FlyoutSide = \"right\" | \"left\" | \"below\";\n\n/**\n * Builder: where a submenu opens beside its row — right when it fits, else left, else just under it.\n * \"Fits\" is measured against the nearest `[data-menu-bounds]` box (a panel that clips), else the window.\n */\nfunction flyoutSide(row: HTMLElement, width: number): FlyoutSide {\n const r = row.getBoundingClientRect();\n const bounds = row.closest<HTMLElement>(\"[data-menu-bounds]\")?.getBoundingClientRect() ?? { left: 0, right: window.innerWidth };\n if (r.right + width + 8 <= bounds.right) return \"right\";\n if (r.left - width - 8 >= bounds.left) return \"left\";\n return \"below\";\n}\n\n/** Live: the flyout's place on the screen — beside its row, flipped and clamped to stay on screen. */\nexport function placeFlyout(row: DOMRect, size: { width: number; height: number }, view: { width: number; height: number }) {\n const gap = 2;\n const margin = 8;\n let left = row.right + gap;\n let side: \"right\" | \"left\" = \"right\";\n if (left + size.width > view.width - margin) {\n const flipped = row.left - gap - size.width;\n if (flipped >= margin) {\n left = flipped;\n side = \"left\";\n } else left = Math.max(margin, view.width - margin - size.width);\n }\n let top = row.top - 5;\n if (top + size.height > view.height - margin) top = Math.max(margin, view.height - margin - size.height);\n return { left, top, side };\n}\n\nfunction Submenu({ node }: { node: ResolvedSubmenu }) {\n const env = useEnv();\n const intent = useContext(PanelIntent);\n const { look } = env;\n const open = intent?.openId === node.id;\n const [side, setSide] = useState<FlyoutSide>(\"right\");\n const [fixed, setFixed] = useState<{ left: number; top: number } | null>(null);\n const rowRef = useRef<HTMLButtonElement>(null);\n const flyRef = useRef<HTMLDivElement>(null);\n const reason = reasonOf(node);\n const greyed = node.disabled;\n\n useLayoutEffect(() => {\n if (!open || env.flyout !== \"fixed\" || !rowRef.current || !flyRef.current) return;\n const r = rowRef.current.getBoundingClientRect();\n const f = flyRef.current.getBoundingClientRect();\n const at = placeFlyout(r, { width: f.width || look.width, height: f.height }, { width: window.innerWidth, height: window.innerHeight });\n setFixed({ left: at.left, top: at.top });\n setSide(at.side);\n }, [open, env.flyout, look.width]);\n useEffect(() => {\n if (!open) setFixed(null);\n if (open && intent?.viaKey && flyRef.current) {\n const first = rowsOf(flyRef.current)[0];\n (first ?? flyRef.current).focus();\n }\n }, [open, intent?.viaKey]);\n\n const closeFlyout = useCallback(() => {\n intent?.close();\n rowRef.current?.focus();\n }, [intent]);\n\n const flyStyle: CSSProperties =\n env.flyout === \"fixed\"\n ? { position: \"fixed\", width: look.width, maxHeight: \"calc(100vh - 16px)\", overflowY: \"auto\", left: fixed?.left ?? -9999, top: fixed?.top ?? -9999, visibility: fixed ? \"visible\" : \"hidden\" }\n : { width: look.width };\n\n return (\n <div className=\"cm-sub\">\n {/* ui-exception: a menu row of the menu component itself */}\n <button\n ref={rowRef}\n type=\"button\"\n role=\"menuitem\"\n tabIndex={-1}\n data-cm-row=\"\"\n data-cm-label={node.label}\n {...(greyed ? {} : { \"data-cm-sub\": node.id })}\n aria-haspopup=\"menu\"\n aria-expanded={open}\n aria-disabled={greyed || undefined}\n {...(reason ? { title: reason, \"aria-description\": reason } : {})}\n {...toneAttrs(node.tone, node.toneLabel)}\n {...statusAttrs(node)}\n className={`cm-item ${node.placeholder ? \"cm-slot\" : \"\"} ${open ? \"cm-open\" : \"\"} ${env.highlightId && env.highlightId === node.designId ? \"cm-hl\" : \"\"}`}\n onPointerEnter={(e) => {\n if (env.flyout === \"absolute\") setSide(flyoutSide(e.currentTarget, look.width));\n if (greyed) intent?.enter(null, e.currentTarget);\n else intent?.enter(node.id, e.currentTarget);\n }}\n onClick={(e) => {\n if (node.designId) env.onSelectNode?.(node.designId);\n if (greyed) return;\n if (env.flyout === \"absolute\") setSide(flyoutSide(e.currentTarget, look.width));\n intent?.openNow(node.id, e.detail === 0);\n }}\n >\n {look.showIcons && <span className=\"cm-icon-slot\"><Icon icon={node.icon} /></span>}\n <span className=\"cm-text\">\n <span className=\"cm-label\">{node.label}</span>\n {look.showHints && node.hint && <span className=\"cm-hint\">{node.hint}</span>}\n </span>\n {node.loading && <span className=\"cm-count\">…</span>}\n {node.count && <span className=\"cm-count\">{node.count}</span>}\n {env.renderIcon(\"ChevronRight\", \"cm-icon cm-chev\")}\n </button>\n {open && (\n <Panel className=\"cm-menu cm-flyout\" flyoutId={node.id} style={flyStyle} onClose={closeFlyout}>\n <FlyoutBody node={node} flyRef={flyRef} side={env.flyout === \"absolute\" ? side : undefined} />\n </Panel>\n )}\n </div>\n );\n}\n\n/** The flyout's rows (kept apart so the panel element itself is what `flyRef` measures). */\nfunction FlyoutBody({ node, flyRef, side }: { node: ResolvedSubmenu; flyRef: React.RefObject<HTMLDivElement | null>; side: FlyoutSide | undefined }) {\n const ref = useRef<HTMLSpanElement>(null);\n useLayoutEffect(() => {\n const panel = ref.current?.closest<HTMLDivElement>('[role=\"menu\"]') ?? null;\n flyRef.current = panel;\n if (panel && side) panel.setAttribute(\"data-side\", side);\n });\n return (\n <>\n <span ref={ref} hidden />\n {node.loading && node.children.length === 0 ? (\n <div className=\"cm-empty\">Loading…</div>\n ) : node.children.length === 0 ? (\n <div className=\"cm-empty\">{node.emptyLabel ?? \"Empty submenu\"}</div>\n ) : (\n <Nodes nodes={node.children} />\n )}\n </>\n );\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  var _chunkPDHJTEZKcjs = require('./chunk-PDHJTEZK.cjs');
6
6
 
7
7
 
8
- var _chunkQXHLM7SScjs = require('./chunk-QXHLM7SS.cjs');
8
+ var _chunkLDMYNYFCcjs = require('./chunk-LDMYNYFC.cjs');
9
9
 
10
10
  // src/design/catalog.ts
11
11
  var o = (id, label, icon, group, extra = {}) => ({ id, label, icon, group, status: "live", ...extra });
@@ -341,7 +341,7 @@ function MenuPreview({ design, renderIcon, highlightId, onSelectNode, onPick, av
341
341
  path: h.path
342
342
  }))
343
343
  };
344
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkQXHLM7SScjs.MenuPanel, { nodes, env, ...className ? { className } : {}, ...style ? { style } : {} });
344
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkLDMYNYFCcjs.MenuPanel, { nodes, env, ...className ? { className } : {}, ...style ? { style } : {} });
345
345
  }
346
346
 
347
347
 
@@ -354,4 +354,4 @@ function MenuPreview({ design, renderIcon, highlightId, onSelectNode, onPick, av
354
354
 
355
355
 
356
356
  exports.CATALOG = CATALOG; exports.CATALOG_GROUPS = CATALOG_GROUPS; exports.catalogOption = catalogOption; exports.TEMPLATES = TEMPLATES; exports.SLOT_SOURCES = SLOT_SOURCES; exports.slotSource = slotSource; exports.slotOptions = slotOptions; exports.MenuPreview = MenuPreview;
357
- //# sourceMappingURL=chunk-ANJYKQAA.cjs.map
357
+ //# sourceMappingURL=chunk-MBU4Y22O.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/context-menu/dist/chunk-ANJYKQAA.cjs","../src/design/catalog.ts","../src/design/MenuPreview.tsx","../src/design/preview-nodes.ts"],"names":["item"],"mappings":"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AACR;AAea;AACE,EAAA;AACG,EAAA;AACV,EAAA;AACH,EAAA;AACa,IAAA;AACP,IAAA;AACO,IAAA;AACA,IAAA;AACd,EAAA;AACJ;AAEgB;AACP,EAAA;AACT;AAGgB;AACA,EAAA;AACD,EAAA;AACD,EAAA;AACd;ADhFkB;AACA;AEnKA;AFqKA;AACA;AG9JH;AACT,EAAA;AACO,IAAA;AACX,EAAA;AACa,EAAA;AACA,EAAA;AACA,EAAA;AACJ,EAAA;AACX;AAEc;AACL,EAAA;AACC,IAAA;AACG,IAAA;AACC,IAAA;AACE,IAAA;AACH,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACC,IAAA;AACI,IAAA;AACL,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,IAAA;AACV,EAAA;AACF;AAEc;AACG,EAAA;AACF,EAAA;AACJ,EAAA;AACG,IAAA;AACH,IAAA;AACC,MAAA;AACG,MAAA;AACC,MAAA;AACE,MAAA;AACJ,MAAA;AACC,MAAA;AACT,MAAA;AACU,MAAA;AACC,QAAA;AACT,QAAA;AACQ,UAAA;AACC,UAAA;AACP,UAAA;AACO,UAAA;AACC,UAAA;AACR,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACgB,EAAA;AACF,EAAA;AACL,EAAA;AACE,IAAA;AACX,EAAA;AACO,EAAA;AACC,IAAA;AACG,IAAA;AACC,IAAA;AACE,IAAA;AACE,IAAA;AACJ,IAAA;AACG,IAAA;AACb,IAAA;AACU,IAAA;AAKI,MAAA;AACM,MAAA;AACH,MAAA;AACD,MAAA;AACE,MAAA;AACJ,MAAA;AACK,MAAA;AACL,MAAA;AACI,MAAA;AACV,IAAA;AACV,EAAA;AACF;AAEc;AACiB,EAAA;AACb,EAAA;AACN,IAAA;AACI,IAAA;AACN,IAAA;AACK,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACD,QAAA;AACA,QAAA;AACI,QAAA;AACD,QAAA;AACH,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACI,QAAA;AACA,QAAA;AACX,MAAA;AACQ,IAAA;AACE,MAAA;AACb,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAGgB;AACC,EAAA;AACD,EAAA;AACR,EAAA;AACD,EAAA;AACE,EAAA;AACT;AHuJkB;AACA;AE3MT;AA3DA;AACE,EAAA;AACI,EAAA;AACA,EAAA;AACf;AASgB;AACE,EAAA;AACA,EAAA;AACR,IAAA;AACE,MAAA;AACI,MAAA;AACD,IAAA;AACE,MAAA;AACb,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAIgB;AACA,EAAA;AACA,EAAA;AACY,EAAA;AACX,IAAA;AACb,IAAA;AACQ,IAAA;AACR,IAAA;AACA,IAAA;AACaA,IAAAA;AACF,MAAA;AACX,IAAA;AACA,IAAA;AACA,IAAA;AACS,IAAA;AAEC,MAAA;AACE,QAAA;AACA,QAAA;AACI,QAAA;AACD,QAAA;AACE,QAAA;AACA,QAAA;AACD,QAAA;AACC,QAAA;AACA,QAAA;AACD,QAAA;AACZ,MAAA;AACQ,MAAA;AACR,IAAA;AACN,EAAA;AACO,EAAA;AACT;AF6PkB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/aidream/aidream/apps/shared/context-menu/dist/chunk-ANJYKQAA.cjs","sourcesContent":[null,"// src/design/catalog.ts — @ai-matrx/context-menu: EVERY OPTION A MENU CAN OFFER, as a palette.\n//\n// The builder's left column. Each entry is something the live menu shows somewhere today, taken\n// from the real menu's own lists in matrx-frontend (features/context-menu-v3):\n// - regroup/engine-rows.ts → the engine rows (`cm:*`)\n// - regroup/proposed-grouping.ts → the 13 \"grouped by what you get\" groups and their rules\n// - proposed/proposed-arrangement.ts → the three icon strips and the object-first submenus\n// - the page rows the demos show (notes, quiz row, table row, chat message)\n// Ids are the live menu's ids wherever one exists, so a saved design names the real row.\n// When the menu source moves into this package, this list is generated from it instead.\n\nimport type { MenuNode } from \"./model\";\n\nexport type CatalogGroup =\n | \"Clipboard\"\n | \"Edit\"\n | \"AI\"\n | \"Save & export\"\n | \"Share & publish\"\n | \"Organize\"\n | \"History & compare\"\n | \"Listen\"\n | \"Page: notes\"\n | \"Page: record\"\n | \"Page: chat message\"\n | \"Tools & admin\";\n\nexport interface CatalogOption {\n /** The live menu's id where one exists (`cm:copy`), else this catalog's own. */\n id: string;\n label: string;\n icon: string;\n group: CatalogGroup;\n shortcut?: string;\n hint?: string;\n danger?: boolean;\n ai?: boolean;\n /** \"Live\" = drawn by today's menu; \"Proposed\" = only in a proposal page so far. */\n status: \"live\" | \"proposed\";\n}\n\nconst o = (\n id: string,\n label: string,\n icon: string,\n group: CatalogGroup,\n extra: Partial<Omit<CatalogOption, \"id\" | \"label\" | \"icon\" | \"group\">> = {},\n): CatalogOption => ({ id, label, icon, group, status: \"live\", ...extra });\n\nexport const CATALOG: readonly CatalogOption[] = [\n // Clipboard — the verb strip\n o(\"cm:copy\", \"Copy\", \"Copy\", \"Clipboard\", { shortcut: \"⌘C\" }),\n o(\"cm:cut\", \"Cut\", \"Scissors\", \"Clipboard\", { shortcut: \"⌘X\" }),\n o(\"cm:paste\", \"Paste\", \"ClipboardPaste\", \"Clipboard\", { shortcut: \"⌘V\" }),\n o(\"cm:undo\", \"Undo\", \"Undo2\", \"Clipboard\", { shortcut: \"⌘Z\" }),\n o(\"cm:redo\", \"Redo\", \"Redo2\", \"Clipboard\", { shortcut: \"⇧⌘Z\" }),\n o(\"cm:find\", \"Find & Replace\", \"Search\", \"Clipboard\", { shortcut: \"⌘F\" }),\n o(\"cm:select-all\", \"Select All\", \"TextSelect\", \"Clipboard\", { shortcut: \"⌘A\" }),\n o(\"cm:copy-as\", \"Copy as…\", \"CopyPlus\", \"Clipboard\"),\n o(\"cm:json\", \"Copy as JSON\", \"Braces\", \"Clipboard\"),\n o(\"copy-html-page\", \"Copy as HTML page\", \"FileCode\", \"Clipboard\"),\n o(\"cm:insert-reference\", \"Insert / Copy reference…\", \"AtSign\", \"Clipboard\"),\n\n // Edit\n o(\"cm:save\", \"Save\", \"Save\", \"Edit\", { shortcut: \"⌘S\" }),\n o(\"duplicate\", \"Duplicate\", \"CopyPlus\", \"Edit\"),\n o(\"rename\", \"Rename\", \"PenLine\", \"Edit\"),\n o(\"edit\", \"Edit\", \"Pencil\", \"Edit\"),\n o(\"favorite\", \"Add to favorites\", \"Star\", \"Edit\"),\n o(\"archive\", \"Archive\", \"Archive\", \"Edit\", { danger: true }),\n o(\"cm:delete\", \"Delete\", \"Trash2\", \"Edit\", { danger: true }),\n\n // AI\n o(\"cm:chat\", \"Chat about this\", \"MessageSquare\", \"AI\", { ai: true }),\n o(\"cm:quick-actions\", \"Quick Actions\", \"Zap\", \"AI\", { ai: true }),\n o(\"cm:placement:ai-action\", \"AI Actions\", \"Sparkles\", \"AI\", { ai: true }),\n o(\"cm:placement:content-block\", \"Content blocks\", \"Blocks\", \"AI\", { ai: true }),\n o(\"cm:placement:organization-tool\", \"Org Items\", \"Building2\", \"AI\", { ai: true }),\n o(\"cm:placement:user-tool\", \"My Items\", \"User\", \"AI\", { ai: true }),\n o(\"cm:placement:quick-action\", \"Quick actions (placement)\", \"Zap\", \"AI\", { ai: true }),\n o(\"cm:placement:bound-agent\", \"Agents\", \"Bot\", \"AI\", { ai: true }),\n o(\"send-to-agent\", \"Send to agent…\", \"Send\", \"AI\", { ai: true }),\n o(\"continue-in-chat\", \"Continue in chat\", \"MessagesSquare\", \"AI\", { ai: true }),\n o(\"regenerate-response\", \"Regenerate\", \"RefreshCw\", \"AI\", { ai: true }),\n o(\"code-block-chart\", \"Make a chart\", \"ChartColumn\", \"AI\", { ai: true }),\n o(\"ai-improve\", \"Improve writing\", \"Wand2\", \"AI\", { ai: true, status: \"proposed\" }),\n o(\"ai-summarize\", \"Summarize\", \"ListCollapse\", \"AI\", { ai: true, status: \"proposed\" }),\n o(\"ai-explain\", \"Explain\", \"Lightbulb\", \"AI\", { ai: true, status: \"proposed\" }),\n o(\"ai-translate\", \"Translate…\", \"Languages\", \"AI\", { ai: true, status: \"proposed\" }),\n\n // Save & export\n o(\"cm:export\", \"Export\", \"FileOutput\", \"Save & export\"),\n o(\"cm:convert\", \"Convert to…\", \"ArrowRightLeft\", \"Save & export\"),\n o(\"save-as-file\", \"Save as file\", \"FileDown\", \"Save & export\"),\n o(\"download-pdf\", \"Download PDF\", \"FileText\", \"Save & export\"),\n o(\"download-docx\", \"Download Word\", \"FileType\", \"Save & export\"),\n o(\"download-html\", \"Download HTML\", \"FileCode2\", \"Save & export\"),\n o(\"print\", \"Print\", \"Printer\", \"Save & export\", { shortcut: \"⌘P\" }),\n\n // Share & publish\n o(\"cm:share\", \"Share…\", \"Share2\", \"Share & publish\"),\n o(\"share-link\", \"Copy share link\", \"Link\", \"Share & publish\"),\n o(\"send-google-doc\", \"Send to Google Docs\", \"FileUp\", \"Share & publish\"),\n o(\"email-to-me\", \"Email to me\", \"Mail\", \"Share & publish\"),\n o(\"html-preview\", \"Preview as web page\", \"Eye\", \"Share & publish\"),\n o(\"share-webpage\", \"Publish as web page\", \"Globe\", \"Share & publish\"),\n\n // Organize\n o(\"cm:attach\", \"Attach to…\", \"Paperclip\", \"Organize\"),\n o(\"set-context-value\", \"Set as context\", \"Pin\", \"Organize\"),\n o(\"pin-message\", \"Pin\", \"Pin\", \"Organize\"),\n o(\"notes-and-comments\", \"Notes & comments\", \"StickyNote\", \"Organize\"),\n o(\"move\", \"Move to folder…\", \"FolderInput\", \"Organize\"),\n o(\"tags\", \"Tags…\", \"Tags\", \"Organize\"),\n\n // History & compare\n o(\"cm:view-history\", \"View history\", \"History\", \"History & compare\"),\n o(\"edit-history\", \"Edit history\", \"GitCommitVertical\", \"History & compare\"),\n o(\"fork-at-message\", \"Fork from here\", \"GitBranch\", \"History & compare\"),\n o(\"cm:compare\", \"Compare…\", \"GitCompareArrows\", \"History & compare\"),\n o(\"compare-with-clipboard\", \"Compare with clipboard\", \"ClipboardList\", \"History & compare\"),\n o(\"set-compare-base\", \"Set as compare base\", \"Flag\", \"History & compare\"),\n\n // Listen\n o(\"cm:speak\", \"Read aloud\", \"Volume2\", \"Listen\"),\n o(\"cm:listen\", \"Dictate\", \"Mic\", \"Listen\"),\n o(\"tts-voice-settings\", \"Voice settings\", \"AudioLines\", \"Listen\"),\n\n // Page rows: notes\n o(\"notes:close-tab\", \"Close tab\", \"X\", \"Page: notes\"),\n o(\"notes:close-others\", \"Close other tabs\", \"PanelTopClose\", \"Page: notes\"),\n o(\"notes:export-md\", \"Export as Markdown\", \"FileDown\", \"Page: notes\"),\n o(\"notes:share-clipboard\", \"Copy to clipboard\", \"ClipboardCopy\", \"Page: notes\"),\n o(\"notes:convert-artifacts\", \"Convert to artifacts\", \"Shapes\", \"Page: notes\"),\n o(\"notes:trash\", \"Move to Trash\", \"Trash2\", \"Page: notes\", { danger: true }),\n\n // Page rows: a record (quiz row, table row)\n o(\"row:open\", \"Open\", \"SquareArrowOutUpRight\", \"Page: record\"),\n o(\"row:open-tab\", \"Open in new tab\", \"ExternalLink\", \"Page: record\"),\n o(\"row:take\", \"Take\", \"Play\", \"Page: record\"),\n o(\"row:results\", \"Results\", \"ChartBar\", \"Page: record\"),\n o(\"row:manage\", \"Manage\", \"Settings2\", \"Page: record\"),\n o(\"row:restore\", \"Restore\", \"ArchiveRestore\", \"Page: record\"),\n\n // Page rows: chat message\n o(\"regenerate-latest\", \"Regenerate latest\", \"RotateCcw\", \"Page: chat message\", { ai: true }),\n o(\"fork-and-regenerate\", \"Fork and regenerate\", \"GitFork\", \"Page: chat message\", { ai: true }),\n o(\"feedback-up\", \"Good response\", \"ThumbsUp\", \"Page: chat message\"),\n o(\"feedback-down\", \"Bad response\", \"ThumbsDown\", \"Page: chat message\"),\n\n // Tools & admin\n o(\"cm:x:surface\", \"This page (location, context, agents)\", \"MapPin\", \"Tools & admin\"),\n o(\"cm:admin\", \"Admin tools\", \"Shield\", \"Tools & admin\"),\n o(\"feedback\", \"Send feedback\", \"MessageSquareWarning\", \"Tools & admin\"),\n o(\"keyboard-shortcuts\", \"Keyboard shortcuts\", \"Keyboard\", \"Tools & admin\", { status: \"proposed\" }),\n];\n\nexport const CATALOG_GROUPS: readonly CatalogGroup[] = [...new Set(CATALOG.map((c) => c.group))];\n\nexport function catalogOption(id: string): CatalogOption | undefined {\n return CATALOG.find((c) => c.id === id);\n}\n\n/** Ready-made pieces: dropping one inserts the whole block. */\nexport interface CatalogTemplate {\n id: string;\n label: string;\n icon: string;\n build: () => MenuNode;\n}\n\nlet seq = 0;\nconst tid = (p: string) => `${p}_t${Date.now().toString(36)}${(seq++).toString(36)}`;\n\nfunction itemFrom(id: string): MenuNode {\n const c = catalogOption(id);\n return {\n id: tid(\"i\"),\n kind: \"item\",\n catalogId: id,\n label: c?.label ?? id,\n icon: c?.icon ?? \"Circle\",\n ...(c?.shortcut ? { shortcut: c.shortcut } : {}),\n ...(c?.danger ? { danger: true } : {}),\n ...(c?.ai ? { ai: true } : {}),\n };\n}\n\nconst strip = (label: string, ids: string[]): MenuNode => ({\n id: tid(\"s\"),\n kind: \"section\",\n label,\n layout: \"row\",\n children: ids.map(itemFrom),\n});\n\nconst sub = (label: string, icon: string, ids: string[]): MenuNode => ({\n id: tid(\"m\"),\n kind: \"submenu\",\n label,\n icon,\n children: ids.map(itemFrom),\n});\n\nexport const TEMPLATES: readonly CatalogTemplate[] = [\n { id: \"strip-thing\", label: \"Icon strip — a thing\", icon: \"Rows2\", build: () => strip(\"\", [\"cm:copy\", \"cm:share\", \"duplicate\", \"favorite\", \"save-as-file\", \"archive\"]) },\n { id: \"strip-edit\", label: \"Icon strip — editable text\", icon: \"Rows2\", build: () => strip(\"\", [\"cm:cut\", \"cm:copy\", \"cm:paste\", \"cm:undo\", \"cm:redo\", \"cm:find\"]) },\n { id: \"strip-read\", label: \"Icon strip — read-only text\", icon: \"Rows2\", build: () => strip(\"\", [\"cm:copy\", \"cm:speak\", \"cm:find\", \"cm:share\"]) },\n { id: \"sub-intelligence\", label: \"Intelligence ▸\", icon: \"Sparkles\", build: () => sub(\"Intelligence\", \"Sparkles\", [\"cm:chat\", \"cm:placement:ai-action\", \"cm:placement:bound-agent\", \"send-to-agent\"]) },\n { id: \"sub-save-share\", label: \"Save & share ▸\", icon: \"Share2\", build: () => sub(\"Save & share\", \"Share2\", [\"cm:copy-as\", \"save-as-file\", \"cm:convert\", \"share-webpage\", \"cm:share\"]) },\n { id: \"sub-organize\", label: \"Organize ▸\", icon: \"Tags\", build: () => sub(\"Organize\", \"Tags\", [\"cm:attach\", \"pin-message\", \"cm:view-history\", \"cm:compare\"]) },\n { id: \"sub-tools\", label: \"Tools ▸\", icon: \"Wrench\", build: () => sub(\"Tools\", \"Wrench\", [\"cm:speak\", \"cm:quick-actions\", \"feedback\", \"cm:x:surface\", \"cm:admin\"]) },\n];\n\n// ── placeholders ─────────────────────────────────────────────────────────────\n\n/** What a placeholder can hold. A catalog group holds that group's options the design has not placed. */\nexport interface SlotSource {\n id: string;\n label: string;\n /** The default placeholder wording (\"All other AI items\"). */\n phrase: string;\n icon: string;\n /** Filled by the live menu from data (agents, a page's own rows) — the preview cannot list them. */\n runtime?: boolean;\n}\n\nexport const SLOT_SOURCES: readonly SlotSource[] = [\n { id: \"any\", label: \"Everything not placed\", phrase: \"Everything else\", icon: \"Ellipsis\" },\n { id: \"agents\", label: \"Agents & shortcuts (library)\", phrase: \"All other agents\", icon: \"Bot\", runtime: true },\n { id: \"page-rows\", label: \"This page's own rows\", phrase: \"All of this page's actions\", icon: \"PanelTop\", runtime: true },\n ...CATALOG_GROUPS.map((g) => ({\n id: `group:${g}`,\n label: g,\n phrase: g === \"AI\" ? \"All other AI items\" : `All other ${g.toLowerCase()} options`,\n icon: CATALOG.find((c) => c.group === g)?.icon ?? \"Circle\",\n })),\n];\n\nexport function slotSource(id: string): SlotSource | undefined {\n return SLOT_SOURCES.find((s) => s.id === id);\n}\n\n/** The catalog options a placeholder stands for: its kind, minus every option the design already places. */\nexport function slotOptions(fills: string, placed: ReadonlySet<string>): CatalogOption[] {\n if (fills === \"agents\" || fills === \"page-rows\") return [];\n const pool = fills === \"any\" ? CATALOG : CATALOG.filter((c) => `group:${c.group}` === fills);\n return pool.filter((c) => !placed.has(c.id));\n}\n","// src/design/MenuPreview.tsx — @ai-matrx/context-menu: DRAWS A DESIGN AS THE MENU IT DESCRIBES.\n//\n// The builder's live preview and its right-click test menu. It draws through THE ONE menu renderer\n// (`designed/MenuPanel`) that the live menu uses — the design goes in as `previewNodes` instead of\n// bound rows — so what the designer sees is what opens. Icons come from the host (`renderIcon`).\n\nimport { useMemo, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { isContainer, type MenuDesign, type MenuItemNode, type MenuNode } from \"./model\";\nimport { previewNodes, type AvailabilityPreview } from \"./preview-nodes\";\nimport { MenuPanel, type MenuPanelEnv } from \"../designed/MenuPanel\";\nimport type { ResolvedHit } from \"../designed/bindings\";\n\nexport type { AvailabilityPreview } from \"./preview-nodes\";\n\nexport interface MenuPreviewProps {\n design: MenuDesign;\n renderIcon: (name: string, className?: string) => ReactNode;\n /** The entry highlighted as \"selected in the builder\". */\n highlightId?: string | null;\n /** A click on any entry. */\n onSelectNode?: (id: string) => void;\n /** A click on an item (the menu's action). */\n onPick?: (item: MenuItemNode) => void;\n /** \"unavailable\": every entry with an unavailable rule shows as that rule says. Default \"available\". */\n availability?: AvailabilityPreview;\n className?: string;\n style?: CSSProperties;\n}\n\n// ── search ───────────────────────────────────────────────────────────────────\n\n/** The words an entry answers to: its label, its hint and its keywords. */\nfunction searchable(node: MenuNode): string {\n if (node.kind === \"divider\" || node.kind === \"search\") return \"\";\n const hint = node.kind === \"item\" || node.kind === \"submenu\" ? (node.hint ?? \"\") : \"\";\n return [node.label, hint, ...(node.keywords ?? [])].join(\" \").toLowerCase();\n}\n\nexport interface Hit {\n node: MenuItemNode;\n /** Where it lives (\"Intelligence › AI Actions\"). */\n path: string;\n}\n\n/** While searching: every matching item, wherever it sits, with the submenus it is inside. */\nexport function searchMenu(nodes: readonly MenuNode[], q: string, trail: string[] = [], out: Hit[] = []): Hit[] {\n const words = q.split(/\\s+/).filter(Boolean);\n for (const n of nodes) {\n if (n.kind === \"item\") {\n const text = `${searchable(n)} ${trail.join(\" \").toLowerCase()}`;\n if (words.every((w) => text.includes(w))) out.push({ node: n, path: trail.join(\" › \") });\n } else if (isContainer(n)) {\n searchMenu(n.children, q, n.kind === \"submenu\" ? [...trail, n.label] : n.label ? [...trail, n.label] : trail, out);\n }\n }\n return out;\n}\n\n// ── the menu ─────────────────────────────────────────────────────────────────\n\nexport function MenuPreview({ design, renderIcon, highlightId, onSelectNode, onPick, availability = \"available\", className, style }: MenuPreviewProps) {\n const [query, setQuery] = useState(\"\");\n const nodes = useMemo(() => previewNodes(design, availability), [design, availability]);\n const env: MenuPanelEnv = {\n look: design.look,\n renderIcon,\n flyout: \"absolute\",\n highlightId,\n onSelectNode,\n onActivate: (item) => {\n if (item.source) onPick?.(item.source);\n },\n query,\n setQuery,\n search: (q): ResolvedHit[] =>\n searchMenu(design.nodes, q).map((h) => ({\n node: {\n kind: \"item\",\n id: h.node.id,\n designId: h.node.id,\n label: h.node.label,\n ...(h.node.icon ? { icon: { name: h.node.icon } } : {}),\n ...(h.node.shortcut ? { shortcut: h.node.shortcut } : {}),\n disabled: Boolean(h.node.disabled),\n ...(h.node.danger ? { danger: true } : {}),\n ...(h.node.ai ? { ai: true } : {}),\n source: h.node,\n },\n path: h.path,\n })),\n };\n return <MenuPanel nodes={nodes} env={env} {...(className ? { className } : {})} {...(style ? { style } : {})} />;\n}\n","// src/design/preview-nodes.ts — @ai-matrx/context-menu: A DESIGN AS THE BUILDER PREVIEWS IT.\n//\n// The builder and the live menu draw through ONE renderer (`designed/MenuPanel`). The live menu\n// feeds it the design bound to real rows (`designed/bindings`); the builder feeds it this: the\n// design as drawn, every entry kept, placeholders shown as dashed rows, and — when previewing\n// \"unavailable\" — each entry as its own unavailable rule says.\n\nimport { slotOptions, slotSource } from \"./catalog\";\nimport { isContainer, placedCatalogIds, type MenuDesign, type MenuItemNode, type MenuNode, type MenuSlotNode } from \"./model\";\nimport type { ResolvedItem, ResolvedNode } from \"../designed/types\";\n\n/** Preview what the person sees when some entries are unavailable (each by its own rule). */\nexport type AvailabilityPreview = \"available\" | \"unavailable\";\n\nfunction shown(node: MenuNode, availability: AvailabilityPreview): { hidden: boolean; greyed: boolean } {\n if (availability === \"available\" || !node.unavailableWhen) {\n return { hidden: false, greyed: node.kind === \"item\" && Boolean(node.disabled) };\n }\n const mode = node.unavailable ?? \"disable\";\n if (mode === \"hide\") return { hidden: true, greyed: false };\n if (mode === \"required\") return { hidden: false, greyed: false };\n return { hidden: false, greyed: true };\n}\n\nfunction item(node: MenuItemNode, greyed: boolean): ResolvedItem {\n return {\n kind: \"item\",\n id: node.id,\n designId: node.id,\n label: node.label,\n ...(node.icon ? { icon: { name: node.icon } } : {}),\n ...(node.shortcut ? { shortcut: node.shortcut } : {}),\n ...(node.hint ? { hint: node.hint } : {}),\n ...(node.keywords ? { keywords: node.keywords } : {}),\n disabled: greyed,\n ...(greyed && node.unavailableWhen ? { disabledReason: node.unavailableWhen } : {}),\n ...(node.danger ? { danger: true } : {}),\n ...(node.ai ? { ai: true } : {}),\n ...(node.tone ? { tone: node.tone } : {}),\n ...(node.toneLabel ? { toneLabel: true } : {}),\n ...(node.toggle ? { toggle: true, checked: Boolean(node.defaultOn) } : {}),\n ...(node.switch ? { switch: true, checked: Boolean(node.defaultOn) } : {}),\n source: node,\n };\n}\n\nfunction slot(node: MenuSlotNode, placed: ReadonlySet<string>): ResolvedNode {\n const source = slotSource(node.fills);\n const icon = node.icon ?? source?.icon ?? \"Ellipsis\";\n if (node.shape === \"feature\") {\n const n = Math.max(0, Math.min(12, node.featureItems ?? 4));\n return {\n kind: \"section\",\n id: node.id,\n designId: node.id,\n label: node.label || \"This feature\",\n layout: \"list\",\n feature: true,\n placeholder: true,\n children: [\n ...Array.from({ length: n }, (_, i): ResolvedNode => ({ kind: \"item\", id: `${node.id}:${i}`, designId: node.id, label: `Feature option ${i + 1}`, icon: { name: \"Circle\" }, disabled: false, placeholder: true })),\n {\n kind: \"submenu\",\n id: `${node.id}:more`,\n designId: node.id,\n label: \"More\",\n icon: { name: \"Ellipsis\" },\n disabled: false,\n placeholder: true,\n children: [{ kind: \"note\", id: `${node.id}:more-note`, text: \"The rest of this feature's options, filled at runtime\", tone: \"runtime\" }],\n },\n ],\n };\n }\n const options = slotOptions(node.fills, placed);\n const count = source?.runtime ? \"live\" : String(options.length);\n if (node.shape === \"items\") {\n return { kind: \"item\", id: node.id, designId: node.id, label: node.label, icon: { name: icon }, disabled: false, placeholder: true, count };\n }\n return {\n kind: \"submenu\",\n id: node.id,\n designId: node.id,\n label: node.label,\n icon: { name: icon },\n disabled: false,\n placeholder: true,\n count,\n children: source?.runtime\n ? [{ kind: \"note\", id: `${node.id}:note`, text: `Filled at runtime: ${source.label}`, tone: \"runtime\" }]\n : options.length === 0\n ? [{ kind: \"note\", id: `${node.id}:note`, text: \"Nothing left — every option of this kind is placed\", tone: \"empty\" }]\n : options.map((o) => ({\n kind: \"item\" as const,\n id: `slot-${node.id}-${o.id}`,\n label: o.label,\n icon: { name: o.icon },\n disabled: false,\n ...(o.shortcut ? { shortcut: o.shortcut } : {}),\n ...(o.ai ? { ai: true } : {}),\n ...(o.danger ? { danger: true } : {}),\n source: { id: `slot-${node.id}-${o.id}`, kind: \"item\", label: o.label, icon: o.icon, catalogId: o.id },\n })),\n };\n}\n\nfunction walk(nodes: readonly MenuNode[], placed: ReadonlySet<string>, availability: AvailabilityPreview): ResolvedNode[] {\n const out: ResolvedNode[] = [];\n for (const n of nodes) {\n const { hidden, greyed } = shown(n, availability);\n if (hidden) continue;\n if (n.kind === \"divider\") out.push({ kind: \"divider\", id: n.id, designId: n.id });\n else if (n.kind === \"search\") out.push({ kind: \"search\", id: n.id, designId: n.id, placeholder: n.placeholder, autofocus: n.autofocus });\n else if (n.kind === \"item\") out.push(item(n, greyed));\n else if (n.kind === \"slot\") out.push(slot(n, placed));\n else if (n.kind === \"submenu\") {\n out.push({\n kind: \"submenu\",\n id: n.id,\n designId: n.id,\n label: n.label,\n ...(n.icon ? { icon: { name: n.icon } } : {}),\n ...(n.hint ? { hint: n.hint } : {}),\n ...(n.tone ? { tone: n.tone } : {}),\n ...(n.toneLabel ? { toneLabel: true } : {}),\n disabled: greyed,\n children: walk(n.children, placed, availability),\n });\n } else if (isContainer(n)) {\n out.push({ kind: \"section\", id: n.id, designId: n.id, label: n.label, layout: n.layout ?? \"list\", ...(n.collapsible ? { collapsible: true } : {}), children: walk(n.children, placed, availability) });\n }\n }\n return out;\n}\n\n/** The design as the builder draws it (the old \"search box at the top\" look switch included). */\nexport function previewNodes(design: MenuDesign, availability: AvailabilityPreview = \"available\"): ResolvedNode[] {\n const placed = placedCatalogIds(design.nodes);\n const nodes = walk(design.nodes, placed, availability);\n const hasSearch = design.nodes.some((n) => n.kind === \"search\");\n if (!hasSearch && design.look.search) nodes.unshift({ kind: \"search\", id: \"look-search\", placeholder: \"Type to filter…\", autofocus: true });\n return nodes;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/aidream/aidream/apps/shared/context-menu/dist/chunk-MBU4Y22O.cjs","../src/design/catalog.ts","../src/design/MenuPreview.tsx","../src/design/preview-nodes.ts"],"names":["item"],"mappings":"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AACR;AAea;AACE,EAAA;AACG,EAAA;AACV,EAAA;AACH,EAAA;AACa,IAAA;AACP,IAAA;AACO,IAAA;AACA,IAAA;AACd,EAAA;AACJ;AAEgB;AACP,EAAA;AACT;AAGgB;AACA,EAAA;AACD,EAAA;AACD,EAAA;AACd;ADhFkB;AACA;AEnKA;AFqKA;AACA;AG9JH;AACT,EAAA;AACO,IAAA;AACX,EAAA;AACa,EAAA;AACA,EAAA;AACA,EAAA;AACJ,EAAA;AACX;AAEc;AACL,EAAA;AACC,IAAA;AACG,IAAA;AACC,IAAA;AACE,IAAA;AACH,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACC,IAAA;AACI,IAAA;AACL,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACD,IAAA;AACV,EAAA;AACF;AAEc;AACG,EAAA;AACF,EAAA;AACJ,EAAA;AACG,IAAA;AACH,IAAA;AACC,MAAA;AACG,MAAA;AACC,MAAA;AACE,MAAA;AACJ,MAAA;AACC,MAAA;AACT,MAAA;AACU,MAAA;AACC,QAAA;AACT,QAAA;AACQ,UAAA;AACC,UAAA;AACP,UAAA;AACO,UAAA;AACC,UAAA;AACR,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACgB,EAAA;AACF,EAAA;AACL,EAAA;AACE,IAAA;AACX,EAAA;AACO,EAAA;AACC,IAAA;AACG,IAAA;AACC,IAAA;AACE,IAAA;AACE,IAAA;AACJ,IAAA;AACG,IAAA;AACb,IAAA;AACU,IAAA;AAKI,MAAA;AACM,MAAA;AACH,MAAA;AACD,MAAA;AACE,MAAA;AACJ,MAAA;AACK,MAAA;AACL,MAAA;AACI,MAAA;AACV,IAAA;AACV,EAAA;AACF;AAEc;AACiB,EAAA;AACb,EAAA;AACN,IAAA;AACI,IAAA;AACN,IAAA;AACK,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACD,QAAA;AACA,QAAA;AACI,QAAA;AACD,QAAA;AACH,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACI,QAAA;AACA,QAAA;AACX,MAAA;AACQ,IAAA;AACE,MAAA;AACb,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAGgB;AACC,EAAA;AACD,EAAA;AACR,EAAA;AACD,EAAA;AACE,EAAA;AACT;AHuJkB;AACA;AE3MT;AA3DA;AACE,EAAA;AACI,EAAA;AACA,EAAA;AACf;AASgB;AACE,EAAA;AACA,EAAA;AACR,IAAA;AACE,MAAA;AACI,MAAA;AACD,IAAA;AACE,MAAA;AACb,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAIgB;AACA,EAAA;AACA,EAAA;AACY,EAAA;AACX,IAAA;AACb,IAAA;AACQ,IAAA;AACR,IAAA;AACA,IAAA;AACaA,IAAAA;AACF,MAAA;AACX,IAAA;AACA,IAAA;AACA,IAAA;AACS,IAAA;AAEC,MAAA;AACE,QAAA;AACA,QAAA;AACI,QAAA;AACD,QAAA;AACE,QAAA;AACA,QAAA;AACD,QAAA;AACC,QAAA;AACA,QAAA;AACD,QAAA;AACZ,MAAA;AACQ,MAAA;AACR,IAAA;AACN,EAAA;AACO,EAAA;AACT;AF6PkB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/aidream/aidream/apps/shared/context-menu/dist/chunk-MBU4Y22O.cjs","sourcesContent":[null,"// src/design/catalog.ts — @ai-matrx/context-menu: EVERY OPTION A MENU CAN OFFER, as a palette.\n//\n// The builder's left column. Each entry is something the live menu shows somewhere today, taken\n// from the real menu's own lists in matrx-frontend (features/context-menu-v3):\n// - regroup/engine-rows.ts → the engine rows (`cm:*`)\n// - regroup/proposed-grouping.ts → the 13 \"grouped by what you get\" groups and their rules\n// - proposed/proposed-arrangement.ts → the three icon strips and the object-first submenus\n// - the page rows the demos show (notes, quiz row, table row, chat message)\n// Ids are the live menu's ids wherever one exists, so a saved design names the real row.\n// When the menu source moves into this package, this list is generated from it instead.\n\nimport type { MenuNode } from \"./model\";\n\nexport type CatalogGroup =\n | \"Clipboard\"\n | \"Edit\"\n | \"AI\"\n | \"Save & export\"\n | \"Share & publish\"\n | \"Organize\"\n | \"History & compare\"\n | \"Listen\"\n | \"Page: notes\"\n | \"Page: record\"\n | \"Page: chat message\"\n | \"Tools & admin\";\n\nexport interface CatalogOption {\n /** The live menu's id where one exists (`cm:copy`), else this catalog's own. */\n id: string;\n label: string;\n icon: string;\n group: CatalogGroup;\n shortcut?: string;\n hint?: string;\n danger?: boolean;\n ai?: boolean;\n /** \"Live\" = drawn by today's menu; \"Proposed\" = only in a proposal page so far. */\n status: \"live\" | \"proposed\";\n}\n\nconst o = (\n id: string,\n label: string,\n icon: string,\n group: CatalogGroup,\n extra: Partial<Omit<CatalogOption, \"id\" | \"label\" | \"icon\" | \"group\">> = {},\n): CatalogOption => ({ id, label, icon, group, status: \"live\", ...extra });\n\nexport const CATALOG: readonly CatalogOption[] = [\n // Clipboard — the verb strip\n o(\"cm:copy\", \"Copy\", \"Copy\", \"Clipboard\", { shortcut: \"⌘C\" }),\n o(\"cm:cut\", \"Cut\", \"Scissors\", \"Clipboard\", { shortcut: \"⌘X\" }),\n o(\"cm:paste\", \"Paste\", \"ClipboardPaste\", \"Clipboard\", { shortcut: \"⌘V\" }),\n o(\"cm:undo\", \"Undo\", \"Undo2\", \"Clipboard\", { shortcut: \"⌘Z\" }),\n o(\"cm:redo\", \"Redo\", \"Redo2\", \"Clipboard\", { shortcut: \"⇧⌘Z\" }),\n o(\"cm:find\", \"Find & Replace\", \"Search\", \"Clipboard\", { shortcut: \"⌘F\" }),\n o(\"cm:select-all\", \"Select All\", \"TextSelect\", \"Clipboard\", { shortcut: \"⌘A\" }),\n o(\"cm:copy-as\", \"Copy as…\", \"CopyPlus\", \"Clipboard\"),\n o(\"cm:json\", \"Copy as JSON\", \"Braces\", \"Clipboard\"),\n o(\"copy-html-page\", \"Copy as HTML page\", \"FileCode\", \"Clipboard\"),\n o(\"cm:insert-reference\", \"Insert / Copy reference…\", \"AtSign\", \"Clipboard\"),\n\n // Edit\n o(\"cm:save\", \"Save\", \"Save\", \"Edit\", { shortcut: \"⌘S\" }),\n o(\"duplicate\", \"Duplicate\", \"CopyPlus\", \"Edit\"),\n o(\"rename\", \"Rename\", \"PenLine\", \"Edit\"),\n o(\"edit\", \"Edit\", \"Pencil\", \"Edit\"),\n o(\"favorite\", \"Add to favorites\", \"Star\", \"Edit\"),\n o(\"archive\", \"Archive\", \"Archive\", \"Edit\", { danger: true }),\n o(\"cm:delete\", \"Delete\", \"Trash2\", \"Edit\", { danger: true }),\n\n // AI\n o(\"cm:chat\", \"Chat about this\", \"MessageSquare\", \"AI\", { ai: true }),\n o(\"cm:quick-actions\", \"Quick Actions\", \"Zap\", \"AI\", { ai: true }),\n o(\"cm:placement:ai-action\", \"AI Actions\", \"Sparkles\", \"AI\", { ai: true }),\n o(\"cm:placement:content-block\", \"Content blocks\", \"Blocks\", \"AI\", { ai: true }),\n o(\"cm:placement:organization-tool\", \"Org Items\", \"Building2\", \"AI\", { ai: true }),\n o(\"cm:placement:user-tool\", \"My Items\", \"User\", \"AI\", { ai: true }),\n o(\"cm:placement:quick-action\", \"Quick actions (placement)\", \"Zap\", \"AI\", { ai: true }),\n o(\"cm:placement:bound-agent\", \"Agents\", \"Bot\", \"AI\", { ai: true }),\n o(\"send-to-agent\", \"Send to agent…\", \"Send\", \"AI\", { ai: true }),\n o(\"continue-in-chat\", \"Continue in chat\", \"MessagesSquare\", \"AI\", { ai: true }),\n o(\"regenerate-response\", \"Regenerate\", \"RefreshCw\", \"AI\", { ai: true }),\n o(\"code-block-chart\", \"Make a chart\", \"ChartColumn\", \"AI\", { ai: true }),\n o(\"ai-improve\", \"Improve writing\", \"Wand2\", \"AI\", { ai: true, status: \"proposed\" }),\n o(\"ai-summarize\", \"Summarize\", \"ListCollapse\", \"AI\", { ai: true, status: \"proposed\" }),\n o(\"ai-explain\", \"Explain\", \"Lightbulb\", \"AI\", { ai: true, status: \"proposed\" }),\n o(\"ai-translate\", \"Translate…\", \"Languages\", \"AI\", { ai: true, status: \"proposed\" }),\n\n // Save & export\n o(\"cm:export\", \"Export\", \"FileOutput\", \"Save & export\"),\n o(\"cm:convert\", \"Convert to…\", \"ArrowRightLeft\", \"Save & export\"),\n o(\"save-as-file\", \"Save as file\", \"FileDown\", \"Save & export\"),\n o(\"download-pdf\", \"Download PDF\", \"FileText\", \"Save & export\"),\n o(\"download-docx\", \"Download Word\", \"FileType\", \"Save & export\"),\n o(\"download-html\", \"Download HTML\", \"FileCode2\", \"Save & export\"),\n o(\"print\", \"Print\", \"Printer\", \"Save & export\", { shortcut: \"⌘P\" }),\n\n // Share & publish\n o(\"cm:share\", \"Share…\", \"Share2\", \"Share & publish\"),\n o(\"share-link\", \"Copy share link\", \"Link\", \"Share & publish\"),\n o(\"send-google-doc\", \"Send to Google Docs\", \"FileUp\", \"Share & publish\"),\n o(\"email-to-me\", \"Email to me\", \"Mail\", \"Share & publish\"),\n o(\"html-preview\", \"Preview as web page\", \"Eye\", \"Share & publish\"),\n o(\"share-webpage\", \"Publish as web page\", \"Globe\", \"Share & publish\"),\n\n // Organize\n o(\"cm:attach\", \"Attach to…\", \"Paperclip\", \"Organize\"),\n o(\"set-context-value\", \"Set as context\", \"Pin\", \"Organize\"),\n o(\"pin-message\", \"Pin\", \"Pin\", \"Organize\"),\n o(\"notes-and-comments\", \"Notes & comments\", \"StickyNote\", \"Organize\"),\n o(\"move\", \"Move to folder…\", \"FolderInput\", \"Organize\"),\n o(\"tags\", \"Tags…\", \"Tags\", \"Organize\"),\n\n // History & compare\n o(\"cm:view-history\", \"View history\", \"History\", \"History & compare\"),\n o(\"edit-history\", \"Edit history\", \"GitCommitVertical\", \"History & compare\"),\n o(\"fork-at-message\", \"Fork from here\", \"GitBranch\", \"History & compare\"),\n o(\"cm:compare\", \"Compare…\", \"GitCompareArrows\", \"History & compare\"),\n o(\"compare-with-clipboard\", \"Compare with clipboard\", \"ClipboardList\", \"History & compare\"),\n o(\"set-compare-base\", \"Set as compare base\", \"Flag\", \"History & compare\"),\n\n // Listen\n o(\"cm:speak\", \"Read aloud\", \"Volume2\", \"Listen\"),\n o(\"cm:listen\", \"Dictate\", \"Mic\", \"Listen\"),\n o(\"tts-voice-settings\", \"Voice settings\", \"AudioLines\", \"Listen\"),\n\n // Page rows: notes\n o(\"notes:close-tab\", \"Close tab\", \"X\", \"Page: notes\"),\n o(\"notes:close-others\", \"Close other tabs\", \"PanelTopClose\", \"Page: notes\"),\n o(\"notes:export-md\", \"Export as Markdown\", \"FileDown\", \"Page: notes\"),\n o(\"notes:share-clipboard\", \"Copy to clipboard\", \"ClipboardCopy\", \"Page: notes\"),\n o(\"notes:convert-artifacts\", \"Convert to artifacts\", \"Shapes\", \"Page: notes\"),\n o(\"notes:trash\", \"Move to Trash\", \"Trash2\", \"Page: notes\", { danger: true }),\n\n // Page rows: a record (quiz row, table row)\n o(\"row:open\", \"Open\", \"SquareArrowOutUpRight\", \"Page: record\"),\n o(\"row:open-tab\", \"Open in new tab\", \"ExternalLink\", \"Page: record\"),\n o(\"row:take\", \"Take\", \"Play\", \"Page: record\"),\n o(\"row:results\", \"Results\", \"ChartBar\", \"Page: record\"),\n o(\"row:manage\", \"Manage\", \"Settings2\", \"Page: record\"),\n o(\"row:restore\", \"Restore\", \"ArchiveRestore\", \"Page: record\"),\n\n // Page rows: chat message\n o(\"regenerate-latest\", \"Regenerate latest\", \"RotateCcw\", \"Page: chat message\", { ai: true }),\n o(\"fork-and-regenerate\", \"Fork and regenerate\", \"GitFork\", \"Page: chat message\", { ai: true }),\n o(\"feedback-up\", \"Good response\", \"ThumbsUp\", \"Page: chat message\"),\n o(\"feedback-down\", \"Bad response\", \"ThumbsDown\", \"Page: chat message\"),\n\n // Tools & admin\n o(\"cm:x:surface\", \"This page (location, context, agents)\", \"MapPin\", \"Tools & admin\"),\n o(\"cm:admin\", \"Admin tools\", \"Shield\", \"Tools & admin\"),\n o(\"feedback\", \"Send feedback\", \"MessageSquareWarning\", \"Tools & admin\"),\n o(\"keyboard-shortcuts\", \"Keyboard shortcuts\", \"Keyboard\", \"Tools & admin\", { status: \"proposed\" }),\n];\n\nexport const CATALOG_GROUPS: readonly CatalogGroup[] = [...new Set(CATALOG.map((c) => c.group))];\n\nexport function catalogOption(id: string): CatalogOption | undefined {\n return CATALOG.find((c) => c.id === id);\n}\n\n/** Ready-made pieces: dropping one inserts the whole block. */\nexport interface CatalogTemplate {\n id: string;\n label: string;\n icon: string;\n build: () => MenuNode;\n}\n\nlet seq = 0;\nconst tid = (p: string) => `${p}_t${Date.now().toString(36)}${(seq++).toString(36)}`;\n\nfunction itemFrom(id: string): MenuNode {\n const c = catalogOption(id);\n return {\n id: tid(\"i\"),\n kind: \"item\",\n catalogId: id,\n label: c?.label ?? id,\n icon: c?.icon ?? \"Circle\",\n ...(c?.shortcut ? { shortcut: c.shortcut } : {}),\n ...(c?.danger ? { danger: true } : {}),\n ...(c?.ai ? { ai: true } : {}),\n };\n}\n\nconst strip = (label: string, ids: string[]): MenuNode => ({\n id: tid(\"s\"),\n kind: \"section\",\n label,\n layout: \"row\",\n children: ids.map(itemFrom),\n});\n\nconst sub = (label: string, icon: string, ids: string[]): MenuNode => ({\n id: tid(\"m\"),\n kind: \"submenu\",\n label,\n icon,\n children: ids.map(itemFrom),\n});\n\nexport const TEMPLATES: readonly CatalogTemplate[] = [\n { id: \"strip-thing\", label: \"Icon strip — a thing\", icon: \"Rows2\", build: () => strip(\"\", [\"cm:copy\", \"cm:share\", \"duplicate\", \"favorite\", \"save-as-file\", \"archive\"]) },\n { id: \"strip-edit\", label: \"Icon strip — editable text\", icon: \"Rows2\", build: () => strip(\"\", [\"cm:cut\", \"cm:copy\", \"cm:paste\", \"cm:undo\", \"cm:redo\", \"cm:find\"]) },\n { id: \"strip-read\", label: \"Icon strip — read-only text\", icon: \"Rows2\", build: () => strip(\"\", [\"cm:copy\", \"cm:speak\", \"cm:find\", \"cm:share\"]) },\n { id: \"sub-intelligence\", label: \"Intelligence ▸\", icon: \"Sparkles\", build: () => sub(\"Intelligence\", \"Sparkles\", [\"cm:chat\", \"cm:placement:ai-action\", \"cm:placement:bound-agent\", \"send-to-agent\"]) },\n { id: \"sub-save-share\", label: \"Save & share ▸\", icon: \"Share2\", build: () => sub(\"Save & share\", \"Share2\", [\"cm:copy-as\", \"save-as-file\", \"cm:convert\", \"share-webpage\", \"cm:share\"]) },\n { id: \"sub-organize\", label: \"Organize ▸\", icon: \"Tags\", build: () => sub(\"Organize\", \"Tags\", [\"cm:attach\", \"pin-message\", \"cm:view-history\", \"cm:compare\"]) },\n { id: \"sub-tools\", label: \"Tools ▸\", icon: \"Wrench\", build: () => sub(\"Tools\", \"Wrench\", [\"cm:speak\", \"cm:quick-actions\", \"feedback\", \"cm:x:surface\", \"cm:admin\"]) },\n];\n\n// ── placeholders ─────────────────────────────────────────────────────────────\n\n/** What a placeholder can hold. A catalog group holds that group's options the design has not placed. */\nexport interface SlotSource {\n id: string;\n label: string;\n /** The default placeholder wording (\"All other AI items\"). */\n phrase: string;\n icon: string;\n /** Filled by the live menu from data (agents, a page's own rows) — the preview cannot list them. */\n runtime?: boolean;\n}\n\nexport const SLOT_SOURCES: readonly SlotSource[] = [\n { id: \"any\", label: \"Everything not placed\", phrase: \"Everything else\", icon: \"Ellipsis\" },\n { id: \"agents\", label: \"Agents & shortcuts (library)\", phrase: \"All other agents\", icon: \"Bot\", runtime: true },\n { id: \"page-rows\", label: \"This page's own rows\", phrase: \"All of this page's actions\", icon: \"PanelTop\", runtime: true },\n ...CATALOG_GROUPS.map((g) => ({\n id: `group:${g}`,\n label: g,\n phrase: g === \"AI\" ? \"All other AI items\" : `All other ${g.toLowerCase()} options`,\n icon: CATALOG.find((c) => c.group === g)?.icon ?? \"Circle\",\n })),\n];\n\nexport function slotSource(id: string): SlotSource | undefined {\n return SLOT_SOURCES.find((s) => s.id === id);\n}\n\n/** The catalog options a placeholder stands for: its kind, minus every option the design already places. */\nexport function slotOptions(fills: string, placed: ReadonlySet<string>): CatalogOption[] {\n if (fills === \"agents\" || fills === \"page-rows\") return [];\n const pool = fills === \"any\" ? CATALOG : CATALOG.filter((c) => `group:${c.group}` === fills);\n return pool.filter((c) => !placed.has(c.id));\n}\n","// src/design/MenuPreview.tsx — @ai-matrx/context-menu: DRAWS A DESIGN AS THE MENU IT DESCRIBES.\n//\n// The builder's live preview and its right-click test menu. It draws through THE ONE menu renderer\n// (`designed/MenuPanel`) that the live menu uses — the design goes in as `previewNodes` instead of\n// bound rows — so what the designer sees is what opens. Icons come from the host (`renderIcon`).\n\nimport { useMemo, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { isContainer, type MenuDesign, type MenuItemNode, type MenuNode } from \"./model\";\nimport { previewNodes, type AvailabilityPreview } from \"./preview-nodes\";\nimport { MenuPanel, type MenuPanelEnv } from \"../designed/MenuPanel\";\nimport type { ResolvedHit } from \"../designed/bindings\";\n\nexport type { AvailabilityPreview } from \"./preview-nodes\";\n\nexport interface MenuPreviewProps {\n design: MenuDesign;\n renderIcon: (name: string, className?: string) => ReactNode;\n /** The entry highlighted as \"selected in the builder\". */\n highlightId?: string | null;\n /** A click on any entry. */\n onSelectNode?: (id: string) => void;\n /** A click on an item (the menu's action). */\n onPick?: (item: MenuItemNode) => void;\n /** \"unavailable\": every entry with an unavailable rule shows as that rule says. Default \"available\". */\n availability?: AvailabilityPreview;\n className?: string;\n style?: CSSProperties;\n}\n\n// ── search ───────────────────────────────────────────────────────────────────\n\n/** The words an entry answers to: its label, its hint and its keywords. */\nfunction searchable(node: MenuNode): string {\n if (node.kind === \"divider\" || node.kind === \"search\") return \"\";\n const hint = node.kind === \"item\" || node.kind === \"submenu\" ? (node.hint ?? \"\") : \"\";\n return [node.label, hint, ...(node.keywords ?? [])].join(\" \").toLowerCase();\n}\n\nexport interface Hit {\n node: MenuItemNode;\n /** Where it lives (\"Intelligence › AI Actions\"). */\n path: string;\n}\n\n/** While searching: every matching item, wherever it sits, with the submenus it is inside. */\nexport function searchMenu(nodes: readonly MenuNode[], q: string, trail: string[] = [], out: Hit[] = []): Hit[] {\n const words = q.split(/\\s+/).filter(Boolean);\n for (const n of nodes) {\n if (n.kind === \"item\") {\n const text = `${searchable(n)} ${trail.join(\" \").toLowerCase()}`;\n if (words.every((w) => text.includes(w))) out.push({ node: n, path: trail.join(\" › \") });\n } else if (isContainer(n)) {\n searchMenu(n.children, q, n.kind === \"submenu\" ? [...trail, n.label] : n.label ? [...trail, n.label] : trail, out);\n }\n }\n return out;\n}\n\n// ── the menu ─────────────────────────────────────────────────────────────────\n\nexport function MenuPreview({ design, renderIcon, highlightId, onSelectNode, onPick, availability = \"available\", className, style }: MenuPreviewProps) {\n const [query, setQuery] = useState(\"\");\n const nodes = useMemo(() => previewNodes(design, availability), [design, availability]);\n const env: MenuPanelEnv = {\n look: design.look,\n renderIcon,\n flyout: \"absolute\",\n highlightId,\n onSelectNode,\n onActivate: (item) => {\n if (item.source) onPick?.(item.source);\n },\n query,\n setQuery,\n search: (q): ResolvedHit[] =>\n searchMenu(design.nodes, q).map((h) => ({\n node: {\n kind: \"item\",\n id: h.node.id,\n designId: h.node.id,\n label: h.node.label,\n ...(h.node.icon ? { icon: { name: h.node.icon } } : {}),\n ...(h.node.shortcut ? { shortcut: h.node.shortcut } : {}),\n disabled: Boolean(h.node.disabled),\n ...(h.node.danger ? { danger: true } : {}),\n ...(h.node.ai ? { ai: true } : {}),\n source: h.node,\n },\n path: h.path,\n })),\n };\n return <MenuPanel nodes={nodes} env={env} {...(className ? { className } : {})} {...(style ? { style } : {})} />;\n}\n","// src/design/preview-nodes.ts — @ai-matrx/context-menu: A DESIGN AS THE BUILDER PREVIEWS IT.\n//\n// The builder and the live menu draw through ONE renderer (`designed/MenuPanel`). The live menu\n// feeds it the design bound to real rows (`designed/bindings`); the builder feeds it this: the\n// design as drawn, every entry kept, placeholders shown as dashed rows, and — when previewing\n// \"unavailable\" — each entry as its own unavailable rule says.\n\nimport { slotOptions, slotSource } from \"./catalog\";\nimport { isContainer, placedCatalogIds, type MenuDesign, type MenuItemNode, type MenuNode, type MenuSlotNode } from \"./model\";\nimport type { ResolvedItem, ResolvedNode } from \"../designed/types\";\n\n/** Preview what the person sees when some entries are unavailable (each by its own rule). */\nexport type AvailabilityPreview = \"available\" | \"unavailable\";\n\nfunction shown(node: MenuNode, availability: AvailabilityPreview): { hidden: boolean; greyed: boolean } {\n if (availability === \"available\" || !node.unavailableWhen) {\n return { hidden: false, greyed: node.kind === \"item\" && Boolean(node.disabled) };\n }\n const mode = node.unavailable ?? \"disable\";\n if (mode === \"hide\") return { hidden: true, greyed: false };\n if (mode === \"required\") return { hidden: false, greyed: false };\n return { hidden: false, greyed: true };\n}\n\nfunction item(node: MenuItemNode, greyed: boolean): ResolvedItem {\n return {\n kind: \"item\",\n id: node.id,\n designId: node.id,\n label: node.label,\n ...(node.icon ? { icon: { name: node.icon } } : {}),\n ...(node.shortcut ? { shortcut: node.shortcut } : {}),\n ...(node.hint ? { hint: node.hint } : {}),\n ...(node.keywords ? { keywords: node.keywords } : {}),\n disabled: greyed,\n ...(greyed && node.unavailableWhen ? { disabledReason: node.unavailableWhen } : {}),\n ...(node.danger ? { danger: true } : {}),\n ...(node.ai ? { ai: true } : {}),\n ...(node.tone ? { tone: node.tone } : {}),\n ...(node.toneLabel ? { toneLabel: true } : {}),\n ...(node.toggle ? { toggle: true, checked: Boolean(node.defaultOn) } : {}),\n ...(node.switch ? { switch: true, checked: Boolean(node.defaultOn) } : {}),\n source: node,\n };\n}\n\nfunction slot(node: MenuSlotNode, placed: ReadonlySet<string>): ResolvedNode {\n const source = slotSource(node.fills);\n const icon = node.icon ?? source?.icon ?? \"Ellipsis\";\n if (node.shape === \"feature\") {\n const n = Math.max(0, Math.min(12, node.featureItems ?? 4));\n return {\n kind: \"section\",\n id: node.id,\n designId: node.id,\n label: node.label || \"This feature\",\n layout: \"list\",\n feature: true,\n placeholder: true,\n children: [\n ...Array.from({ length: n }, (_, i): ResolvedNode => ({ kind: \"item\", id: `${node.id}:${i}`, designId: node.id, label: `Feature option ${i + 1}`, icon: { name: \"Circle\" }, disabled: false, placeholder: true })),\n {\n kind: \"submenu\",\n id: `${node.id}:more`,\n designId: node.id,\n label: \"More\",\n icon: { name: \"Ellipsis\" },\n disabled: false,\n placeholder: true,\n children: [{ kind: \"note\", id: `${node.id}:more-note`, text: \"The rest of this feature's options, filled at runtime\", tone: \"runtime\" }],\n },\n ],\n };\n }\n const options = slotOptions(node.fills, placed);\n const count = source?.runtime ? \"live\" : String(options.length);\n if (node.shape === \"items\") {\n return { kind: \"item\", id: node.id, designId: node.id, label: node.label, icon: { name: icon }, disabled: false, placeholder: true, count };\n }\n return {\n kind: \"submenu\",\n id: node.id,\n designId: node.id,\n label: node.label,\n icon: { name: icon },\n disabled: false,\n placeholder: true,\n count,\n children: source?.runtime\n ? [{ kind: \"note\", id: `${node.id}:note`, text: `Filled at runtime: ${source.label}`, tone: \"runtime\" }]\n : options.length === 0\n ? [{ kind: \"note\", id: `${node.id}:note`, text: \"Nothing left — every option of this kind is placed\", tone: \"empty\" }]\n : options.map((o) => ({\n kind: \"item\" as const,\n id: `slot-${node.id}-${o.id}`,\n label: o.label,\n icon: { name: o.icon },\n disabled: false,\n ...(o.shortcut ? { shortcut: o.shortcut } : {}),\n ...(o.ai ? { ai: true } : {}),\n ...(o.danger ? { danger: true } : {}),\n source: { id: `slot-${node.id}-${o.id}`, kind: \"item\", label: o.label, icon: o.icon, catalogId: o.id },\n })),\n };\n}\n\nfunction walk(nodes: readonly MenuNode[], placed: ReadonlySet<string>, availability: AvailabilityPreview): ResolvedNode[] {\n const out: ResolvedNode[] = [];\n for (const n of nodes) {\n const { hidden, greyed } = shown(n, availability);\n if (hidden) continue;\n if (n.kind === \"divider\") out.push({ kind: \"divider\", id: n.id, designId: n.id });\n else if (n.kind === \"search\") out.push({ kind: \"search\", id: n.id, designId: n.id, placeholder: n.placeholder, autofocus: n.autofocus });\n else if (n.kind === \"item\") out.push(item(n, greyed));\n else if (n.kind === \"slot\") out.push(slot(n, placed));\n else if (n.kind === \"submenu\") {\n out.push({\n kind: \"submenu\",\n id: n.id,\n designId: n.id,\n label: n.label,\n ...(n.icon ? { icon: { name: n.icon } } : {}),\n ...(n.hint ? { hint: n.hint } : {}),\n ...(n.tone ? { tone: n.tone } : {}),\n ...(n.toneLabel ? { toneLabel: true } : {}),\n disabled: greyed,\n children: walk(n.children, placed, availability),\n });\n } else if (isContainer(n)) {\n out.push({ kind: \"section\", id: n.id, designId: n.id, label: n.label, layout: n.layout ?? \"list\", ...(n.collapsible ? { collapsible: true } : {}), children: walk(n.children, placed, availability) });\n }\n }\n return out;\n}\n\n/** The design as the builder draws it (the old \"search box at the top\" look switch included). */\nexport function previewNodes(design: MenuDesign, availability: AvailabilityPreview = \"available\"): ResolvedNode[] {\n const placed = placedCatalogIds(design.nodes);\n const nodes = walk(design.nodes, placed, availability);\n const hasSearch = design.nodes.some((n) => n.kind === \"search\");\n if (!hasSearch && design.look.search) nodes.unshift({ kind: \"search\", id: \"look-search\", placeholder: \"Type to filter…\", autofocus: true });\n return nodes;\n}\n"]}