@xenosystem/blocks 0.13.17 → 0.13.18

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.
@@ -1,5 +1,4 @@
1
1
  import {
2
- ChevronGlyph,
3
2
  CollapseGlyph,
4
3
  DiamondIcon,
5
4
  EntryIcon,
@@ -22,7 +21,7 @@ import {
22
21
  VisibilityIcon,
23
22
  XIcon,
24
23
  XIcon2
25
- } from "../chunk-PI5WOATA.js";
24
+ } from "../chunk-7S2RKUAM.js";
26
25
  import "../chunk-2KG3PWR4.js";
27
26
 
28
27
  // src/edit/layers/panel.ts
@@ -1488,9 +1487,9 @@ function LayersPanelView({ controller }) {
1488
1487
  const getSnapshot = useCallback(() => controller.getRevision(), [controller]);
1489
1488
  useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1490
1489
  const view = layersViewState(controller);
1491
- const ui7 = (intent) => applyLayersUi(controller, intent);
1490
+ const ui8 = (intent) => applyLayersUi(controller, intent);
1492
1491
  return /* @__PURE__ */ jsxs(Column, { className: "xeno-layers-panel", children: [
1493
- view.searchShown ? /* @__PURE__ */ jsx2(Toolbar, { children: /* @__PURE__ */ jsx2(SearchField, { value: view.filter, onChange: (v) => ui7({ kind: "filter", value: v }), placeholder: "Search layers", label: "Search layers" }) }) : null,
1492
+ view.searchShown ? /* @__PURE__ */ jsx2(Toolbar, { children: /* @__PURE__ */ jsx2(SearchField, { value: view.filter, onChange: (v) => ui8({ kind: "filter", value: v }), placeholder: "Search layers", label: "Search layers" }) }) : null,
1494
1493
  /* @__PURE__ */ jsxs(LayersScroller, { controller, children: [
1495
1494
  /* @__PURE__ */ jsx2(RowList, { role: "tree", "aria-label": "Layers", children: /* @__PURE__ */ jsx2(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((row2) => /* @__PURE__ */ jsx2(LayerRow, { row: row2, thumbnailSize: view.thumbnailSize, controller }, row2.id)) }) }),
1496
1495
  view.empty ? /* @__PURE__ */ jsx2(EmptyState, { title: "No layers" }) : null
@@ -1603,7 +1602,7 @@ function LayersThumbnail({ thumbnailKey, size, controller }) {
1603
1602
  return /* @__PURE__ */ jsx2(Thumbnail, { size, data: controller.getThumbnail(thumbnailKey) });
1604
1603
  }
1605
1604
  function LayerRow({ row: row2, thumbnailSize, controller }) {
1606
- const ui7 = (intent) => applyLayersUi(controller, intent);
1605
+ const ui8 = (intent) => applyLayersUi(controller, intent);
1607
1606
  return /* @__PURE__ */ jsx2(
1608
1607
  Row,
1609
1608
  {
@@ -1618,11 +1617,11 @@ function LayerRow({ row: row2, thumbnailSize, controller }) {
1618
1617
  title: row2.title,
1619
1618
  icon: row2.hasThumbnail ? /* @__PURE__ */ jsx2(LayersThumbnail, { thumbnailKey: row2.thumbnailKey, size: thumbnailSize, controller }) : /* @__PURE__ */ jsx2(KindGlyph, {}),
1620
1619
  leading: /* @__PURE__ */ jsxs(Fragment, { children: [
1621
- row2.showVisibility ? /* @__PURE__ */ jsx2(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx2(VisibilityIcon, { open: row2.visible }), label: row2.visibilityLabel, active: row2.visible, onClick: () => ui7({ kind: "toggleProp", id: row2.id, prop: "visible" }) }) : null,
1622
- row2.showLock ? /* @__PURE__ */ jsx2(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx2(LockStateIcon, { locked: row2.locked }), label: row2.lockLabel, active: row2.locked, onClick: () => ui7({ kind: "toggleProp", id: row2.id, prop: "locked" }) }) : null,
1623
- row2.showSolo ? /* @__PURE__ */ jsx2(ChipToggle, { glyph: "S", label: "Solo", on: row2.solo, onChange: () => ui7({ kind: "toggleProp", id: row2.id, prop: "solo" }) }) : null,
1624
- row2.showMute ? /* @__PURE__ */ jsx2(ChipToggle, { glyph: "M", label: "Mute", on: row2.muted, onChange: () => ui7({ kind: "toggleProp", id: row2.id, prop: "muted" }) }) : null,
1625
- row2.showArm ? /* @__PURE__ */ jsx2(ChipToggle, { glyph: "R", label: "Arm", on: row2.armed, onChange: () => ui7({ kind: "toggleProp", id: row2.id, prop: "armed" }) }) : null,
1620
+ row2.showVisibility ? /* @__PURE__ */ jsx2(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx2(VisibilityIcon, { open: row2.visible }), label: row2.visibilityLabel, active: row2.visible, onClick: () => ui8({ kind: "toggleProp", id: row2.id, prop: "visible" }) }) : null,
1621
+ row2.showLock ? /* @__PURE__ */ jsx2(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx2(LockStateIcon, { locked: row2.locked }), label: row2.lockLabel, active: row2.locked, onClick: () => ui8({ kind: "toggleProp", id: row2.id, prop: "locked" }) }) : null,
1622
+ row2.showSolo ? /* @__PURE__ */ jsx2(ChipToggle, { glyph: "S", label: "Solo", on: row2.solo, onChange: () => ui8({ kind: "toggleProp", id: row2.id, prop: "solo" }) }) : null,
1623
+ row2.showMute ? /* @__PURE__ */ jsx2(ChipToggle, { glyph: "M", label: "Mute", on: row2.muted, onChange: () => ui8({ kind: "toggleProp", id: row2.id, prop: "muted" }) }) : null,
1624
+ row2.showArm ? /* @__PURE__ */ jsx2(ChipToggle, { glyph: "R", label: "Arm", on: row2.armed, onChange: () => ui8({ kind: "toggleProp", id: row2.id, prop: "armed" }) }) : null,
1626
1625
  row2.showColor ? /* @__PURE__ */ jsx2(ColorTag, { color: row2.colorLabel }) : null
1627
1626
  ] }),
1628
1627
  label: row2.renaming ? /* @__PURE__ */ jsx2(LayersRenameField, { id: row2.id, controller }) : row2.name,
@@ -1630,9 +1629,9 @@ function LayerRow({ row: row2, thumbnailSize, controller }) {
1630
1629
  row2.adornments.map((a) => /* @__PURE__ */ jsx2(Badge, { size: "sm", title: a.title, children: a.text }, a.key)),
1631
1630
  row2.showOpacity ? /* @__PURE__ */ jsx2(Text, { role: "number", children: row2.opacity }) : null
1632
1631
  ] }),
1633
- onExpandedChange: () => ui7({ kind: "toggleExpanded", id: row2.id }),
1634
- onDoubleClick: () => ui7({ kind: "beginRename", id: row2.id }),
1635
- onContextMenuAt: (x, y) => ui7({ kind: "contextMenu", id: row2.id, x, y })
1632
+ onExpandedChange: () => ui8({ kind: "toggleExpanded", id: row2.id }),
1633
+ onDoubleClick: () => ui8({ kind: "beginRename", id: row2.id }),
1634
+ onContextMenuAt: (x, y) => ui8({ kind: "contextMenu", id: row2.id, x, y })
1636
1635
  }
1637
1636
  );
1638
1637
  }
@@ -2024,8 +2023,8 @@ function constraintsOf(field) {
2024
2023
  scale: field.scale
2025
2024
  };
2026
2025
  }
2027
- function parseNumeric(text7) {
2028
- const trimmed = String(text7).trim();
2026
+ function parseNumeric(text8) {
2027
+ const trimmed = String(text8).trim();
2029
2028
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
2030
2029
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
2031
2030
  if (!match) return { ok: false, reason: "nan" };
@@ -2050,8 +2049,8 @@ function normalizeNumeric(value, c) {
2050
2049
  function formatNumeric(value, field) {
2051
2050
  if (!Number.isFinite(value)) return "";
2052
2051
  const precision = field.precision;
2053
- const text7 = typeof precision === "number" && Number.isFinite(precision) ? value.toFixed(Math.max(0, Math.floor(precision))) : String(value);
2054
- return field.unit ? `${text7}${field.unit}` : text7;
2052
+ const text8 = typeof precision === "number" && Number.isFinite(precision) ? value.toFixed(Math.max(0, Math.floor(precision))) : String(value);
2053
+ return field.unit ? `${text8}${field.unit}` : text8;
2055
2054
  }
2056
2055
  function usesLogScale(c) {
2057
2056
  return c.scale === "log" && typeof c.min === "number" && typeof c.max === "number" && c.min > 0 && c.max > c.min;
@@ -2385,44 +2384,44 @@ var InspectorPanelController = class {
2385
2384
  if (!field || !isFieldEditable(field)) return;
2386
2385
  const mixed = isFieldMixed(field);
2387
2386
  const value = this.getFieldValue(fieldId);
2388
- const text7 = mixed ? "" : field.type === "number" && typeof value === "number" ? formatNumeric(value, field) : value === void 0 || value === null ? "" : String(value);
2389
- this.drafts.set(fieldId, { text: text7, original: value });
2387
+ const text8 = mixed ? "" : field.type === "number" && typeof value === "number" ? formatNumeric(value, field) : value === void 0 || value === null ? "" : String(value);
2388
+ this.drafts.set(fieldId, { text: text8, original: value });
2390
2389
  this.focusedFieldId = fieldId;
2391
2390
  this.emitFieldFocus(fieldId);
2392
2391
  this.notify();
2393
2392
  }
2394
2393
  /** Update the draft text (typing). Emits nothing under the `blur` policy. */
2395
- setDraft(fieldId, text7) {
2394
+ setDraft(fieldId, text8) {
2396
2395
  const draft = this.drafts.get(fieldId);
2397
2396
  if (!draft) {
2398
- this.drafts.set(fieldId, { text: text7, original: this.getFieldValue(fieldId) });
2397
+ this.drafts.set(fieldId, { text: text8, original: this.getFieldValue(fieldId) });
2399
2398
  } else {
2400
- draft.text = text7;
2399
+ draft.text = text8;
2401
2400
  }
2402
2401
  const field = this.getField(fieldId);
2403
2402
  if (field && commitPolicyOf(field) === "live") {
2404
- const parsed = this.parseDraft(fieldId, text7);
2403
+ const parsed = this.parseDraft(fieldId, text8);
2405
2404
  if (parsed.ok) this.emitSet(fieldId, parsed.value);
2406
2405
  }
2407
2406
  this.notify();
2408
2407
  }
2409
2408
  /** Parse a draft string into a field-appropriate value. */
2410
- parseDraft(fieldId, text7) {
2409
+ parseDraft(fieldId, text8) {
2411
2410
  const field = this.getField(fieldId);
2412
2411
  if (!field) return { ok: false };
2413
2412
  if (field.type === "number") {
2414
- const parsed = parseNumeric(text7);
2413
+ const parsed = parseNumeric(text8);
2415
2414
  if (!parsed.ok) return { ok: false };
2416
2415
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOf(field)) };
2417
2416
  }
2418
2417
  if (field.type === "json") {
2419
2418
  try {
2420
- return { ok: true, value: JSON.parse(text7) };
2419
+ return { ok: true, value: JSON.parse(text8) };
2421
2420
  } catch {
2422
2421
  return { ok: false };
2423
2422
  }
2424
2423
  }
2425
- return { ok: true, value: text7 };
2424
+ return { ok: true, value: text8 };
2426
2425
  }
2427
2426
  /**
2428
2427
  * Commit the draft (Enter / blur). Invalid input reverts instead of committing garbage.
@@ -3348,7 +3347,7 @@ function NumberWidget({ field, controller, component }) {
3348
3347
  },
3349
3348
  [controller, editable, field.id, component]
3350
3349
  );
3351
- const text7 = component !== void 0 ? (() => {
3350
+ const text8 = component !== void 0 ? (() => {
3352
3351
  const raw = controller.getFieldValue(field.id);
3353
3352
  const v = Array.isArray(raw) ? raw[component] : void 0;
3354
3353
  return typeof v === "number" ? formatNumeric(v, field) : "";
@@ -3360,7 +3359,7 @@ function NumberWidget({ field, controller, component }) {
3360
3359
  Input2,
3361
3360
  {
3362
3361
  inputMode: "decimal",
3363
- value: text7,
3362
+ value: text8,
3364
3363
  placeholder: mixed ? controller.getMixedPlaceholder() : field.placeholder,
3365
3364
  mixed,
3366
3365
  readOnly: !editable,
@@ -3578,7 +3577,7 @@ function ActionGlyph({ glyph }) {
3578
3577
  return /* @__PURE__ */ jsx4("span", { "aria-hidden": true, children: glyph });
3579
3578
  }
3580
3579
  function SectionView({ section, controller }) {
3581
- const ui7 = (intent) => applyInspectorUi(controller, intent);
3580
+ const ui8 = (intent) => applyInspectorUi(controller, intent);
3582
3581
  const hasActions = section.actions.length > 0 || section.hasAddMenu;
3583
3582
  return /* @__PURE__ */ jsxs3(
3584
3583
  Section,
@@ -3587,13 +3586,13 @@ function SectionView({ section, controller }) {
3587
3586
  flush: true,
3588
3587
  collapsible: section.collapsible,
3589
3588
  collapsed: section.collapsed,
3590
- onCollapsedChange: () => ui7({ kind: "toggleSectionCollapsed", sectionId: section.id }),
3589
+ onCollapsedChange: () => ui8({ kind: "toggleSectionCollapsed", sectionId: section.id }),
3591
3590
  enabled: section.enabled,
3592
- onEnabledChange: () => ui7({ kind: "toggleSectionEnabled", sectionId: section.id }),
3591
+ onEnabledChange: () => ui8({ kind: "toggleSectionEnabled", sectionId: section.id }),
3593
3592
  badge: section.badge ? /* @__PURE__ */ jsx4(Badge2, { children: section.badge }) : null,
3594
3593
  actions: hasActions ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
3595
- section.actions.map((action) => /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ActionGlyph, { glyph: action.glyph }), label: action.label, disabled: action.disabled, onClick: () => ui7({ kind: "runAction", sectionId: section.id, actionId: action.id }) }, action.id)),
3596
- section.hasAddMenu ? /* @__PURE__ */ jsx4(Menu, { size: "sm", trigger: "\uFF0B", items: section.addItems, label: section.addLabel, align: "end", onSelect: (id) => ui7({ kind: "addItem", sectionId: section.id, itemId: id }) }) : null
3594
+ section.actions.map((action) => /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ActionGlyph, { glyph: action.glyph }), label: action.label, disabled: action.disabled, onClick: () => ui8({ kind: "runAction", sectionId: section.id, actionId: action.id }) }, action.id)),
3595
+ section.hasAddMenu ? /* @__PURE__ */ jsx4(Menu, { size: "sm", trigger: "\uFF0B", items: section.addItems, label: section.addLabel, align: "end", onSelect: (id) => ui8({ kind: "addItem", sectionId: section.id, itemId: id }) }) : null
3597
3596
  ] }) : null,
