@xenosystem/blocks 0.12.5 → 0.12.7

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.
@@ -882,15 +882,15 @@ var LayersPanelController = class {
882
882
  requestVisibleThumbnails() {
883
883
  if (!this.thumbnailsEnabled) return;
884
884
  if (!this.getColumns().includes("thumbnail")) return;
885
- for (const row of this.getWindowRows()) {
886
- const key = row.node.thumbnailKey;
885
+ for (const row2 of this.getWindowRows()) {
886
+ const key = row2.node.thumbnailKey;
887
887
  if (!key || this.thumbnails.has(key) || this.requestedThumbs.has(key)) continue;
888
888
  this.requestedThumbs.add(key);
889
889
  const request = {
890
890
  key,
891
891
  width: this.thumbnailSize,
892
892
  height: this.thumbnailSize,
893
- nodeId: row.id
893
+ nodeId: row2.id
894
894
  };
895
895
  this.host.emit("thumbnailRequest", request);
896
896
  }
@@ -1216,22 +1216,22 @@ function layersViewState(c) {
1216
1216
  filter: c.getFilter(),
1217
1217
  totalHeight: window_.totalHeight,
1218
1218
  offsetTop: window_.offsetTop,
1219
- rows: c.getWindowRows().map((row) => {
1220
- const node = row.node;
1219
+ rows: c.getWindowRows().map((row2) => {
1220
+ const node = row2.node;
1221
1221
  const container = isContainer(node);
1222
1222
  return {
1223
- id: row.id,
1223
+ id: row2.id,
1224
1224
  name: node.name,
1225
1225
  title: node.name,
1226
- depth: row.depth,
1226
+ depth: row2.depth,
1227
1227
  height: rowHeight,
1228
- selected: c.isSelected(row.id),
1229
- active: activeId === row.id,
1230
- dimmed: !row.matched,
1231
- dropHint: drag?.target && drag.target.targetId === row.id ? drag.target.position : null,
1232
- expanded: container ? row.expanded : void 0,
1233
- renaming: renamingId === row.id,
1234
- notRenaming: renamingId !== row.id,
1228
+ selected: c.isSelected(row2.id),
1229
+ active: activeId === row2.id,
1230
+ dimmed: !row2.matched,
1231
+ dropHint: drag?.target && drag.target.targetId === row2.id ? drag.target.position : null,
1232
+ expanded: container ? row2.expanded : void 0,
1233
+ renaming: renamingId === row2.id,
1234
+ notRenaming: renamingId !== row2.id,
1235
1235
  hasThumbnail: columns.includes("thumbnail") && !!node.thumbnailKey,
1236
1236
  noThumbnail: !(columns.includes("thumbnail") && !!node.thumbnailKey),
1237
1237
  thumbnailKey: node.thumbnailKey,
@@ -1513,11 +1513,11 @@ function LayersPanelView({ controller }) {
1513
1513
  const getSnapshot = useCallback(() => controller.getRevision(), [controller]);
1514
1514
  useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
1515
1515
  const view = layersViewState(controller);
1516
- const ui4 = (intent) => applyLayersUi(controller, intent);
1516
+ const ui5 = (intent) => applyLayersUi(controller, intent);
1517
1517
  return /* @__PURE__ */ jsxs2(Column, { className: "xeno-layers-panel", children: [
1518
- view.searchShown ? /* @__PURE__ */ jsx3(Toolbar, { children: /* @__PURE__ */ jsx3(SearchField, { value: view.filter, onChange: (v) => ui4({ kind: "filter", value: v }), placeholder: "Search layers", label: "Search layers" }) }) : null,
1518
+ view.searchShown ? /* @__PURE__ */ jsx3(Toolbar, { children: /* @__PURE__ */ jsx3(SearchField, { value: view.filter, onChange: (v) => ui5({ kind: "filter", value: v }), placeholder: "Search layers", label: "Search layers" }) }) : null,
1519
1519
  /* @__PURE__ */ jsxs2(LayersScroller, { controller, children: [
1520
- /* @__PURE__ */ jsx3(RowList, { role: "tree", "aria-label": "Layers", children: /* @__PURE__ */ jsx3(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((row) => /* @__PURE__ */ jsx3(LayerRow, { row, thumbnailSize: view.thumbnailSize, controller }, row.id)) }) }),
1520
+ /* @__PURE__ */ jsx3(RowList, { role: "tree", "aria-label": "Layers", children: /* @__PURE__ */ jsx3(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((row2) => /* @__PURE__ */ jsx3(LayerRow, { row: row2, thumbnailSize: view.thumbnailSize, controller }, row2.id)) }) }),
1521
1521
  view.empty ? /* @__PURE__ */ jsx3(EmptyState, { title: "No layers" }) : null
1522
1522
  ] }),
1523
1523
  /* @__PURE__ */ jsx3(StatusBar, { left: /* @__PURE__ */ jsx3(Text, { role: "number", children: view.count }), right: view.orderLabel ? /* @__PURE__ */ jsx3(Text, { role: "meta", children: view.orderLabel }) : null })
@@ -1627,40 +1627,41 @@ function LayersRenameField({ id, controller }) {
1627
1627
  function LayersThumbnail({ thumbnailKey, size, controller }) {
1628
1628
  return /* @__PURE__ */ jsx3(Thumbnail, { size, data: controller.getThumbnail(thumbnailKey) });
1629
1629
  }
1630
- function LayerRow({ row, thumbnailSize, controller }) {
1631
- const ui4 = (intent) => applyLayersUi(controller, intent);
1630
+ function LayerRow({ row: row2, thumbnailSize, controller }) {
1631
+ const ui5 = (intent) => applyLayersUi(controller, intent);
1632
1632
  return /* @__PURE__ */ jsx3(
1633
1633
  Row,
1634
1634
  {
1635
- "data-layer-row": row.id,
1636
- height: row.height,
1637
- depth: row.depth,
1638
- selected: row.selected,
1639
- active: row.active,
1640
- dimmed: row.dimmed,
1641
- dropHint: row.dropHint,
1642
- expanded: row.expanded,
1643
- title: row.title,
1644
- icon: row.hasThumbnail ? /* @__PURE__ */ jsx3(LayersThumbnail, { thumbnailKey: row.thumbnailKey, size: thumbnailSize, controller }) : /* @__PURE__ */ jsx3(KindGlyph, {}),
1635
+ "data-layer-row": row2.id,
1636
+ height: row2.height,
1637
+ depth: row2.depth,
1638
+ selected: row2.selected,
1639
+ active: row2.active,
1640
+ dimmed: row2.dimmed,
1641
+ dropHint: row2.dropHint,
1642
+ expanded: row2.expanded,
1643
+ title: row2.title,
1644
+ icon: row2.hasThumbnail ? /* @__PURE__ */ jsx3(LayersThumbnail, { thumbnailKey: row2.thumbnailKey, size: thumbnailSize, controller }) : /* @__PURE__ */ jsx3(KindGlyph, {}),
1645
1645
  leading: /* @__PURE__ */ jsxs2(Fragment, { children: [
1646
- row.showVisibility ? /* @__PURE__ */ jsx3(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx3(VisibilityIcon, { open: row.visible }), label: row.visibilityLabel, active: row.visible, onClick: () => ui4({ kind: "toggleProp", id: row.id, prop: "visible" }) }) : null,
1647
- row.showLock ? /* @__PURE__ */ jsx3(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx3(LockStateIcon, { locked: row.locked }), label: row.lockLabel, active: row.locked, onClick: () => ui4({ kind: "toggleProp", id: row.id, prop: "locked" }) }) : null,
1648
- row.showSolo ? /* @__PURE__ */ jsx3(ChipToggle, { glyph: "S", label: "Solo", on: row.solo, onChange: () => ui4({ kind: "toggleProp", id: row.id, prop: "solo" }) }) : null,
1649
- row.showMute ? /* @__PURE__ */ jsx3(ChipToggle, { glyph: "M", label: "Mute", on: row.muted, onChange: () => ui4({ kind: "toggleProp", id: row.id, prop: "muted" }) }) : null,
1650
- row.showArm ? /* @__PURE__ */ jsx3(ChipToggle, { glyph: "R", label: "Arm", on: row.armed, onChange: () => ui4({ kind: "toggleProp", id: row.id, prop: "armed" }) }) : null,
1651
- row.showColor ? /* @__PURE__ */ jsx3(ColorTag, { color: row.colorLabel }) : null
1646
+ row2.showVisibility ? /* @__PURE__ */ jsx3(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx3(VisibilityIcon, { open: row2.visible }), label: row2.visibilityLabel, active: row2.visible, onClick: () => ui5({ kind: "toggleProp", id: row2.id, prop: "visible" }) }) : null,
1647
+ row2.showLock ? /* @__PURE__ */ jsx3(IconButton, { size: "sm", icon: /* @__PURE__ */ jsx3(LockStateIcon, { locked: row2.locked }), label: row2.lockLabel, active: row2.locked, onClick: () => ui5({ kind: "toggleProp", id: row2.id, prop: "locked" }) }) : null,
1648
+ row2.showSolo ? /* @__PURE__ */ jsx3(ChipToggle, { glyph: "S", label: "Solo", on: row2.solo, onChange: () => ui5({ kind: "toggleProp", id: row2.id, prop: "solo" }) }) : null,
1649
+ row2.showMute ? /* @__PURE__ */ jsx3(ChipToggle, { glyph: "M", label: "Mute", on: row2.muted, onChange: () => ui5({ kind: "toggleProp", id: row2.id, prop: "muted" }) }) : null,
1650
+ row2.showArm ? /* @__PURE__ */ jsx3(ChipToggle, { glyph: "R", label: "Arm", on: row2.armed, onChange: () => ui5({ kind: "toggleProp", id: row2.id, prop: "armed" }) }) : null,
1651
+ row2.showColor ? /* @__PURE__ */ jsx3(ColorTag, { color: row2.colorLabel }) : null
1652
1652
  ] }),
1653
- label: row.renaming ? /* @__PURE__ */ jsx3(LayersRenameField, { id: row.id, controller }) : row.name,
1653
+ label: row2.renaming ? /* @__PURE__ */ jsx3(LayersRenameField, { id: row2.id, controller }) : row2.name,
1654
1654
  trailing: /* @__PURE__ */ jsxs2(Fragment, { children: [
1655
- row.adornments.map((a) => /* @__PURE__ */ jsx3(Badge, { size: "sm", title: a.title, children: a.text }, a.key)),
1656
- row.showOpacity ? /* @__PURE__ */ jsx3(Text, { role: "number", children: row.opacity }) : null
1655
+ row2.adornments.map((a) => /* @__PURE__ */ jsx3(Badge, { size: "sm", title: a.title, children: a.text }, a.key)),
1656
+ row2.showOpacity ? /* @__PURE__ */ jsx3(Text, { role: "number", children: row2.opacity }) : null
1657
1657
  ] }),
1658
- onExpandedChange: () => ui4({ kind: "toggleExpanded", id: row.id }),
1659
- onDoubleClick: () => ui4({ kind: "beginRename", id: row.id }),
1660
- onContextMenuAt: (x, y) => ui4({ kind: "contextMenu", id: row.id, x, y })
1658
+ onExpandedChange: () => ui5({ kind: "toggleExpanded", id: row2.id }),
1659
+ onDoubleClick: () => ui5({ kind: "beginRename", id: row2.id }),
1660
+ onContextMenuAt: (x, y) => ui5({ kind: "contextMenu", id: row2.id, x, y })
1661
1661
  }
1662
1662
  );
1663
1663
  }
1664
+ var LAYERS_RUNTIME_UNITS = ["layers/Scroller", "layers/RenameField", "layers/Thumbnail", "layers/KindGlyph", "layers/VisibilityIcon", "layers/LockStateIcon"];
1664
1665
  function layersRuntimeComponents(controller) {
1665
1666
  return {
1666
1667
  "layers/Scroller": ({ children }) => /* @__PURE__ */ jsx3(LayersScroller, { controller, children }),
@@ -2051,8 +2052,8 @@ function constraintsOf(field) {
2051
2052
  scale: field.scale
2052
2053
  };
2053
2054
  }
2054
- function parseNumeric(text4) {
2055
- const trimmed = String(text4).trim();
2055
+ function parseNumeric(text5) {
2056
+ const trimmed = String(text5).trim();
2056
2057
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
2057
2058
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
2058
2059
  if (!match) return { ok: false, reason: "nan" };
@@ -2077,8 +2078,8 @@ function normalizeNumeric(value, c) {
2077
2078
  function formatNumeric(value, field) {
2078
2079
  if (!Number.isFinite(value)) return "";
2079
2080
  const precision = field.precision;
2080
- const text4 = typeof precision === "number" && Number.isFinite(precision) ? value.toFixed(Math.max(0, Math.floor(precision))) : String(value);
2081
- return field.unit ? `${text4}${field.unit}` : text4;
2081
+ const text5 = typeof precision === "number" && Number.isFinite(precision) ? value.toFixed(Math.max(0, Math.floor(precision))) : String(value);
2082
+ return field.unit ? `${text5}${field.unit}` : text5;
2082
2083
  }
2083
2084
  function usesLogScale(c) {
2084
2085
  return c.scale === "log" && typeof c.min === "number" && typeof c.max === "number" && c.min > 0 && c.max > c.min;
@@ -2412,44 +2413,44 @@ var InspectorPanelController = class {
2412
2413
  if (!field || !isFieldEditable(field)) return;
2413
2414
  const mixed = isFieldMixed(field);
2414
2415
  const value = this.getFieldValue(fieldId);
2415
- const text4 = mixed ? "" : field.type === "number" && typeof value === "number" ? formatNumeric(value, field) : value === void 0 || value === null ? "" : String(value);
2416
- this.drafts.set(fieldId, { text: text4, original: value });
2416
+ const text5 = mixed ? "" : field.type === "number" && typeof value === "number" ? formatNumeric(value, field) : value === void 0 || value === null ? "" : String(value);
2417
+ this.drafts.set(fieldId, { text: text5, original: value });
2417
2418
  this.focusedFieldId = fieldId;
2418
2419
  this.emitFieldFocus(fieldId);
2419
2420
  this.notify();
2420
2421
  }
2421
2422
  /** Update the draft text (typing). Emits nothing under the `blur` policy. */
2422
- setDraft(fieldId, text4) {
2423
+ setDraft(fieldId, text5) {
2423
2424
  const draft = this.drafts.get(fieldId);
2424
2425
  if (!draft) {
2425
- this.drafts.set(fieldId, { text: text4, original: this.getFieldValue(fieldId) });
2426
+ this.drafts.set(fieldId, { text: text5, original: this.getFieldValue(fieldId) });
2426
2427
  } else {
2427
- draft.text = text4;
2428
+ draft.text = text5;
2428
2429
  }
2429
2430
  const field = this.getField(fieldId);
2430
2431
  if (field && commitPolicyOf(field) === "live") {
2431
- const parsed = this.parseDraft(fieldId, text4);
2432
+ const parsed = this.parseDraft(fieldId, text5);
2432
2433
  if (parsed.ok) this.emitSet(fieldId, parsed.value);
2433
2434
  }
2434
2435
  this.notify();
2435
2436
  }
2436
2437
  /** Parse a draft string into a field-appropriate value. */
2437
- parseDraft(fieldId, text4) {
2438
+ parseDraft(fieldId, text5) {
2438
2439
  const field = this.getField(fieldId);
2439
2440
  if (!field) return { ok: false };
2440
2441
  if (field.type === "number") {
2441
- const parsed = parseNumeric(text4);
2442
+ const parsed = parseNumeric(text5);
2442
2443
  if (!parsed.ok) return { ok: false };
2443
2444
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOf(field)) };
2444
2445
  }
2445
2446
  if (field.type === "json") {
2446
2447
  try {
2447
- return { ok: true, value: JSON.parse(text4) };
2448
+ return { ok: true, value: JSON.parse(text5) };
2448
2449
  } catch {
2449
2450
  return { ok: false };
2450
2451
  }
2451
2452
  }
2452
- return { ok: true, value: text4 };
2453
+ return { ok: true, value: text5 };
2453
2454
  }
2454
2455
  /**
2455
2456
  * Commit the draft (Enter / blur). Invalid input reverts instead of committing garbage.
@@ -3424,7 +3425,7 @@ function NumberWidget({ field, controller, component }) {
3424
3425
  },
3425
3426
  [controller, editable, field.id, component]
3426
3427
  );
3427
- const text4 = component !== void 0 ? (() => {
3428
+ const text5 = component !== void 0 ? (() => {
3428
3429
  const raw = controller.getFieldValue(field.id);
3429
3430
  const v = Array.isArray(raw) ? raw[component] : void 0;
3430
3431
  return typeof v === "number" ? formatNumeric(v, field) : "";
@@ -3436,7 +3437,7 @@ function NumberWidget({ field, controller, component }) {
3436
3437
  Input2,
3437
3438
  {
3438
3439
  inputMode: "decimal",
3439
- value: text4,
3440
+ value: text5,
3440
3441
  placeholder: mixed ? controller.getMixedPlaceholder() : field.placeholder,
3441
3442
  mixed,
3442
3443
  readOnly: !editable,
@@ -3654,7 +3655,7 @@ function ActionGlyph({ glyph }) {
3654
3655
  return /* @__PURE__ */ jsx6("span", { "aria-hidden": true, children: glyph });
3655
3656
  }
3656
3657
  function SectionView({ section, controller }) {
3657
- const ui4 = (intent) => applyInspectorUi(controller, intent);
3658
+ const ui5 = (intent) => applyInspectorUi(controller, intent);
3658
3659
  const hasActions = section.actions.length > 0 || section.hasAddMenu;
3659
3660
  return /* @__PURE__ */ jsxs5(
3660
3661
  Section,
@@ -3663,51 +3664,51 @@ function SectionView({ section, controller }) {
3663
3664
  flush: true,
3664
3665
  collapsible: section.collapsible,
3665
3666
  collapsed: section.collapsed,
3666
- onCollapsedChange: () => ui4({ kind: "toggleSectionCollapsed", sectionId: section.id }),
3667
+ onCollapsedChange: () => ui5({ kind: "toggleSectionCollapsed", sectionId: section.id }),
3667
3668
  enabled: section.enabled,
3668
- onEnabledChange: () => ui4({ kind: "toggleSectionEnabled", sectionId: section.id }),
3669
+ onEnabledChange: () => ui5({ kind: "toggleSectionEnabled", sectionId: section.id }),
3669
3670
  badge: section.badge ? /* @__PURE__ */ jsx6(Badge2, { children: section.badge }) : null,
3670
3671
  actions: hasActions ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3671
- section.actions.map((action) => /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ActionGlyph, { glyph: action.glyph }), label: action.label, disabled: action.disabled, onClick: () => ui4({ kind: "runAction", sectionId: section.id, actionId: action.id }) }, action.id)),
3672
- section.hasAddMenu ? /* @__PURE__ */ jsx6(Select2, { size: "sm", placeholder: "\uFF0B", options: section.addOptions, value: "", label: section.addLabel, onChange: (id) => ui4({ kind: "addItem", sectionId: section.id, itemId: id }) }) : null
3672
+ section.actions.map((action) => /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ActionGlyph, { glyph: action.glyph }), label: action.label, disabled: action.disabled, onClick: () => ui5({ kind: "runAction", sectionId: section.id, actionId: action.id }) }, action.id)),
3673
+ section.hasAddMenu ? /* @__PURE__ */ jsx6(Select2, { size: "sm", placeholder: "\uFF0B", options: section.addOptions, value: "", label: section.addLabel, onChange: (id) => ui5({ kind: "addItem", sectionId: section.id, itemId: id }) }) : null
3673
3674
  ] }) : null,
3674
3675
  children: [
3675
3676
  section.showEmptyHint ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: section.emptyHint }) }) : null,
3676
- /* @__PURE__ */ jsx6(FieldGroup, { labelWidth: section.labelWidth, children: section.fields.map((row) => /* @__PURE__ */ jsx6(FieldRow, { row, controller }, row.id)) })
3677
+ /* @__PURE__ */ jsx6(FieldGroup, { labelWidth: section.labelWidth, children: section.fields.map((row2) => /* @__PURE__ */ jsx6(FieldRow, { row: row2, controller }, row2.id)) })
3677
3678
  ]
3678
3679
  }
3679
3680
  );
3680
3681
  }
3681
- function FieldRow({ row, controller }) {
3682
- const ui4 = (intent) => applyInspectorUi(controller, intent);
3682
+ function FieldRow({ row: row2, controller }) {
3683
+ const ui5 = (intent) => applyInspectorUi(controller, intent);
3683
3684
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
3684
3685
  /* @__PURE__ */ jsxs5(
3685
3686
  Field,
3686
3687
  {
3687
- label: row.label,
3688
- title: row.title,
3689
- focused: row.focused,
3690
- onLabelClick: () => ui4({ kind: "focusField", fieldId: row.id }),
3691
- onContextMenu: (x, y) => ui4({ kind: "contextMenu", fieldId: row.id, x, y }),
3692
- leading: row.showKeyframes ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3693
- /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(StopwatchIcon, {}), label: "Toggle animation", active: row.animated, onClick: () => ui4({ kind: "keyframe", fieldId: row.id, op: "toggleAnim" }) }),
3694
- row.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(PrevIcon, {}), label: "Previous keyframe", disabled: row.prevDisabled, onClick: () => ui4({ kind: "keyframe", fieldId: row.id, op: "prev" }) }) : null,
3695
- row.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(DiamondIcon, { filled: row.onKeyframe }), label: row.keyframeLabel, onClick: () => ui4({ kind: "keyframe", fieldId: row.id, op: row.keyframeOp }) }) : null,
3696
- row.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(NextIcon, {}), label: "Next keyframe", disabled: row.nextDisabled, onClick: () => ui4({ kind: "keyframe", fieldId: row.id, op: "next" }) }) : null
3688
+ label: row2.label,
3689
+ title: row2.title,
3690
+ focused: row2.focused,
3691
+ onLabelClick: () => ui5({ kind: "focusField", fieldId: row2.id }),
3692
+ onContextMenu: (x, y) => ui5({ kind: "contextMenu", fieldId: row2.id, x, y }),
3693
+ leading: row2.showKeyframes ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
3694
+ /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(StopwatchIcon, {}), label: "Toggle animation", active: row2.animated, onClick: () => ui5({ kind: "keyframe", fieldId: row2.id, op: "toggleAnim" }) }),
3695
+ row2.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(PrevIcon, {}), label: "Previous keyframe", disabled: row2.prevDisabled, onClick: () => ui5({ kind: "keyframe", fieldId: row2.id, op: "prev" }) }) : null,
3696
+ row2.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(DiamondIcon, { filled: row2.onKeyframe }), label: row2.keyframeLabel, onClick: () => ui5({ kind: "keyframe", fieldId: row2.id, op: row2.keyframeOp }) }) : null,
3697
+ row2.animated ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(NextIcon, {}), label: "Next keyframe", disabled: row2.nextDisabled, onClick: () => ui5({ kind: "keyframe", fieldId: row2.id, op: "next" }) }) : null
3697
3698
  ] }) : null,
