@xenosystem/blocks 0.15.0 → 0.15.2
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,
|
package/dist/data/index.js
CHANGED
package/dist/edit/index.d.ts
CHANGED
|
@@ -981,8 +981,21 @@ interface XenoInspectorField {
|
|
|
981
981
|
field: string;
|
|
982
982
|
in: XenoFieldValue[];
|
|
983
983
|
};
|
|
984
|
+
/**
|
|
985
|
+
* Inside `itemFields`: a sub-field with `token.bindable` carries its binding IN THE ITEM as
|
|
986
|
+
* `${subId}Token` (a token id or empty). The row's token menu reads it, and a pick rewrites the item
|
|
987
|
+
* and emits ONE `set` for the list — the same contract as a value edit, so a host binds a stroke's
|
|
988
|
+
* colour or an effect's shadow from the item it already receives.
|
|
989
|
+
*/
|
|
984
990
|
/** For a structured `list`: the label drawn on each item row (e.g. `Fill`, `Shadow`). */
|
|
985
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;
|
|
986
999
|
/** Axis labels for `vector` (e.g. `['X','Y']`). */
|
|
987
1000
|
components?: string[];
|
|
988
1001
|
/** Scalar ⇔ per-element expander (Canvas's corner radius). */
|
|
@@ -1424,6 +1437,11 @@ declare class InspectorPanelController {
|
|
|
1424
1437
|
* normal field `set`; otherwise it is an `action` with `actionId: 'setEnabled'`.
|
|
1425
1438
|
*/
|
|
1426
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;
|
|
1427
1445
|
/** Run a section action button. */
|
|
1428
1446
|
runAction(sectionId: string, actionId: string): void;
|
|
1429
1447
|
/** Pick an entry from a section's Add menu. */
|
|
@@ -1685,6 +1703,7 @@ interface InspectorViewField {
|
|
|
1685
1703
|
showToken: boolean;
|
|
1686
1704
|
tokenActionLabel: string;
|
|
1687
1705
|
showReset: boolean;
|
|
1706
|
+
showCopy: boolean;
|
|
1688
1707
|
showInfoNote: boolean;
|
|
1689
1708
|
infoNote?: string;
|
|
1690
1709
|
/** The field record itself — what `inspector/FieldWidget` renders. */
|
|
@@ -1774,6 +1793,9 @@ type InspectorUiIntent = {
|
|
|
1774
1793
|
} | {
|
|
1775
1794
|
kind: 'resetField';
|
|
1776
1795
|
fieldId: string;
|
|
1796
|
+
} | {
|
|
1797
|
+
kind: 'copyField';
|
|
1798
|
+
fieldId: string;
|
|
1777
1799
|
};
|
|
1778
1800
|
/** The link menu's items: the field's group of tokens (all, when it names none), then Unbind and Create-from-value. */
|
|
1779
1801
|
declare function tokenItemsFor(field: XenoInspectorField, tokens: readonly XenoInspectorToken[], bound: boolean): InspectorViewField['tokenItems'];
|
package/dist/edit/index.js
CHANGED
|
@@ -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-
|
|
25
|
+
} from "../chunk-LKYPCOTE.js";
|
|
25
26
|
import "../chunk-2KG3PWR4.js";
|
|
26
27
|
|
|
27
28
|
// src/edit/layers/panel.ts
|
|
@@ -2011,7 +2012,9 @@ function findField(schema, fieldId) {
|
|
|
2011
2012
|
if (!list || !sub) return void 0;
|
|
2012
2013
|
const item = (Array.isArray(list.value) ? list.value : []).find((it) => it?.id === v.itemId);
|
|
2013
2014
|
if (!item) return void 0;
|
|
2014
|
-
|
|
2015
|
+
const tokenId = sub.token?.bindable ? item[`${v.subId}Token`] : void 0;
|
|
2016
|
+
const token = sub.token ? { ...sub.token, ...typeof tokenId === "string" && tokenId ? { tokenId } : { tokenId: null } } : void 0;
|
|
2017
|
+
return { ...sub, id: fieldId, value: item[v.subId], disabled: sub.disabled || list.disabled, readOnly: sub.readOnly || list.readOnly, ...token ? { token } : {} };
|
|
2015
2018
|
}
|
|
2016
2019
|
function findSectionOf(schema, fieldId) {
|
|
2017
2020
|
return schema.sections.find((s) => s.fields.some((f) => f.id === fieldId));
|
|
@@ -2629,6 +2632,23 @@ var InspectorPanelController = class {
|
|
|
2629
2632
|
this.emit({ type: "action", sectionId, actionId: "setEnabled", value: next });
|
|
2630
2633
|
return true;
|
|
2631
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
|
+
}
|
|
2632
2652
|
/** Run a section action button. */
|
|
2633
2653
|
runAction(sectionId, actionId) {
|
|
2634
2654
|
this.emit({ type: "action", sectionId, actionId });
|
|
@@ -2673,6 +2693,13 @@ var InspectorPanelController = class {
|
|
|
2673
2693
|
bindToken(fieldId, tokenId) {
|
|
2674
2694
|
const field = findField(this.schema, fieldId);
|
|
2675
2695
|
if (!field) return false;
|
|
2696
|
+
const virtual = parseListFieldId(fieldId);
|
|
2697
|
+
if (virtual) {
|
|
2698
|
+
const items = this.getFieldValue(virtual.listId) ?? [];
|
|
2699
|
+
const next = items.map((it) => it.id === virtual.itemId ? { ...it, [`${virtual.subId}Token`]: tokenId ?? "" } : it);
|
|
2700
|
+
this.emit({ type: "set", targetIds: this.targetIds(), fieldId: virtual.listId, value: next });
|
|
2701
|
+
return true;
|
|
2702
|
+
}
|
|
2676
2703
|
this.emit({ type: "bindToken", targetIds: this.targetIds(), fieldId, tokenId });
|
|
2677
2704
|
return true;
|
|
2678
2705
|
}
|
|
@@ -3052,6 +3079,7 @@ function inspectorViewState(c) {
|
|
|
3052
3079
|
showToken: showTokens && field.token?.bindable !== false,
|
|
3053
3080
|
tokenActionLabel: bound ? "Unbind token" : "Bind token",
|
|
3054
3081
|
showReset: field.defaultValue !== void 0 && isFieldEditable(field),
|
|
3082
|
+
showCopy: !!field.copyable,
|
|
3055
3083
|
showInfoNote: !!field.description && field.type === "info",
|
|
3056
3084
|
infoNote: field.description,
|
|
3057
3085
|
field
|
|
@@ -3114,6 +3142,9 @@ function applyInspectorUi(c, intent) {
|
|
|
3114
3142
|
case "resetField":
|
|
3115
3143
|
c.resetField(intent.fieldId);
|
|
3116
3144
|
break;
|
|
3145
|
+
case "copyField":
|
|
3146
|
+
c.copyField(intent.fieldId);
|
|
3147
|
+
break;
|
|
3117
3148
|
}
|
|
3118
3149
|
}
|
|
3119
3150
|
var inspectorViewManifest = {
|
|
@@ -3235,7 +3266,8 @@ var INSPECTOR_DECLARATION = {
|
|
|
3235
3266
|
props: { size: { value: "sm" }, trigger: { node: { component: "icons/Link" } }, items: { item: "tokenItems" }, label: { item: "tokenActionLabel" }, align: { value: "end" } },
|
|
3236
3267
|
on: { onSelect: ui2("pickToken", { fieldId: { item: "id" }, itemId: { arg: "" } }) }
|
|
3237
3268
|
},
|
|
3238
|
-
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" } })
|
|
3239
3271
|
]
|
|
3240
3272
|
}
|
|
3241
3273
|
}
|
|
@@ -3266,15 +3298,16 @@ var INSPECTOR_DECLARATION = {
|
|
|
3266
3298
|
};
|
|
3267
3299
|
|
|
3268
3300
|
// src/edit/inspector/react/InspectorPanelView.tsx
|
|
3269
|
-
import { Badge as Badge2, Column as Column2, EmptyState as EmptyState2, Field, FieldGroup, IconButton as IconButton3, Menu, ScrollArea as ScrollArea2, SearchField as SearchField2, Section, Select as Select2, Stack as Stack2, Text as Text3 } from "@xenosystem/components/chrome";
|
|
3301
|
+
import { Badge as Badge2, Column as Column2, EmptyState as EmptyState2, Field, FieldGroup, IconButton as IconButton3, Menu as Menu2, ScrollArea as ScrollArea2, SearchField as SearchField2, Section, Select as Select2, Stack as Stack2, Text as Text3 } from "@xenosystem/components/chrome";
|
|
3270
3302
|
|
|
3271
3303
|
// src/edit/inspector/react/widgets.tsx
|
|
3272
3304
|
import { useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
3273
|
-
import
|
|
3305
|
+
import "react";
|
|
3274
3306
|
import {
|
|
3275
3307
|
AxisLabel,
|
|
3276
3308
|
IconButton as IconButton2,
|
|
3277
3309
|
Input as Input2,
|
|
3310
|
+
Menu,
|
|
3278
3311
|
ProportionBar,
|
|
3279
3312
|
SegmentedControl,
|
|
3280
3313
|
Select,
|
|
@@ -3561,7 +3594,27 @@ function ListWidget({ field, controller, renderField }) {
|
|
|
3561
3594
|
field.itemLabel ? /* @__PURE__ */ jsx3(Text2, { role: "caption", children: `${field.itemLabel} ${i + 1}` }) : null,
|
|
3562
3595
|
field.itemFields.filter((sub) => itemShowsSubField(sub, item)).map((sub) => {
|
|
3563
3596
|
const virtual = controller.getField(`${field.id}[${item.id}].${sub.id}`);
|
|
3564
|
-
|
|
3597
|
+
if (!virtual) return null;
|
|
3598
|
+
const bound = !!virtual.token?.tokenId;
|
|
3599
|
+
const tokens = controller.getTokens();
|
|
3600
|
+
const menu = sub.token?.bindable && controller.areTokensShown() ? /* @__PURE__ */ jsx3(
|
|
3601
|
+
Menu,
|
|
3602
|
+
{
|
|
3603
|
+
size: "sm",
|
|
3604
|
+
trigger: /* @__PURE__ */ jsx3(LinkIcon, {}),
|
|
3605
|
+
items: tokenItemsFor(virtual, tokens, bound),
|
|
3606
|
+
label: bound ? "Unbind token" : "Bind token",
|
|
3607
|
+
align: "end",
|
|
3608
|
+
onSelect: (id) => {
|
|
3609
|
+
if (id === "__unbind") controller.bindToken(virtual.id, null);
|
|
3610
|
+
else if (id.startsWith("token:")) controller.bindToken(virtual.id, id.slice(6));
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
) : null;
|
|
3614
|
+
return /* @__PURE__ */ jsxs2(Stack, { direction: "row", gap: 2, pad: "none", children: [
|
|
3615
|
+
bound ? /* @__PURE__ */ jsx3(Text2, { role: "meta", children: virtual.token?.tokenName ?? tokens.find((t) => t.id === virtual.token?.tokenId)?.name ?? virtual.token?.tokenId }) : renderField(virtual),
|
|
3616
|
+
menu
|
|
3617
|
+
] }, sub.id);
|
|
3565
3618
|
})
|
|
3566
3619
|
] }) : /* @__PURE__ */ jsx3(Text2, { role: "meta", children: typeof item === "string" ? item : JSON.stringify(item) }),
|
|
3567
3620
|
editable ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
|
|
@@ -3646,7 +3699,7 @@ function SectionView({ section, controller }) {
|
|
|
3646
3699
|
badge: section.badge ? /* @__PURE__ */ jsx4(Badge2, { children: section.badge }) : null,
|
|
3647
3700
|
actions: hasActions ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
3648
3701
|
section.actions.map((action) => /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ActionGlyph, { glyph: action.glyph }), label: action.label, disabled: action.disabled, onClick: () => ui10({ kind: "runAction", sectionId: section.id, actionId: action.id }) }, action.id)),
|
|
3649
|
-
section.hasAddMenu ? /* @__PURE__ */ jsx4(
|
|
3702
|
+
section.hasAddMenu ? /* @__PURE__ */ jsx4(Menu2, { size: "sm", trigger: "\uFF0B", items: section.addItems, label: section.addLabel, align: "end", onSelect: (id) => ui10({ kind: "addItem", sectionId: section.id, itemId: id }) }) : null
|
|
3650
3703
|
] }) : null,
|
|
3651
3704
|
children: [
|
|
3652
3705
|
section.showEmptyHint ? /* @__PURE__ */ jsx4(Stack2, { pad: "inset", children: /* @__PURE__ */ jsx4(Text3, { role: "caption", children: section.emptyHint }) }) : null,
|
|
@@ -3675,8 +3728,9 @@ function FieldRow({ row: row2, controller }) {
|
|
|
3675
3728
|
trailing: /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
3676
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,
|
|
3677
3730
|
row2.showKeyframes ? /* @__PURE__ */ jsx4(Badge2, { size: "sm", children: row2.keyframeChip }) : null,
|
|
3678
|
-
row2.showToken ? /* @__PURE__ */ jsx4(
|
|
3679
|
-
row2.showReset ? /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(ResetIcon, {}), label: "Reset to default", onClick: () => ui10({ kind: "resetField", fieldId: row2.id }) }) : null
|
|
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,
|
|
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
|
|
3680
3734
|
] }),
|
|
3681
3735
|
children: [
|
|
3682
3736
|
row2.bound ? /* @__PURE__ */ jsx4(Badge2, { title: row2.tokenTitle, children: row2.tokenLabel }) : null,
|
package/dist/icons/index.js
CHANGED
package/dist/ops/index.js
CHANGED
package/dist/time/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenosystem/blocks",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
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",
|