@ai-matrx/context-menu 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,28 @@
1
1
  "use client";
2
2
 
3
3
  // src/design/model.ts
4
+ var MENU_TONES = [
5
+ "default",
6
+ "primary",
7
+ "success",
8
+ "warning",
9
+ "info",
10
+ "destructive",
11
+ "sky",
12
+ "blue",
13
+ "indigo",
14
+ "violet",
15
+ "fuchsia",
16
+ "rose",
17
+ "orange",
18
+ "amber",
19
+ "lime",
20
+ "emerald",
21
+ "teal",
22
+ "cyan",
23
+ "slate"
24
+ ];
25
+ var UNAVAILABLE_MODES = ["disable", "hide", "required"];
4
26
  var MENU_SURFACES = [
5
27
  "text-selection",
6
28
  "editable-field",
@@ -312,54 +334,92 @@ function slotOptions(fills, placed) {
312
334
  }
313
335
 
314
336
  // src/design/MenuPreview.tsx
315
- import { useMemo, useState } from "react";
337
+ import { useEffect, useMemo, useRef, useState } from "react";
316
338
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
317
- function matches(node, q) {
318
- if (!q) return true;
319
- if (node.kind === "divider") return false;
320
- if (node.kind === "item" || node.kind === "slot") return node.label.toLowerCase().includes(q);
321
- return node.label.toLowerCase().includes(q) || node.children.some((c) => matches(c, q));
339
+ function searchable(node) {
340
+ if (node.kind === "divider" || node.kind === "search") return "";
341
+ const hint = node.kind === "item" || node.kind === "submenu" ? node.hint ?? "" : "";
342
+ return [node.label, hint, ...node.keywords ?? []].join(" ").toLowerCase();
322
343
  }
323
- function flatten(nodes, q, out = []) {
344
+ function searchMenu(nodes, q, trail = [], out = []) {
345
+ const words = q.split(/\s+/).filter(Boolean);
324
346
  for (const n of nodes) {
325
- if (n.kind === "item" && matches(n, q)) out.push(n);
326
- else if (isContainer(n)) flatten(n.children, q, out);
347
+ if (n.kind === "item") {
348
+ const text = `${searchable(n)} ${trail.join(" ").toLowerCase()}`;
349
+ if (words.every((w) => text.includes(w))) out.push({ node: n, path: trail.join(" \u203A ") });
350
+ } else if (isContainer(n)) {
351
+ searchMenu(n.children, q, n.kind === "submenu" ? [...trail, n.label] : n.label ? [...trail, n.label] : trail, out);
352
+ }
327
353
  }
328
354
  return out;
329
355
  }
330
- function MenuPreview({ design, renderIcon, highlightId, onSelectNode, onPick, className, style }) {
356
+ function MenuPreview({ design, renderIcon, highlightId, onSelectNode, onPick, availability = "available", className, style }) {
331
357
  const [query, setQuery] = useState("");
332
358
  const q = query.trim().toLowerCase();
333
359
  const { look } = design;
334
- const results = useMemo(() => q ? flatten(design.nodes, q) : null, [design.nodes, q]);
335
360
  const placed = useMemo(() => placedCatalogIds(design.nodes), [design.nodes]);
336
- const ctx = { design, renderIcon, highlightId, onSelectNode, onPick, placed };
337
- return /* @__PURE__ */ jsxs(
338
- "div",
339
- {
340
- role: "menu",
341
- "data-density": look.density,
342
- className: `cm-menu ${className ?? ""}`,
343
- style: { width: look.width, ...style },
344
- children: [
345
- look.search && /* @__PURE__ */ jsxs("div", { className: "cm-search", children: [
346
- renderIcon("Search", "cm-icon"),
347
- /* @__PURE__ */ jsx("input", { value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Type to filter\u2026", "aria-label": "Filter menu" })
348
- ] }),
349
- results ? results.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "No matches" }) : results.map((n) => /* @__PURE__ */ jsx(Item, { node: n, ctx }, n.id)) : design.nodes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Empty menu \u2014 drag options in" }) : /* @__PURE__ */ jsx(Nodes, { nodes: design.nodes, ctx })
350
- ]
351
- }
352
- );
361
+ const ctx = { design, renderIcon, highlightId, onSelectNode, onPick, placed, availability, query, setQuery };
362
+ const searchAt = design.nodes.findIndex((n) => n.kind === "search");
363
+ const topSearch = searchAt < 0 && look.search ? { id: "look-search", kind: "search", placeholder: "Type to filter\u2026", autofocus: true } : null;
364
+ const hits = useMemo(() => q ? searchMenu(design.nodes, q) : null, [design.nodes, q]);
365
+ const above = searchAt >= 0 ? design.nodes.slice(0, searchAt) : [];
366
+ return /* @__PURE__ */ jsxs("div", { role: "menu", "data-density": look.density, className: `cm-menu ${className ?? ""}`, style: { width: look.width, ...style }, children: [
367
+ topSearch && /* @__PURE__ */ jsx(SearchBox, { node: topSearch, ctx }),
368
+ design.nodes.length === 0 && !topSearch ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Empty menu \u2014 drag options in" }) : hits ? /* @__PURE__ */ jsxs(Fragment, { children: [
369
+ /* @__PURE__ */ jsx(Nodes, { nodes: above, ctx }),
370
+ searchAt >= 0 && /* @__PURE__ */ jsx(SearchBox, { node: design.nodes[searchAt], ctx }),
371
+ hits.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "No matches" }) : hits.map((h) => /* @__PURE__ */ jsx(Item, { node: h.node, ctx, path: h.path }, h.node.id))
372
+ ] }) : /* @__PURE__ */ jsx(Nodes, { nodes: design.nodes, ctx })
373
+ ] });
374
+ }
375
+ function SearchBox({ node, ctx }) {
376
+ const ref = useRef(null);
377
+ useEffect(() => {
378
+ if (node.autofocus) ref.current?.focus({ preventScroll: true });
379
+ }, [node.autofocus]);
380
+ return /* @__PURE__ */ jsxs("div", { className: `cm-search ${ctx.highlightId === node.id ? "cm-hl" : ""}`, onClick: () => ctx.onSelectNode?.(node.id), children: [
381
+ ctx.renderIcon("Search", "cm-icon"),
382
+ /* @__PURE__ */ jsx(
383
+ "input",
384
+ {
385
+ ref,
386
+ value: ctx.query,
387
+ onChange: (e) => ctx.setQuery(e.target.value),
388
+ onKeyDown: (e) => {
389
+ if (e.key === "Escape" && ctx.query) {
390
+ e.stopPropagation();
391
+ ctx.setQuery("");
392
+ }
393
+ },
394
+ placeholder: node.placeholder || "Type to filter\u2026",
395
+ "aria-label": "Filter menu"
396
+ }
397
+ )
398
+ ] });
399
+ }
400
+ function shown(node, ctx) {
401
+ if (ctx.availability === "available" || !node.unavailableWhen) {
402
+ return { hidden: false, greyed: node.kind === "item" && Boolean(node.disabled) };
403
+ }
404
+ const mode = node.unavailable ?? "disable";
405
+ if (mode === "hide") return { hidden: true, greyed: false };
406
+ if (mode === "required") return { hidden: false, greyed: false };
407
+ return { hidden: false, greyed: true };
353
408
  }