3698
3699
  trailing: /* @__PURE__ */ jsxs5(Fragment3, { children: [
3699
- row.showEasing ? /* @__PURE__ */ jsx6(Select2, { size: "sm", options: row.easingOptions, value: row.easing, label: "Easing", onChange: (v) => ui4({ kind: "setEasing", fieldId: row.id, easing: v }) }) : null,
3700
- row.showKeyframes ? /* @__PURE__ */ jsx6(Badge2, { size: "sm", children: row.keyframeChip }) : null,
3701
- row.showToken ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(LinkIcon, {}), label: row.tokenActionLabel, active: row.bound, onClick: () => ui4({ kind: "bindToken", fieldId: row.id }) }) : null,
3702
- row.showReset ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ResetIcon, {}), label: "Reset to default", onClick: () => ui4({ kind: "resetField", fieldId: row.id }) }) : null
3700
+ row2.showEasing ? /* @__PURE__ */ jsx6(Select2, { size: "sm", options: row2.easingOptions, value: row2.easing, label: "Easing", onChange: (v) => ui5({ kind: "setEasing", fieldId: row2.id, easing: v }) }) : null,
3701
+ row2.showKeyframes ? /* @__PURE__ */ jsx6(Badge2, { size: "sm", children: row2.keyframeChip }) : null,
3702
+ row2.showToken ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(LinkIcon, {}), label: row2.tokenActionLabel, active: row2.bound, onClick: () => ui5({ kind: "bindToken", fieldId: row2.id }) }) : null,
3703
+ row2.showReset ? /* @__PURE__ */ jsx6(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx6(ResetIcon, {}), label: "Reset to default", onClick: () => ui5({ kind: "resetField", fieldId: row2.id }) }) : null
3703
3704
  ] }),