3598
3597
  children: [
3599
3598
  section.showEmptyHint ? /* @__PURE__ */ jsx4(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx4(Text3, { role: "caption", children: section.emptyHint }) }) : null,
@@ -3603,7 +3602,7 @@ function SectionView({ section, controller }) {
3603
3602
  );
3604
3603
  }
3605
3604
  function FieldRow({ row: row2, controller }) {
3606
- const ui7 = (intent) => applyInspectorUi(controller, intent);
3605
+ const ui8 = (intent) => applyInspectorUi(controller, intent);
3607
3606
  return /* @__PURE__ */ jsxs3(Fragment3, { children: [
3608
3607
  /* @__PURE__ */ jsxs3(
3609
3608
  Field,
@@ -3611,19 +3610,19 @@ function FieldRow({ row: row2, controller }) {
3611
3610
  label: row2.label,
3612
3611
  title: row2.title,
3613
3612
  focused: row2.focused,
3614
- onLabelClick: () => ui7({ kind: "focusField", fieldId: row2.id }),
3615
- onContextMenu: (x, y) => ui7({ kind: "contextMenu", fieldId: row2.id, x, y }),
3613
+ onLabelClick: () => ui8({ kind: "focusField", fieldId: row2.id }),
3614
+ onContextMenu: (x, y) => ui8({ kind: "contextMenu", fieldId: row2.id, x, y }),
3616
3615
  leading: row2.showKeyframes ? /* @__PURE__ */ jsxs3(Fragment3, { children: [
3617
- /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(StopwatchIcon, {}), label: "Toggle animation", active: row2.animated, onClick: () => ui7({ kind: "keyframe", fieldId: row2.id, op: "toggleAnim" }) }),
3618
- row2.animated ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PrevIcon, {}), label: "Previous keyframe", disabled: row2.prevDisabled, onClick: () => ui7({ kind: "keyframe", fieldId: row2.id, op: "prev" }) }) : null,
3619
- row2.animated ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(DiamondIcon, { filled: row2.onKeyframe }), label: row2.keyframeLabel, onClick: () => ui7({ kind: "keyframe", fieldId: row2.id, op: row2.keyframeOp }) }) : null,
3620
- row2.animated ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(NextIcon, {}), label: "Next keyframe", disabled: row2.nextDisabled, onClick: () => ui7({ kind: "keyframe", fieldId: row2.id, op: "next" }) }) : null
3616
+ /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(StopwatchIcon, {}), label: "Toggle animation", active: row2.animated, onClick: () => ui8({ kind: "keyframe", fieldId: row2.id, op: "toggleAnim" }) }),
3617
+ row2.animated ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PrevIcon, {}), label: "Previous keyframe", disabled: row2.prevDisabled, onClick: () => ui8({ kind: "keyframe", fieldId: row2.id, op: "prev" }) }) : null,
3618
+ row2.animated ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(DiamondIcon, { filled: row2.onKeyframe }), label: row2.keyframeLabel, onClick: () => ui8({ kind: "keyframe", fieldId: row2.id, op: row2.keyframeOp }) }) : null,
3619
+ row2.animated ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(NextIcon, {}), label: "Next keyframe", disabled: row2.nextDisabled, onClick: () => ui8({ kind: "keyframe", fieldId: row2.id, op: "next" }) }) : null
3621
3620
  ] }) : null,
3622
3621
  trailing: /* @__PURE__ */ jsxs3(Fragment3, { children: [
3623
- row2.showEasing ? /* @__PURE__ */ jsx4(Select2, { size: "sm", options: row2.easingOptions, value: row2.easing, label: "Easing", onChange: (v) => ui7({ kind: "setEasing", fieldId: row2.id, easing: v }) }) : null,
3622
+ row2.showEasing ? /* @__PURE__ */ jsx4(Select2, { size: "sm", options: row2.easingOptions, value: row2.easing, label: "Easing", onChange: (v) => ui8({ kind: "setEasing", fieldId: row2.id, easing: v }) }) : null,
3624
3623
  row2.showKeyframes ? /* @__PURE__ */ jsx4(Badge2, { size: "sm", children: row2.keyframeChip }) : null,
3625
- row2.showToken ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(LinkIcon, {}), label: row2.tokenActionLabel, active: row2.bound, onClick: () => ui7({ kind: "bindToken", fieldId: row2.id }) }) : null,
3626
- row2.showReset ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ResetIcon, {}), label: "Reset to default", onClick: () => ui7({ kind: "resetField", fieldId: row2.id }) }) : null
3624
+ row2.showToken ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(LinkIcon, {}), label: row2.tokenActionLabel, active: row2.bound, onClick: () => ui8({ kind: "bindToken", fieldId: row2.id }) }) : null,
3625
+ row2.showReset ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ResetIcon, {}), label: "Reset to default", onClick: () => ui8({ kind: "resetField", fieldId: row2.id }) }) : null
3627
3626
  ] }),
3628
3627
  children: [
3629
3628
  row2.bound ? /* @__PURE__ */ jsx4(Badge2, { title: row2.tokenTitle, children: row2.tokenLabel }) : null,
@@ -4876,23 +4875,23 @@ function HistoryPanelView({ controller }) {
4876
4875
  const getSnapshot = useCallback4(() => controller.getRevision(), [controller]);
4877
4876
  useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
4878
4877
  const view = historyViewState(controller);
4879
- const ui7 = (intent) => applyHistoryUi(controller, intent);
4878
+ const ui8 = (intent) => applyHistoryUi(controller, intent);
4880
4879
  return /* @__PURE__ */ jsxs4(Column3, { className: "xeno-history-panel", children: [
4881
4880
  /* @__PURE__ */ jsx5(
4882
4881
  Toolbar2,
4883
4882
  {
4884
4883
  left: /* @__PURE__ */ jsxs4(Fragment4, { children: [
4885
- /* @__PURE__ */ jsx5(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx5(UndoIcon, {}), label: "Undo", disabled: !view.canUndo, onClick: () => ui7({ kind: "undo" }) }),
4886
- /* @__PURE__ */ jsx5(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx5(RedoIcon, {}), label: "Redo", disabled: !view.canRedo, onClick: () => ui7({ kind: "redo" }) })
4884
+ /* @__PURE__ */ jsx5(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx5(UndoIcon, {}), label: "Undo", disabled: !view.canUndo, onClick: () => ui8({ kind: "undo" }) }),
4885
+ /* @__PURE__ */ jsx5(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx5(RedoIcon, {}), label: "Redo", disabled: !view.canRedo, onClick: () => ui8({ kind: "redo" }) })
4887
4886
  ] }),
4888
4887
  right: /* @__PURE__ */ jsxs4(Fragment4, { children: [
4889
4888
  /* @__PURE__ */ jsx5(Text4, { role: "number", children: view.counter }),
4890
- view.clearAllowed ? /* @__PURE__ */ jsx5(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx5(TrashIcon, {}), label: "Clear history", onClick: () => ui7({ kind: "requestClear" }) }) : null
4889
+ view.clearAllowed ? /* @__PURE__ */ jsx5(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx5(TrashIcon, {}), label: "Clear history", onClick: () => ui8({ kind: "requestClear" }) }) : null
4891
4890
  ] }),
4892
- children: view.searchShown ? /* @__PURE__ */ jsx5(SearchField3, { value: view.filter, onChange: (v) => ui7({ kind: "filter", value: v }), placeholder: "Search history", label: "Search history" }) : null
4891
+ children: view.searchShown ? /* @__PURE__ */ jsx5(SearchField3, { value: view.filter, onChange: (v) => ui8({ kind: "filter", value: v }), placeholder: "Search history", label: "Search history" }) : null
4893
4892
  }
4894
4893
  ),
4895
- view.confirmingClear ? /* @__PURE__ */ jsx5(InlineConfirm, { message: "Clear all history?", label: "Confirm clear history", confirmLabel: "Clear", onConfirm: () => ui7({ kind: "confirmClear" }), onCancel: () => ui7({ kind: "cancelClear" }) }) : null,
4894
+ view.confirmingClear ? /* @__PURE__ */ jsx5(InlineConfirm, { message: "Clear all history?", label: "Confirm clear history", confirmLabel: "Clear", onConfirm: () => ui8({ kind: "confirmClear" }), onCancel: () => ui8({ kind: "cancelClear" }) }) : null,
4896
4895
  view.pruned ? /* @__PURE__ */ jsx5(Text4, { role: "caption", children: "Earlier history discarded (limit reached)" }) : null,