354
409
  function Nodes({ nodes, ctx }) {
355
410
  return /* @__PURE__ */ jsx(Fragment, { children: nodes.map((n) => {
411
+ if (shown(n, ctx).hidden) return null;
356
412
  if (n.kind === "divider") return /* @__PURE__ */ jsx("div", { role: "separator", className: "cm-divider" }, n.id);
357
413
  if (n.kind === "item") return /* @__PURE__ */ jsx(Item, { node: n, ctx }, n.id);
358
414
  if (n.kind === "submenu") return /* @__PURE__ */ jsx(Submenu, { node: n, ctx }, n.id);
359
- if (n.kind === "slot") return /* @__PURE__ */ jsx(Slot, { node: n, ctx }, n.id);
415
+ if (n.kind === "slot") return n.shape === "feature" ? /* @__PURE__ */ jsx(FeatureSlot, { node: n, ctx }, n.id) : /* @__PURE__ */ jsx(Slot, { node: n, ctx }, n.id);
416
+ if (n.kind === "search") return /* @__PURE__ */ jsx(SearchBox, { node: n, ctx }, n.id);
360
417
  return /* @__PURE__ */ jsx(Section, { node: n, ctx }, n.id);
361
418
  }) });
362
419
  }
420
+ function toneAttrs(tone, toneLabel) {
421
+ return tone && tone !== "default" ? { "data-tone": tone, ...toneLabel ? { "data-tone-label": "" } : {} } : {};
422
+ }
363
423
  function Section({ node, ctx }) {
364
424
  const [open, setOpen] = useState(!node.collapsible);
365
425
  const layout = node.layout ?? "list";
@@ -381,11 +441,12 @@ function Section({ node, ctx }) {
381
441
  }
382
442
  ),