3704
3705
  children: [
3705
- row.bound ? /* @__PURE__ */ jsx6(Badge2, { title: row.tokenTitle, children: row.tokenLabel }) : null,
3706
- row.unbound ? /* @__PURE__ */ jsx6(FieldWidget, { field: row.field, controller }) : null
3706
+ row2.bound ? /* @__PURE__ */ jsx6(Badge2, { title: row2.tokenTitle, children: row2.tokenLabel }) : null,
3707
+ row2.unbound ? /* @__PURE__ */ jsx6(FieldWidget, { field: row2.field, controller }) : null
3707
3708
  ]
3708
3709
  }
3709
3710
  ),
3710
- row.showInfoNote ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: row.infoNote }) }) : null
3711
+ row2.showInfoNote ? /* @__PURE__ */ jsx6(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx6(Text3, { role: "caption", children: row2.infoNote }) }) : null
3711
3712
  ] });
3712
3713
  }
3713
3714
  function FieldWidget({
@@ -3748,6 +3749,7 @@ function FieldWidget({
3748
3749
  return /* @__PURE__ */ jsx6(JsonWidget, { field, controller });
3749
3750
  }
3750
3751
  }
3752
+ var INSPECTOR_RUNTIME_UNITS = ["inspector/FieldWidget", "inspector/Properties", "inspector/ActionGlyph", "inspector/StopwatchIcon", "inspector/DiamondIcon", "inspector/PrevIcon", "inspector/NextIcon", "inspector/LinkIcon", "inspector/ResetIcon"];
3751
3753
  function inspectorRuntimeComponents(controller) {
3752
3754
  return {
3753
3755
  "inspector/FieldWidget": ({ field }) => /* @__PURE__ */ jsx6(FieldWidget, { field, controller }),
@@ -4498,9 +4500,9 @@ var HistoryPanelController = class {
4498
4500
  case "ArrowUp": {
4499
4501
  const delta = event.key === "ArrowDown" ? 1 : -1;
4500
4502
  const next = focusIndex < 0 ? 0 : Math.min(rows.length - 1, Math.max(0, focusIndex + delta));
4501
- const row = rows[next];
4502
- if (row) {
4503
- this.focusedEntryId = row.entryId;
4503
+ const row2 = rows[next];
4504
+ if (row2) {
4505
+ this.focusedEntryId = row2.entryId;
4504
4506
  this.scrollIndexIntoView(next, rows.length);
4505
4507
  }
4506
4508
  this.notify();
@@ -4509,9 +4511,9 @@ var HistoryPanelController = class {
4509
4511
  case "Home":
4510
4512
  case "End": {
4511
4513
  const index = event.key === "Home" ? 0 : rows.length - 1;
4512
- const row = rows[index];
4513
- if (row) {
4514
- this.focusedEntryId = row.entryId;
4514
+ const row2 = rows[index];
4515
+ if (row2) {
4516
+ this.focusedEntryId = row2.entryId;
4515
4517
  this.scrollIndexIntoView(index, rows.length);
4516
4518
  }
4517
4519
  this.notify();
@@ -4751,22 +4753,22 @@ function historyViewState(c) {
4751
4753
  pruned: c.isPruned(),
4752
4754
  totalHeight: window_.totalHeight,
4753
4755
  offsetTop: window_.offsetTop,
4754
- rows: c.getWindowRows().map((row) => {
4755
- const entry = row.entry;
4756
+ rows: c.getWindowRows().map((row2) => {
4757
+ const entry = row2.entry;
4756
4758
  const pending = entry?.status === "pending";
4757
4759
  const source = entry?.source;
4758
4760
  return {
4759
- key: row.entryId ?? "__original__",
4760
- entryId: row.entryId,
4761
- label: row.label,
4762
- meta: row.kind === "entry" ? row.ordinal : void 0,
4763
- current: row.current,
4764
- future: row.future,
4765
- focused: focused === row.entryId,
4761
+ key: row2.entryId ?? "__original__",
4762
+ entryId: row2.entryId,
4763
+ label: row2.label,
4764
+ meta: row2.kind === "entry" ? row2.ordinal : void 0,
4765
+ current: row2.current,
4766
+ future: row2.future,
4767
+ focused: focused === row2.entryId,
4766
4768
  jumpable: jumpAllowed && entry?.caps?.jumpable !== false,
4767
4769
  showIcon: showIcons,
4768
4770
  hideIcon: !showIcons,
4769
- iconName: row.kind === "original" ? "original" : c.getIconName(entry?.kind),
4771
+ iconName: row2.kind === "original" ? "original" : c.getIconName(entry?.kind),
4770
4772
  pending,
4771
4773
  failed: entry?.status === "failed",
4772
4774
  showSource: showSource && !!source && source !== "user",
@@ -5046,26 +5048,26 @@ function HistoryPanelView({ controller }) {
5046
5048
  const getSnapshot = useCallback4(() => controller.getRevision(), [controller]);
5047
5049
  useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
5048
5050
  const view = historyViewState(controller);
5049
- const ui4 = (intent) => applyHistoryUi(controller, intent);
5051
+ const ui5 = (intent) => applyHistoryUi(controller, intent);
5050
5052
  return /* @__PURE__ */ jsxs7(Column3, { className: "xeno-history-panel", children: [
5051
5053
  /* @__PURE__ */ jsx8(
5052
5054
  Toolbar2,
5053
5055
  {
5054
5056
  left: /* @__PURE__ */ jsxs7(Fragment4, { children: [
5055
- /* @__PURE__ */ jsx8(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx8(UndoIcon, {}), label: "Undo", disabled: view.cannotUndo, onClick: () => ui4({ kind: "undo" }) }),
5056
- /* @__PURE__ */ jsx8(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx8(RedoIcon, {}), label: "Redo", disabled: view.cannotRedo, onClick: () => ui4({ kind: "redo" }) })
5057
+ /* @__PURE__ */ jsx8(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx8(UndoIcon, {}), label: "Undo", disabled: view.cannotUndo, onClick: () => ui5({ kind: "undo" }) }),
5058
+ /* @__PURE__ */ jsx8(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx8(RedoIcon, {}), label: "Redo", disabled: view.cannotRedo, onClick: () => ui5({ kind: "redo" }) })
5057
5059
  ] }),
5058
5060
  right: /* @__PURE__ */ jsxs7(Fragment4, { children: [
5059
5061
  /* @__PURE__ */ jsx8(Text4, { role: "number", children: view.counter }),
5060
- view.clearAllowed ? /* @__PURE__ */ jsx8(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx8(TrashIcon, {}), label: "Clear history", onClick: () => ui4({ kind: "requestClear" }) }) : null
5062
+ view.clearAllowed ? /* @__PURE__ */ jsx8(IconButton4, { size: "sm", icon: /* @__PURE__ */ jsx8(TrashIcon, {}), label: "Clear history", onClick: () => ui5({ kind: "requestClear" }) }) : null
5061
5063
  ] }),
5062
- children: view.searchShown ? /* @__PURE__ */ jsx8(SearchField3, { value: view.filter, onChange: (v) => ui4({ kind: "filter", value: v }), placeholder: "Search history", label: "Search history" }) : null
5064
+ children: view.searchShown ? /* @__PURE__ */ jsx8(SearchField3, { value: view.filter, onChange: (v) => ui5({ kind: "filter", value: v }), placeholder: "Search history", label: "Search history" }) : null
5063
5065
  }
5064
5066
  ),
5065
- view.confirmingClear ? /* @__PURE__ */ jsx8(InlineConfirm, { message: "Clear all history?", label: "Confirm clear history", confirmLabel: "Clear", onConfirm: () => ui4({ kind: "confirmClear" }), onCancel: () => ui4({ kind: "cancelClear" }) }) : null,
5067
+ view.confirmingClear ? /* @__PURE__ */ jsx8(InlineConfirm, { message: "Clear all history?", label: "Confirm clear history", confirmLabel: "Clear", onConfirm: () => ui5({ kind: "confirmClear" }), onCancel: () => ui5({ kind: "cancelClear" }) }) : null,
5066
5068
  view.pruned ? /* @__PURE__ */ jsx8(Text4, { role: "caption", children: "Earlier history discarded (limit reached)" }) : null,
5067
5069
  /* @__PURE__ */ jsxs7(HistoryScroller, { controller, children: [
5068
- /* @__PURE__ */ jsx8(RowList2, { role: "listbox", "aria-label": "History", children: /* @__PURE__ */ jsx8(VirtualWindow2, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((row) => /* @__PURE__ */ jsx8(HistoryRowView, { row, controller }, row.key)) }) }),
5070
+ /* @__PURE__ */ jsx8(RowList2, { role: "listbox", "aria-label": "History", children: /* @__PURE__ */ jsx8(VirtualWindow2, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((row2) => /* @__PURE__ */ jsx8(HistoryRowView, { row: row2, controller }, row2.key)) }) }),
5069
5071
  view.empty ? /* @__PURE__ */ jsx8(EmptyState3, { title: "No history" }) : null
5070
5072
  ] }),
5071
5073
  view.hasMemory ? /* @__PURE__ */ jsx8(StatusBar2, { left: /* @__PURE__ */ jsx8(Text4, { role: "number", children: view.memoryText }), right: view.memoryDisk ? /* @__PURE__ */ jsx8(Text4, { role: "meta", children: view.memoryDisk }) : null }) : null
@@ -5119,36 +5121,37 @@ function HistoryCancelButton({ entryId, controller }) {
5119
5121
  }
5120
5122
  );
5121
5123
  }
5122
- function HistoryRowView({ row, controller }) {
5123
- const ui4 = (intent) => applyHistoryUi(controller, intent);
5124
+ function HistoryRowView({ row: row2, controller }) {
5125
+ const ui5 = (intent) => applyHistoryUi(controller, intent);
5124
5126
  return /* @__PURE__ */ jsx8(
5125
5127
  Row2,
5126
5128
  {
5127
- height: row.height,
5128
- selected: row.current,
5129
- focused: row.focused,
5130
- struck: row.future,
5131
- noIcon: row.hideIcon,
5132
- interactive: row.jumpable,
5133
- icon: /* @__PURE__ */ jsx8(EntryIcon, { name: row.iconName }),
5134
- label: row.label,
5135
- meta: row.meta,
5136
- "data-history-row": row.key,
5137
- title: row.label,
5129
+ height: row2.height,
5130
+ selected: row2.current,
5131
+ focused: row2.focused,
5132
+ struck: row2.future,
5133
+ noIcon: row2.hideIcon,
5134
+ interactive: row2.jumpable,
5135
+ icon: /* @__PURE__ */ jsx8(EntryIcon, { name: row2.iconName }),
5136
+ label: row2.label,
5137
+ meta: row2.meta,
5138
+ "data-history-row": row2.key,
5139
+ title: row2.label,
5138
5140
  trailing: /* @__PURE__ */ jsxs7(Fragment4, { children: [
5139
- row.pending ? /* @__PURE__ */ jsx8(PendingGlyph, {}) : null,
5140
- row.failed ? /* @__PURE__ */ jsx8(Badge3, { size: "sm", tone: "error", children: "failed" }) : null,
5141
- row.showSource ? /* @__PURE__ */ jsx8(Badge3, { size: "sm", title: row.sourceTitle, children: row.sourceGlyph }) : null,
5142
- row.showCoalesced ? /* @__PURE__ */ jsx8(Badge3, { size: "sm", title: row.coalescedTitle, children: row.coalescedText }) : null,
5143
- row.showTime ? /* @__PURE__ */ jsx8(Text4, { role: "meta", children: row.time }) : null,
5144
- row.pending ? /* @__PURE__ */ jsx8(HistoryCancelButton, { entryId: row.cancelId, controller }) : null
5141
+ row2.pending ? /* @__PURE__ */ jsx8(PendingGlyph, {}) : null,
5142
+ row2.failed ? /* @__PURE__ */ jsx8(Badge3, { size: "sm", tone: "error", children: "failed" }) : null,
5143
+ row2.showSource ? /* @__PURE__ */ jsx8(Badge3, { size: "sm", title: row2.sourceTitle, children: row2.sourceGlyph }) : null,
5144
+ row2.showCoalesced ? /* @__PURE__ */ jsx8(Badge3, { size: "sm", title: row2.coalescedTitle, children: row2.coalescedText }) : null,
5145
+ row2.showTime ? /* @__PURE__ */ jsx8(Text4, { role: "meta", children: row2.time }) : null,
5146
+ row2.pending ? /* @__PURE__ */ jsx8(HistoryCancelButton, { entryId: row2.cancelId, controller }) : null
5145
5147
  ] }),
5146
- onClick: () => ui4({ kind: "jump", entryId: row.entryId }),
5147
- onMouseEnter: () => ui4({ kind: "hover", entryId: row.entryId }),
5148
- onContextMenuAt: (x, y) => ui4({ kind: "contextMenu", entryId: row.entryId, x, y })
5148
+ onClick: () => ui5({ kind: "jump", entryId: row2.entryId }),
5149
+ onMouseEnter: () => ui5({ kind: "hover", entryId: row2.entryId }),
5150
+ onContextMenuAt: (x, y) => ui5({ kind: "contextMenu", entryId: row2.entryId, x, y })
5149
5151
  }
5150
5152
  );
5151
5153
  }
5154
+ var HISTORY_RUNTIME_UNITS = ["history/Scroller", "history/CancelButton", "history/EntryIcon", "history/PendingGlyph", "history/UndoIcon", "history/RedoIcon", "history/TrashIcon"];
5152
5155
  function historyRuntimeComponents(controller) {
5153
5156
  return {
5154
5157
  "history/Scroller": ({ children }) => /* @__PURE__ */ jsx8(HistoryScroller, { controller, children }),
@@ -6183,6 +6186,181 @@ var ColorPanelController = class {
6183
6186
  import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef5 } from "react";
6184
6187
  import { useSyncExternalStore as useSyncExternalStore4 } from "react";
6185
6188
 
6189
+ // src/edit/color/declaration.ts
6190
+ var SPACE_LABEL = { hex: "HEX", rgb: "RGB", hsl: "HSL", hsv: "HSV", oklch: "OKLCH" };
6191
+ var COMPONENT_LABELS = { rgb: ["R", "G", "B"], hsl: ["H", "S", "L"], oklch: ["L", "C", "H"] };
6192
+ function roundForSpace(space, v) {
6193
+ if (space === "oklch") return Math.round(v * 10) / 10;
6194
+ return Math.round(v);
6195
+ }
6196
+ function colorViewState(c) {
6197
+ const value = c.getColorValue();
6198
+ const space = c.getSpace();
6199
+ const comps = value.components ?? [];
6200
+ const labels = COMPONENT_LABELS[space] ?? [];
6201
+ const active = c.getActiveChannel();
6202
+ return {
6203
+ hex: value.hex,
6204
+ bound: c.isBound(),
6205
+ fgBg: c.getChannelsMode() === "fg-bg",
6206
+ fgActive: active === "fg",
6207
+ bgActive: active === "bg",
6208
+ space,
6209
+ spaceOptions: EDIT_SPACES.map((sp) => ({ value: sp, label: SPACE_LABEL[sp] })),
6210
+ isHex: space === "hex",
6211
+ isComponents: space !== "hex",
6212
+ components: labels.map((label, index) => ({ key: label, label, index, value: roundForSpace(space, comps[index] ?? 0) })),
6213
+ alphaShown: c.isAlphaShown(),
6214
+ alphaPercent: Math.round(value.alpha * 100),
6215
+ hasTokens: c.hasTokens(),
6216
+ boundTokenId: c.getBoundTokenId() ?? "",
6217
+ tokenOptions: c.getTokens().map((t) => ({ value: t.id, label: t.name })),
6218
+ swatches: c.getSwatches().map((hex) => ({ key: hex, hex })),
6219
+ hasRecent: c.getRecent().length > 0,
6220
+ recent: c.getRecent().map((hex, i) => ({ key: `${hex}-${i}`, hex }))
6221
+ };
6222
+ }
6223
+ function applyColorUi(c, intent) {
6224
+ switch (intent.kind) {
6225
+ case "setChannel":
6226
+ c.setChannel(intent.channel);
6227
+ break;
6228
+ case "swapChannels":
6229
+ c.swapChannels();
6230
+ break;
6231
+ case "setSpace":
6232
+ c.setSpace(intent.space);
6233
+ break;
6234
+ case "setHex":
6235
+ c.setFromColor(intent.value, void 0, { commit: true });
6236
+ break;
6237
+ case "setComponent": {
6238
+ const comps = [...c.getColorValue().components ?? [0, 0, 0]];
6239
+ comps[intent.index] = intent.value;
6240
+ c.setFromComponents(c.getSpace(), comps, { commit: true });
6241
+ break;
6242
+ }
6243
+ case "setAlpha":
6244
+ c.setAlpha(intent.percent / 100, { commit: true });
6245
+ break;
6246
+ case "eyedropper":
6247
+ c.requestEyedropper();
6248
+ break;
6249
+ case "bindToken":
6250
+ if (intent.tokenId) c.bindToken(intent.tokenId);
6251
+ else c.unbindToken();
6252
+ break;
6253
+ case "unbindToken":
6254
+ c.unbindToken();
6255
+ break;
6256
+ case "addSwatch":
6257
+ void c.addSwatch();
6258
+ break;
6259
+ case "selectColor":
6260
+ c.selectColor(intent.hex);
6261
+ break;
6262
+ case "removeSwatch":
6263
+ void c.removeSwatch(intent.hex);
6264
+ break;
6265
+ }
6266
+ }
6267
+ var colorViewManifest = {
6268
+ id: colorManifest.id,
6269
+ version: "1.0.0",
6270
+ title: "Color",
6271
+ category: "edit",
6272
+ layout: { slot: "side" },
6273
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The ColorViewState the runtime derives from the controller on every draw." }],
6274
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
6275
+ commands: []
6276
+ };
6277
+ var EMPTY4 = { hex: "", bound: false, fgBg: false, fgActive: true, bgActive: false, space: "hex", spaceOptions: EDIT_SPACES.map((sp) => ({ value: sp, label: SPACE_LABEL[sp] })), isHex: true, isComponents: false, components: [], alphaShown: false, alphaPercent: 100, hasTokens: false, boundTokenId: "", tokenOptions: [], swatches: [], hasRecent: false, recent: [] };
6278
+ var BEHAVIOURS4 = {
6279
+ "edit/colorPanelView": (inputs) => inputs.view ?? EMPTY4
6280
+ };
6281
+ var text4 = (b) => ({ component: "text", props: { text: b } });
6282
+ var ui4 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
6283
+ var row = (children, extra = {}, when) => ({
6284
+ component: "chrome/Stack",
6285
+ ...when ? { when } : {},
6286
+ props: { direction: { value: "row" }, pad: { value: "none" }, ...extra },
6287
+ children
6288
+ });
6289
+ var COLOR_DECLARATION = {
6290
+ format: "xeno.block@1",
6291
+ manifest: colorViewManifest,
6292
+ behaviour: "edit/colorPanelView",
6293
+ composition: {
6294
+ component: "chrome/Column",
6295
+ props: { className: { value: "xeno-color-panel" } },
6296
+ children: [
6297
+ {
6298
+ component: "chrome/Stack",
6299
+ props: { gap: { value: 8 } },
6300
+ children: [
6301
+ row(
6302
+ [
6303
+ { component: "chrome/ColorChip", props: { color: { input: "view.hex" }, label: { value: "Foreground" }, pressed: { input: "view.fgActive" } }, on: { onClick: ui4("setChannel", { channel: { value: "fg" } }) } },
6304
+ { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "color/SwapIcon" } }, label: { value: "Swap FG/BG (X)" } }, on: { onClick: ui4("swapChannels") } },
6305
+ { component: "chrome/ColorChip", props: { color: { value: null }, label: { value: "Background" }, pressed: { input: "view.bgActive" } }, on: { onClick: ui4("setChannel", { channel: { value: "bg" } }) } }
6306
+ ],
6307
+ {},
6308
+ { input: "view.fgBg" }
6309
+ ),
6310
+ { component: "color/Picker" },
6311
+ row(
6312
+ [
6313
+ { component: "chrome/Select", props: { size: { value: "sm" }, label: { value: "Color model" }, value: { input: "view.space" }, options: { input: "view.spaceOptions" } }, on: { onChange: ui4("setSpace", { space: { arg: "" } }) } },
6314
+ { component: "chrome/Input", when: { input: "view.isHex" }, props: { variant: { value: "mono" }, label: { value: "Hex" }, value: { input: "view.hex" }, disabled: { input: "view.bound" } }, on: { onChange: ui4("setHex", { value: { arg: "" } }) } },
6315
+ { component: "chrome/NumberInput", each: { input: "view.components", key: "key" }, props: { caption: { item: "label" }, value: { item: "value" }, disabled: { input: "view.bound" } }, on: { onChange: ui4("setComponent", { index: { item: "index" }, value: { arg: "" } }) } },
6316
+ { component: "chrome/NumberInput", when: { input: "view.alphaShown" }, props: { caption: { value: "A" }, label: { value: "Alpha" }, min: { value: 0 }, max: { value: 100 }, value: { input: "view.alphaPercent" }, disabled: { input: "view.bound" } }, on: { onChange: ui4("setAlpha", { percent: { arg: "" } }) } },
6317
+ { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "color/EyedropperIcon" } }, label: { value: "Pick from screen" } }, on: { onClick: ui4("eyedropper") } }
6318
+ ],
6319
+ { gap: { value: 4 } }
6320
+ ),
6321
+ {
6322
+ component: "chrome/Field",
6323
+ when: { input: "view.hasTokens" },
6324
+ props: { label: { value: "Token" }, htmlFor: { value: "xeno-color-token" } },
6325
+ children: [
6326
+ row(
6327
+ [
6328
+ { component: "chrome/Select", props: { size: { value: "sm" }, label: { value: "Bind token" }, value: { input: "view.boundTokenId" }, placeholder: { value: "\u2014 raw \u2014" }, options: { input: "view.tokenOptions" } }, on: { onChange: ui4("bindToken", { tokenId: { arg: "" } }) } },
6329
+ { component: "chrome/TextButton", when: { input: "view.bound" }, on: { onClick: ui4("unbindToken") }, children: [text4({ value: "Unbind" })] }
6330
+ ],
6331
+ { gap: { value: 4 } }
6332
+ )
6333
+ ]
6334
+ },
6335
+ {
6336
+ component: "chrome/Section",
6337
+ props: { title: { value: "Swatches" }, flush: { value: true }, actions: { node: { component: "chrome/TextButton", on: { onClick: ui4("addSwatch") }, children: [text4({ value: "+ Save" })] } } },
6338
+ children: [
6339
+ {
6340
+ component: "chrome/SwatchGrid",
6341
+ props: { label: { value: "Swatches" } },
6342
+ children: [{ component: "chrome/ColorChip", each: { input: "view.swatches", key: "key" }, props: { size: { value: "grid" }, color: { item: "hex" }, label: { item: "hex" } }, on: { onClick: ui4("selectColor", { hex: { item: "hex" } }), onContextMenu: ui4("removeSwatch", { hex: { item: "hex" } }) } }]
6343
+ }
6344
+ ]
6345
+ },
6346
+ {
6347
+ component: "chrome/Section",
6348
+ when: { input: "view.hasRecent" },
6349
+ props: { title: { value: "Recent" }, flush: { value: true } },
6350
+ children: [
6351
+ {
6352
+ component: "chrome/SwatchGrid",
6353
+ props: { label: { value: "Recent" } },
6354
+ children: [{ component: "chrome/ColorChip", each: { input: "view.recent", key: "key" }, props: { size: { value: "grid" }, color: { item: "hex" }, label: { item: "hex" } }, on: { onClick: ui4("selectColor", { hex: { item: "hex" } }) } }]
6355
+ }
6356
+ ]
6357
+ }
6358
+ ]
6359
+ }
6360
+ ]
6361
+ }
6362
+ };
6363
+
6186
6364
  // src/edit/color/react/viewHelpers.ts
6187
6365
  function hsvFromHexOrBlack(hex) {
6188
6366
  const rgba = parseHexColor(hex);
@@ -6276,24 +6454,39 @@ function SwapIcon() {
6276
6454
 
6277
6455
  // src/edit/color/react/ColorPanelView.tsx
6278
6456
  import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
6279
- var SPACE_LABEL = {
6280
- hex: "HEX",
6281
- rgb: "RGB",
6282
- hsl: "HSL",
6283
- hsv: "HSV",
6284
- oklch: "OKLCH"
6285
- };
6286
- var COMPONENT_LABELS = {
6287
- rgb: ["R", "G", "B"],
6288
- hsl: ["H", "S", "L"],
6289
- oklch: ["L", "C", "H"]
6290
- };
6291
6457
  function ColorPanelView({ controller }) {
6458
+ const subscribe = useCallback5((cb) => controller.subscribe(cb), [controller]);
6459
+ const getSnapshot = useCallback5(() => controller.getRevision(), [controller]);
6460
+ useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
6461
+ const view = colorViewState(controller);
6462
+ const ui5 = (intent) => applyColorUi(controller, intent);
6463
+ return /* @__PURE__ */ jsx10(Column4, { className: "xeno-color-panel", children: /* @__PURE__ */ jsxs9(Stack3, { gap: 8, children: [
6464
+ view.fgBg ? /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", children: [
6465
+ /* @__PURE__ */ jsx10(ColorChip, { color: view.hex, label: "Foreground", pressed: view.fgActive, onClick: () => ui5({ kind: "setChannel", channel: "fg" }) }),
6466
+ /* @__PURE__ */ jsx10(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx10(SwapIcon, {}), label: "Swap FG/BG (X)", onClick: () => ui5({ kind: "swapChannels" }) }),
6467
+ /* @__PURE__ */ jsx10(ColorChip, { color: null, label: "Background", pressed: view.bgActive, onClick: () => ui5({ kind: "setChannel", channel: "bg" }) })
6468
+ ] }) : null,
6469
+ /* @__PURE__ */ jsx10(ColorPicker, { controller }),
6470
+ /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", gap: 4, children: [
6471
+ /* @__PURE__ */ jsx10(Select3, { size: "sm", label: "Color model", value: view.space, options: view.spaceOptions, onChange: (v) => ui5({ kind: "setSpace", space: v }) }),
6472
+ view.isHex ? /* @__PURE__ */ jsx10(Input3, { variant: "mono", label: "Hex", value: view.hex, onChange: (v) => ui5({ kind: "setHex", value: v }), disabled: view.bound }) : null,
6473
+ view.components.map((comp) => /* @__PURE__ */ jsx10(NumberInput, { caption: comp.label, value: comp.value, onChange: (n) => ui5({ kind: "setComponent", index: comp.index, value: n }), disabled: view.bound }, comp.key)),
6474
+ view.alphaShown ? /* @__PURE__ */ jsx10(NumberInput, { caption: "A", label: "Alpha", min: 0, max: 100, value: view.alphaPercent, onChange: (n) => ui5({ kind: "setAlpha", percent: n }), disabled: view.bound }) : null,
6475
+ /* @__PURE__ */ jsx10(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx10(EyedropperIcon, {}), label: "Pick from screen", onClick: () => ui5({ kind: "eyedropper" }) })
6476
+ ] }),
6477
+ view.hasTokens ? /* @__PURE__ */ jsx10(Field2, { label: "Token", htmlFor: "xeno-color-token", children: /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", gap: 4, children: [
6478
+ /* @__PURE__ */ jsx10(Select3, { size: "sm", label: "Bind token", value: view.boundTokenId, placeholder: "\u2014 raw \u2014", options: view.tokenOptions, onChange: (id) => ui5({ kind: "bindToken", tokenId: id }) }),
6479
+ view.bound ? /* @__PURE__ */ jsx10(TextButton2, { onClick: () => ui5({ kind: "unbindToken" }), children: "Unbind" }) : null
6480
+ ] }) }) : null,
6481
+ /* @__PURE__ */ jsx10(Section2, { title: "Swatches", flush: true, actions: /* @__PURE__ */ jsx10(TextButton2, { onClick: () => ui5({ kind: "addSwatch" }), children: "+ Save" }), children: /* @__PURE__ */ jsx10(SwatchGrid, { label: "Swatches", children: view.swatches.map((sw) => /* @__PURE__ */ jsx10(ColorChip, { size: "grid", color: sw.hex, label: sw.hex, onClick: () => ui5({ kind: "selectColor", hex: sw.hex }), onContextMenu: () => ui5({ kind: "removeSwatch", hex: sw.hex }) }, sw.key)) }) }),
6482
+ view.hasRecent ? /* @__PURE__ */ jsx10(Section2, { title: "Recent", flush: true, children: /* @__PURE__ */ jsx10(SwatchGrid, { label: "Recent", children: view.recent.map((r) => /* @__PURE__ */ jsx10(ColorChip, { size: "grid", color: r.hex, label: r.hex, onClick: () => ui5({ kind: "selectColor", hex: r.hex }) }, r.key)) }) }) : null
6483
+ ] }) });
6484
+ }
6485
+ function ColorPicker({ controller }) {
6292
6486
  const subscribe = useCallback5((cb) => controller.subscribe(cb), [controller]);
6293
6487
  const getSnapshot = useCallback5(() => controller.getRevision(), [controller]);
6294
6488
  useSyncExternalStore4(subscribe, getSnapshot, getSnapshot);
6295
6489
  const value = controller.getColorValue();
6296
- const space = controller.getSpace();
6297
6490
  const bound = controller.isBound();
6298
6491
  const hsv = hsvFromHexOrBlack(value.hex);
6299
6492
  const spectrumRef = useRef5(null);
@@ -6364,116 +6557,83 @@ function ColorPanelView({ controller }) {
6364
6557
  controller.endDrag();
6365
6558
  }
6366
6559
  }, [controller]);
