@xenosystem/blocks 0.15.1 → 0.15.3

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.
@@ -242,6 +242,7 @@ function RedoIcon({ size = 11 }) {
242
242
 
243
243
  // src/edit/inspector/react/icons.tsx
244
244
  import Link from "@xenosystem/elements/elements/link";
245
+ import Copy2 from "@xenosystem/elements/elements/copy";
245
246
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
246
247
  var base = (size) => ({
247
248
  width: size,
@@ -271,6 +272,7 @@ function NextIcon({ size = 9 }) {
271
272
  return /* @__PURE__ */ jsx6("svg", { ...base(size), children: /* @__PURE__ */ jsx6("path", { d: "m9 18 6-6-6-6" }) });
272
273
  }
273
274
  var LinkIcon = fromElement(Link, 10);
275
+ var CopyIcon = fromElement(Copy2, 10);
274
276
  function ResetIcon({ size = 10 }) {
275
277
  return /* @__PURE__ */ jsxs5("svg", { ...base(size), children: [
276
278
  /* @__PURE__ */ jsx6("path", { d: "M3 12a9 9 0 1 0 3-6.7L3 8" }),
@@ -489,6 +491,7 @@ export {
489
491
  PrevIcon,
490
492
  NextIcon,
491
493
  LinkIcon,
494
+ CopyIcon,
492
495
  ResetIcon,
493
496
  XIcon2,
494
497
  KindGlyph,
@@ -12,7 +12,7 @@ import {
12
12
  RefreshGlyph,
13
13
  SortAscGlyph,
14
14
  SortDescGlyph
15
- } from "../chunk-4IDY5FHM.js";
15
+ } from "../chunk-LKYPCOTE.js";
16
16
  import "../chunk-2KG3PWR4.js";
17
17
 
18
18
  // src/data/table/panel.ts
@@ -989,6 +989,13 @@ interface XenoInspectorField {
989
989
  */
990
990
  /** For a structured `list`: the label drawn on each item row (e.g. `Fill`, `Shadow`). */
991
991
  itemLabel?: string;
992
+ /**
993
+ * Draw a Copy button beside the field. The block writes the field's text to the clipboard itself
994
+ * when it has one; where it does not (a sandboxed frame), it emits `action { actionId: 'copy' }`
995
+ * with the value so the host can. Raised by Canvas's Dev handoff (2026-09-18): a read-only code
996
+ * block whose whole point is being copied.
997
+ */
998
+ copyable?: boolean;
992
999
  /** Axis labels for `vector` (e.g. `['X','Y']`). */
993
1000
  components?: string[];
994
1001
  /** Scalar ⇔ per-element expander (Canvas's corner radius). */
@@ -1430,6 +1437,11 @@ declare class InspectorPanelController {
1430
1437
  * normal field `set`; otherwise it is an `action` with `actionId: 'setEnabled'`.
1431
1438
  */
1432
1439
  toggleSectionEnabled(sectionId: string): boolean;
1440
+ /**
1441
+ * Copy a field's text. The clipboard is used when this runtime has one; otherwise the host gets
1442
+ * `action { sectionId, actionId: 'copy', value }` and copies on the panel's behalf.
1443
+ */
1444
+ copyField(fieldId: string): boolean;
1433
1445
  /** Run a section action button. */
1434
1446
  runAction(sectionId: string, actionId: string): void;
1435
1447
  /** Pick an entry from a section's Add menu. */
@@ -1691,6 +1703,7 @@ interface InspectorViewField {
1691
1703
  showToken: boolean;
1692
1704
  tokenActionLabel: string;
1693
1705
  showReset: boolean;
1706
+ showCopy: boolean;
1694
1707
  showInfoNote: boolean;
1695
1708
  infoNote?: string;
1696
1709
  /** The field record itself — what `inspector/FieldWidget` renders. */
@@ -1780,6 +1793,9 @@ type InspectorUiIntent = {
1780
1793
  } | {
1781
1794
  kind: 'resetField';
1782
1795
  fieldId: string;
1796
+ } | {
1797
+ kind: 'copyField';
1798
+ fieldId: string;
1783
1799
  };
1784
1800
  /** The link menu's items: the field's group of tokens (all, when it names none), then Unbind and Create-from-value. */
1785
1801
  declare function tokenItemsFor(field: XenoInspectorField, tokens: readonly XenoInspectorToken[], bound: boolean): InspectorViewField['tokenItems'];
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  CollapseGlyph,
3
+ CopyIcon,
3
4
  DiamondIcon,
4
5
  EntryIcon,
5
6
  ExpandGlyph,
@@ -21,7 +22,7 @@ import {
21
22
  VisibilityIcon,
22
23
  XIcon,
23
24
  XIcon2
24
- } from "../chunk-4IDY5FHM.js";
25
+ } from "../chunk-LKYPCOTE.js";
25
26
  import "../chunk-2KG3PWR4.js";
26
27
 
27
28
  // src/edit/layers/panel.ts
@@ -2631,6 +2632,23 @@ var InspectorPanelController = class {
2631
2632
  this.emit({ type: "action", sectionId, actionId: "setEnabled", value: next });
2632
2633
  return true;
2633
2634
  }
2635
+ /**
2636
+ * Copy a field's text. The clipboard is used when this runtime has one; otherwise the host gets
2637
+ * `action { sectionId, actionId: 'copy', value }` and copies on the panel's behalf.
2638
+ */
2639
+ copyField(fieldId) {
2640
+ const field = findField(this.schema, fieldId);
2641
+ if (!field) return false;
2642
+ const text11 = String(this.getFieldValue(fieldId) ?? "");
2643
+ const clip = globalThis.navigator?.clipboard;
2644
+ if (clip?.writeText) {
2645
+ void clip.writeText(text11).catch(() => void 0);
2646
+ return true;
2647
+ }
2648
+ const sectionId = findSectionOf(this.schema, fieldId)?.id ?? "";
2649
+ this.emit({ type: "action", sectionId, actionId: "copy", value: text11 });
2650
+ return true;
2651
+ }
2634
2652
  /** Run a section action button. */
2635
2653
  runAction(sectionId, actionId) {
2636
2654
  this.emit({ type: "action", sectionId, actionId });
@@ -3061,6 +3079,7 @@ function inspectorViewState(c) {
3061
3079
  showToken: showTokens && field.token?.bindable !== false,
3062
3080
  tokenActionLabel: bound ? "Unbind token" : "Bind token",
3063
3081
  showReset: field.defaultValue !== void 0 && isFieldEditable(field),
3082
+ showCopy: !!field.copyable,
3064
3083
  showInfoNote: !!field.description && field.type === "info",
3065
3084
  infoNote: field.description,
3066
3085
  field
@@ -3123,6 +3142,9 @@ function applyInspectorUi(c, intent) {
3123
3142
  case "resetField":
3124
3143
  c.resetField(intent.fieldId);
3125
3144
  break;
3145
+ case "copyField":
3146
+ c.copyField(intent.fieldId);
3147
+ break;
3126
3148
  }
3127
3149
  }
3128
3150
  var inspectorViewManifest = {
@@ -3244,7 +3266,8 @@ var INSPECTOR_DECLARATION = {
3244
3266
  props: { size: { value: "sm" }, trigger: { node: { component: "icons/Link" } }, items: { item: "tokenItems" }, label: { item: "tokenActionLabel" }, align: { value: "end" } },
3245
3267
  on: { onSelect: ui2("pickToken", { fieldId: { item: "id" }, itemId: { arg: "" } }) }
3246
3268
  },
3247
- iconButton("Reset", { value: "Reset to default" }, ui2("resetField", { fieldId: { item: "id" } }), { when: { item: "showReset" } })
3269
+ iconButton("Reset", { value: "Reset to default" }, ui2("resetField", { fieldId: { item: "id" } }), { when: { item: "showReset" } }),
3270
+ iconButton("Copy", { value: "Copy" }, ui2("copyField", { fieldId: { item: "id" } }), { when: { item: "showCopy" } })
3248
3271
  ]
3249
3272
  }
3250
3273
  }
@@ -3706,7 +3729,8 @@ function FieldRow({ row: row2, controller }) {
3706
3729
  row2.showEasing ? /* @__PURE__ */ jsx4(Select2, { size: "sm", options: row2.easingOptions, value: row2.easing, label: "Easing", onChange: (v) => ui10({ kind: "setEasing", fieldId: row2.id, easing: v }) }) : null,
3707
3730
  row2.showKeyframes ? /* @__PURE__ */ jsx4(Badge2, { size: "sm", children: row2.keyframeChip }) : null,
3708
3731
  row2.showToken ? /* @__PURE__ */ jsx4(Menu2, { size: "sm", trigger: /* @__PURE__ */ jsx4(LinkIcon, {}), items: row2.tokenItems, label: row2.tokenActionLabel, align: "end", onSelect: (id) => ui10({ kind: "pickToken", fieldId: row2.id, itemId: id }) }) : null,
3709
- row2.showReset ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ResetIcon, {}), label: "Reset to default", onClick: () => ui10({ kind: "resetField", fieldId: row2.id }) }) : null
3732
+ row2.showReset ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ResetIcon, {}), label: "Reset to default", onClick: () => ui10({ kind: "resetField", fieldId: row2.id }) }) : null,
3733
+ row2.showCopy ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CopyIcon, {}), label: "Copy", onClick: () => ui10({ kind: "copyField", fieldId: row2.id }) }) : null
3710
3734
  ] }),