4897
4896
  /* @__PURE__ */ jsxs4(HistoryScroller, { controller, children: [
4898
4897
  /* @__PURE__ */ jsx5(RowList2, { role: "listbox", "aria-label": "History", children: /* @__PURE__ */ jsx5(VirtualWindow2, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((row2) => /* @__PURE__ */ jsx5(HistoryRowView, { row: row2, controller }, row2.key)) }) }),
@@ -4951,7 +4950,7 @@ function HistoryCancelButton({ entryId, controller }) {
4951
4950
  );
4952
4951
  }
4953
4952
  function HistoryRowView({ row: row2, controller }) {
4954
- const ui7 = (intent) => applyHistoryUi(controller, intent);
4953
+ const ui8 = (intent) => applyHistoryUi(controller, intent);
4955
4954
  return /* @__PURE__ */ jsx5(
4956
4955
  Row2,
4957
4956
  {
@@ -4974,9 +4973,9 @@ function HistoryRowView({ row: row2, controller }) {
4974
4973
  row2.showTime ? /* @__PURE__ */ jsx5(Text4, { role: "meta", children: row2.time }) : null,
4975
4974
  row2.pending ? /* @__PURE__ */ jsx5(HistoryCancelButton, { entryId: row2.cancelId, controller }) : null
4976
4975
  ] }),
4977
- onClick: () => ui7({ kind: "jump", entryId: row2.entryId }),
4978
- onMouseEnter: () => ui7({ kind: "hover", entryId: row2.entryId }),
4979
- onContextMenuAt: (x, y) => ui7({ kind: "contextMenu", entryId: row2.entryId, x, y })
4976
+ onClick: () => ui8({ kind: "jump", entryId: row2.entryId }),
4977
+ onMouseEnter: () => ui8({ kind: "hover", entryId: row2.entryId }),
4978
+ onContextMenuAt: (x, y) => ui8({ kind: "contextMenu", entryId: row2.entryId, x, y })
4980
4979
  }
4981
4980
  );
4982
4981
  }
@@ -6262,27 +6261,27 @@ function ColorPanelView({ controller }) {
6262
6261
  const getSnapshot = useCallback5(() => controller.getRevision(), [controller]);
6263
6262
  useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
6264
6263
  const view = colorViewState(controller);
6265
- const ui7 = (intent) => applyColorUi(controller, intent);
6264
+ const ui8 = (intent) => applyColorUi(controller, intent);
6266
6265
  return /* @__PURE__ */ jsx6(Column4, { className: "xeno-color-panel", children: /* @__PURE__ */ jsxs5(Stack3, { gap: 8, children: [
6267
6266
  view.fgBg ? /* @__PURE__ */ jsxs5(Stack3, { direction: "row", pad: "none", children: [
6268
- /* @__PURE__ */ jsx6(ColorChip, { color: view.hex, label: "Foreground", pressed: view.fgActive, onClick: () => ui7({ kind: "setChannel", channel: "fg" }) }),
6269
- /* @__PURE__ */ jsx6(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx6(SwapIcon, {}), label: "Swap FG/BG (X)", onClick: () => ui7({ kind: "swapChannels" }) }),
6270
- /* @__PURE__ */ jsx6(ColorChip, { color: null, label: "Background", pressed: view.bgActive, onClick: () => ui7({ kind: "setChannel", channel: "bg" }) })
6267
+ /* @__PURE__ */ jsx6(ColorChip, { color: view.hex, label: "Foreground", pressed: view.fgActive, onClick: () => ui8({ kind: "setChannel", channel: "fg" }) }),
6268
+ /* @__PURE__ */ jsx6(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx6(SwapIcon, {}), label: "Swap FG/BG (X)", onClick: () => ui8({ kind: "swapChannels" }) }),
6269
+ /* @__PURE__ */ jsx6(ColorChip, { color: null, label: "Background", pressed: view.bgActive, onClick: () => ui8({ kind: "setChannel", channel: "bg" }) })
6271
6270
  ] }) : null,
6272
6271
  /* @__PURE__ */ jsx6(ColorPicker, { controller }),
6273
6272
  /* @__PURE__ */ jsxs5(Stack3, { direction: "row", pad: "none", gap: 4, children: [
6274
- /* @__PURE__ */ jsx6(Select3, { size: "sm", label: "Color model", value: view.space, options: view.spaceOptions, onChange: (v) => ui7({ kind: "setSpace", space: v }) }),
6275
- view.isHex ? /* @__PURE__ */ jsx6(Input3, { variant: "mono", label: "Hex", value: view.hex, onChange: (v) => ui7({ kind: "setHex", value: v }), disabled: view.bound }) : null,
6276
- view.components.map((comp) => /* @__PURE__ */ jsx6(NumberInput, { caption: comp.label, value: comp.value, onChange: (n) => ui7({ kind: "setComponent", index: comp.index, value: n }), disabled: view.bound }, comp.key)),
6277
- view.alphaShown ? /* @__PURE__ */ jsx6(NumberInput, { caption: "A", label: "Alpha", min: 0, max: 100, value: view.alphaPercent, onChange: (n) => ui7({ kind: "setAlpha", percent: n }), disabled: view.bound }) : null,
6278
- /* @__PURE__ */ jsx6(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx6(EyedropperIcon, {}), label: "Pick from screen", onClick: () => ui7({ kind: "eyedropper" }) })
6273
+ /* @__PURE__ */ jsx6(Select3, { size: "sm", label: "Color model", value: view.space, options: view.spaceOptions, onChange: (v) => ui8({ kind: "setSpace", space: v }) }),
6274
+ view.isHex ? /* @__PURE__ */ jsx6(Input3, { variant: "mono", label: "Hex", value: view.hex, onChange: (v) => ui8({ kind: "setHex", value: v }), disabled: view.bound }) : null,
6275
+ view.components.map((comp) => /* @__PURE__ */ jsx6(NumberInput, { caption: comp.label, value: comp.value, onChange: (n) => ui8({ kind: "setComponent", index: comp.index, value: n }), disabled: view.bound }, comp.key)),
6276
+ view.alphaShown ? /* @__PURE__ */ jsx6(NumberInput, { caption: "A", label: "Alpha", min: 0, max: 100, value: view.alphaPercent, onChange: (n) => ui8({ kind: "setAlpha", percent: n }), disabled: view.bound }) : null,
6277
+ /* @__PURE__ */ jsx6(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx6(EyedropperIcon, {}), label: "Pick from screen", onClick: () => ui8({ kind: "eyedropper" }) })
6279
6278
  ] }),
6280
6279
  view.hasTokens ? /* @__PURE__ */ jsx6(Field2, { label: "Token", htmlFor: "xeno-color-token", children: /* @__PURE__ */ jsxs5(Stack3, { direction: "row", pad: "none", gap: 4, children: [
6281
- /* @__PURE__ */ jsx6(Select3, { size: "sm", label: "Bind token", value: view.boundTokenId, placeholder: "\u2014 raw \u2014", options: view.tokenOptions, onChange: (id) => ui7({ kind: "bindToken", tokenId: id }) }),
6282
- view.bound ? /* @__PURE__ */ jsx6(TextButton2, { onClick: () => ui7({ kind: "unbindToken" }), children: "Unbind" }) : null
6280
+ /* @__PURE__ */ jsx6(Select3, { size: "sm", label: "Bind token", value: view.boundTokenId, placeholder: "\u2014 raw \u2014", options: view.tokenOptions, onChange: (id) => ui8({ kind: "bindToken", tokenId: id }) }),
6281
+ view.bound ? /* @__PURE__ */ jsx6(TextButton2, { onClick: () => ui8({ kind: "unbindToken" }), children: "Unbind" }) : null
6283
6282
  ] }) }) : null,
6284
- /* @__PURE__ */ jsx6(Section2, { title: "Swatches", flush: true, actions: /* @__PURE__ */ jsx6(TextButton2, { onClick: () => ui7({ kind: "addSwatch" }), children: "+ Save" }), children: /* @__PURE__ */ jsx6(SwatchGrid, { label: "Swatches", children: view.swatches.map((sw) => /* @__PURE__ */ jsx6(ColorChip, { size: "grid", color: sw.hex, label: sw.hex, onClick: () => ui7({ kind: "selectColor", hex: sw.hex }), onContextMenu: () => ui7({ kind: "removeSwatch", hex: sw.hex }) }, sw.key)) }) }),
6285
- view.hasRecent ? /* @__PURE__ */ jsx6(Section2, { title: "Recent", flush: true, children: /* @__PURE__ */ jsx6(SwatchGrid, { label: "Recent", children: view.recent.map((r) => /* @__PURE__ */ jsx6(ColorChip, { size: "grid", color: r.hex, label: r.hex, onClick: () => ui7({ kind: "selectColor", hex: r.hex }) }, r.key)) }) }) : null
6283
+ /* @__PURE__ */ jsx6(Section2, { title: "Swatches", flush: true, actions: /* @__PURE__ */ jsx6(TextButton2, { onClick: () => ui8({ kind: "addSwatch" }), children: "+ Save" }), children: /* @__PURE__ */ jsx6(SwatchGrid, { label: "Swatches", children: view.swatches.map((sw) => /* @__PURE__ */ jsx6(ColorChip, { size: "grid", color: sw.hex, label: sw.hex, onClick: () => ui8({ kind: "selectColor", hex: sw.hex }), onContextMenu: () => ui8({ kind: "removeSwatch", hex: sw.hex }) }, sw.key)) }) }),
6284
+ view.hasRecent ? /* @__PURE__ */ jsx6(Section2, { title: "Recent", flush: true, children: /* @__PURE__ */ jsx6(SwatchGrid, { label: "Recent", children: view.recent.map((r) => /* @__PURE__ */ jsx6(ColorChip, { size: "grid", color: r.hex, label: r.hex, onClick: () => ui8({ kind: "selectColor", hex: r.hex }) }, r.key)) }) }) : null
6286
6285
  ] }) });
6287
6286
  }