383
443
  open && /* @__PURE__ */ jsx("div", { className: "cm-section-body", children: layout === "list" ? /* @__PURE__ */ jsx(Nodes, { nodes: node.children, ctx }) : node.children.map(
384
- (c) => c.kind === "item" ? /* @__PURE__ */ jsx(Tile, { node: c, ctx }, c.id) : /* @__PURE__ */ jsx(Nodes, { nodes: [c], ctx }, c.id)
444
+ (c) => shown(c, ctx).hidden ? null : c.kind === "item" ? /* @__PURE__ */ jsx(Tile, { node: c, ctx }, c.id) : /* @__PURE__ */ jsx(Nodes, { nodes: [c], ctx }, c.id)
385
445
  ) })
386
446
  ] });
387
447
  }
388
448
  function Tile({ node, ctx }) {
449
+ const { greyed } = shown(node, ctx);
389
450
  return (
390
451
  // ui-exception: a menu row of the menu component itself
391
452
  /* @__PURE__ */ jsx(
@@ -394,7 +455,8 @@ function Tile({ node, ctx }) {
394
455
  type: "button",
395
456
  role: "menuitem",
396
457
  title: node.label + (node.shortcut ? ` ${node.shortcut}` : ""),
397
- disabled: node.disabled,
458
+ disabled: greyed,
459
+ ...toneAttrs(node.tone, node.toneLabel),
398
460
  className: `cm-tile ${node.danger ? "cm-danger" : ""} ${node.ai ? "cm-ai" : ""} ${ctx.highlightId === node.id ? "cm-hl" : ""}`,
399
461
  onClick: () => {
400
462
  ctx.onSelectNode?.(node.id);
@@ -405,31 +467,37 @@ function Tile({ node, ctx }) {
405
467
  )
406
468
  );
407
469
  }
408
- function Item({ node, ctx }) {
470
+ function Item({ node, ctx, path }) {
409
471
  const { look } = ctx.design;
410
- const [checked, setChecked] = useState(false);
472
+ const [checked, setChecked] = useState(Boolean(node.defaultOn));
473
+ useEffect(() => setChecked(Boolean(node.defaultOn)), [node.defaultOn]);
474
+ const { greyed } = shown(node, ctx);
475
+ const settable = node.toggle || node.switch;
476
+ const hint = path || (look.showHints ? node.hint : void 0);
411
477
  return (
412
478
  // ui-exception: a menu row of the menu component itself
413
479
  /* @__PURE__ */ jsxs(
414
480
  "button",
415
481
  {
416
482
  type: "button",
417
- role: node.toggle ? "menuitemcheckbox" : "menuitem",
418
- "aria-checked": node.toggle ? checked : void 0,
419
- disabled: node.disabled,
483
+ role: settable ? "menuitemcheckbox" : "menuitem",
484
+ "aria-checked": settable ? checked : void 0,
485
+ disabled: greyed,
486
+ ...toneAttrs(node.tone, node.toneLabel),
420
487
  className: `cm-item ${node.danger ? "cm-danger" : ""} ${node.ai ? "cm-ai" : ""} ${ctx.highlightId === node.id ? "cm-hl" : ""}`,
421
488
  onClick: () => {
422
489
  ctx.onSelectNode?.(node.id);
423
- if (node.toggle) setChecked((c) => !c);
490
+ if (settable) setChecked((c) => !c);
424
491
  ctx.onPick?.(node);
425
492
  },
426
493
  children: [
427
494
  look.showIcons && /* @__PURE__ */ jsx("span", { className: "cm-icon-slot", children: node.toggle ? checked ? ctx.renderIcon("Check", "cm-icon") : null : node.icon ? ctx.renderIcon(node.icon, "cm-icon") : null }),
428
495
  /* @__PURE__ */ jsxs("span", { className: "cm-text", children: [
429
496
  /* @__PURE__ */ jsx("span", { className: "cm-label", children: node.label }),
430
- look.showHints && node.hint && /* @__PURE__ */ jsx("span", { className: "cm-hint", children: node.hint })
497
+ hint && /* @__PURE__ */ jsx("span", { className: "cm-hint", children: hint })
431
498
  ] }),
432
- look.showShortcuts && node.shortcut && /* @__PURE__ */ jsx("kbd", { className: "cm-shortcut", children: node.shortcut })
499
+ look.showShortcuts && node.shortcut && !node.switch && /* @__PURE__ */ jsx("kbd", { className: "cm-shortcut", children: node.shortcut }),
500
+ node.switch && /* @__PURE__ */ jsx("span", { className: "cm-switch", "data-on": checked ? "" : void 0, "aria-hidden": "true" })
433
501
  ]
434
502
  }
435
503
  )
@@ -439,35 +507,46 @@ function Submenu({ node, ctx }) {
439
507
  const { look } = ctx.design;
440
508
  const [open, setOpen] = useState(false);
441
509
  const [side, setSide] = useState("right");
442
- const show = (el) => {
443
- setSide(flyoutSide(el, look.width));
444
- setOpen(true);
445
- };
446
- return /* @__PURE__ */ jsxs("div", { className: "cm-sub", onMouseEnter: (e) => show(e.currentTarget), onMouseLeave: () => setOpen(false), children: [
447
- /* @__PURE__ */ jsxs(
448
- "button",
449
- {
450
- type: "button",
451
- role: "menuitem",
452
- "aria-haspopup": "menu",
453
- "aria-expanded": open,
454
- className: `cm-item ${ctx.highlightId === node.id ? "cm-hl" : ""}`,
455
- onClick: () => {
456
- ctx.onSelectNode?.(node.id);
457
- setOpen(true);
458
- },
459
- children: [
460
- look.showIcons && /* @__PURE__ */ jsx("span", { className: "cm-icon-slot", children: node.icon ? ctx.renderIcon(node.icon, "cm-icon") : null }),
461
- /* @__PURE__ */ jsxs("span", { className: "cm-text", children: [
462
- /* @__PURE__ */ jsx("span", { className: "cm-label", children: node.label }),
463
- look.showHints && node.hint && /* @__PURE__ */ jsx("span", { className: "cm-hint", children: node.hint })
464
- ] }),
465
- ctx.renderIcon("ChevronRight", "cm-icon cm-chev")
466
- ]
467
- }
468
- ),
469
- open && /* @__PURE__ */ jsx("div", { role: "menu", className: "cm-menu cm-flyout", "data-side": side, "data-density": look.density, style: { width: look.width }, children: node.children.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Empty submenu" }) : /* @__PURE__ */ jsx(Nodes, { nodes: node.children, ctx }) })
470
- ] });
510
+ const { greyed } = shown(node, ctx);
511
+ return /* @__PURE__ */ jsxs(
512
+ "div",
513
+ {
514
+ className: "cm-sub",
515
+ onMouseEnter: (e) => {
516
+ if (greyed) return;
517
+ setSide(flyoutSide(e.currentTarget, look.width));
518
+ setOpen(true);
519
+ },
520
+ onMouseLeave: () => setOpen(false),
521
+ children: [
522
+ /* @__PURE__ */ jsxs(
523
+ "button",
524
+ {
525
+ type: "button",
526
+ role: "menuitem",
527
+ "aria-haspopup": "menu",
528
+ "aria-expanded": open,
529
+ disabled: greyed,
530
+ ...toneAttrs(node.tone, node.toneLabel),
531
+ className: `cm-item ${ctx.highlightId === node.id ? "cm-hl" : ""}`,
532
+ onClick: () => {
533
+ ctx.onSelectNode?.(node.id);
534
+ setOpen(true);
535
+ },
536
+ children: [
537
+ look.showIcons && /* @__PURE__ */ jsx("span", { className: "cm-icon-slot", children: node.icon ? ctx.renderIcon(node.icon, "cm-icon") : null }),
538
+ /* @__PURE__ */ jsxs("span", { className: "cm-text", children: [
539
+ /* @__PURE__ */ jsx("span", { className: "cm-label", children: node.label }),
540
+ look.showHints && node.hint && /* @__PURE__ */ jsx("span", { className: "cm-hint", children: node.hint })
541
+ ] }),
542
+ ctx.renderIcon("ChevronRight", "cm-icon cm-chev")
543
+ ]
544
+ }
545
+ ),
546
+ open && /* @__PURE__ */ jsx("div", { role: "menu", className: "cm-menu cm-flyout", "data-side": side, "data-density": look.density, style: { width: look.width }, children: node.children.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Empty submenu" }) : /* @__PURE__ */ jsx(Nodes, { nodes: node.children, ctx }) })
547
+ ]
548
+ }
549
+ );
471
550
  }
472
551
  function flyoutSide(row, width) {
473
552
  const r = row.getBoundingClientRect();
@@ -476,37 +555,42 @@ function flyoutSide(row, width) {
476
555
  if (r.left - width - 8 >= bounds.left) return "left";
477
556
  return "below";
478
557
  }
479
- function Slot({ node, ctx }) {
558
+ function SlotRow({ node, ctx, label, icon, count, more, onOpen }) {
480
559
  const { look } = ctx.design;
481
560
  const source = slotSource(node.fills);
482
- const options = slotOptions(node.fills, ctx.placed);
483
- const [open, setOpen] = useState(false);
484
- const [side, setSide] = useState("right");
485
- const count = source?.runtime ? "live" : String(options.length);
486
- const row = (
561
+ return (
487
562
  // ui-exception: a menu row of the menu component itself
488
563
  /* @__PURE__ */ jsxs(
489
564
  "button",
490
565
  {
491
566
  type: "button",
492
567
  role: "menuitem",
493
- "aria-haspopup": node.shape === "submenu" ? "menu" : void 0,
568
+ "aria-haspopup": more ? "menu" : void 0,
494
569
  title: source ? `Placeholder: ${source.label}` : "Placeholder",
495
570
  className: `cm-item cm-slot ${ctx.highlightId === node.id ? "cm-hl" : ""}`,
496
571
  onClick: () => {
497
572
  ctx.onSelectNode?.(node.id);
498
- if (node.shape === "submenu") setOpen(true);
573
+ onOpen?.();
499
574
  },
500
575
  children: [
501
- look.showIcons && /* @__PURE__ */ jsx("span", { className: "cm-icon-slot", children: ctx.renderIcon(node.icon ?? source?.icon ?? "Ellipsis", "cm-icon") }),
502
- /* @__PURE__ */ jsx("span", { className: "cm-text", children: /* @__PURE__ */ jsx("span", { className: "cm-label", children: node.label }) }),
503
- /* @__PURE__ */ jsx("span", { className: "cm-count", children: count }),
504
- node.shape === "submenu" && ctx.renderIcon("ChevronRight", "cm-icon cm-chev")
576
+ look.showIcons && /* @__PURE__ */ jsx("span", { className: "cm-icon-slot", children: ctx.renderIcon(icon, "cm-icon") }),
577
+ /* @__PURE__ */ jsx("span", { className: "cm-text", children: /* @__PURE__ */ jsx("span", { className: "cm-label", children: label }) }),
578
+ count && /* @__PURE__ */ jsx("span", { className: "cm-count", children: count }),
579
+ more && ctx.renderIcon("ChevronRight", "cm-icon cm-chev")
505
580
  ]
506
581
  }
507
582
  )
508
583
  );
509
- if (node.shape === "items") return row;
584
+ }
585
+ function Slot({ node, ctx }) {
586
+ const { look } = ctx.design;
587
+ const source = slotSource(node.fills);
588
+ const options = slotOptions(node.fills, ctx.placed);
589
+ const [open, setOpen] = useState(false);
590
+ const [side, setSide] = useState("right");
591
+ const icon = node.icon ?? source?.icon ?? "Ellipsis";
592
+ const count = source?.runtime ? "live" : String(options.length);
593
+ if (node.shape === "items") return /* @__PURE__ */ jsx(SlotRow, { node, ctx, label: node.label, icon, count });
510
594
  return /* @__PURE__ */ jsxs(
511
595
  "div",
512
596
  {
@@ -517,24 +601,60 @@ function Slot({ node, ctx }) {
517
601
  },
518
602
  onMouseLeave: () => setOpen(false),
519
603
  children: [
520
- row,
604
+ /* @__PURE__ */ jsx(SlotRow, { node, ctx, label: node.label, icon, count, more: true, onOpen: () => setOpen(true) }),
521
605
  open && /* @__PURE__ */ jsx("div", { role: "menu", className: "cm-menu cm-flyout", "data-side": side, "data-density": look.density, style: { width: look.width }, children: source?.runtime ? /* @__PURE__ */ jsxs("div", { className: "cm-runtime", children: [
522
606
  "Filled at runtime: ",
523
607
  source.label
524
- ] }) : options.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Nothing left \u2014 every option of this kind is placed" }) : options.map((o2) => /* @__PURE__ */ jsx(Item, { node: { id: `slot-${node.id}-${o2.id}`, kind: "item", label: o2.label, icon: o2.icon, catalogId: o2.id, ...o2.shortcut ? { shortcut: o2.shortcut } : {}, ...o2.ai ? { ai: true } : {}, ...o2.danger ? { danger: true } : {} }, ctx }, o2.id)) })
608
+ ] }) : options.length === 0 ? /* @__PURE__ */ jsx("div", { className: "cm-empty", children: "Nothing left \u2014 every option of this kind is placed" }) : options.map((o2) => /* @__PURE__ */ jsx(
609
+ Item,
610
+ {
611
+ node: { id: `slot-${node.id}-${o2.id}`, kind: "item", label: o2.label, icon: o2.icon, catalogId: o2.id, ...o2.shortcut ? { shortcut: o2.shortcut } : {}, ...o2.ai ? { ai: true } : {}, ...o2.danger ? { danger: true } : {} },
612
+ ctx
613
+ },
614
+ o2.id
615
+ )) })
525
616
  ]
526
617
  }
527
618
  );
528
619
  }
620
+ function FeatureSlot({ node, ctx }) {
621
+ const { look } = ctx.design;
622
+ const n = Math.max(0, Math.min(12, node.featureItems ?? 4));
623
+ const [open, setOpen] = useState(false);
624
+ const [side, setSide] = useState("right");
625
+ return /* @__PURE__ */ jsxs("div", { className: `cm-section ${ctx.highlightId === node.id ? "cm-hl" : ""}`, "data-layout": "list", children: [
626
+ /* @__PURE__ */ jsx("button", { type: "button", className: "cm-section-label cm-slot-label", onClick: () => ctx.onSelectNode?.(node.id), children: node.label || "This feature" }),
627
+ /* @__PURE__ */ jsxs("div", { className: "cm-section-body", children: [
628
+ Array.from({ length: n }, (_, i) => /* @__PURE__ */ jsx(SlotRow, { node, ctx, label: `Feature option ${i + 1}`, icon: "Circle" }, i)),
629
+ /* @__PURE__ */ jsxs(
630
+ "div",
631
+ {
632
+ className: "cm-sub",
633
+ onMouseEnter: (e) => {
634
+ setSide(flyoutSide(e.currentTarget, look.width));
635
+ setOpen(true);
636
+ },
637
+ onMouseLeave: () => setOpen(false),
638
+ children: [
639
+ /* @__PURE__ */ jsx(SlotRow, { node, ctx, label: "More", icon: "Ellipsis", more: true, onOpen: () => setOpen(true) }),
640
+ open && /* @__PURE__ */ jsx("div", { role: "menu", className: "cm-menu cm-flyout", "data-side": side, "data-density": look.density, style: { width: look.width }, children: /* @__PURE__ */ jsx("div", { className: "cm-runtime", children: "The rest of this feature's options, filled at runtime" }) })
641
+ ]
642
+ }
643
+ )
644
+ ] })
645
+ ] });
646
+ }
529
647
  export {
530
648
  CATALOG,
531
649
  CATALOG_GROUPS,
532
650
  DEFAULT_LOOK,
533
651
  DESIGN_FORMAT,
534
652
  MENU_SURFACES,
653
+ MENU_TONES,
535
654
  MenuPreview,
536
655
  SLOT_SOURCES,
537
656
  TEMPLATES,
657
+ UNAVAILABLE_MODES,
538
658
  canMove,
539
659
  catalogOption,
540
660
  catalogUseCounts,