3711
3735
  children: [
3712
3736
  row2.bound ? /* @__PURE__ */ jsx4(Badge2, { title: row2.tokenTitle, children: row2.tokenLabel }) : null,
@@ -2,7 +2,7 @@ import {
2
2
  ICONS,
3
3
  ICON_UNITS,
4
4
  Svg
5
- } from "../chunk-4IDY5FHM.js";
5
+ } from "../chunk-LKYPCOTE.js";
6
6
  import "../chunk-2KG3PWR4.js";
7
7
  export {
8
8
  ICONS,
@@ -449,6 +449,8 @@ declare class ConsoleController {
449
449
  /** Collapse adjacent duplicates. */
450
450
  setCollapse(collapse: boolean): void;
451
451
  /** Drop every record. Emits nothing — clearing a view is not an event a host acts on. */
452
+ /** Ask the host to recompute the records (a lint, an audit) — nothing changes here until it pushes. */
453
+ requestRefresh(): void;
452
454
  clear(): void;
453
455
  /**
454
456
  * A record was opened.
@@ -486,6 +488,7 @@ interface ConsoleRenderContext {
486
488
  rowHeight: number;
487
489
  showTimestamps: boolean;
488
490
  emptyHint?: string;
491
+ showRefresh?: boolean;
489
492
  };
490
493
  }
491
494
  /** Options for {@link createConsolePanel}. */
@@ -563,6 +566,7 @@ interface ConsoleViewModel {
563
566
  errors?: string;
564
567
  warns?: string;
565
568
  multiSource: boolean;
569
+ showRefresh: boolean;
566
570
  sources: ConsoleViewSource[];
567
571
  empty: boolean;
568
572
  emptyTitle: string;
@@ -585,6 +589,8 @@ type ConsoleUiIntent = {
585
589
  kind: 'copyVisible';
586
590
  } | {
587
591
  kind: 'clear';
592
+ } | {
593
+ kind: 'refresh';
588
594
  } | {
589
595
  kind: 'source';
590
596
  id: string;
@@ -602,6 +608,7 @@ interface ConsoleViewOptions {
602
608
  rowHeight?: number;
603
609
  showTimestamps?: boolean;
604
610
  emptyHint?: string;
611
+ showRefresh?: boolean;
605
612
  }
606
613
  /** Derive the view from the live controller — the runtime calls this on every draw. */
607
614
  declare function consoleViewState(c: ConsoleController, options?: ConsoleViewOptions): ConsoleViewModel;
package/dist/ops/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ICONS,
11
11
  PlusGlyph,
12
12
  XGlyph
13
- } from "../chunk-4IDY5FHM.js";
13
+ } from "../chunk-LKYPCOTE.js";
14
14
  import "../chunk-2KG3PWR4.js";
15
15
 
16
16
  // src/ops/console/panel.ts
@@ -422,6 +422,10 @@ var ConsoleController = class {
422
422
  this.notify();
423
423
  }
424
424
  /** Drop every record. Emits nothing — clearing a view is not an event a host acts on. */
425
+ /** Ask the host to recompute the records (a lint, an audit) — nothing changes here until it pushes. */
426
+ requestRefresh() {
427
+ this.host.emit("refreshRequest", {});
428
+ }
425
429
  clear() {
426
430
  this.records = [];
427
431
  this.dropped = 0;
@@ -522,6 +526,12 @@ var consoleManifest = {
522
526
  name: "Context Menu Request",
523
527
  type: "object",
524
528
  description: "The host opens its own menu."
529
+ },
530
+ {
531
+ id: "refreshRequest",
532
+ name: "Refresh Requested",
533
+ type: "object",
534
+ description: "{} \u2014 the person pressed Refresh (config `showRefresh`), so a host whose records are a COMPUTATION (a lint, an audit, a query) re-runs it and pushes a `replace`. A live stream never shows the button."
525
535
  }
526
536
  ],
527
537
  commands: [
@@ -558,6 +568,13 @@ var consoleManifest = {
558
568
  }
559
569
  ],
560
570
  config: [
571
+ {
572
+ key: "showRefresh",
573
+ label: "Refresh button",
574
+ type: "boolean",
575
+ defaultValue: false,
576
+ description: "For records that are computed on demand rather than streamed: a Refresh button that emits `refreshRequest`."
577
+ },
561
578
  {
562
579
  key: "maxRecords",
563
580
  label: "Buffer size",
@@ -667,6 +684,7 @@ function consoleViewState(c, options = {}) {
667
684
  errors: s.counts.error > 0 ? String(s.counts.error) : void 0,
668
685
  warns: s.counts.warn > 0 ? String(s.counts.warn) : void 0,
669
686
  multiSource: s.sources.length > 1,
687
+ showRefresh: options.showRefresh === true,
670
688
  sources: s.sources.map((src) => ({ id: src.id, label: src.label ?? src.id, on: src.enabled, title: `${src.count} records` })),
671
689
  empty: s.rows.length === 0,
672
690
  emptyTitle: s.total > 0 ? "No matches" : "No output",
@@ -705,6 +723,9 @@ function applyConsoleUi(c, intent) {
705
723
  case "clear":
706
724
  c.clear();
707
725
  break;
726
+ case "refresh":
727
+ c.requestRefresh();
728
+ break;
708
729
  case "source":
709
730
  c.toggleSource(intent.id, intent.on);
710
731
  break;
@@ -725,7 +746,7 @@ var consoleViewManifest = {
725
746
  outputs: [{ id: "ui", name: "UI intent", type: "object", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
726
747
  commands: []
727
748
  };
728
- var EMPTY = { search: "", levels: [], level: "trace", multiSource: false, sources: [], empty: true, emptyTitle: "No output", emptyHint: "Run something to see its output here.", listed: false, rowHeight: 20, totalHeight: 0, offsetTop: 0, rows: [], countLabel: "0 of 0" };
749
+ var EMPTY = { search: "", levels: [], level: "trace", multiSource: false, showRefresh: false, sources: [], empty: true, emptyTitle: "No output", emptyHint: "Run something to see its output here.", listed: false, rowHeight: 20, totalHeight: 0, offsetTop: 0, rows: [], countLabel: "0 of 0" };
729
750
  var BEHAVIOURS = {
730
751
  "ops/consolePanelView": (inputs) => inputs.view ?? EMPTY
731
752
  };
@@ -758,6 +779,7 @@ var CONSOLE_DECLARATION = {
758
779
  children: [
759
780
  { component: "chrome/Badge", when: { input: "view.errors" }, props: { tone: { value: "error" } }, children: [text({ input: "view.errors" })] },
760
781
  { component: "chrome/Badge", when: { input: "view.warns" }, props: { tone: { value: "warning" } }, children: [text({ input: "view.warns" })] },
782
+ { component: "chrome/IconButton", when: { input: "view.showRefresh" }, props: { icon: { node: { component: "icons/Refresh" } }, label: { value: "Refresh" } }, on: { onClick: ui("refresh") } },
761
783
  { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Copy" } }, label: { value: "Copy visible" } }, on: { onClick: ui("copyVisible") } },
762
784
  { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Clear" } }, label: { value: "Clear" } }, on: { onClick: ui("clear") } }
763
785
  ]
@@ -828,6 +850,7 @@ var CONSOLE_DECLARATION = {
828
850
  // src/ops/console/react/ConsolePanelView.tsx
829
851
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
830
852
  var CopyGlyph = ICONS["icons/Copy"];
853
+ var RefreshGlyph = ICONS["icons/Refresh"];
831
854
  var ClearGlyph = ICONS["icons/Clear"];
832
855
  function ConsoleScroller({ controller, rowHeight = 20, children }) {
833
856
  const state = useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
@@ -882,6 +905,7 @@ function ConsolePanelView({ controller, ...options }) {
882
905
  right: /* @__PURE__ */ jsxs(ToolbarGroup, { end: true, children: [
883
906
  view.errors ? /* @__PURE__ */ jsx(Badge, { tone: "error", children: view.errors }) : null,
884
907
  view.warns ? /* @__PURE__ */ jsx(Badge, { tone: "warning", children: view.warns }) : null,
908
+ view.showRefresh ? /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(RefreshGlyph, {}), label: "Refresh", onClick: () => ui5({ kind: "refresh" }) }) : null,
885
909
  /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(CopyGlyph, {}), label: "Copy visible", onClick: () => ui5({ kind: "copyVisible" }) }),
886
910
  /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(ClearGlyph, {}), label: "Clear", onClick: () => ui5({ kind: "clear" }) })
887
911
  ] })
@@ -935,7 +959,8 @@ function createConsolePanel(options = {}) {
935
959
  const resolve = (config2) => ({
936
960
  rowHeight: numberConfig(config2.rowHeight, 20),
937
961
  showTimestamps: config2.showTimestamps !== false,
938
- emptyHint: typeof config2.emptyHint === "string" ? config2.emptyHint : void 0
962
+ emptyHint: typeof config2.emptyHint === "string" ? config2.emptyHint : void 0,
963
+ showRefresh: config2.showRefresh === true
939
964
  });
940
965
  let renderConfig = resolve(host.config ?? {});
941
966
  let unrender = null;
@@ -13,7 +13,7 @@ import {
13
13
  StopIcon,
14
14
  TRANSPORT_ICONS,
15
15
  TRANSPORT_RUNTIME_UNITS
16
- } from "../chunk-4IDY5FHM.js";
16
+ } from "../chunk-LKYPCOTE.js";
17
17
  import {
18
18
  __reExport
19
19
  } from "../chunk-2KG3PWR4.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenosystem/blocks",
3
- "version": "0.15.1",
3
+ "version": "0.15.3",
4
4
  "description": "XENO Blocks — rung 3 of the front ladder: components composed into a contract-bearing unit (manifest: ports, intents, commands, agent surface), rendered by @xenosystem/workbench. ONE package, one subpath per family (/auth, /edit, /data, /time, /ops, /trust, /agent), one named export per block — XENO PACKAGE NAMING - STANDARD.md.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",