6367
- const onHexInput = useCallback5(
6368
- (raw) => {
6369
- controller.setFromColor(raw, void 0, { commit: true });
6370
- },
6371
- [controller]
6372
- );
6373
- const onComponentInput = useCallback5(
6374
- (index, n) => {
6375
- const comps2 = [...value.components ?? [0, 0, 0]];
6376
- comps2[index] = n;
6377
- controller.setFromComponents(space, comps2, { commit: true });
6378
- },
6379
- [controller, space, value.components]
6380
- );
6381
- const componentLabels = COMPONENT_LABELS[space];
6382
- const comps = value.components ?? [];
6383
- const activeChannel = controller.getActiveChannel();
6384
- return /* @__PURE__ */ jsx10(Column4, { className: "xeno-color-panel", children: /* @__PURE__ */ jsxs9(Stack3, { gap: 8, children: [
6385
- controller.getChannelsMode() === "fg-bg" ? /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", children: [
6386
- /* @__PURE__ */ jsx10(ColorChip, { color: value.hex, label: "Foreground", pressed: activeChannel === "fg", onClick: () => controller.setChannel("fg") }),
6387
- /* @__PURE__ */ jsx10(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx10(SwapIcon, {}), label: "Swap FG/BG (X)", onClick: () => controller.swapChannels() }),
6388
- /* @__PURE__ */ jsx10(ColorChip, { color: null, label: "Background", pressed: activeChannel === "bg", onClick: () => controller.setChannel("bg") })
6389
- ] }) : null,
6390
- /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", children: [
6391
- /* @__PURE__ */ jsx10(
6392
- CanvasSurface,
6393
- {
6394
- ref: spectrumRef,
6395
- width: 200,
6396
- height: 140,
6397
- grow: true,
6398
- cursor: "crosshair",
6399
- disabled: bound,
6400
- "aria-label": "Saturation and value",
6401
- onPointerDown: (e) => onSpectrumPointer(e, true),
6402
- onPointerMove: (e) => onSpectrumPointer(e, false),
6403
- onPointerUp: endDrag
6404
- }
6405
- ),
6406
- /* @__PURE__ */ jsx10(
6407
- CanvasSurface,
6408
- {
6409
- ref: hueRef,
6410
- width: 14,
6411
- height: 140,
6412
- cursor: "ns-resize",
6413
- disabled: bound,
6414
- "aria-label": "Hue",
6415
- onPointerDown: (e) => onHuePointer(e, true),
6416
- onPointerMove: (e) => onHuePointer(e, false),
6417
- onPointerUp: endDrag
6418
- }
6419
- ),
6420
- controller.isAlphaShown() ? /* @__PURE__ */ jsx10(
6421
- CanvasSurface,
6422
- {
6423
- ref: alphaRef,
6424
- width: 14,
6425
- height: 140,
6426
- cursor: "ns-resize",
6427
- disabled: bound,
6428
- "aria-label": "Alpha",
6429
- onPointerDown: (e) => onAlphaPointer(e, true),
6430
- onPointerMove: (e) => onAlphaPointer(e, false),
6431
- onPointerUp: endDrag
6432
- }
6433
- ) : null
6434
- ] }),
6435
- /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", gap: 4, children: [
6436
- /* @__PURE__ */ jsx10(Select3, { size: "sm", label: "Color model", value: space, options: EDIT_SPACES.map((sp) => ({ value: sp, label: SPACE_LABEL[sp] })), onChange: (v) => controller.setSpace(v) }),
6437
- space === "hex" ? /* @__PURE__ */ jsx10(Input3, { variant: "mono", label: "Hex", value: value.hex, onChange: onHexInput, disabled: bound }) : (componentLabels ?? []).map((label, i) => /* @__PURE__ */ jsx10(NumberInput, { caption: label, value: roundForSpace(space, i, comps[i] ?? 0), onChange: (n) => onComponentInput(i, n), disabled: bound }, label)),
6438
- controller.isAlphaShown() ? /* @__PURE__ */ jsx10(NumberInput, { caption: "A", label: "Alpha", min: 0, max: 100, value: Math.round(value.alpha * 100), onChange: (n) => controller.setAlpha(n / 100, { commit: true }), disabled: bound }) : null,
6439
- /* @__PURE__ */ jsx10(IconButton5, { size: "sm", icon: /* @__PURE__ */ jsx10(EyedropperIcon, {}), label: "Pick from screen", onClick: () => controller.requestEyedropper() })
6440
- ] }),
6441
- controller.hasTokens() ? /* @__PURE__ */ jsx10(Field2, { label: "Token", htmlFor: "xeno-color-token", children: /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", gap: 4, children: [
6442
- /* @__PURE__ */ jsx10(
6443
- Select3,
6444
- {
6445
- size: "sm",
6446
- label: "Bind token",
6447
- value: controller.getBoundTokenId() ?? "",
6448
- placeholder: "\u2014 raw \u2014",
6449
- options: controller.getTokens().map((t) => ({ value: t.id, label: t.name })),
6450
- onChange: (id) => id ? controller.bindToken(id) : controller.unbindToken()
6451
- }
6452
- ),
6453
- bound ? /* @__PURE__ */ jsx10(TextButton2, { onClick: () => controller.unbindToken(), children: "Unbind" }) : null
6454
- ] }) }) : null,
6455
- /* @__PURE__ */ jsx10(Section2, { title: "Swatches", flush: true, actions: /* @__PURE__ */ jsx10(TextButton2, { onClick: () => void controller.addSwatch(), children: "+ Save" }), children: /* @__PURE__ */ jsx10(SwatchGrid, { label: "Swatches", children: controller.getSwatches().map((hex) => /* @__PURE__ */ jsx10(ColorChip, { size: "grid", color: hex, label: hex, onClick: () => controller.selectColor(hex), onContextMenu: () => void controller.removeSwatch(hex) }, hex)) }) }),
6456
- controller.getRecent().length > 0 ? /* @__PURE__ */ jsx10(Section2, { title: "Recent", flush: true, children: /* @__PURE__ */ jsx10(SwatchGrid, { label: "Recent", children: controller.getRecent().map((hex, i) => /* @__PURE__ */ jsx10(ColorChip, { size: "grid", color: hex, label: hex, onClick: () => controller.selectColor(hex) }, `${hex}-${i}`)) }) }) : null
6457
- ] }) });
6560
+ return /* @__PURE__ */ jsxs9(Stack3, { direction: "row", pad: "none", children: [
6561
+ /* @__PURE__ */ jsx10(
6562
+ CanvasSurface,
6563
+ {
6564
+ ref: spectrumRef,
6565
+ width: 200,
6566
+ height: 140,
6567
+ grow: true,
6568
+ cursor: "crosshair",
6569
+ disabled: bound,
6570
+ "aria-label": "Saturation and value",
6571
+ onPointerDown: (e) => onSpectrumPointer(e, true),
6572
+ onPointerMove: (e) => onSpectrumPointer(e, false),
6573
+ onPointerUp: endDrag
6574
+ }
6575
+ ),
6576
+ /* @__PURE__ */ jsx10(
6577
+ CanvasSurface,
6578
+ {
6579
+ ref: hueRef,
6580
+ width: 14,
6581
+ height: 140,
6582
+ cursor: "ns-resize",
6583
+ disabled: bound,
6584
+ "aria-label": "Hue",
6585
+ onPointerDown: (e) => onHuePointer(e, true),
6586
+ onPointerMove: (e) => onHuePointer(e, false),
6587
+ onPointerUp: endDrag
6588
+ }
6589
+ ),
6590
+ controller.isAlphaShown() ? /* @__PURE__ */ jsx10(
6591
+ CanvasSurface,
6592
+ {
6593
+ ref: alphaRef,
6594
+ width: 14,
6595
+ height: 140,
6596
+ cursor: "ns-resize",
6597
+ disabled: bound,
6598
+ "aria-label": "Alpha",
6599
+ onPointerDown: (e) => onAlphaPointer(e, true),
6600
+ onPointerMove: (e) => onAlphaPointer(e, false),
6601
+ onPointerUp: endDrag
6602
+ }
6603
+ ) : null
6604
+ ] });
6605
+ }
6606
+ var COLOR_RUNTIME_UNITS = ["color/Picker", "color/SwapIcon", "color/EyedropperIcon"];
6607
+ function colorRuntimeComponents(controller) {
6608
+ return {
6609
+ "color/Picker": () => /* @__PURE__ */ jsx10(ColorPicker, { controller }),
6610
+ "color/SwapIcon": SwapIcon,
6611
+ "color/EyedropperIcon": EyedropperIcon
6612
+ };
6458
6613
  }
