@canvas-harness/core 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +36 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -7
- package/dist/index.d.ts +37 -7
- package/dist/index.js +36 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2529,15 +2529,36 @@ type SerializedClipboard = {
|
|
|
2529
2529
|
*/
|
|
2530
2530
|
declare const serializeSelection: (store: CanvasStore) => SerializedClipboard;
|
|
2531
2531
|
type DeserializeOptions = {
|
|
2532
|
-
/**
|
|
2532
|
+
/**
|
|
2533
|
+
* Relative world-space offset added to every pasted node's `x/y`
|
|
2534
|
+
* (and to free-floating edge endpoints). Takes precedence over
|
|
2535
|
+
* `at` when both are passed. Default `(20, 20)` when neither is
|
|
2536
|
+
* given.
|
|
2537
|
+
*/
|
|
2533
2538
|
offset?: Vec2;
|
|
2539
|
+
/**
|
|
2540
|
+
* Absolute world-space target — the *center* of the pasted bbox
|
|
2541
|
+
* lands here. Used by `paste()` to place the paste at the cursor;
|
|
2542
|
+
* pass directly for programmatic absolute placement. Ignored if
|
|
2543
|
+
* `offset` is also set.
|
|
2544
|
+
*/
|
|
2545
|
+
at?: Vec2;
|
|
2534
2546
|
/** Override the selection on the store after applying. Default true. */
|
|
2535
2547
|
select?: boolean;
|
|
2536
2548
|
};
|
|
2537
2549
|
/**
|
|
2538
2550
|
* Applies a clipboard payload to the store. New ids are minted; edge
|
|
2539
|
-
* endpoints are rewired;
|
|
2540
|
-
*
|
|
2551
|
+
* endpoints are rewired; the resulting nodes + edges become the new
|
|
2552
|
+
* selection by default. Positioning precedence:
|
|
2553
|
+
*
|
|
2554
|
+
* 1. `opts.offset` (relative) — used as-is.
|
|
2555
|
+
* 2. `opts.at` (absolute) — offset computed so the clip's bbox
|
|
2556
|
+
* center lands on this point.
|
|
2557
|
+
* 3. Default — relative `(20, 20)` offset.
|
|
2558
|
+
*
|
|
2559
|
+
* Free-floating edge endpoints (`{ worldPoint }`) also receive the
|
|
2560
|
+
* offset so an edge with an unattached end stays connected to the
|
|
2561
|
+
* surrounding nodes after the paste.
|
|
2541
2562
|
*
|
|
2542
2563
|
* One `store.batch` — one undo step.
|
|
2543
2564
|
*
|
|
@@ -2577,8 +2598,17 @@ declare const cut: (store: CanvasStore) => Promise<SerializedClipboard>;
|
|
|
2577
2598
|
/**
|
|
2578
2599
|
* Paste from the system clipboard (or a supplied payload). Every node
|
|
2579
2600
|
* + edge gets a fresh id; edge endpoints rewire to the new ids; the
|
|
2580
|
-
*
|
|
2581
|
-
*
|
|
2601
|
+
* resulting nodes + edges become the new selection. Wrapped in one
|
|
2602
|
+
* undoable batch.
|
|
2603
|
+
*
|
|
2604
|
+
* Positioning, in precedence order:
|
|
2605
|
+
* 1. `opts.offset` — relative offset, used as-is.
|
|
2606
|
+
* 2. `opts.at` — absolute target; the paste's bbox center lands here.
|
|
2607
|
+
* 3. The store's current cursor (`interactionState.pointer`) — the
|
|
2608
|
+
* paste lands centered under the cursor. This is the default
|
|
2609
|
+
* `paste(store)` behavior on a Cmd+V keybind.
|
|
2610
|
+
* 4. Fallback `(20, 20)` relative offset when nothing else is known
|
|
2611
|
+
* (e.g. fresh session with no pointermove yet).
|
|
2582
2612
|
*
|
|
2583
2613
|
* Returns the new node ids on success, or `null` if the clipboard
|
|
2584
2614
|
* didn't contain a canvas-harness payload.
|
|
@@ -2587,8 +2617,8 @@ declare const cut: (store: CanvasStore) => Promise<SerializedClipboard>;
|
|
|
2587
2617
|
* <button onClick={() => paste(store)}>Paste</button>
|
|
2588
2618
|
*
|
|
2589
2619
|
* @example
|
|
2590
|
-
* // Programmatic paste
|
|
2591
|
-
* paste(store, savedClip, {
|
|
2620
|
+
* // Programmatic paste at a specific world point:
|
|
2621
|
+
* paste(store, savedClip, { at: { x: 300, y: 200 }, select: false })
|
|
2592
2622
|
*/
|
|
2593
2623
|
declare const paste: (store: CanvasStore, payload?: SerializedClipboard, opts?: DeserializeOptions) => Promise<(NodeId | EdgeId)[] | null>;
|
|
2594
2624
|
|
package/dist/index.d.ts
CHANGED
|
@@ -2529,15 +2529,36 @@ type SerializedClipboard = {
|
|
|
2529
2529
|
*/
|
|
2530
2530
|
declare const serializeSelection: (store: CanvasStore) => SerializedClipboard;
|
|
2531
2531
|
type DeserializeOptions = {
|
|
2532
|
-
/**
|
|
2532
|
+
/**
|
|
2533
|
+
* Relative world-space offset added to every pasted node's `x/y`
|
|
2534
|
+
* (and to free-floating edge endpoints). Takes precedence over
|
|
2535
|
+
* `at` when both are passed. Default `(20, 20)` when neither is
|
|
2536
|
+
* given.
|
|
2537
|
+
*/
|
|
2533
2538
|
offset?: Vec2;
|
|
2539
|
+
/**
|
|
2540
|
+
* Absolute world-space target — the *center* of the pasted bbox
|
|
2541
|
+
* lands here. Used by `paste()` to place the paste at the cursor;
|
|
2542
|
+
* pass directly for programmatic absolute placement. Ignored if
|
|
2543
|
+
* `offset` is also set.
|
|
2544
|
+
*/
|
|
2545
|
+
at?: Vec2;
|
|
2534
2546
|
/** Override the selection on the store after applying. Default true. */
|
|
2535
2547
|
select?: boolean;
|
|
2536
2548
|
};
|
|
2537
2549
|
/**
|
|
2538
2550
|
* Applies a clipboard payload to the store. New ids are minted; edge
|
|
2539
|
-
* endpoints are rewired;
|
|
2540
|
-
*
|
|
2551
|
+
* endpoints are rewired; the resulting nodes + edges become the new
|
|
2552
|
+
* selection by default. Positioning precedence:
|
|
2553
|
+
*
|
|
2554
|
+
* 1. `opts.offset` (relative) — used as-is.
|
|
2555
|
+
* 2. `opts.at` (absolute) — offset computed so the clip's bbox
|
|
2556
|
+
* center lands on this point.
|
|
2557
|
+
* 3. Default — relative `(20, 20)` offset.
|
|
2558
|
+
*
|
|
2559
|
+
* Free-floating edge endpoints (`{ worldPoint }`) also receive the
|
|
2560
|
+
* offset so an edge with an unattached end stays connected to the
|
|
2561
|
+
* surrounding nodes after the paste.
|
|
2541
2562
|
*
|
|
2542
2563
|
* One `store.batch` — one undo step.
|
|
2543
2564
|
*
|
|
@@ -2577,8 +2598,17 @@ declare const cut: (store: CanvasStore) => Promise<SerializedClipboard>;
|
|
|
2577
2598
|
/**
|
|
2578
2599
|
* Paste from the system clipboard (or a supplied payload). Every node
|
|
2579
2600
|
* + edge gets a fresh id; edge endpoints rewire to the new ids; the
|
|
2580
|
-
*
|
|
2581
|
-
*
|
|
2601
|
+
* resulting nodes + edges become the new selection. Wrapped in one
|
|
2602
|
+
* undoable batch.
|
|
2603
|
+
*
|
|
2604
|
+
* Positioning, in precedence order:
|
|
2605
|
+
* 1. `opts.offset` — relative offset, used as-is.
|
|
2606
|
+
* 2. `opts.at` — absolute target; the paste's bbox center lands here.
|
|
2607
|
+
* 3. The store's current cursor (`interactionState.pointer`) — the
|
|
2608
|
+
* paste lands centered under the cursor. This is the default
|
|
2609
|
+
* `paste(store)` behavior on a Cmd+V keybind.
|
|
2610
|
+
* 4. Fallback `(20, 20)` relative offset when nothing else is known
|
|
2611
|
+
* (e.g. fresh session with no pointermove yet).
|
|
2582
2612
|
*
|
|
2583
2613
|
* Returns the new node ids on success, or `null` if the clipboard
|
|
2584
2614
|
* didn't contain a canvas-harness payload.
|
|
@@ -2587,8 +2617,8 @@ declare const cut: (store: CanvasStore) => Promise<SerializedClipboard>;
|
|
|
2587
2617
|
* <button onClick={() => paste(store)}>Paste</button>
|
|
2588
2618
|
*
|
|
2589
2619
|
* @example
|
|
2590
|
-
* // Programmatic paste
|
|
2591
|
-
* paste(store, savedClip, {
|
|
2620
|
+
* // Programmatic paste at a specific world point:
|
|
2621
|
+
* paste(store, savedClip, { at: { x: 300, y: 200 }, select: false })
|
|
2592
2622
|
*/
|
|
2593
2623
|
declare const paste: (store: CanvasStore, payload?: SerializedClipboard, opts?: DeserializeOptions) => Promise<(NodeId | EdgeId)[] | null>;
|
|
2594
2624
|
|
package/dist/index.js
CHANGED
|
@@ -4988,8 +4988,7 @@ var createRenderer = (opts) => {
|
|
|
4988
4988
|
if (excludedNodes?.has(node.id)) continue;
|
|
4989
4989
|
const isEditingThis = editingNodeId === node.id;
|
|
4990
4990
|
if (isDrawablePrimitive(node.type)) {
|
|
4991
|
-
const
|
|
4992
|
-
const useRough = isSolidStroke && roughEnabled && (node.style?.roughness ?? 0) > 0;
|
|
4991
|
+
const useRough = roughEnabled && (node.style?.roughness ?? 0) > 0;
|
|
4993
4992
|
const roughReady = useRough ? getRoughCanvasCtor() !== null : false;
|
|
4994
4993
|
const composite = isCompositePrimitive(node.type);
|
|
4995
4994
|
drawWithNodeTransform(surface.ctx, node, () => {
|
|
@@ -5329,8 +5328,7 @@ var createRenderer = (opts) => {
|
|
|
5329
5328
|
return;
|
|
5330
5329
|
}
|
|
5331
5330
|
if (isDrawablePrimitive(node.type)) {
|
|
5332
|
-
const
|
|
5333
|
-
const useRough = isSolidStroke && dragRoughEnabled && (node.style?.roughness ?? 0) > 0;
|
|
5331
|
+
const useRough = dragRoughEnabled && (node.style?.roughness ?? 0) > 0;
|
|
5334
5332
|
const roughReady = useRough ? getRoughCanvasCtor() !== null : false;
|
|
5335
5333
|
if (useRough && roughReady) {
|
|
5336
5334
|
if (isCompositePrimitive(node.type)) {
|
|
@@ -5959,8 +5957,30 @@ var endInside = (end, ids) => {
|
|
|
5959
5957
|
if (!isAttached(end)) return true;
|
|
5960
5958
|
return ids.has(end.nodeId);
|
|
5961
5959
|
};
|
|
5960
|
+
var clipBboxCenter = (nodes) => {
|
|
5961
|
+
if (nodes.length === 0) return { x: 0, y: 0 };
|
|
5962
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
5963
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
5964
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
5965
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
5966
|
+
for (const n of nodes) {
|
|
5967
|
+
if (n.x < minX) minX = n.x;
|
|
5968
|
+
if (n.y < minY) minY = n.y;
|
|
5969
|
+
if (n.x + n.w > maxX) maxX = n.x + n.w;
|
|
5970
|
+
if (n.y + n.h > maxY) maxY = n.y + n.h;
|
|
5971
|
+
}
|
|
5972
|
+
return { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
|
|
5973
|
+
};
|
|
5962
5974
|
var deserializeClipboard = (store, clip, opts = {}) => {
|
|
5963
|
-
|
|
5975
|
+
let offset;
|
|
5976
|
+
if (opts.offset) {
|
|
5977
|
+
offset = opts.offset;
|
|
5978
|
+
} else if (opts.at && clip.nodes.length > 0) {
|
|
5979
|
+
const center = clipBboxCenter(clip.nodes);
|
|
5980
|
+
offset = { x: opts.at.x - center.x, y: opts.at.y - center.y };
|
|
5981
|
+
} else {
|
|
5982
|
+
offset = { x: 20, y: 20 };
|
|
5983
|
+
}
|
|
5964
5984
|
const select = opts.select ?? true;
|
|
5965
5985
|
const nodeMap = /* @__PURE__ */ new Map();
|
|
5966
5986
|
const edgeMap = /* @__PURE__ */ new Map();
|
|
@@ -5973,7 +5993,9 @@ var deserializeClipboard = (store, clip, opts = {}) => {
|
|
|
5973
5993
|
y: n.y + offset.y
|
|
5974
5994
|
}));
|
|
5975
5995
|
const remapEnd = (end) => {
|
|
5976
|
-
if (!isAttached(end))
|
|
5996
|
+
if (!isAttached(end)) {
|
|
5997
|
+
return { worldPoint: { x: end.worldPoint.x + offset.x, y: end.worldPoint.y + offset.y } };
|
|
5998
|
+
}
|
|
5977
5999
|
const newId = nodeMap.get(end.nodeId);
|
|
5978
6000
|
return newId ? { nodeId: newId, localOffset: end.localOffset } : end;
|
|
5979
6001
|
};
|
|
@@ -6016,7 +6038,14 @@ var cut = async (store) => {
|
|
|
6016
6038
|
var paste = async (store, payload, opts) => {
|
|
6017
6039
|
const clip = payload ?? await readClipboard();
|
|
6018
6040
|
if (!clip) return null;
|
|
6019
|
-
|
|
6041
|
+
let effective = opts;
|
|
6042
|
+
if (!opts?.offset && !opts?.at) {
|
|
6043
|
+
const pointer = store.getInteractionState().pointer;
|
|
6044
|
+
if (pointer) {
|
|
6045
|
+
effective = { ...opts, at: { x: pointer.worldX, y: pointer.worldY } };
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
const ids = deserializeClipboard(store, clip, effective);
|
|
6020
6049
|
return ids;
|
|
6021
6050
|
};
|
|
6022
6051
|
var writeClipboard = async (clip) => {
|