6288
6287
  function ColorPicker({ controller }) {
@@ -8944,11 +8943,11 @@ var FindController = class {
8944
8943
  * @param text - The new query text.
8945
8944
  * @param immediate - Skip the debounce (a paste, or pressing Enter).
8946
8945
  */
8947
- setText(text7, immediate = false) {
8948
- if (text7 === this.text) return;
8949
- this.text = text7;
8946
+ setText(text8, immediate = false) {
8947
+ if (text8 === this.text) return;
8948
+ this.text = text8;
8950
8949
  this.index = -1;
8951
- if (text7 === "") {
8950
+ if (text8 === "") {
8952
8951
  this.clearResults("idle");
8953
8952
  this.emitQuery(true);
8954
8953
  return;
@@ -8976,8 +8975,8 @@ var FindController = class {
8976
8975
  this.notify();
8977
8976
  }
8978
8977
  /** The replacement text. Never emitted on its own — a replacement is not a search. */
8979
- setReplaceText(text7) {
8980
- this.replaceText = text7;
8978
+ setReplaceText(text8) {
8979
+ this.replaceText = text8;
8981
8980
  this.notify();
8982
8981
  }
8983
8982
  /** Show or hide the replace row. */
@@ -9626,7 +9625,7 @@ function findRuntimeComponents(controller) {
9626
9625
  function FindPanelView({ controller, showResults, placeholder }) {
9627
9626
  useSyncExternalStore7(controller.subscribe, controller.getState, controller.getState);
9628
9627
  const view = findViewState(controller, { showResults, placeholder });
9629
- const ui7 = (intent) => applyFindUi(controller, intent);
9628
+ const ui8 = (intent) => applyFindUi(controller, intent);
9630
9629
  const renderRow = (row2) => /* @__PURE__ */ jsx9(
9631
9630
  Row3,
9632
9631
  {
@@ -9634,8 +9633,8 @@ function FindPanelView({ controller, showResults, placeholder }) {
9634
9633
  selected: row2.current,
9635
9634
  meta: row2.location,
9636
9635
  label: /* @__PURE__ */ jsx9(Highlight, { segments: row2.segments }),
9637
- onClick: () => ui7({ kind: "select", matchId: row2.id }),
9638
- onContextMenuAt: (x, y) => ui7({ kind: "contextMenu", matchId: row2.id, x, y })
9636
+ onClick: () => ui8({ kind: "select", matchId: row2.id }),
9637
+ onContextMenuAt: (x, y) => ui8({ kind: "contextMenu", matchId: row2.id, x, y })
9639
9638
  },
9640
9639
  row2.id
9641
9640
  );
@@ -9643,11 +9642,11 @@ function FindPanelView({ controller, showResults, placeholder }) {
9643
9642
  /* @__PURE__ */ jsx9(
9644
9643
  Toolbar4,
9645
9644
  {
9646
- left: /* @__PURE__ */ jsx9(SearchField4, { value: view.text, placeholder: view.placeholder, onChange: (value) => ui7({ kind: "text", value }) }),
9645
+ left: /* @__PURE__ */ jsx9(SearchField4, { value: view.text, placeholder: view.placeholder, onChange: (value) => ui8({ kind: "text", value }) }),
9647
9646
  right: /* @__PURE__ */ jsxs8(ToolbarGroup2, { end: true, children: [
9648
- /* @__PURE__ */ jsx9(IconButton6, { label: "Match case", active: view.caseSensitive, icon: /* @__PURE__ */ jsx9(CaseIcon, { size: 12 }), onClick: () => ui7({ kind: "option", option: "caseSensitive" }) }),
9649
- /* @__PURE__ */ jsx9(IconButton6, { label: "Whole word", active: view.wholeWord, icon: /* @__PURE__ */ jsx9(WordIcon, { size: 12 }), onClick: () => ui7({ kind: "option", option: "wholeWord" }) }),
9650
- /* @__PURE__ */ jsx9(IconButton6, { label: "Regular expression", active: view.regex, icon: /* @__PURE__ */ jsx9(RegexIcon, { size: 12 }), onClick: () => ui7({ kind: "option", option: "regex" }) })
9647
+ /* @__PURE__ */ jsx9(IconButton6, { label: "Match case", active: view.caseSensitive, icon: /* @__PURE__ */ jsx9(CaseIcon, { size: 12 }), onClick: () => ui8({ kind: "option", option: "caseSensitive" }) }),
9648
+ /* @__PURE__ */ jsx9(IconButton6, { label: "Whole word", active: view.wholeWord, icon: /* @__PURE__ */ jsx9(WordIcon, { size: 12 }), onClick: () => ui8({ kind: "option", option: "wholeWord" }) }),
9649
+ /* @__PURE__ */ jsx9(IconButton6, { label: "Regular expression", active: view.regex, icon: /* @__PURE__ */ jsx9(RegexIcon, { size: 12 }), onClick: () => ui8({ kind: "option", option: "regex" }) })
9651
9650
  ] })
9652
9651
  }
9653
9652
  ),
@@ -9662,9 +9661,9 @@ function FindPanelView({ controller, showResults, placeholder }) {
9662
9661
  view.countShown ? /* @__PURE__ */ jsx9(Text5, { role: "number", children: view.counter }) : null
9663
9662
  ] }),
9664
9663
  right: /* @__PURE__ */ jsxs8(ToolbarGroup2, { end: true, children: [
9665
- /* @__PURE__ */ jsx9(IconButton6, { label: "Previous match", disabled: !view.hasMatches, icon: /* @__PURE__ */ jsx9(Chevron, { direction: "up", size: 12 }), onClick: () => ui7({ kind: "previous" }) }),
9666
- /* @__PURE__ */ jsx9(IconButton6, { label: "Next match", disabled: !view.hasMatches, icon: /* @__PURE__ */ jsx9(Chevron, { direction: "down", size: 12 }), onClick: () => ui7({ kind: "next" }) }),
9667
- /* @__PURE__ */ jsx9(TextButton3, { onClick: () => ui7({ kind: "toggleReplace" }), children: view.replaceLabel })
9664
+ /* @__PURE__ */ jsx9(IconButton6, { label: "Previous match", disabled: !view.hasMatches, icon: /* @__PURE__ */ jsx9(Chevron, { direction: "up", size: 12 }), onClick: () => ui8({ kind: "previous" }) }),
9665
+ /* @__PURE__ */ jsx9(IconButton6, { label: "Next match", disabled: !view.hasMatches, icon: /* @__PURE__ */ jsx9(Chevron, { direction: "down", size: 12 }), onClick: () => ui8({ kind: "next" }) }),
9666
+ /* @__PURE__ */ jsx9(TextButton3, { onClick: () => ui8({ kind: "toggleReplace" }), children: view.replaceLabel })
9668
9667
  ] })
9669
9668
  }
9670
9669
  ),
@@ -9672,10 +9671,10 @@ function FindPanelView({ controller, showResults, placeholder }) {
9672
9671
  view.replaceVisible ? /* @__PURE__ */ jsx9(
9673
9672
  Toolbar4,
9674
9673
  {
9675
- left: /* @__PURE__ */ jsx9(SearchField4, { value: view.replaceText, placeholder: "Replace with", onChange: (value) => ui7({ kind: "replaceText", value }) }),
9674
+ left: /* @__PURE__ */ jsx9(SearchField4, { value: view.replaceText, placeholder: "Replace with", onChange: (value) => ui8({ kind: "replaceText", value }) }),
9676
9675
  right: /* @__PURE__ */ jsxs8(ToolbarGroup2, { end: true, children: [
9677
- /* @__PURE__ */ jsx9(TextButton3, { disabled: !view.canReplace, onClick: () => ui7({ kind: "replaceCurrent" }), children: "Replace" }),
9678
- /* @__PURE__ */ jsx9(TextButton3, { disabled: !view.canReplace, onClick: () => ui7({ kind: "replaceAll" }), children: view.allLabel })
9676
+ /* @__PURE__ */ jsx9(TextButton3, { disabled: !view.canReplace, onClick: () => ui8({ kind: "replaceCurrent" }), children: "Replace" }),
9677
+ /* @__PURE__ */ jsx9(TextButton3, { disabled: !view.canReplace, onClick: () => ui8({ kind: "replaceAll" }), children: view.allLabel })
9679
9678
  ] })
9680
9679
  }
9681
9680
  ) : null,
@@ -10574,37 +10573,37 @@ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
10574
10573
  function CommentsPanelView({ controller, ...options }) {
10575
10574
  useSyncExternalStore8(controller.subscribe, controller.getState, controller.getState);
10576
10575
  const view = commentsViewState(controller, options);
10577
- const ui7 = (intent) => applyCommentsUi(controller, intent);
10578
- const card = (t) => /* @__PURE__ */ jsxs9(ThreadCard, { active: t.active, resolved: t.resolved, onContextMenuAt: (x, y) => ui7({ kind: "contextMenu", id: t.id, x, y }), children: [
10576
+ const ui8 = (intent) => applyCommentsUi(controller, intent);
10577
+ const card = (t) => /* @__PURE__ */ jsxs9(ThreadCard, { active: t.active, resolved: t.resolved, onContextMenuAt: (x, y) => ui8({ kind: "contextMenu", id: t.id, x, y }), children: [
10579
10578
  /* @__PURE__ */ jsxs9(Stack4, { direction: "row", gap: 4, pad: "none", children: [
10580
10579
  t.hasAnchorBadge ? /* @__PURE__ */ jsx10(Badge6, { tone: t.anchorTone, title: t.anchorTitle, children: t.anchorLabel }) : null,
10581
10580
  t.resolved ? /* @__PURE__ */ jsx10(Badge6, { children: "resolved" }) : null,
10582
10581
  /* @__PURE__ */ jsx10(Text6, { role: "caption", children: t.age })
10583
10582
  ] }),
10584
- t.hasQuote ? /* @__PURE__ */ jsx10(Quote, { orphaned: t.orphaned, onActivate: () => ui7({ kind: "activate", id: t.id }), children: t.quote }) : null,
10583
+ t.hasQuote ? /* @__PURE__ */ jsx10(Quote, { orphaned: t.orphaned, onActivate: () => ui8({ kind: "activate", id: t.id }), children: t.quote }) : null,
10585
10584
  t.messages.map((m) => /* @__PURE__ */ jsx10(Message, { author: m.author, age: m.age, edited: m.edited, children: m.body }, m.id)),
10586
- t.hasReactions ? /* @__PURE__ */ jsx10(Stack4, { direction: "row", gap: 4, pad: "none", children: t.reactions.map((r) => /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui7({ kind: "react", id: r.threadId, emoji: r.emoji, on: r.reactTo }), children: r.label }, r.emoji)) }) : null,
10585
+ t.hasReactions ? /* @__PURE__ */ jsx10(Stack4, { direction: "row", gap: 4, pad: "none", children: t.reactions.map((r) => /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui8({ kind: "react", id: r.threadId, emoji: r.emoji, on: r.reactTo }), children: r.label }, r.emoji)) }) : null,
10587
10586
  t.replying ? /* @__PURE__ */ jsxs9(Stack4, { direction: "row", gap: 4, pad: "none", children: [
10588
- /* @__PURE__ */ jsx10(Input4, { value: t.replyDraft, placeholder: "Reply", autoFocus: true, label: "Reply", onChange: (value) => ui7({ kind: "replyDraft", value }), onKeyDown: (e) => ui7({ kind: "replyKey", id: t.id, key: e.key, shift: e.shiftKey }) }),
10589
- /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui7({ kind: "reply", id: t.id }), children: "Send" })
10587
+ /* @__PURE__ */ jsx10(Input4, { value: t.replyDraft, placeholder: "Reply", autoFocus: true, label: "Reply", onChange: (value) => ui8({ kind: "replyDraft", value }), onKeyDown: (e) => ui8({ kind: "replyKey", id: t.id, key: e.key, shift: e.shiftKey }) }),
10588
+ /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui8({ kind: "reply", id: t.id }), children: "Send" })
10590
10589
  ] }) : null,
10591
10590
  t.idle ? /* @__PURE__ */ jsxs9(Stack4, { direction: "row", gap: 6, pad: "none", children: [
10592
- /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui7({ kind: "startReply", id: t.id }), children: "Reply" }),
10593
- /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui7({ kind: "resolve", id: t.id, resolved: t.resolveTo }), children: t.resolveLabel }),
10594
- /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui7({ kind: "remove", id: t.id }), children: "Delete" })
10591
+ /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui8({ kind: "startReply", id: t.id }), children: "Reply" }),
10592
+ /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui8({ kind: "resolve", id: t.id, resolved: t.resolveTo }), children: t.resolveLabel }),
10593
+ /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui8({ kind: "remove", id: t.id }), children: "Delete" })
10595
10594
  ] }) : null
10596
10595
  ] }, t.id);
10597
10596
  return /* @__PURE__ */ jsxs9(Column6, { children: [
10598
10597
  /* @__PURE__ */ jsx10(
10599
10598
  Toolbar5,
10600
10599
  {
10601
- left: /* @__PURE__ */ jsx10(SegmentedControl2, { label: "Show", options: view.shows, value: view.show, onChange: (show) => ui7({ kind: "show", show }) }),
10602
- right: /* @__PURE__ */ jsx10(ToolbarGroup3, { end: true, children: /* @__PURE__ */ jsx10(SegmentedControl2, { label: "Sort", options: view.sorts, value: view.sort, onChange: (sort) => ui7({ kind: "sort", sort }) }) })
10600
+ left: /* @__PURE__ */ jsx10(SegmentedControl2, { label: "Show", options: view.shows, value: view.show, onChange: (show) => ui8({ kind: "show", show }) }),
10601
+ right: /* @__PURE__ */ jsx10(ToolbarGroup3, { end: true, children: /* @__PURE__ */ jsx10(SegmentedControl2, { label: "Sort", options: view.sorts, value: view.sort, onChange: (sort) => ui8({ kind: "sort", sort }) }) })
10603
10602
  }
10604
10603
  ),
10605
10604
  view.orphanedNotice ? /* @__PURE__ */ jsxs9(Note, { children: [
10606
10605
  view.orphanedNotice,
10607
- view.orphanedHidden ? /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui7({ kind: "showOrphaned" }), children: "Show" }) : null
10606
+ view.orphanedHidden ? /* @__PURE__ */ jsx10(TextButton4, { onClick: () => ui8({ kind: "showOrphaned" }), children: "Show" }) : null
10608
10607
  ] }) : null,
10609
10608
  /* @__PURE__ */ jsxs9(ScrollArea5, { children: [
10610
10609
  view.filteredEmpty ? /* @__PURE__ */ jsx10(EmptyState6, { title: "Nothing matches this filter", hint: view.filteredHint }) : null,
@@ -10613,8 +10612,8 @@ function CommentsPanelView({ controller, ...options }) {
10613
10612
  view.hasResolved ? /* @__PURE__ */ jsx10(Section4, { title: view.resolvedTitle, flush: true, children: view.resolved.map(card) }) : null
10614
10613
  ] }),
10615
10614
  /* @__PURE__ */ jsxs9(Stack4, { gap: 2, pad: "gutter", className: "xeno-comments-composer", children: [
10616
- /* @__PURE__ */ jsx10(TextArea2, { value: view.draft, placeholder: view.placeholder, disabled: view.composerDisabled, rows: 2, label: "New comment", onChange: (value) => ui7({ kind: "draft", value }), onKeyDown: (e) => ui7({ kind: "draftKey", key: e.key, ctrl: e.ctrlKey, meta: e.metaKey }) }),
10617
- /* @__PURE__ */ jsx10(Toolbar5, { divided: false, right: /* @__PURE__ */ jsx10(ToolbarGroup3, { end: true, children: /* @__PURE__ */ jsx10(TextButton4, { strong: true, disabled: view.commentDisabled, onClick: () => ui7({ kind: "create" }), children: "Comment" }) }) })
10615
+ /* @__PURE__ */ jsx10(TextArea2, { value: view.draft, placeholder: view.placeholder, disabled: view.composerDisabled, rows: 2, label: "New comment", onChange: (value) => ui8({ kind: "draft", value }), onKeyDown: (e) => ui8({ kind: "draftKey", key: e.key, ctrl: e.ctrlKey, meta: e.metaKey }) }),
10616
+ /* @__PURE__ */ jsx10(Toolbar5, { divided: false, right: /* @__PURE__ */ jsx10(ToolbarGroup3, { end: true, children: /* @__PURE__ */ jsx10(TextButton4, { strong: true, disabled: view.commentDisabled, onClick: () => ui8({ kind: "create" }), children: "Comment" }) }) })
10618
10617
  ] }),
10619
10618
  /* @__PURE__ */ jsx10(StatusBar6, { left: view.countLabel, right: view.staleLabel })
10620
10619
  ] });