6459
6614
  function clamp01(n) {
6460
6615
  return Math.max(0, Math.min(1, Number.isFinite(n) ? n : 0));
6461
6616
  }
6462
- function roundForSpace(space, index, v) {
6463
- if (space === "oklch") return Math.round(v * 10) / 10;
6464
- return Math.round(v);
6465
- }
6466
6617
 
6467
6618
  // src/edit/color/panel.ts
6468
6619
  function createColorPanel(options = {}) {
6469
6620
  return {
6470
6621
  manifest: colorManifest,
6471
6622
  activate(host) {
6472
- const controller = new ColorPanelController(host, options);
6623
+ const { render: customRender, ...controllerOptions } = options;
6624
+ const controller = new ColorPanelController(host, controllerOptions);
6473
6625
  void controller.load();
6474
6626
  let root = null;
6627
+ let unrender = null;
6475
6628
  return {
6476
6629
  render(el) {
6630
+ if (customRender) {
6631
+ unrender = customRender(el, controller);
6632
+ return () => {
6633
+ unrender?.();
6634
+ unrender = null;
6635
+ };
6636
+ }
6477
6637
  root = createRoot4(el);
6478
6638
  root.render(createElement4(ColorPanelView, { controller }));
6479
6639
  return () => {
@@ -6496,6 +6656,8 @@ function createColorPanel(options = {}) {
6496
6656
  dispose() {
6497
6657
  root?.unmount();
6498
6658
  root = null;
6659
+ unrender?.();
6660
+ unrender = null;
6499
6661
  controller.dispose();
6500
6662
  }
6501
6663
  };
@@ -8981,11 +9143,11 @@ var FindController = class {
8981
9143
  * @param text - The new query text.
8982
9144
  * @param immediate - Skip the debounce (a paste, or pressing Enter).
8983
9145
  */
8984
- setText(text4, immediate = false) {
8985
- if (text4 === this.text) return;
8986
- this.text = text4;
9146
+ setText(text5, immediate = false) {
9147
+ if (text5 === this.text) return;
9148
+ this.text = text5;
8987
9149
  this.index = -1;
8988
- if (text4 === "") {
9150
+ if (text5 === "") {
8989
9151
  this.clearResults("idle");
8990
9152
  this.emitQuery(true);
8991
9153
  return;
@@ -9013,8 +9175,8 @@ var FindController = class {
9013
9175
  this.notify();
9014
9176
  }
9015
9177
  /** The replacement text. Never emitted on its own — a replacement is not a search. */
9016
- setReplaceText(text4) {
9017
- this.replaceText = text4;
9178
+ setReplaceText(text5) {
9179
+ this.replaceText = text5;
9018
9180
  this.notify();
9019
9181
  }
9020
9182
  /** Show or hide the replace row. */
@@ -12304,14 +12466,14 @@ var TreeController = class {
12304
12466
  filter: this.filter,
12305
12467
  labelOf: (node) => node.label,
12306
12468
  isContainer: isTreeContainer
12307
- }).map((row) => ({
12308
- id: row.id,
12309
- node: row.node,
12310
- depth: row.depth,
12311
- container: row.container,
12312
- expanded: row.expanded,
12313
- matched: row.matched,
12314
- childState: childStateOf(row.node)
12469
+ }).map((row2) => ({
12470
+ id: row2.id,
12471
+ node: row2.node,
12472
+ depth: row2.depth,
12473
+ container: row2.container,
12474
+ expanded: row2.expanded,
12475
+ matched: row2.matched,
12476
+ childState: childStateOf(row2.node)
12315
12477
  }));
12316
12478
  return this.rowCache;
12317
12479
  }
@@ -12596,7 +12758,7 @@ var TreeController = class {
12596
12758
  if (rows.length === 0) return false;
12597
12759
  const index = rows.findIndex((r) => r.id === this.focusedId);
12598
12760
  const current = index >= 0 ? index : 0;
12599
- const row = rows[current];
12761
+ const row2 = rows[current];
12600
12762
  switch (event.key) {
12601
12763
  case "ArrowDown":
12602
12764
  this.moveFocus(rows, Math.min(rows.length - 1, current + 1), event);
@@ -12611,15 +12773,15 @@ var TreeController = class {
12611
12773
  this.moveFocus(rows, rows.length - 1, event);
12612
12774
  return true;
12613
12775
  case "ArrowRight":
12614
- if (row?.container && !row.expanded) this.setExpanded(row.id, true);
12615
- else if (row && current + 1 < rows.length) this.moveFocus(rows, current + 1, event);
12776
+ if (row2?.container && !row2.expanded) this.setExpanded(row2.id, true);
12777
+ else if (row2 && current + 1 < rows.length) this.moveFocus(rows, current + 1, event);
12616
12778
  return true;
12617
12779
  case "ArrowLeft": {
12618
- if (row?.container && row.expanded) {
12619
- this.setExpanded(row.id, false);
12780
+ if (row2?.container && row2.expanded) {
12781
+ this.setExpanded(row2.id, false);
12620
12782
  return true;
12621
12783
  }
12622
- const parentId = row?.node.parentId;
12784
+ const parentId = row2?.node.parentId;
12623
12785
  if (parentId) {
12624
12786
  this.focusedId = parentId;
12625
12787
  this.select([parentId]);
@@ -12627,10 +12789,10 @@ var TreeController = class {
12627
12789
  return true;
12628
12790
  }
12629
12791
  case "Enter":
12630
- if (row) this.open(row.id, { alt: event.altKey });
12792
+ if (row2) this.open(row2.id, { alt: event.altKey });
12631
12793
  return true;
12632
12794
  case "F2":
12633
- if (row) this.beginRename(row.id);
12795
+ if (row2) this.beginRename(row2.id);
12634
12796
  return true;
12635
12797
  case "Delete":
12636
12798
  case "Backspace":
@@ -12650,12 +12812,12 @@ var TreeController = class {
12650
12812
  }
12651
12813
  if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
12652
12814
  const fresh = now - this.typeAhead.at > this.typeAheadMs;
12653
- const text4 = (fresh ? "" : this.typeAhead.text) + event.key.toLowerCase();
12654
- this.typeAhead = { text: text4, at: now };
12815
+ const text5 = (fresh ? "" : this.typeAhead.text) + event.key.toLowerCase();
12816
+ this.typeAhead = { text: text5, at: now };
12655
12817
  const from = fresh ? current : current + 1;
12656
12818
  for (let i = 0; i < rows.length; i += 1) {
12657
12819
  const candidate = rows[(from + i) % rows.length];
12658
- if (candidate.node.label.toLowerCase().startsWith(text4)) {
12820
+ if (candidate.node.label.toLowerCase().startsWith(text5)) {
12659
12821
  this.focusedId = candidate.id;
12660
12822
  this.select([candidate.id]);
12661
12823
  return true;
@@ -12681,9 +12843,9 @@ var TreeController = class {
12681
12843
  return true;
12682
12844
  }
12683
12845
  /** Update the rename draft. */
12684
- updateRenameDraft(text4) {
12846
+ updateRenameDraft(text5) {
12685
12847
  if (!this.renaming) return;
12686
- this.renaming = { ...this.renaming, draft: text4 };
12848
+ this.renaming = { ...this.renaming, draft: text5 };
12687
12849
  this.notify();
12688
12850
  }
12689
12851
  /**
@@ -13412,10 +13574,10 @@ function TreePanelView({
13412
13574
  e.preventDefault();
13413
13575
  }
13414
13576
  },
13415
- children: /* @__PURE__ */ jsx17("div", { style: { height: window2.totalHeight, position: "relative" }, children: /* @__PURE__ */ jsx17("div", { style: { transform: `translateY(${window2.offsetTop}px)` }, children: slice.map((row) => /* @__PURE__ */ jsx17(
13577
+ children: /* @__PURE__ */ jsx17("div", { style: { height: window2.totalHeight, position: "relative" }, children: /* @__PURE__ */ jsx17("div", { style: { transform: `translateY(${window2.offsetTop}px)` }, children: slice.map((row2) => /* @__PURE__ */ jsx17(
13416
13578
  TreeRowView,
13417
13579
  {
13418
- row,
13580
+ row: row2,
13419
13581
  controller,
13420
13582
  state,
13421
13583
  rowHeight,
@@ -13423,7 +13585,7 @@ function TreePanelView({
13423
13585
  openOn,
13424
13586
  columns
13425
13587
  },
13426
- row.id
13588
+ row2.id
13427
13589
  )) }) })
13428
13590
  }
13429
13591
  ) });
@@ -13465,7 +13627,7 @@ function TreePanelView({
13465
13627
  ] });
13466
13628
  }
13467
13629
  function TreeRowView({
13468
- row,
13630
+ row: row2,
13469
13631
  controller,
13470
13632
  state,
13471
13633
  rowHeight,
@@ -13473,18 +13635,18 @@ function TreeRowView({
13473
13635
  openOn,
13474
13636
  columns
13475
13637
  }) {
13476
- const node = row.node;
13477
- const selected = state.selection.includes(row.id);
13478
- const renaming = state.renaming?.id === row.id ? state.renaming : null;
13479
- const drop = state.drag?.targetId === row.id ? state.drag : null;
13480
- const twisty = row.container ? /* @__PURE__ */ jsx17(
13638
+ const node = row2.node;
13639
+ const selected = state.selection.includes(row2.id);
13640
+ const renaming = state.renaming?.id === row2.id ? state.renaming : null;
13641
+ const drop = state.drag?.targetId === row2.id ? state.drag : null;
13642
+ const twisty = row2.container ? /* @__PURE__ */ jsx17(
13481
13643
  "span",
13482
13644
  {
13483
13645
  role: "button",
13484
- "aria-label": row.expanded ? "Collapse" : "Expand",
13646
+ "aria-label": row2.expanded ? "Collapse" : "Expand",
13485
13647
  onClick: (e) => {
13486
13648
  e.stopPropagation();
13487
- controller.setExpanded(row.id, !row.expanded);
13649
+ controller.setExpanded(row2.id, !row2.expanded);
13488
13650
  },
13489
13651
  style: {
13490
13652
  display: "flex",
@@ -13493,7 +13655,7 @@ function TreeRowView({
13493
13655
  width: 12,
13494
13656
  flex: "0 0 auto",
13495
13657
  cursor: "pointer",
13496
- transform: row.expanded ? "rotate(90deg)" : void 0,
13658
+ transform: row2.expanded ? "rotate(90deg)" : void 0,
13497
13659
  transition: "transform 80ms ease",
13498
13660
  opacity: 0.7
13499
13661
  },
@@ -13518,34 +13680,34 @@ function TreeRowView({
13518
13680
  Row4,
13519
13681
  {
13520
13682
  role: "treeitem",
13521
- "aria-expanded": row.container ? row.expanded : void 0,
13522
- "aria-level": row.depth + 1,
13683
+ "aria-expanded": row2.container ? row2.expanded : void 0,
13684
+ "aria-level": row2.depth + 1,
13523
13685
  selected,
13524
13686
  disabled: node.caps?.select === false,
13525
13687
  depth: 0,
13526
- style: { height: rowHeight, paddingLeft: 6 + row.depth * indentPx },
13688
+ style: { height: rowHeight, paddingLeft: 6 + row2.depth * indentPx },
13527
13689
  onClick: (e) => {
13528
- controller.select([row.id], {
13690
+ controller.select([row2.id], {
13529
13691
  toggle: e.ctrlKey || e.metaKey,
13530
13692
  range: e.shiftKey
13531
13693
  });
13532
- if (openOn === "single") controller.open(row.id, { alt: e.altKey });
13694
+ if (openOn === "single") controller.open(row2.id, { alt: e.altKey });
13533
13695
  },
13534
13696
  onDoubleClick: () => {
13535
- if (openOn === "double") controller.open(row.id);
13697
+ if (openOn === "double") controller.open(row2.id);
13536
13698
  },
13537
13699
  onContextMenu: (e) => {
13538
13700
  e.preventDefault();
13539
- controller.requestContextMenu(selected ? state.selection : [row.id], e.clientX, e.clientY);
13701
+ controller.requestContextMenu(selected ? state.selection : [row2.id], e.clientX, e.clientY);
13540
13702
  },
13541
13703
  onPointerDown: (e) => {
13542
13704
  if (e.button !== 0) return;
13543
- controller.beginDrag(selected ? state.selection : [row.id]);
13705
+ controller.beginDrag(selected ? state.selection : [row2.id]);
13544
13706
  },
13545
13707
  onPointerMove: (e) => {
13546
13708
  if (!state.drag) return;
13547
13709
  const rect = e.currentTarget.getBoundingClientRect();
13548
- controller.dragOver(row.id, e.clientY - rect.top);
13710
+ controller.dragOver(row2.id, e.clientY - rect.top);
13549
13711
  },
13550
13712
  onPointerUp: () => {
13551
13713
  if (state.drag) controller.endDrag();
@@ -13579,7 +13741,7 @@ function TreeRowView({
13579
13741
  }
13580
13742
  }
13581
13743
  ) : node.label,
13582
- meta: /* @__PURE__ */ jsx17(ChildStateChip, { row, controller }),
13744
+ meta: /* @__PURE__ */ jsx17(ChildStateChip, { row: row2, controller }),
13583
13745
  trailing: /* @__PURE__ */ jsxs16(Fragment9, { children: [
13584
13746
  columns?.map((columnId) => /* @__PURE__ */ jsx17("span", { style: { opacity: 0.7, fontVariantNumeric: "tabular-nums" }, children: node.columns?.[columnId] ?? "" }, columnId)),
13585
13747
  node.glyphs?.map((glyph) => /* @__PURE__ */ jsx17("span", { title: glyph.title, style: { display: "flex", opacity: 0.7 }, children: /* @__PURE__ */ jsx17(NodeIcon, { icon: glyph.icon }) }, glyph.id)),
@@ -13594,7 +13756,7 @@ function TreeRowView({
13594
13756
  icon: /* @__PURE__ */ jsx17(NodeIcon, { icon: action.icon }),
13595
13757
  label: action.label,
13596
13758
  disabled: action.disabled,
13597
- onClick: () => controller.runAction(row.id, action.id)
13759
+ onClick: () => controller.runAction(row2.id, action.id)
13598
13760
  },
13599
13761
  action.id
13600
13762
  ))
@@ -13605,35 +13767,35 @@ function TreeRowView({
13605
13767
  );
13606
13768
  }
13607
13769
  function ChildStateChip({
13608
- row,
13770
+ row: row2,
13609
13771
  controller
13610
13772
  }) {
13611
- if (!row.container) return null;
13612
- if (row.childState === "loading") {
13773
+ if (!row2.container) return null;
13774
+ if (row2.childState === "loading") {
13613
13775
  return /* @__PURE__ */ jsx17("span", { style: { opacity: 0.5, fontSize: 9 }, children: "loading\u2026" });
13614
13776
  }
13615
- if (row.childState === "error") {
13616
- const error = row.node.error;
13777
+ if (row2.childState === "error") {
13778
+ const error = row2.node.error;
13617
13779
  return /* @__PURE__ */ jsx17(Badge7, { tone: "error", children: error?.code === "denied" ? "no access" : error?.code ?? "failed" });
13618
13780
  }
13619
- if (row.node.hasMore && row.expanded) {
13781
+ if (row2.node.hasMore && row2.expanded) {
13620
13782
  return /* @__PURE__ */ jsx17(
13621
13783
  "span",
13622
13784
  {
13623
13785
  role: "button",
13624
13786
  onClick: (e) => {
13625
13787
  e.stopPropagation();
13626
- controller.loadMore(row.id);
13788
+ controller.loadMore(row2.id);
13627
13789
  },
13628
13790
  style: { cursor: "pointer", opacity: 0.7, fontSize: 9 },
13629
13791
  children: "load more\u2026"
13630
13792
  }
13631
13793
  );
13632
13794
  }
13633
- if (row.node.truncatedAt !== void 0) {
13634
- return /* @__PURE__ */ jsx17(Badge7, { tone: "warning", children: `first ${row.node.truncatedAt}` });
13795
+ if (row2.node.truncatedAt !== void 0) {
13796
+ return /* @__PURE__ */ jsx17(Badge7, { tone: "warning", children: `first ${row2.node.truncatedAt}` });
13635
13797
  }
13636
- if (row.expanded && isEmptyContainer(row.node)) {
13798
+ if (row2.expanded && isEmptyContainer(row2.node)) {
13637
13799
  return /* @__PURE__ */ jsx17("span", { style: { opacity: 0.4, fontSize: 9 }, children: "empty" });
13638
13800
  }
13639
13801
  return null;
@@ -13847,6 +14009,9 @@ function numberConfig(value, fallback) {
13847
14009
  }
13848
14010
  var treePanel = createTreePanel();
13849
14011
  export {
14012
+ BEHAVIOURS4 as COLOR_BEHAVIOURS,
14013
+ COLOR_DECLARATION,
14014
+ COLOR_RUNTIME_UNITS,
13850
14015
  COLOR_SPACES,
13851
14016
  COMMENTS_PANEL_ID,
13852
14017
  CURVE_PANEL_ID,
@@ -13874,21 +14039,25 @@ export {
13874
14039
  GraphController,
13875
14040
  BEHAVIOURS3 as HISTORY_BEHAVIOURS,
13876
14041
  HISTORY_DECLARATION,
14042
+ HISTORY_RUNTIME_UNITS,
13877
14043
  HistoryPanelController,
13878
14044
  HistoryPanelView,
13879
14045
  IMPLEMENTED_TREE_MODES,
13880
14046
  BEHAVIOURS2 as INSPECTOR_BEHAVIOURS,
13881
14047
  INSPECTOR_DECLARATION,
14048
+ INSPECTOR_RUNTIME_UNITS,
13882
14049
  InspectorPanelController,
13883
14050
  InspectorPanelView,
13884
14051
  BEHAVIOURS as LAYERS_BEHAVIOURS,
13885
14052
  LAYERS_DECLARATION,
14053
+ LAYERS_RUNTIME_UNITS,
13886
14054
  LAYER_COLUMNS,
13887
14055
  LayersPanelController,
13888
14056
  LayersPanelView,
13889
14057
  MIXED,
13890
14058
  NODEGRAPH_PANEL_ID,
13891
14059
  NodeGraphPanelView,
14060
+ SPACE_LABEL,
13892
14061
  TREE_PANEL_ID,
13893
14062
  Thumbnail,
13894
14063
  TreeController,
@@ -13906,6 +14075,7 @@ export {
13906
14075
  allIds,
13907
14076
  anchorStateOf,
13908
14077
  appliedCount,
14078
+ applyColorUi,
13909
14079
  applyHistoryUi,
13910
14080
  applyInspectorUi,
13911
14081
  applyLayersUi,
@@ -13923,6 +14093,9 @@ export {
13923
14093
  collectDescendants,
13924
14094
  colorManifest,
13925
14095
  colorPanel,
14096
+ colorRuntimeComponents,
14097
+ colorViewManifest,
14098
+ colorViewState,
13926
14099
  commentsManifest,
13927
14100
  commentsPanel,
13928
14101
  commitPolicyOf,