@@ -12318,6 +12317,8 @@ var TreeController = class {
12318
12317
  mode;
12319
12318
  renaming = null;
12320
12319
  drag = null;
12320
+ /** The scroller's measured viewport — the view state windows the rows from it (`tree/Scroller` reports it). */
12321
+ viewport = { height: 0, scrollTop: 0 };
12321
12322
  /** Newest outstanding load request PER NODE. Anything else that arrives is stale. */
12322
12323
  outstanding = /* @__PURE__ */ new Map();
12323
12324
  /** Type-ahead buffer + the time it was last touched. */
@@ -12367,6 +12368,20 @@ var TreeController = class {
12367
12368
  this.rowCache = null;
12368
12369
  for (const listener of this.listeners) listener();
12369
12370
  }
12371
+ /* ── Viewport ──────────────────────────────────────────────────────────── */
12372
+ /** The scroller reports its viewport; the view state windows the rows from it. */
12373
+ setViewport(height, scrollTop) {
12374
+ if (this.viewport.height === height && this.viewport.scrollTop === scrollTop) return;
12375
+ this.viewport = { height, scrollTop };
12376
+ this.notify();
12377
+ }
12378
+ getViewport() {
12379
+ return this.viewport;
12380
+ }
12381
+ /** The fixed row height the virtualizer and the drop resolver share. */
12382
+ getRowHeight() {
12383
+ return this.rowHeight;
12384
+ }
12370
12385
  /* ── Rows ──────────────────────────────────────────────────────────────── */
12371
12386
  /** The visible rows, flattened through the shared engine. */
12372
12387
  rows() {
@@ -12740,12 +12755,12 @@ var TreeController = class {
12740
12755
  }
12741
12756
  if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
12742
12757
  const fresh = now - this.typeAhead.at > this.typeAheadMs;
12743
- const text7 = (fresh ? "" : this.typeAhead.text) + event.key.toLowerCase();
12744
- this.typeAhead = { text: text7, at: now };
12758
+ const text8 = (fresh ? "" : this.typeAhead.text) + event.key.toLowerCase();
12759
+ this.typeAhead = { text: text8, at: now };
12745
12760
  const from = fresh ? current : current + 1;
12746
12761
  for (let i = 0; i < rows.length; i += 1) {
12747
12762
  const candidate = rows[(from + i) % rows.length];
12748
- if (candidate.node.label.toLowerCase().startsWith(text7)) {
12763
+ if (candidate.node.label.toLowerCase().startsWith(text8)) {
12749
12764
  this.focusedId = candidate.id;
12750
12765
  this.select([candidate.id]);
12751
12766
  return true;
@@ -12771,9 +12786,9 @@ var TreeController = class {
12771
12786
  return true;
12772
12787
  }
12773
12788
  /** Update the rename draft. */
12774
- updateRenameDraft(text7) {
12789
+ updateRenameDraft(text8) {
12775
12790
  if (!this.renaming) return;
12776
- this.renaming = { ...this.renaming, draft: text7 };
12791
+ this.renaming = { ...this.renaming, draft: text8 };
12777
12792
  this.notify();
12778
12793
  }
12779
12794
  /**
@@ -13310,331 +13325,474 @@ var treeManifest = {
13310
13325
  };
13311
13326
 
13312
13327
  // src/edit/tree/react/TreePanelView.tsx
13313
- import { useCallback as useCallback8, useEffect as useEffect8, useRef as useRef9, useState as useState2, useSyncExternalStore as useSyncExternalStore10 } from "react";
13328
+ import { useCallback as useCallback8, useEffect as useEffect8, useRef as useRef9, useSyncExternalStore as useSyncExternalStore10 } from "react";
13314
13329
  import {
13315
13330
  Badge as Badge7,
13331
+ Column as Column7,
13316
13332
  EmptyState as EmptyState8,
13317
13333
  ErrorState,
13318
13334
  IconButton as IconButton7,
13335
+ Input as Input5,
13319
13336
  LoadingState,
13320
13337
  Row as Row4,
13338
+ RowList as RowList4,
13321
13339
  ScrollArea as ScrollArea6,
13322
13340
  SearchField as SearchField5,
13323
13341
  StatusBar as StatusBar8,
13342
+ Text as Text7,
13343
+ TextButton as TextButton5,
13324
13344
  Toolbar as Toolbar7,
13325
- ToolbarGroup as ToolbarGroup5
13345
+ ToolbarGroup as ToolbarGroup5,
13346
+ VirtualWindow as VirtualWindow3
13326
13347
  } from "@xenosystem/components/chrome";
13348
+
13349
+ // src/edit/tree/declaration.ts
13327
13350
  import { computeWindow as computeWindow3 } from "@xenosystem/tree-core";
13328
- import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
13329
- function TreePanelView({
13330
- controller,
13331
- rowHeight = 24,
13332
- indentPx = 14,
13333
- overscan = 6,
13334
- showSearch = true,
13335
- openOn = "double",
13336
- columns,
13337
- emptyHint
13338
- }) {
13339
- const state = useSyncExternalStore10(controller.subscribe, controller.getState, controller.getState);
13340
- const scrollRef = useRef9(null);
13341
- const [viewport, setViewport] = useState2({ height: 0, scrollTop: 0 });
13342
- useEffect8(() => {
13343
- const element = scrollRef.current;
13344
- if (!element || typeof ResizeObserver === "undefined") return;
13345
- const observer = new ResizeObserver(() => {
13346
- setViewport((v) => ({ ...v, height: element.clientHeight }));
13347
- });
13348
- observer.observe(element);
13349
- setViewport((v) => ({ ...v, height: element.clientHeight }));
13350
- return () => observer.disconnect();
13351
- }, []);
13352
- const onScroll = useCallback8((event) => {
13353
- const el = event.currentTarget;
13354
- setViewport((v) => ({ ...v, scrollTop: el.scrollTop }));
13355
- }, []);
13351
+ var iconHalf = (icon) => icon.kind === "text" ? { iconName: null, iconText: icon.value } : { iconName: icon.name, iconText: null };
13352
+ function treeViewState(c, options = {}) {
13353
+ const { overscan = 6, showSearch = true, columns, emptyHint } = options;
13354
+ const state = c.getState();
13356
13355
  const source = state.source;
13357
13356
  const sourceState = source?.sourceState ?? "idle";
13358
- const body = (() => {
13359
- if (!source || sourceState === "idle") {
13360
- return /* @__PURE__ */ jsx12(EmptyState8, { title: "No source", hint: emptyHint ?? "Connect a source to browse it." });
13357
+ const rowHeight = c.getRowHeight();
13358
+ const viewport = c.getViewport();
13359
+ const noSource = !source || sourceState === "idle";
13360
+ const loading = !noSource && sourceState === "loading" && state.rows.length === 0;
13361
+ const failed = !noSource && (sourceState === "error" || sourceState === "denied");
13362
+ const denied = sourceState === "denied";
13363
+ const bare = !noSource && !loading && !failed && state.rows.length === 0;
13364
+ const noMatches = bare && state.filter !== "";
13365
+ const emptyTree = bare && state.filter === "";
13366
+ const listShown = !noSource && !loading && !failed && state.rows.length > 0;
13367
+ const window_ = computeWindow3({
13368
+ scrollTop: viewport.scrollTop,
13369
+ viewportHeight: viewport.height || rowHeight * 24,
13370
+ rowHeight,
13371
+ rowCount: state.rows.length,
13372
+ overscan
13373
+ });
13374
+ const drag = state.drag;
13375
+ const renaming = state.renaming;
13376
+ const rows = listShown ? state.rows.slice(window_.startIndex, window_.endIndex).map((row2) => {
13377
+ const node = row2.node;
13378
+ const drop = drag?.targetId === row2.id ? drag : null;
13379
+ const error = node.error;
13380
+ const chipLoading = row2.container && row2.childState === "loading";
13381
+ const chipError = row2.container && !chipLoading && row2.childState === "error";
13382
+ const chipLoadMore = row2.container && !chipLoading && !chipError && !!node.hasMore && row2.expanded;
13383
+ const chipTruncated = row2.container && !chipLoading && !chipError && !chipLoadMore && node.truncatedAt !== void 0;
13384
+ const chipEmpty = row2.container && !chipLoading && !chipError && !chipLoadMore && !chipTruncated && row2.expanded && isEmptyContainer(node);
13385
+ return {
13386
+ id: row2.id,
13387
+ label: node.label,
13388
+ depth: row2.depth,
13389
+ level: row2.depth + 1,
13390
+ height: rowHeight,
13391
+ selected: state.selection.includes(row2.id),
13392
+ disabled: node.caps?.select === false,
13393
+ expanded: row2.container ? row2.expanded : void 0,
13394
+ dropHint: drop?.position ?? null,
13395
+ dropTitle: drop?.reason,
13396
+ renaming: renaming?.id === row2.id,
13397
+ draft: renaming?.id === row2.id ? renaming.draft : "",
13398
+ ...iconHalf(node.icon ?? defaultIconFor(node.kind)),
13399
+ chipLoading,
13400
+ chipError,
13401
+ // The host's own sentence — never a regex over a message to guess the cause.
13402
+ chipErrorText: error?.code === "denied" ? "no access" : error?.code ?? "failed",
13403
+ chipLoadMore,
13404
+ chipTruncated,
13405
+ chipTruncatedText: `first ${node.truncatedAt ?? 0}`,
13406
+ chipEmpty,
13407
+ columns: (columns ?? []).map((key) => ({ key, text: node.columns?.[key] ?? "" })),
13408
+ glyphs: (node.glyphs ?? []).map((g) => ({ key: g.id, title: g.title, ...iconHalf(g.icon) })),
13409
+ badges: (node.badges ?? []).map((b) => ({ key: b.id, tone: b.tone, text: b.text })),
13410
+ actions: (node.actions ?? []).map((a) => ({ key: a.id, rowId: row2.id, actionId: a.id, label: a.label, disabled: !!a.disabled, ...iconHalf(a.icon) }))
13411
+ };
13412
+ }) : [];
13413
+ const count = state.rows.length;
13414
+ return {
13415
+ searchShown: showSearch,
13416
+ filter: state.filter,
13417
+ noSource,
13418
+ noSourceHint: emptyHint ?? "Connect a source to browse it.",
13419
+ loading,
13420
+ loadingLabel: source?.sourceMessage ?? "Loading\u2026",
13421
+ failed,
13422
+ failedTitle: denied ? "Access not granted" : "Could not read this source",
13423
+ // The HOST's sentence, verbatim. The panel never composes error prose.
13424
+ failedMessage: source?.sourceMessage,
13425
+ retryLabel: denied ? "Grant access" : "Retry",
13426
+ denied,
13427
+ noMatches,
13428
+ noMatchesHint: `Nothing matches \u201C${state.filter}\u201D.`,
13429
+ emptyTree,
13430
+ emptyHint: emptyHint ?? source?.sourceMessage ?? "This source has no items yet.",
13431
+ listShown,
13432
+ totalHeight: window_.totalHeight,
13433
+ offsetTop: window_.offsetTop,
13434
+ rows,
13435
+ countLabel: `${count} item${count === 1 ? "" : "s"}`,
13436
+ selectedLabel: state.selection.length > 0 ? `${state.selection.length} selected` : void 0
13437
+ };
13438
+ }
13439
+ function applyTreeUi(c, intent, options = {}) {
13440
+ switch (intent.kind) {
13441
+ case "filter":
13442
+ c.setFilter(intent.value);
13443
+ break;
13444
+ case "expandAll":
13445
+ c.expandAll();
13446
+ break;
13447
+ case "collapseAll":
13448
+ c.collapseAll();
13449
+ break;
13450
+ case "refresh":
13451
+ c.refresh();
13452
+ break;
13453
+ case "retry": {
13454
+ if (c.getState().source?.sourceState === "denied") c.requestConsent(void 0, "source denied");
13455
+ else c.refresh();
13456
+ break;
13361
13457
  }
13362
- if (sourceState === "loading" && state.rows.length === 0) {
13363
- return /* @__PURE__ */ jsx12(LoadingState, { label: source.sourceMessage ?? "Loading\u2026" });
13458
+ case "toggleExpanded": {
13459
+ const row2 = c.getState().rows.find((r) => r.id === intent.id);
13460
+ if (row2) c.setExpanded(intent.id, !row2.expanded);
13461
+ break;
13364
13462
  }
13365
- if (sourceState === "error" || sourceState === "denied") {
13366
- return /* @__PURE__ */ jsx12(
13367
- ErrorState,
13368
- {
13369
- title: sourceState === "denied" ? "Access not granted" : "Could not read this source",
13370
- message: source.sourceMessage,
13371
- onRetry: sourceState === "denied" ? () => controller.requestConsent(void 0, "source denied") : () => controller.refresh(),
13372
- retryLabel: sourceState === "denied" ? "Grant access" : "Retry"
13373
- }
13374
- );
13463
+ case "click": {
13464
+ c.select([intent.id], { toggle: !!intent.ctrlKey || !!intent.metaKey, range: !!intent.shiftKey });
13465
+ if ((options.openOn ?? "double") === "single") c.open(intent.id, { alt: !!intent.altKey });
13466
+ break;
13375
13467
  }
13376
- if (state.rows.length === 0) {
13377
- return state.filter ? /* @__PURE__ */ jsx12(EmptyState8, { title: "No matches", hint: `Nothing matches \u201C${state.filter}\u201D.` }) : /* @__PURE__ */ jsx12(
13378
- EmptyState8,
13379
- {
13380
- title: "Nothing here",
13381
- hint: emptyHint ?? source.sourceMessage ?? "This source has no items yet."
13382
- }
13383
- );
13468
+ case "doubleClick":
13469
+ if ((options.openOn ?? "double") === "double") c.open(intent.id);
13470
+ break;
13471
+ case "contextMenu": {
13472
+ const state = c.getState();
13473
+ c.requestContextMenu(state.selection.includes(intent.id) ? state.selection : [intent.id], intent.x, intent.y);
13474
+ break;
13384
13475
  }
13385
- const window2 = computeWindow3({
13386
- scrollTop: viewport.scrollTop,
13387
- viewportHeight: viewport.height || rowHeight * 24,
13388
- rowHeight,
13389
- rowCount: state.rows.length,
13390
- overscan
13391
- });
13392
- const slice = state.rows.slice(window2.startIndex, window2.endIndex);
13393
- return /* @__PURE__ */ jsx12(ScrollArea6, { children: /* @__PURE__ */ jsx12(
13394
- "div",
13476
+ case "loadMore":
13477
+ c.loadMore(intent.id);
13478
+ break;
13479
+ case "action":
13480
+ c.runAction(intent.id, intent.actionId);
13481
+ break;
13482
+ case "renameDraft":
13483
+ c.updateRenameDraft(intent.value);
13484
+ break;
13485
+ case "renameKey": {
13486
+ if (intent.key === "Enter") c.commitRename();
13487
+ else if (intent.key === "Escape") c.cancelRename();
13488
+ break;
13489
+ }
13490
+ case "commitRename":
13491
+ c.commitRename();
13492
+ break;
13493
+ }
13494
+ }
13495
+ var treeViewManifest = {
13496
+ id: treeManifest.id,
13497
+ version: "1.0.0",
13498
+ title: "Tree",
13499
+ category: "edit",
13500
+ layout: { slot: "side" },
13501
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The TreeViewModel the runtime derives from the controller on every draw." }],
13502
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, id?, value?, key?, actionId?, x?, y?, ctrlKey?, metaKey?, shiftKey?, altKey? } \u2014 applied by the runtime to the controller." }],
13503
+ commands: []
13504
+ };
13505
+ var EMPTY7 = {
13506
+ searchShown: true,
13507
+ filter: "",
13508
+ noSource: true,
13509
+ noSourceHint: "Connect a source to browse it.",
13510
+ loading: false,
13511
+ loadingLabel: "Loading\u2026",
13512
+ failed: false,
13513
+ failedTitle: "",
13514
+ retryLabel: "Retry",
13515
+ denied: false,
13516
+ noMatches: false,
13517
+ noMatchesHint: "",
13518
+ emptyTree: false,
13519
+ emptyHint: "",
13520
+ listShown: false,
13521
+ totalHeight: 0,
13522
+ offsetTop: 0,
13523
+ rows: [],
13524
+ countLabel: "0 items"
13525
+ };
13526
+ var BEHAVIOURS7 = {
13527
+ "edit/treePanelView": (inputs) => inputs.view ?? EMPTY7
13528
+ };
13529
+ var text7 = (b) => ({ component: "text", props: { text: b } });
13530
+ var ui7 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
13531
+ var nodeIcon = () => ({
13532
+ component: "fragment",
13533
+ children: [
13534
+ { component: "icons/TreeKind", when: { item: "iconName" }, props: { name: { item: "iconName" } } },
13535
+ { component: "chrome/Text", when: { item: "iconText" }, props: { role: { value: "caption" } }, children: [text7({ item: "iconText" })] }
13536
+ ]
13537
+ });
13538
+ var TREE_DECLARATION = {
13539
+ format: "xeno.block@1",
13540
+ manifest: treeViewManifest,
13541
+ behaviour: "edit/treePanelView",
13542
+ composition: {
13543
+ component: "chrome/Column",
13544
+ children: [
13395
13545
  {
13396
- ref: scrollRef,
13397
- onScroll,
13398
- style: { height: "100%", overflow: "auto" },
13399
- role: "tree",
13400
- "aria-multiselectable": true,
13401
- tabIndex: 0,
13402
- onKeyDown: (e) => {
13403
- if (controller.handleKey({ key: e.key, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, metaKey: e.metaKey, altKey: e.altKey })) {
13404
- e.preventDefault();
13546
+ component: "chrome/Toolbar",
13547
+ props: {
13548
+ left: { node: { component: "chrome/SearchField", when: { input: "view.searchShown" }, props: { value: { input: "view.filter" }, placeholder: { value: "Filter\u2026" } }, on: { onChange: ui7("filter", { value: { arg: "" } }) } } },
13549
+ right: {
13550
+ node: {
13551
+ component: "chrome/ToolbarGroup",
13552
+ props: { end: { value: true } },
13553
+ children: [
13554
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Expand" } }, label: { value: "Expand all" } }, on: { onClick: ui7("expandAll") } },
13555
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Collapse" } }, label: { value: "Collapse all" } }, on: { onClick: ui7("collapseAll") } },
13556
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Refresh" } }, label: { value: "Refresh" } }, on: { onClick: ui7("refresh") } }
13557
+ ]
13558
+ }
13405
13559
  }
13406
- },
13407
- children: /* @__PURE__ */ jsx12("div", { style: { height: window2.totalHeight, position: "relative" }, children: /* @__PURE__ */ jsx12("div", { style: { transform: `translateY(${window2.offsetTop}px)` }, children: slice.map((row2) => /* @__PURE__ */ jsx12(
13408
- TreeRowView,
13560
+ }
13561
+ },
13562
+ { component: "chrome/EmptyState", when: { input: "view.noSource" }, props: { title: { value: "No source" }, hint: { input: "view.noSourceHint" } } },
13563
+ { component: "chrome/LoadingState", when: { input: "view.loading" }, props: { label: { input: "view.loadingLabel" } } },
13564
+ { component: "chrome/ErrorState", when: { input: "view.failed" }, props: { title: { input: "view.failedTitle" }, message: { input: "view.failedMessage" }, retryLabel: { input: "view.retryLabel" } }, on: { onRetry: ui7("retry") } },
13565
+ { component: "chrome/EmptyState", when: { input: "view.noMatches" }, props: { title: { value: "No matches" }, hint: { input: "view.noMatchesHint" } } },
13566
+ { component: "chrome/EmptyState", when: { input: "view.emptyTree" }, props: { title: { value: "Nothing here" }, hint: { input: "view.emptyHint" } } },
13567
+ {
13568
+ component: "tree/Scroller",
13569
+ when: { input: "view.listShown" },
13570
+ children: [
13409
13571
  {
13410
- row: row2,
13411
- controller,
13412
- state,
13413
- rowHeight,
13414
- indentPx,
13415
- openOn,
13416
- columns
13417
- },
13418
- row2.id
13419
- )) }) })
13572
+ component: "chrome/RowList",
13573
+ props: { role: { value: "tree" }, "aria-label": { value: "Tree" }, "aria-multiselectable": { value: true } },
13574
+ children: [
13575
+ {
13576
+ component: "chrome/VirtualWindow",
13577
+ props: { totalHeight: { input: "view.totalHeight" }, offsetTop: { input: "view.offsetTop" } },
13578
+ children: [
13579
+ {
13580
+ component: "chrome/Row",
13581
+ each: { input: "view.rows", key: "id" },
13582
+ props: {
13583
+ "data-tree-row": { item: "id" },
13584
+ "aria-level": { item: "level" },
13585
+ height: { item: "height" },
13586
+ depth: { item: "depth" },
13587
+ selected: { item: "selected" },
13588
+ disabled: { item: "disabled" },
13589
+ expanded: { item: "expanded" },
13590
+ dropHint: { item: "dropHint" },
13591
+ title: { item: "dropTitle" },
13592
+ icon: { node: nodeIcon() },
13593
+ label: {
13594
+ node: {
13595
+ component: "fragment",
13596
+ children: [
13597
+ { component: "chrome/Input", when: { item: "renaming" }, props: { autoFocus: { value: true }, value: { item: "draft" }, label: { value: "Name" } }, on: { onChange: ui7("renameDraft", { value: { arg: "" } }), onBlur: ui7("commitRename"), onKeyDown: ui7("renameKey", { key: { arg: "key" } }) } },
13598
+ { component: "text", when: { not: { item: "renaming" } }, props: { text: { item: "label" } } }
13599
+ ]
13600
+ }
13601
+ },
13602
+ meta: {
13603
+ node: {
13604
+ component: "fragment",
13605
+ children: [
13606
+ { component: "chrome/Text", when: { item: "chipLoading" }, props: { role: { value: "caption" } }, children: [text7({ value: "loading\u2026" })] },
13607
+ { component: "chrome/Badge", when: { item: "chipError" }, props: { tone: { value: "error" } }, children: [text7({ item: "chipErrorText" })] },
13608
+ { component: "chrome/TextButton", when: { item: "chipLoadMore" }, children: [text7({ value: "load more\u2026" })], on: { onClick: ui7("loadMore", { id: { item: "id" } }) } },
13609
+ { component: "chrome/Badge", when: { item: "chipTruncated" }, props: { tone: { value: "warning" } }, children: [text7({ item: "chipTruncatedText" })] },
13610
+ { component: "chrome/Text", when: { item: "chipEmpty" }, props: { role: { value: "caption" } }, children: [text7({ value: "empty" })] }
13611
+ ]
13612
+ }
13613
+ },
13614
+ trailing: {
13615
+ node: {
13616
+ component: "fragment",
13617
+ children: [
13618
+ { component: "chrome/Text", each: { item: "columns", key: "key" }, props: { role: { value: "number" } }, children: [text7({ item: "text" })] },
13619
+ { component: "chrome/Text", each: { item: "glyphs", key: "key" }, props: { role: { value: "meta" }, title: { item: "title" } }, children: [nodeIcon()] },
13620
+ { component: "chrome/Badge", each: { item: "badges", key: "key" }, props: { tone: { item: "tone" } }, children: [text7({ item: "text" })] },
13621
+ { component: "chrome/IconButton", each: { item: "actions", key: "key" }, props: { size: { value: "sm" }, icon: { node: nodeIcon() }, label: { item: "label" }, disabled: { item: "disabled" } }, on: { onClick: ui7("action", { id: { item: "rowId" }, actionId: { item: "actionId" } }) } }
13622
+ ]
13623
+ }
13624
+ }
13625
+ },
13626
+ on: {
13627
+ onExpandedChange: ui7("toggleExpanded", { id: { item: "id" } }),
13628
+ onClick: ui7("click", { id: { item: "id" }, ctrlKey: { arg: "ctrlKey" }, metaKey: { arg: "metaKey" }, shiftKey: { arg: "shiftKey" }, altKey: { arg: "altKey" } }),
13629
+ onDoubleClick: ui7("doubleClick", { id: { item: "id" } }),
13630
+ onContextMenuAt: ui7("contextMenu", { id: { item: "id" }, x: { arg: "" }, y: { arg: "", index: 1 } })
13631
+ }
13632
+ }
13633
+ ]
13634
+ }
13635
+ ]
13636
+ }
13637
+ ]
13638
+ },
13639
+ {
13640
+ component: "chrome/StatusBar",
13641
+ props: {
13642
+ left: { node: { component: "chrome/Text", props: { role: { value: "number" } }, children: [text7({ input: "view.countLabel" })] } },
13643
+ right: { node: { component: "chrome/Text", when: { input: "view.selectedLabel" }, props: { role: { value: "meta" } }, children: [text7({ input: "view.selectedLabel" })] } }
13644
+ }
13420
13645
  }
13421
- ) });
13422
- })();
13423
- return /* @__PURE__ */ jsxs11("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
13646
+ ]
13647
+ }
13648
+ };
13649
+
13650
+ // src/edit/tree/react/TreePanelView.tsx
13651
+ import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
13652
+ var DRAG_THRESHOLD2 = 4;
13653
+ function TreePanelView({ controller, ...options }) {
13654
+ useSyncExternalStore10(controller.subscribe, controller.getState, controller.getState);
13655
+ const view = treeViewState(controller, options);
13656
+ const ui8 = (intent) => applyTreeUi(controller, intent, options);
13657
+ const icon = (name, textValue) => /* @__PURE__ */ jsxs11(Fragment8, { children: [
13658
+ name ? /* @__PURE__ */ jsx12(TreeIcon, { name }) : null,
13659
+ textValue ? /* @__PURE__ */ jsx12(Text7, { role: "caption", children: textValue }) : null
13660
+ ] });
13661
+ const row2 = (r) => /* @__PURE__ */ jsx12(
13662
+ Row4,
13663
+ {
13664
+ "data-tree-row": r.id,
13665
+ "aria-level": r.level,
13666
+ height: r.height,
13667
+ depth: r.depth,
13668
+ selected: r.selected,
13669
+ disabled: r.disabled,
13670
+ expanded: r.expanded,
13671
+ dropHint: r.dropHint,
13672
+ title: r.dropTitle,
13673
+ icon: icon(r.iconName, r.iconText),
13674
+ label: /* @__PURE__ */ jsxs11(Fragment8, { children: [
13675
+ r.renaming ? /* @__PURE__ */ jsx12(Input5, { autoFocus: true, value: r.draft, label: "Name", onChange: (value) => ui8({ kind: "renameDraft", value }), onBlur: () => ui8({ kind: "commitRename" }), onKeyDown: (e) => ui8({ kind: "renameKey", key: e.key }) }) : null,
13676
+ !r.renaming ? r.label : null
13677
+ ] }),
13678
+ meta: /* @__PURE__ */ jsxs11(Fragment8, { children: [
13679
+ r.chipLoading ? /* @__PURE__ */ jsx12(Text7, { role: "caption", children: "loading\u2026" }) : null,
13680
+ r.chipError ? /* @__PURE__ */ jsx12(Badge7, { tone: "error", children: r.chipErrorText }) : null,
13681
+ r.chipLoadMore ? /* @__PURE__ */ jsx12(TextButton5, { onClick: () => ui8({ kind: "loadMore", id: r.id }), children: "load more\u2026" }) : null,
13682
+ r.chipTruncated ? /* @__PURE__ */ jsx12(Badge7, { tone: "warning", children: r.chipTruncatedText }) : null,
13683
+ r.chipEmpty ? /* @__PURE__ */ jsx12(Text7, { role: "caption", children: "empty" }) : null
13684
+ ] }),
13685
+ trailing: /* @__PURE__ */ jsxs11(Fragment8, { children: [
13686
+ r.columns.map((c) => /* @__PURE__ */ jsx12(Text7, { role: "number", children: c.text }, c.key)),
13687
+ r.glyphs.map((g) => /* @__PURE__ */ jsx12(Text7, { role: "meta", title: g.title, children: icon(g.iconName, g.iconText) }, g.key)),
13688
+ r.badges.map((b) => /* @__PURE__ */ jsx12(Badge7, { tone: b.tone, children: b.text }, b.key)),
13689
+ r.actions.map((a) => /* @__PURE__ */ jsx12(IconButton7, { size: "sm", icon: icon(a.iconName, a.iconText), label: a.label, disabled: a.disabled, onClick: () => ui8({ kind: "action", id: a.rowId, actionId: a.actionId }) }, a.key))
13690
+ ] }),
13691
+ onExpandedChange: () => ui8({ kind: "toggleExpanded", id: r.id }),
13692
+ onClick: (e) => ui8({ kind: "click", id: r.id, ctrlKey: e.ctrlKey, metaKey: e.metaKey, shiftKey: e.shiftKey, altKey: e.altKey }),
13693
+ onDoubleClick: () => ui8({ kind: "doubleClick", id: r.id }),
13694
+ onContextMenuAt: (x, y) => ui8({ kind: "contextMenu", id: r.id, x, y })
13695
+ },
13696
+ r.id
13697
+ );
13698
+ return /* @__PURE__ */ jsxs11(Column7, { children: [
13424
13699
  /* @__PURE__ */ jsx12(
13425
13700
  Toolbar7,
13426
13701
  {
13427
- left: showSearch ? /* @__PURE__ */ jsx12(
13428
- SearchField5,
13429
- {
13430
- value: state.filter,
13431
- onChange: (term) => controller.setFilter(term),
13432
- placeholder: "Filter\u2026"
13433
- }
13434
- ) : void 0,
13702
+ left: view.searchShown ? /* @__PURE__ */ jsx12(SearchField5, { value: view.filter, placeholder: "Filter\u2026", onChange: (value) => ui8({ kind: "filter", value }) }) : null,
13435
13703
  right: /* @__PURE__ */ jsxs11(ToolbarGroup5, { end: true, children: [
13436
- /* @__PURE__ */ jsx12(IconButton7, { icon: /* @__PURE__ */ jsx12(ExpandGlyph, {}), label: "Expand all", onClick: () => controller.expandAll() }),
13437
- /* @__PURE__ */ jsx12(
13438
- IconButton7,
13439
- {
13440
- icon: /* @__PURE__ */ jsx12(CollapseGlyph, {}),
13441
- label: "Collapse all",
13442
- onClick: () => controller.collapseAll()
13443
- }
13444
- ),
13445
- /* @__PURE__ */ jsx12(IconButton7, { icon: /* @__PURE__ */ jsx12(RefreshGlyph, {}), label: "Refresh", onClick: () => controller.refresh() })
13704
+ /* @__PURE__ */ jsx12(IconButton7, { icon: /* @__PURE__ */ jsx12(ExpandGlyph, {}), label: "Expand all", onClick: () => ui8({ kind: "expandAll" }) }),
13705
+ /* @__PURE__ */ jsx12(IconButton7, { icon: /* @__PURE__ */ jsx12(CollapseGlyph, {}), label: "Collapse all", onClick: () => ui8({ kind: "collapseAll" }) }),
13706
+ /* @__PURE__ */ jsx12(IconButton7, { icon: /* @__PURE__ */ jsx12(RefreshGlyph, {}), label: "Refresh", onClick: () => ui8({ kind: "refresh" }) })
13446
13707
  ] })
13447
13708
  }
13448
13709
  ),
13449
- body,
13450
- /* @__PURE__ */ jsx12(
13451
- StatusBar8,
13452
- {
13453
- left: `${state.rows.length} item${state.rows.length === 1 ? "" : "s"}`,
13454
- right: state.selection.length > 0 ? `${state.selection.length} selected` : void 0
13455
- }
13456
- )
13710
+ view.noSource ? /* @__PURE__ */ jsx12(EmptyState8, { title: "No source", hint: view.noSourceHint }) : null,
13711
+ view.loading ? /* @__PURE__ */ jsx12(LoadingState, { label: view.loadingLabel }) : null,
13712
+ view.failed ? /* @__PURE__ */ jsx12(ErrorState, { title: view.failedTitle, message: view.failedMessage, retryLabel: view.retryLabel, onRetry: () => ui8({ kind: "retry" }) }) : null,
13713
+ view.noMatches ? /* @__PURE__ */ jsx12(EmptyState8, { title: "No matches", hint: view.noMatchesHint }) : null,
13714
+ view.emptyTree ? /* @__PURE__ */ jsx12(EmptyState8, { title: "Nothing here", hint: view.emptyHint }) : null,
13715
+ view.listShown ? /* @__PURE__ */ jsx12(TreeScroller, { controller, children: /* @__PURE__ */ jsx12(RowList4, { role: "tree", "aria-label": "Tree", "aria-multiselectable": true, children: /* @__PURE__ */ jsx12(VirtualWindow3, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map(row2) }) }) }) : null,
13716
+ /* @__PURE__ */ jsx12(StatusBar8, { left: /* @__PURE__ */ jsx12(Text7, { role: "number", children: view.countLabel }), right: view.selectedLabel ? /* @__PURE__ */ jsx12(Text7, { role: "meta", children: view.selectedLabel }) : null })
13457
13717
  ] });
13458
13718
  }
13459
- function TreeRowView({
13460
- row: row2,
13461
- controller,
13462
- state,
13463
- rowHeight,
13464
- indentPx,
13465
- openOn,
13466
- columns
13467
- }) {
13468
- const node = row2.node;
13469
- const selected = state.selection.includes(row2.id);
13470
- const renaming = state.renaming?.id === row2.id ? state.renaming : null;
13471
- const drop = state.drag?.targetId === row2.id ? state.drag : null;
13472
- const twisty = row2.container ? /* @__PURE__ */ jsx12(
13473
- "span",
13474
- {
13475
- role: "button",
13476
- "aria-label": row2.expanded ? "Collapse" : "Expand",
13477
- onClick: (e) => {
13478
- e.stopPropagation();
13479
- controller.setExpanded(row2.id, !row2.expanded);
13480
- },
13481
- style: {
13482
- display: "flex",
13483
- alignItems: "center",
13484
- justifyContent: "center",
13485
- width: 12,
13486
- flex: "0 0 auto",
13487
- cursor: "pointer",
13488
- transform: row2.expanded ? "rotate(90deg)" : void 0,
13489
- transition: "transform 80ms ease",
13490
- opacity: 0.7
13491
- },
13492
- children: /* @__PURE__ */ jsx12(ChevronGlyph, { size: 9 })
13493
- }
13494
- ) : (
13495
- // A leaf still reserves the twisty column, so labels stay in ONE column across mixed rows.
13496
- /* @__PURE__ */ jsx12("span", { style: { width: 12, flex: "0 0 auto" } })
13719
+ function TreeScroller({ controller, children }) {
13720
+ const scrollRef = useRef9(null);
13721
+ const pressRef = useRef9(null);
13722
+ useEffect8(() => {
13723
+ const el = scrollRef.current;
13724
+ if (!el) return;
13725
+ controller.setViewport(el.clientHeight, el.scrollTop);
13726
+ const RO = globalThis.ResizeObserver;
13727
+ if (!RO) return;
13728
+ const ro = new RO(() => controller.setViewport(el.clientHeight, el.scrollTop));
13729
+ ro.observe(el);
13730
+ return () => ro.disconnect();
13731
+ }, [controller]);
13732
+ const onScroll = useCallback8((e) => controller.setViewport(e.currentTarget.clientHeight, e.currentTarget.scrollTop), [controller]);
13733
+ const rowFromPoint = useCallback8((clientX, clientY) => {
13734
+ const el = document.elementFromPoint(clientX, clientY);
13735
+ const rowEl = el?.closest?.("[data-tree-row]");
13736
+ const id = rowEl?.getAttribute("data-tree-row");
13737
+ if (!rowEl || !id) return null;
13738
+ return { id, offsetY: clientY - rowEl.getBoundingClientRect().top };
13739
+ }, []);
13740
+ const onPointerDown = useCallback8(
13741
+ (e) => {
13742
+ if (e.button !== 0) return;
13743
+ const target = e.target;
13744
+ if (target?.closest?.("button,input")) return;
13745
+ const id = target?.closest?.("[data-tree-row]")?.getAttribute("data-tree-row");
13746
+ if (!id) return;
13747
+ const selection = controller.getState().selection;
13748
+ pressRef.current = { ids: selection.includes(id) ? selection : [id], x: e.clientX, y: e.clientY, dragging: false };
13749
+ e.currentTarget.setPointerCapture(e.pointerId);
13750
+ },
13751
+ [controller]
13497
13752
  );
13498
- return /* @__PURE__ */ jsx12(
13499
- "div",
13500
- {
13501
- style: {
13502
- position: "relative",
13503
- // The drop indicator is drawn on the row, not inserted into the list: inserting a
13504
- // placeholder row would change the flatten mid-drag and move the target out from under
13505
- // the pointer.
13506
- boxShadow: drop?.position === "into" ? "inset 0 0 0 1px var(--xeno-active)" : drop?.position === "before" ? "inset 0 1px 0 0 var(--xeno-active)" : drop?.position === "after" ? "inset 0 -1px 0 0 var(--xeno-active)" : void 0
13507
- },
13508
- title: drop?.reason,
13509
- children: /* @__PURE__ */ jsx12(
13510
- Row4,
13511
- {
13512
- role: "treeitem",
13513
- "aria-expanded": row2.container ? row2.expanded : void 0,
13514
- "aria-level": row2.depth + 1,
13515
- selected,
13516
- disabled: node.caps?.select === false,
13517
- depth: 0,
13518
- style: { height: rowHeight, paddingLeft: 6 + row2.depth * indentPx },
13519
- onClick: (e) => {
13520
- controller.select([row2.id], {
13521
- toggle: e.ctrlKey || e.metaKey,
13522
- range: e.shiftKey
13523
- });
13524
- if (openOn === "single") controller.open(row2.id, { alt: e.altKey });
13525
- },
13526
- onDoubleClick: () => {
13527
- if (openOn === "double") controller.open(row2.id);
13528
- },
13529
- onContextMenu: (e) => {
13530
- e.preventDefault();
13531
- controller.requestContextMenu(selected ? state.selection : [row2.id], e.clientX, e.clientY);
13532
- },
13533
- onPointerDown: (e) => {
13534
- if (e.button !== 0) return;
13535
- controller.beginDrag(selected ? state.selection : [row2.id]);
13536
- },
13537
- onPointerMove: (e) => {
13538
- if (!state.drag) return;
13539
- const rect = e.currentTarget.getBoundingClientRect();
13540
- controller.dragOver(row2.id, e.clientY - rect.top);
13541
- },
13542
- onPointerUp: () => {
13543
- if (state.drag) controller.endDrag();
13544
- },
13545
- icon: /* @__PURE__ */ jsxs11(Fragment8, { children: [
13546
- twisty,
13547
- /* @__PURE__ */ jsx12(NodeIcon, { icon: node.icon ?? defaultIconFor(node.kind) })
13548
- ] }),
13549
- label: renaming ? /* @__PURE__ */ jsx12(
13550
- "input",
13551
- {
13552
- autoFocus: true,
13553
- value: renaming.draft,
13554
- onChange: (e) => controller.updateRenameDraft(e.target.value),
13555
- onBlur: () => controller.commitRename(),
13556
- onFocus: (e) => e.currentTarget.select(),
13557
- onClick: (e) => e.stopPropagation(),
13558
- onKeyDown: (e) => {
13559
- e.stopPropagation();
13560
- if (e.key === "Enter") controller.commitRename();
13561
- else if (e.key === "Escape") controller.cancelRename();
13562
- },
13563
- style: {
13564
- width: "100%",
13565
- border: "none",
13566
- outline: "none",
13567
- background: "var(--xeno-canvas)",
13568
- color: "inherit",
13569
- font: "inherit",
13570
- padding: 0
13571
- }
13572
- }
13573
- ) : node.label,
13574
- meta: /* @__PURE__ */ jsx12(ChildStateChip, { row: row2, controller }),
13575
- trailing: /* @__PURE__ */ jsxs11(Fragment8, { children: [
13576
- columns?.map((columnId) => /* @__PURE__ */ jsx12("span", { style: { opacity: 0.7, fontVariantNumeric: "tabular-nums" }, children: node.columns?.[columnId] ?? "" }, columnId)),
13577
- node.glyphs?.map((glyph) => /* @__PURE__ */ jsx12("span", { title: glyph.title, style: { display: "flex", opacity: 0.7 }, children: /* @__PURE__ */ jsx12(NodeIcon, { icon: glyph.icon }) }, glyph.id)),
13578
- node.badges?.map((badge) => (
13579
- // Opaque: the host formatted the text, the panel only places it.
13580
- /* @__PURE__ */ jsx12(Badge7, { tone: badge.tone, className: badge.title ? void 0 : void 0, children: badge.text }, badge.id)
13581
- )),
13582
- node.actions?.map((action) => /* @__PURE__ */ jsx12(
13583
- IconButton7,
13584
- {
13585
- size: "sm",
13586
- icon: /* @__PURE__ */ jsx12(NodeIcon, { icon: action.icon }),
13587
- label: action.label,
13588
- disabled: action.disabled,
13589
- onClick: () => controller.runAction(row2.id, action.id)
13590
- },
13591
- action.id
13592
- ))
13593
- ] })
13594
- }
13595
- )
13596
- }
13753
+ const onPointerMove = useCallback8(
13754
+ (e) => {
13755
+ const press = pressRef.current;
13756
+ if (!press) return;
13757
+ if (!press.dragging) {
13758
+ if (Math.abs(e.clientX - press.x) + Math.abs(e.clientY - press.y) < DRAG_THRESHOLD2) return;
13759
+ press.dragging = true;
13760
+ controller.beginDrag(press.ids);
13761
+ }
13762
+ const hit = rowFromPoint(e.clientX, e.clientY);
13763
+ if (hit) controller.dragOver(hit.id, hit.offsetY);
13764
+ },
13765
+ [controller, rowFromPoint]
13597
13766
  );
13598
- }
13599
- function ChildStateChip({
13600
- row: row2,
13601
- controller
13602
- }) {
13603
- if (!row2.container) return null;
13604
- if (row2.childState === "loading") {
13605
- return /* @__PURE__ */ jsx12("span", { style: { opacity: 0.5, fontSize: 9 }, children: "loading\u2026" });
13606
- }
13607
- if (row2.childState === "error") {
13608
- const error = row2.node.error;
13609
- return /* @__PURE__ */ jsx12(Badge7, { tone: "error", children: error?.code === "denied" ? "no access" : error?.code ?? "failed" });
13610
- }
13611
- if (row2.node.hasMore && row2.expanded) {
13612
- return /* @__PURE__ */ jsx12(
13613
- "span",
13614
- {
13615
- role: "button",
13616
- onClick: (e) => {
13617
- e.stopPropagation();
13618
- controller.loadMore(row2.id);
13619
- },
13620
- style: { cursor: "pointer", opacity: 0.7, fontSize: 9 },
13621
- children: "load more\u2026"
13767
+ const onPointerUp = useCallback8(
13768
+ (e) => {
13769
+ const press = pressRef.current;
13770
+ pressRef.current = null;
13771
+ try {
13772
+ e.currentTarget.releasePointerCapture(e.pointerId);
13773
+ } catch {
13622
13774
  }
13623
- );
13624
- }
13625
- if (row2.node.truncatedAt !== void 0) {
13626
- return /* @__PURE__ */ jsx12(Badge7, { tone: "warning", children: `first ${row2.node.truncatedAt}` });
13627
- }
13628
- if (row2.expanded && isEmptyContainer(row2.node)) {
13629
- return /* @__PURE__ */ jsx12("span", { style: { opacity: 0.4, fontSize: 9 }, children: "empty" });
13630
- }
13631
- return null;
13775
+ if (press?.dragging) controller.endDrag();
13776
+ },
13777
+ [controller]
13778
+ );
13779
+ const onKeyDown = useCallback8(
13780
+ (e) => {
13781
+ if (controller.getState().renaming) return;
13782
+ if (controller.handleKey({ key: e.key, shiftKey: e.shiftKey, ctrlKey: e.ctrlKey, metaKey: e.metaKey, altKey: e.altKey })) {
13783
+ e.preventDefault();
13784
+ e.stopPropagation();
13785
+ }
13786
+ },
13787
+ [controller]
13788
+ );
13789
+ return /* @__PURE__ */ jsx12(ScrollArea6, { ref: scrollRef, onScroll, onKeyDown, onPointerDown, onPointerMove, onPointerUp, tabIndex: 0, children });
13632
13790
  }
13633
- function NodeIcon({ icon }) {
13634
- if (icon.kind === "text") {
13635
- return /* @__PURE__ */ jsx12("span", { style: { fontSize: 11, lineHeight: 1, display: "flex", alignItems: "center" }, children: icon.value });
13636
- }
13637
- return /* @__PURE__ */ jsx12(TreeIcon, { name: icon.name });
13791
+ var TREE_RUNTIME_UNITS = ["tree/Scroller"];
13792
+ function treeRuntimeComponents(controller) {
13793
+ return {
13794
+ "tree/Scroller": ({ children }) => /* @__PURE__ */ jsx12(TreeScroller, { controller, children })
13795
+ };
13638
13796
  }
13639
13797
 
13640
13798
  // src/edit/tree/panel.ts
@@ -13895,11 +14053,15 @@ export {
13895
14053
  NODEGRAPH_PANEL_ID,
13896
14054
  NodeGraphPanelView,
13897
14055
  SPACE_LABEL,
14056
+ BEHAVIOURS7 as TREE_BEHAVIOURS,
14057
+ TREE_DECLARATION,
13898
14058
  TREE_PANEL_ID,
14059
+ TREE_RUNTIME_UNITS,
13899
14060
  Thumbnail,
13900
14061
  TreeController,
13901
14062
  TreeIcon,
13902
14063
  TreePanelView,
14064
+ TreeScroller,
13903
14065
  VIEWPORT_PANEL_ID,
13904
14066
  VIEWPORT_SLOTS,
13905
14067
  VIEW_STATE_OPS,
@@ -13920,6 +14082,7 @@ export {
13920
14082
  applyLayersUi,
13921
14083
  applyPrecision,
13922
14084
  applyTangentDrag,
14085
+ applyTreeUi,
13923
14086
  ariaSliderProps,
13924
14087
  boxesIntersect,
13925
14088
  canConnect,
@@ -14081,6 +14244,9 @@ export {
14081
14244
  totalCount,
14082
14245
  treeManifest,
14083
14246
  treePanel,
14247
+ treeRuntimeComponents,
14248
+ treeViewManifest,
14249
+ treeViewState,
14084
14250
  usesLogScale,
14085
14251
  valueExtent,
14086
14252
  valueToPosition,