@neta-art/cohub 11.1.0 → 11.3.0
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/board/editor/context.d.ts +1 -0
- package/dist/board/editor/context.js +1 -0
- package/dist/board/editor/editor.d.ts +1 -0
- package/dist/board/editor/editor.js +7 -0
- package/dist/board/editor/interaction.d.ts +4 -0
- package/dist/board/editor/interaction.js +16 -7
- package/dist/board/editor/selection-transform.d.ts +1 -1
- package/dist/board/editor/selection-transform.js +13 -2
- package/dist/board/export/index.d.ts +2 -0
- package/dist/board/export/index.js +1 -0
- package/dist/board/export/scene.d.ts +2 -0
- package/dist/board/export/scene.js +4 -0
- package/dist/board/model/file-preview.d.ts +0 -1
- package/dist/board/model/file-preview.js +10 -1
- package/dist/board/model/index.d.ts +2 -1
- package/dist/board/model/index.js +2 -1
- package/dist/board/model/script-api.d.ts +69 -0
- package/dist/board/model/script-api.js +77 -0
- package/dist/board/render/audio-waveform.d.ts +1 -1
- package/dist/board/render/audio-waveform.js +8 -5
- package/dist/board/render/card-tag.d.ts +24 -0
- package/dist/board/render/card-tag.js +205 -0
- package/dist/board/render/index.d.ts +7 -4
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +2 -1
- package/dist/board/render/media-interaction.js +14 -6
- package/dist/board/render/palette.js +2 -2
- package/dist/board/render/renderers/arrow-card-renderer.js +20 -16
- package/dist/board/render/renderers/audio-card-renderer.d.ts +8 -1
- package/dist/board/render/renderers/audio-card-renderer.js +64 -60
- package/dist/board/render/renderers/base-card-renderer.d.ts +1 -26
- package/dist/board/render/renderers/base-card-renderer.js +6 -92
- package/dist/board/render/renderers/board-renderer-registry.d.ts +8 -0
- package/dist/board/render/renderers/board-renderer-registry.js +3 -3
- package/dist/board/render/renderers/custom-card-renderer.js +4 -7
- package/dist/board/render/renderers/effect-card-renderer.js +0 -5
- package/dist/board/render/renderers/file-card-renderer.d.ts +1 -11
- package/dist/board/render/renderers/file-card-renderer.js +43 -105
- package/dist/board/render/renderers/frame-card-renderer.js +14 -15
- package/dist/board/render/renderers/image-card-renderer.js +62 -67
- package/dist/board/render/renderers/media-key.d.ts +6 -0
- package/dist/board/render/renderers/media-key.js +39 -0
- package/dist/board/render/renderers/shape-card-renderer.js +6 -12
- package/dist/board/render/renderers/task-card-renderer.d.ts +1 -9
- package/dist/board/render/renderers/task-card-renderer.js +82 -56
- package/dist/board/render/renderers/unknown-card-renderer.js +17 -16
- package/dist/board/render/renderers/video-card-renderer.js +73 -80
- package/dist/board/render/stroke.d.ts +1 -1
- package/dist/board/render/stroke.js +5 -4
- package/dist/board/render/style.d.ts +29 -0
- package/dist/board/render/style.js +54 -0
- package/dist/board/render/surface.d.ts +34 -0
- package/dist/board/render/surface.js +132 -0
- package/dist/board/render/text-fit.d.ts +8 -0
- package/dist/board/render/text-fit.js +60 -0
- package/dist/board/render/text-resolution.d.ts +2 -1
- package/dist/board/render/text-resolution.js +4 -1
- package/dist/board/stage/index.d.ts +3 -2
- package/dist/board/stage/index.js +2 -1
- package/dist/board/stage/overlay.d.ts +41 -0
- package/dist/board/stage/overlay.js +182 -0
- package/dist/board/stage/scene.d.ts +2 -43
- package/dist/board/stage/scene.js +23 -131
- package/dist/board/stage/stage.d.ts +2 -0
- package/dist/board/stage/stage.js +99 -32
- package/dist/chunks/app-realtime.js +1697 -0
- package/dist/chunks/environment.d.ts +997 -3
- package/dist/chunks/http.d.ts +393 -3
- package/dist/chunks/http.js +40 -1
- package/dist/chunks/websocket.d.ts +4 -0
- package/dist/chunks/websocket.js +11 -2
- package/dist/http.d.ts +2 -2
- package/dist/index.d.ts +4 -323
- package/dist/index.js +58 -1660
- package/dist/protocol/dist/board-model.js +51 -51
- package/dist/protocol/dist/board-schema.d.ts +1 -2
- package/dist/protocol/dist/board-schema.js +7 -22
- package/package.json +2 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { CORNER_RESIZE_HANDLES, frameCorners, frameHandlePosition, frameRayIntersection, rotationHandleAnchor, rotationHandlePosition } from "../model/geometry.js";
|
|
2
|
+
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
3
|
+
import { tracePolyline } from "../render/stroke.js";
|
|
4
|
+
import { cardOutlinePoints, isBoardCard, traceCardOutline } from "../render/surface.js";
|
|
5
|
+
//#region src/board/stage/overlay.ts
|
|
6
|
+
const HOVER_ALPHA = .55;
|
|
7
|
+
const PEER_ALPHA = .6;
|
|
8
|
+
function expandFrame(frame, by) {
|
|
9
|
+
return {
|
|
10
|
+
...frame,
|
|
11
|
+
x: frame.x - by,
|
|
12
|
+
y: frame.y - by,
|
|
13
|
+
width: frame.width + by * 2,
|
|
14
|
+
height: frame.height + by * 2
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function traceFrame(graphics, frame) {
|
|
18
|
+
const corners = frameCorners(frame);
|
|
19
|
+
const first = corners[0];
|
|
20
|
+
if (!first) return graphics;
|
|
21
|
+
graphics.moveTo(first.x, first.y);
|
|
22
|
+
for (const point of corners.slice(1)) graphics.lineTo(point.x, point.y);
|
|
23
|
+
return graphics.closePath();
|
|
24
|
+
}
|
|
25
|
+
function edgeOf(frame, handle) {
|
|
26
|
+
const at = (corner) => frameHandlePosition(frame, corner);
|
|
27
|
+
switch (handle) {
|
|
28
|
+
case "n": return [at("nw"), at("ne")];
|
|
29
|
+
case "e": return [at("ne"), at("se")];
|
|
30
|
+
case "s": return [at("sw"), at("se")];
|
|
31
|
+
case "w": return [at("nw"), at("sw")];
|
|
32
|
+
default: return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function strokeOutline(graphics, frame, card, style, line, inv, dash, march) {
|
|
36
|
+
const shape = card ? style : {
|
|
37
|
+
corner: "round",
|
|
38
|
+
radius: 0
|
|
39
|
+
};
|
|
40
|
+
const rect = {
|
|
41
|
+
x: -frame.width / 2,
|
|
42
|
+
y: -frame.height / 2,
|
|
43
|
+
width: frame.width,
|
|
44
|
+
height: frame.height
|
|
45
|
+
};
|
|
46
|
+
const angle = (frame.rotation || 0) * Math.PI / 180;
|
|
47
|
+
graphics.save();
|
|
48
|
+
graphics.setTransform(Math.cos(angle), Math.sin(angle), -Math.sin(angle), Math.cos(angle), frame.x + frame.width / 2, frame.y + frame.height / 2);
|
|
49
|
+
if (dash > 0) tracePolyline(graphics, cardOutlinePoints(rect, shape), [dash * inv, dash * inv], false, march * inv);
|
|
50
|
+
else traceCardOutline(graphics, rect, shape);
|
|
51
|
+
graphics.stroke({
|
|
52
|
+
...line,
|
|
53
|
+
cap: "butt",
|
|
54
|
+
join: "miter"
|
|
55
|
+
});
|
|
56
|
+
graphics.restore();
|
|
57
|
+
}
|
|
58
|
+
function drawHandle(graphics, point, style, palette, inv, active) {
|
|
59
|
+
const size = (active ? style.selection.handleSize + 2 : style.selection.handleSize) * inv;
|
|
60
|
+
if (style.selection.handle === "square") graphics.rect(point.x - size / 2, point.y - size / 2, size, size);
|
|
61
|
+
else graphics.circle(point.x, point.y, size / 2);
|
|
62
|
+
graphics.fill({
|
|
63
|
+
color: active ? palette.brand : palette.surface,
|
|
64
|
+
alpha: 1
|
|
65
|
+
}).stroke({
|
|
66
|
+
color: palette.brand,
|
|
67
|
+
width: style.selection.width * inv,
|
|
68
|
+
alignment: 1
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function strokeArrow(graphics, item, scene, color, inv, alpha) {
|
|
72
|
+
const arrow = item;
|
|
73
|
+
const path = resolveSceneArrow(arrow, scene).path;
|
|
74
|
+
const first = path[0];
|
|
75
|
+
if (!first) return;
|
|
76
|
+
graphics.moveTo(first.x, first.y);
|
|
77
|
+
for (const point of path.slice(1)) graphics.lineTo(point.x, point.y);
|
|
78
|
+
graphics.stroke({
|
|
79
|
+
color,
|
|
80
|
+
width: (arrow.style.strokeWidth ?? 2) + 6 * inv,
|
|
81
|
+
alpha,
|
|
82
|
+
cap: "round",
|
|
83
|
+
join: "round"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function drawBoardOverlay(graphics, input) {
|
|
87
|
+
const { zoom, palette, style, scene, selected, transform, controls, hoveredControl } = input;
|
|
88
|
+
const inv = 1 / Math.max(zoom, 1e-4);
|
|
89
|
+
const brand = palette.brand;
|
|
90
|
+
const outset = style.selection.outset * inv;
|
|
91
|
+
if (input.marquee) {
|
|
92
|
+
const { x, y, width, height } = input.marquee;
|
|
93
|
+
graphics.rect(x, y, width, height).fill({
|
|
94
|
+
color: brand,
|
|
95
|
+
alpha: .08
|
|
96
|
+
});
|
|
97
|
+
graphics.rect(x, y, width, height).stroke({
|
|
98
|
+
color: brand,
|
|
99
|
+
width: inv,
|
|
100
|
+
alpha: .7
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (input.bindTarget) traceFrame(graphics, input.bindTarget).stroke({
|
|
104
|
+
color: brand,
|
|
105
|
+
width: 2 * inv,
|
|
106
|
+
alpha: .9
|
|
107
|
+
});
|
|
108
|
+
for (const port of input.ports ?? []) {
|
|
109
|
+
const hovered = input.hoveredPort && Math.abs(input.hoveredPort.x - port.x) < .001 && Math.abs(input.hoveredPort.y - port.y) < .001;
|
|
110
|
+
const radius = (hovered ? port.radius * 1.5 : port.radius) * inv;
|
|
111
|
+
if (style.corner === "pixel") graphics.rect(port.x - radius, port.y - radius, radius * 2, radius * 2);
|
|
112
|
+
else graphics.circle(port.x, port.y, radius);
|
|
113
|
+
graphics.fill({
|
|
114
|
+
color: palette.surface,
|
|
115
|
+
alpha: 1
|
|
116
|
+
}).stroke({
|
|
117
|
+
color: brand,
|
|
118
|
+
width: 1.5 * inv,
|
|
119
|
+
alpha: hovered ? 1 : .8
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const hovered = input.hovered;
|
|
123
|
+
if (hovered && hovered.type === "arrow") strokeArrow(graphics, hovered, scene, palette.muted, inv, .2);
|
|
124
|
+
else if (hovered) strokeOutline(graphics, expandFrame(hovered.frame, outset), isBoardCard(hovered), style, {
|
|
125
|
+
color: palette.muted,
|
|
126
|
+
width: inv * style.selection.width,
|
|
127
|
+
alpha: HOVER_ALPHA
|
|
128
|
+
}, inv, 0, 0);
|
|
129
|
+
if (selected.length === 0) return;
|
|
130
|
+
const single = selected.length === 1 ? selected[0] : void 0;
|
|
131
|
+
const dash = style.selection.dash;
|
|
132
|
+
const line = {
|
|
133
|
+
color: brand,
|
|
134
|
+
width: style.selection.width * inv,
|
|
135
|
+
alpha: 1
|
|
136
|
+
};
|
|
137
|
+
for (const item of selected) if (item.type === "arrow") strokeArrow(graphics, item, scene, brand, inv, .3);
|
|
138
|
+
else if (!single) strokeOutline(graphics, expandFrame(item.frame, outset), isBoardCard(item), style, {
|
|
139
|
+
...line,
|
|
140
|
+
alpha: PEER_ALPHA
|
|
141
|
+
}, inv, 0, 0);
|
|
142
|
+
if (!transform) return;
|
|
143
|
+
if (single?.type !== "arrow") {
|
|
144
|
+
const card = single ? isBoardCard(single) : false;
|
|
145
|
+
strokeOutline(graphics, expandFrame(transform.frame, outset), card, style, line, inv, dash, input.march);
|
|
146
|
+
}
|
|
147
|
+
if (!controls) return;
|
|
148
|
+
if (input.arrowEndpoints?.length) {
|
|
149
|
+
for (const point of input.arrowEndpoints) drawHandle(graphics, point, style, palette, inv, false);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const frame = expandFrame(transform.frame, outset);
|
|
153
|
+
if (hoveredControl?.kind === "resize" && transform.resizeMode === "free") {
|
|
154
|
+
const edge = edgeOf(frame, hoveredControl.handle);
|
|
155
|
+
if (edge) graphics.moveTo(edge[0].x, edge[0].y).lineTo(edge[1].x, edge[1].y).stroke({
|
|
156
|
+
color: brand,
|
|
157
|
+
width: 2.5 * inv,
|
|
158
|
+
alpha: 1
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (transform.resizeMode !== "none") for (const handle of CORNER_RESIZE_HANDLES) {
|
|
162
|
+
const active = hoveredControl?.kind === "resize" && hoveredControl.handle === handle;
|
|
163
|
+
drawHandle(graphics, frameHandlePosition(frame, handle), style, palette, inv, active);
|
|
164
|
+
}
|
|
165
|
+
if (transform.canRotate && (input.pointerType === "touch" || transform.resizeMode === "none")) {
|
|
166
|
+
const pointer = input.rotationPointer;
|
|
167
|
+
const rotation = pointer ?? rotationHandlePosition(frame, zoom);
|
|
168
|
+
const anchor = pointer ? frameRayIntersection(frame, pointer) : rotationHandleAnchor(frame);
|
|
169
|
+
const active = pointer !== null || hoveredControl?.kind === "rotate";
|
|
170
|
+
graphics.moveTo(anchor.x, anchor.y).lineTo(rotation.x, rotation.y).stroke({
|
|
171
|
+
color: brand,
|
|
172
|
+
width: inv,
|
|
173
|
+
alpha: .8
|
|
174
|
+
});
|
|
175
|
+
graphics.circle(rotation.x, rotation.y, (active ? 6 : 5) * inv).fill({ color: active ? brand : palette.surface }).stroke({
|
|
176
|
+
color: brand,
|
|
177
|
+
width: 1.5 * inv
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
export { drawBoardOverlay, expandFrame };
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { BoardDrawPoint } from "../../protocol/dist/board-model.js";
|
|
2
|
-
import { BoardFrame } from "../../protocol/dist/board-layout.js";
|
|
3
|
-
import { Rect, WorldPoint } from "../model/geometry.js";
|
|
4
2
|
import { BoardScene as BoardScene$1, BoardSceneItem } from "../model/scene.js";
|
|
5
3
|
import "../model/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import "../editor/index.js";
|
|
8
|
-
import { BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette } from "../render/renderers/board-renderer-registry.js";
|
|
4
|
+
import { BoardCardRendererResolver, BoardRenderContext } from "../render/renderers/board-renderer-registry.js";
|
|
9
5
|
import "../render/index.js";
|
|
10
6
|
import { Container, Graphics } from "pixi.js";
|
|
11
7
|
//#region src/board/stage/scene.d.ts
|
|
@@ -29,42 +25,6 @@ type SceneSyncInput = {
|
|
|
29
25
|
geometryVersion: number;
|
|
30
26
|
gestureActive: boolean;
|
|
31
27
|
};
|
|
32
|
-
type SceneOverlayInput = {
|
|
33
|
-
zoom: number;
|
|
34
|
-
pointerType: string;
|
|
35
|
-
marquee: Rect | null;
|
|
36
|
-
selection: string[];
|
|
37
|
-
transform: BoardSelectionTransform | null;
|
|
38
|
-
controls: boolean;
|
|
39
|
-
hoveredControl: BoardTransformControl | null;
|
|
40
|
-
rotationPointer: WorldPoint | null;
|
|
41
|
-
arrowEndpoints?: Array<{
|
|
42
|
-
x: number;
|
|
43
|
-
y: number;
|
|
44
|
-
}>;
|
|
45
|
-
ports?: ReadonlyArray<{
|
|
46
|
-
x: number;
|
|
47
|
-
y: number;
|
|
48
|
-
radius: number;
|
|
49
|
-
}>;
|
|
50
|
-
hoveredPort?: {
|
|
51
|
-
x: number;
|
|
52
|
-
y: number;
|
|
53
|
-
} | null;
|
|
54
|
-
arrowDraft?: {
|
|
55
|
-
from: {
|
|
56
|
-
x: number;
|
|
57
|
-
y: number;
|
|
58
|
-
};
|
|
59
|
-
to: {
|
|
60
|
-
x: number;
|
|
61
|
-
y: number;
|
|
62
|
-
};
|
|
63
|
-
size: number;
|
|
64
|
-
targetFrame: BoardFrame | null;
|
|
65
|
-
} | null;
|
|
66
|
-
bindTarget?: BoardFrame | null;
|
|
67
|
-
};
|
|
68
28
|
type BoardSceneNode = {
|
|
69
29
|
item: BoardSceneItem;
|
|
70
30
|
container: Container;
|
|
@@ -74,7 +34,6 @@ type BoardScene = {
|
|
|
74
34
|
getNode: (itemId: string) => BoardSceneNode | null;
|
|
75
35
|
readonly animated: boolean;
|
|
76
36
|
tick: (clock: number) => number | null;
|
|
77
|
-
drawOverlay: (input: SceneOverlayInput, palette: BoardRenderPalette) => void;
|
|
78
37
|
destroy: (context: BoardRenderContext) => void;
|
|
79
38
|
};
|
|
80
39
|
declare function createBoardScene(options: {
|
|
@@ -84,4 +43,4 @@ declare function createBoardScene(options: {
|
|
|
84
43
|
onFarLayerFrame?: () => void;
|
|
85
44
|
}): BoardScene;
|
|
86
45
|
//#endregion
|
|
87
|
-
export { BoardLiveStroke, BoardScene, BoardSceneNode,
|
|
46
|
+
export { BoardLiveStroke, BoardScene, BoardSceneNode, SceneSyncInput, createBoardScene };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { parseBoardItem } from "../../protocol/dist/board-model.js";
|
|
2
2
|
import { drawPointsBounds } from "../../protocol/dist/board-layout.js";
|
|
3
|
-
import { CORNER_RESIZE_HANDLES, frameCorners, frameHandlePosition, frameRayIntersection, rotationHandleAnchor, rotationHandlePosition } from "../model/geometry.js";
|
|
4
3
|
import { clippingAncestor, isClippingFrame, syncClipGroup } from "../render/clip.js";
|
|
5
4
|
import { Graphics } from "pixi.js";
|
|
6
5
|
//#region src/board/stage/scene.ts
|
|
@@ -9,6 +8,7 @@ const FAR_LAYER_EXIT = 350;
|
|
|
9
8
|
const FAR_PASS_BATCH = 240;
|
|
10
9
|
const FAR_LIVE_LIMIT = 64;
|
|
11
10
|
const Z_FAR_LAYER = -1;
|
|
11
|
+
const Z_LIFTED = 1e9;
|
|
12
12
|
const Z_LIVE_STROKE = Number.MAX_SAFE_INTEGER - 1;
|
|
13
13
|
const Z_OVERLAY = Number.MAX_SAFE_INTEGER;
|
|
14
14
|
const POOL_LIMIT_PER_RENDERER = 48;
|
|
@@ -21,26 +21,6 @@ function idSetSignature(ids) {
|
|
|
21
21
|
}
|
|
22
22
|
return hash;
|
|
23
23
|
}
|
|
24
|
-
function traceFrame(graphics, frame) {
|
|
25
|
-
const corners = frameCorners(frame);
|
|
26
|
-
const first = corners[0];
|
|
27
|
-
if (!first) return graphics;
|
|
28
|
-
graphics.moveTo(first.x, first.y);
|
|
29
|
-
for (let index = 1; index < corners.length; index += 1) {
|
|
30
|
-
const point = corners[index];
|
|
31
|
-
if (point) graphics.lineTo(point.x, point.y);
|
|
32
|
-
}
|
|
33
|
-
return graphics.closePath();
|
|
34
|
-
}
|
|
35
|
-
function frameEdgePoints(frame, handle) {
|
|
36
|
-
switch (handle) {
|
|
37
|
-
case "n": return [frameHandlePosition(frame, "nw"), frameHandlePosition(frame, "ne")];
|
|
38
|
-
case "e": return [frameHandlePosition(frame, "ne"), frameHandlePosition(frame, "se")];
|
|
39
|
-
case "s": return [frameHandlePosition(frame, "sw"), frameHandlePosition(frame, "se")];
|
|
40
|
-
case "w": return [frameHandlePosition(frame, "nw"), frameHandlePosition(frame, "sw")];
|
|
41
|
-
default: return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
24
|
function createBoardScene(options) {
|
|
45
25
|
const { world, overlay, getRenderer } = options;
|
|
46
26
|
world.sortableChildren = true;
|
|
@@ -55,6 +35,8 @@ function createBoardScene(options) {
|
|
|
55
35
|
let farSig = null;
|
|
56
36
|
let farActive = false;
|
|
57
37
|
let lastStructureVersion = -1;
|
|
38
|
+
let lastZoom = NaN;
|
|
39
|
+
let lifted = /* @__PURE__ */ new Set();
|
|
58
40
|
const liveStrokes = /* @__PURE__ */ new Map();
|
|
59
41
|
function setHeldKey(context, cardId, desiredKey) {
|
|
60
42
|
const heldKey = heldKeys.get(cardId) ?? null;
|
|
@@ -138,6 +120,7 @@ function createBoardScene(options) {
|
|
|
138
120
|
};
|
|
139
121
|
cards.set(item.id, entry);
|
|
140
122
|
setHeldKey(context, item.id, context.assetKey(item));
|
|
123
|
+
renderer.zoom?.(container, context.zoom);
|
|
141
124
|
return entry;
|
|
142
125
|
}
|
|
143
126
|
let farFront = createFarLayer();
|
|
@@ -355,7 +338,8 @@ function createBoardScene(options) {
|
|
|
355
338
|
const hovered = context.hoveredId === id;
|
|
356
339
|
const resizing = context.resizingIds.has(id);
|
|
357
340
|
const scriptRevision = context.scripts?.revision?.(id) ?? 0;
|
|
358
|
-
const
|
|
341
|
+
const observed = entry.renderer.observesSelection === true && (selected !== entry.selected || hovered !== entry.hovered);
|
|
342
|
+
const changed = input.staleIds?.has(id) || scriptRevision !== entry.scriptRevision || item !== entry.item || entry.renderer.animated || observed || resizing !== entry.resizing || globalSig !== entry.globalSig;
|
|
359
343
|
entry.scriptRevision = scriptRevision;
|
|
360
344
|
entry.item = item;
|
|
361
345
|
entry.selected = selected;
|
|
@@ -370,7 +354,13 @@ function createBoardScene(options) {
|
|
|
370
354
|
}
|
|
371
355
|
}
|
|
372
356
|
if (structureChanged || liveSetChanged) syncClips(scene);
|
|
373
|
-
if (
|
|
357
|
+
if (context.zoom !== lastZoom) {
|
|
358
|
+
lastZoom = context.zoom;
|
|
359
|
+
for (const entry of cards.values()) entry.renderer.zoom?.(entry.container, lastZoom);
|
|
360
|
+
}
|
|
361
|
+
const liftChanged = context.liftedIds !== lifted;
|
|
362
|
+
lifted = context.liftedIds;
|
|
363
|
+
if (structureChanged || farModeChanged || liveSetChanged || liftChanged) applyChildOrder(scene);
|
|
374
364
|
syncLiveStrokes(input);
|
|
375
365
|
}
|
|
376
366
|
function tick(clock) {
|
|
@@ -383,114 +373,16 @@ function createBoardScene(options) {
|
|
|
383
373
|
}
|
|
384
374
|
return next;
|
|
385
375
|
}
|
|
386
|
-
function applyChildOrder() {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const inv = 1 / zoom;
|
|
395
|
-
const brand = palette.brand;
|
|
396
|
-
if (marquee) {
|
|
397
|
-
overlay.rect(marquee.x, marquee.y, marquee.width, marquee.height).fill({
|
|
398
|
-
color: brand,
|
|
399
|
-
alpha: .08
|
|
400
|
-
});
|
|
401
|
-
overlay.rect(marquee.x, marquee.y, marquee.width, marquee.height).stroke({
|
|
402
|
-
color: brand,
|
|
403
|
-
width: inv,
|
|
404
|
-
alpha: .7
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
if (bindTarget) traceFrame(overlay, bindTarget).stroke({
|
|
408
|
-
color: brand,
|
|
409
|
-
width: 2 * inv,
|
|
410
|
-
alpha: .9
|
|
411
|
-
});
|
|
412
|
-
if (arrowDraft) {
|
|
413
|
-
const { from, to, size, targetFrame } = arrowDraft;
|
|
414
|
-
if (targetFrame) traceFrame(overlay, targetFrame).stroke({
|
|
415
|
-
color: brand,
|
|
416
|
-
width: 2 * inv,
|
|
417
|
-
alpha: .9
|
|
418
|
-
});
|
|
419
|
-
overlay.moveTo(from.x, from.y).lineTo(to.x, to.y).stroke({
|
|
420
|
-
color: brand,
|
|
421
|
-
width: Math.max(size, 1.5) * inv,
|
|
422
|
-
alpha: .85,
|
|
423
|
-
cap: "round"
|
|
424
|
-
});
|
|
425
|
-
overlay.circle(to.x, to.y, 3.5 * inv).fill({
|
|
426
|
-
color: brand,
|
|
427
|
-
alpha: .95
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
if (ports && ports.length > 0) for (const port of ports) {
|
|
431
|
-
const hovered = hoveredPort && Math.abs(hoveredPort.x - port.x) < .001 && Math.abs(hoveredPort.y - port.y) < .001;
|
|
432
|
-
const r = (hovered ? port.radius * 1.5 : port.radius) * inv;
|
|
433
|
-
overlay.circle(port.x, port.y, r).fill({
|
|
434
|
-
color: palette.surface,
|
|
435
|
-
alpha: 1
|
|
436
|
-
}).stroke({
|
|
437
|
-
color: brand,
|
|
438
|
-
width: 1.5 * inv,
|
|
439
|
-
alpha: hovered ? 1 : .8
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
if (!transform || selection.length === 0) return;
|
|
443
|
-
const source = transform.frame;
|
|
444
|
-
traceFrame(overlay, source).stroke({
|
|
445
|
-
color: brand,
|
|
446
|
-
width: 1.5 * inv,
|
|
447
|
-
alpha: .95
|
|
448
|
-
});
|
|
449
|
-
if (!controls) return;
|
|
450
|
-
if (arrowEndpoints && arrowEndpoints.length > 0) {
|
|
451
|
-
const r = 5 * inv;
|
|
452
|
-
for (const point of arrowEndpoints) overlay.circle(point.x, point.y, r).fill({
|
|
453
|
-
color: palette.surface,
|
|
454
|
-
alpha: 1
|
|
455
|
-
}).stroke({
|
|
456
|
-
color: brand,
|
|
457
|
-
width: 1.5 * inv
|
|
458
|
-
});
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
if (hoveredControl?.kind === "resize" && transform.resizeMode === "free") {
|
|
462
|
-
const edge = frameEdgePoints(source, hoveredControl.handle);
|
|
463
|
-
if (edge) overlay.moveTo(edge[0].x, edge[0].y).lineTo(edge[1].x, edge[1].y).stroke({
|
|
464
|
-
color: brand,
|
|
465
|
-
width: 2.5 * inv,
|
|
466
|
-
alpha: 1
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
if (transform.resizeMode !== "none") for (const handle of CORNER_RESIZE_HANDLES) {
|
|
470
|
-
const position = frameHandlePosition(source, handle);
|
|
471
|
-
const hovered = hoveredControl?.kind === "resize" && hoveredControl.handle === handle;
|
|
472
|
-
overlay.circle(position.x, position.y, (hovered ? 5 : 4) * inv).fill({
|
|
473
|
-
color: hovered ? brand : palette.surface,
|
|
474
|
-
alpha: 1
|
|
475
|
-
}).stroke({
|
|
476
|
-
color: brand,
|
|
477
|
-
width: 1.5 * inv
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
if (transform.canRotate && (pointerType === "touch" || transform.resizeMode === "none")) {
|
|
481
|
-
const rotation = rotationPointer ?? rotationHandlePosition(source, zoom);
|
|
482
|
-
const anchor = rotationPointer ? frameRayIntersection(source, rotationPointer) : rotationHandleAnchor(source);
|
|
483
|
-
const hovered = rotationPointer !== null || hoveredControl?.kind === "rotate";
|
|
484
|
-
overlay.moveTo(anchor.x, anchor.y).lineTo(rotation.x, rotation.y).stroke({
|
|
485
|
-
color: brand,
|
|
486
|
-
width: inv,
|
|
487
|
-
alpha: .8
|
|
488
|
-
});
|
|
489
|
-
overlay.circle(rotation.x, rotation.y, (hovered ? 6 : 5) * inv).fill({ color: hovered ? brand : palette.surface }).stroke({
|
|
490
|
-
color: brand,
|
|
491
|
-
width: 1.5 * inv
|
|
492
|
-
});
|
|
376
|
+
function applyChildOrder(scene) {
|
|
377
|
+
const raised = new Set(lifted);
|
|
378
|
+
for (const id of lifted) if (scene.get(id)?.type === "frame") for (const child of scene.descendants(id)) raised.add(child);
|
|
379
|
+
const order = (id) => (orderById.get(id) ?? 0) + (raised.has(id) ? Z_LIFTED : 0);
|
|
380
|
+
for (const [id, entry] of cards) entry.container.zIndex = order(id);
|
|
381
|
+
for (const [id, entry] of clips) {
|
|
382
|
+
entry.group.zIndex = order(id) + .5;
|
|
383
|
+
entry.group.sortChildren();
|
|
493
384
|
}
|
|
385
|
+
world.sortChildren();
|
|
494
386
|
}
|
|
495
387
|
function destroy(context) {
|
|
496
388
|
for (const entry of cards.values()) entry.renderer.destroy?.(entry.container, context);
|
|
@@ -509,6 +401,7 @@ function createBoardScene(options) {
|
|
|
509
401
|
farActive = false;
|
|
510
402
|
visibleMemo = null;
|
|
511
403
|
lastStructureVersion = -1;
|
|
404
|
+
lastZoom = NaN;
|
|
512
405
|
}
|
|
513
406
|
return {
|
|
514
407
|
sync,
|
|
@@ -523,7 +416,6 @@ function createBoardScene(options) {
|
|
|
523
416
|
return animatedIds.size > 0;
|
|
524
417
|
},
|
|
525
418
|
tick,
|
|
526
|
-
drawOverlay,
|
|
527
419
|
destroy
|
|
528
420
|
};
|
|
529
421
|
}
|
|
@@ -10,6 +10,7 @@ import "../model/index.js";
|
|
|
10
10
|
import { BoardPointerEvent } from "../editor/interaction.js";
|
|
11
11
|
import { BoardEditor } from "../editor/editor.js";
|
|
12
12
|
import "../editor/index.js";
|
|
13
|
+
import { BoardRenderStyle } from "../render/style.js";
|
|
13
14
|
import { BoardCardRendererResolver, BoardRenderLabels, BoardRenderPalette } from "../render/renderers/board-renderer-registry.js";
|
|
14
15
|
import "../render/index.js";
|
|
15
16
|
import { BoardAssetManager } from "./assets.js";
|
|
@@ -22,6 +23,7 @@ type BoardStageTheme = {
|
|
|
22
23
|
palette: BoardRenderPalette;
|
|
23
24
|
colors: BoardShapeColors;
|
|
24
25
|
colorScheme: "dark" | "light";
|
|
26
|
+
style?: BoardRenderStyle;
|
|
25
27
|
imageBackground?: boolean;
|
|
26
28
|
};
|
|
27
29
|
type BoardStagePointerPhase = "down" | "move" | "up" | "leave";
|
|
@@ -14,12 +14,14 @@ import { createBoardBackground, updateBoardBackground } from "../render/board-ba
|
|
|
14
14
|
import { getBoardResolution, textZoomBucket } from "../render/text-resolution.js";
|
|
15
15
|
import { DEFAULT_BOARD_RENDER_LABELS, getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
16
16
|
import { createBoardScriptHost } from "../render/script-host.js";
|
|
17
|
+
import { defaultBoardRenderStyle } from "../render/style.js";
|
|
17
18
|
import { createBoardPlayer } from "../player/index.js";
|
|
18
19
|
import { createBoardScriptObservers } from "../model/script-observers.js";
|
|
19
20
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
20
21
|
import { createBoardScene } from "./scene.js";
|
|
21
22
|
import { scheduleBoardMaintenance } from "./maintenance.js";
|
|
22
23
|
import { createBoardScriptPointerRouter } from "./script-pointer.js";
|
|
24
|
+
import { drawBoardOverlay } from "./overlay.js";
|
|
23
25
|
import { createBoardItemViewLayer } from "./views.js";
|
|
24
26
|
import { Application, Container, Graphics, RendererType } from "pixi.js";
|
|
25
27
|
//#region src/board/stage/stage.ts
|
|
@@ -42,7 +44,19 @@ function defaultTheme() {
|
|
|
42
44
|
colorScheme: "dark"
|
|
43
45
|
};
|
|
44
46
|
}
|
|
47
|
+
const NO_IDS = /* @__PURE__ */ new Set();
|
|
48
|
+
const MARCH_STEP_MS = 160;
|
|
49
|
+
const MARCH_SMOOTH_MS = 40;
|
|
45
50
|
const ROTATE_CURSOR = `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M21 12a9 9 0 1 1-2.64-6.36L21 8M21 3v5h-5' fill='none' stroke='%23fff' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M21 12a9 9 0 1 1-2.64-6.36L21 8M21 3v5h-5' fill='none' stroke='%231d1d1f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 12 12, crosshair`;
|
|
51
|
+
const defaultStyles = /* @__PURE__ */ new Map();
|
|
52
|
+
function styleOf(theme) {
|
|
53
|
+
if (theme.style) return theme.style;
|
|
54
|
+
const cached = defaultStyles.get(theme.colorScheme);
|
|
55
|
+
if (cached) return cached;
|
|
56
|
+
const style = defaultBoardRenderStyle(theme.colorScheme);
|
|
57
|
+
defaultStyles.set(theme.colorScheme, style);
|
|
58
|
+
return style;
|
|
59
|
+
}
|
|
46
60
|
function mountBoardStage(host, options) {
|
|
47
61
|
const { editor, assets } = options;
|
|
48
62
|
const theme = options.theme ?? defaultTheme;
|
|
@@ -148,6 +162,19 @@ function mountBoardStage(host, options) {
|
|
|
148
162
|
}
|
|
149
163
|
if (sizes.length > 0) editor.adoptMediaNaturalSizes(sizes);
|
|
150
164
|
}
|
|
165
|
+
let lifted = null;
|
|
166
|
+
function liftedIds() {
|
|
167
|
+
const interaction = editor.interaction;
|
|
168
|
+
if (interaction.type !== "translating" || !interaction.moved) {
|
|
169
|
+
lifted = null;
|
|
170
|
+
return NO_IDS;
|
|
171
|
+
}
|
|
172
|
+
if (lifted?.origin !== interaction.origin) lifted = {
|
|
173
|
+
origin: interaction.origin,
|
|
174
|
+
ids: new Set(interaction.origin.keys())
|
|
175
|
+
};
|
|
176
|
+
return lifted.ids;
|
|
177
|
+
}
|
|
151
178
|
function renderContext(current, renderScene, time) {
|
|
152
179
|
const interaction = editor.interaction;
|
|
153
180
|
return {
|
|
@@ -158,9 +185,12 @@ function mountBoardStage(host, options) {
|
|
|
158
185
|
selectedIds: new Set(editor.selection),
|
|
159
186
|
hoveredId: editor.hoverId,
|
|
160
187
|
resizingIds: interaction.type === "resizing" ? new Set(interaction.origin.keys()) : /* @__PURE__ */ new Set(),
|
|
188
|
+
liftedIds: liftedIds(),
|
|
161
189
|
palette: current.palette,
|
|
162
190
|
colors: current.colors,
|
|
163
191
|
colorScheme: current.colorScheme,
|
|
192
|
+
style: styleOf(current),
|
|
193
|
+
themeKey: current.key,
|
|
164
194
|
rendererType: app?.renderer.type === RendererType.CANVAS ? "canvas" : "gpu",
|
|
165
195
|
zoom: renderCamera().zoom,
|
|
166
196
|
reducedMotion: options.reducedMotion?.() ?? false,
|
|
@@ -275,6 +305,72 @@ function mountBoardStage(host, options) {
|
|
|
275
305
|
});
|
|
276
306
|
}
|
|
277
307
|
}
|
|
308
|
+
let march = 0;
|
|
309
|
+
let marchTimer = null;
|
|
310
|
+
function stopMarch() {
|
|
311
|
+
if (marchTimer !== null) clearTimeout(marchTimer);
|
|
312
|
+
marchTimer = null;
|
|
313
|
+
}
|
|
314
|
+
function scheduleMarch(style) {
|
|
315
|
+
const stepped = style.motion === "stepped";
|
|
316
|
+
if (marchTimer !== null || !active || disposed) return;
|
|
317
|
+
marchTimer = setTimeout(() => {
|
|
318
|
+
marchTimer = null;
|
|
319
|
+
march += stepped ? 2 : 1;
|
|
320
|
+
drawInteraction();
|
|
321
|
+
scheduleRender();
|
|
322
|
+
}, stepped ? MARCH_STEP_MS : MARCH_SMOOTH_MS);
|
|
323
|
+
}
|
|
324
|
+
function drawInteraction() {
|
|
325
|
+
if (!overlay) return;
|
|
326
|
+
const current = theme();
|
|
327
|
+
const style = styleOf(current);
|
|
328
|
+
const renderScene = editor.scene;
|
|
329
|
+
const camera = renderCamera();
|
|
330
|
+
const interaction = editor.interaction;
|
|
331
|
+
const selected = editor.selection.flatMap((id) => renderScene.get(id) ?? []);
|
|
332
|
+
const single = selected.length === 1 ? selected[0] : void 0;
|
|
333
|
+
let arrowEndpoints;
|
|
334
|
+
if (single?.type === "arrow" && !single.locked) {
|
|
335
|
+
const resolved = resolveSceneArrow(single, renderScene);
|
|
336
|
+
arrowEndpoints = [
|
|
337
|
+
resolved.start.point,
|
|
338
|
+
resolved.mid,
|
|
339
|
+
resolved.end.point
|
|
340
|
+
];
|
|
341
|
+
}
|
|
342
|
+
const hoverId = interaction.type === "idle" ? editor.hoverId : null;
|
|
343
|
+
const hovered = hoverId && !editor.selection.includes(hoverId) ? renderScene.get(hoverId) ?? null : null;
|
|
344
|
+
overlay.clear();
|
|
345
|
+
drawBoardOverlay(overlay, {
|
|
346
|
+
zoom: camera.zoom,
|
|
347
|
+
pointerType: editor.pointerType,
|
|
348
|
+
palette: current.palette,
|
|
349
|
+
style,
|
|
350
|
+
scene: renderScene,
|
|
351
|
+
hovered,
|
|
352
|
+
selected,
|
|
353
|
+
march,
|
|
354
|
+
marquee: editor.marquee,
|
|
355
|
+
transform: editor.selectionTransform,
|
|
356
|
+
controls: editor.tool === "select",
|
|
357
|
+
hoveredControl: editor.hoveredTransformControl,
|
|
358
|
+
rotationPointer: interaction.type === "rotating" ? interaction.current : null,
|
|
359
|
+
arrowEndpoints,
|
|
360
|
+
ports: editor.connectionPorts,
|
|
361
|
+
hoveredPort: editor.hoveredConnectionPort,
|
|
362
|
+
bindTarget: editor.bindTargetFrame ?? editor.arrowDraft?.targetFrame ?? null
|
|
363
|
+
});
|
|
364
|
+
options.drawOverlay?.(overlay, {
|
|
365
|
+
zoom: camera.zoom,
|
|
366
|
+
theme: current,
|
|
367
|
+
scene: renderScene
|
|
368
|
+
});
|
|
369
|
+
drawTransient(overlay, current);
|
|
370
|
+
editor.controlOutset = style.selection.outset;
|
|
371
|
+
if (style.selection.dash > 0 && selected.length > 0 && !(options.reducedMotion?.() ?? false)) scheduleMarch(style);
|
|
372
|
+
else stopMarch();
|
|
373
|
+
}
|
|
278
374
|
function cursorFor() {
|
|
279
375
|
if (scriptMode === "interact" && scriptHover) {
|
|
280
376
|
const output = scriptOutputs.get(scriptHover.owner);
|
|
@@ -506,38 +602,7 @@ function mountBoardStage(host, options) {
|
|
|
506
602
|
gestureActive: editor.gestureActive && editor.interaction.type !== "panning"
|
|
507
603
|
});
|
|
508
604
|
staleIds.clear();
|
|
509
|
-
|
|
510
|
-
let arrowEndpoints;
|
|
511
|
-
if (single?.type === "arrow" && !single.locked) {
|
|
512
|
-
const resolved = resolveSceneArrow(single, renderScene);
|
|
513
|
-
arrowEndpoints = [
|
|
514
|
-
resolved.start.point,
|
|
515
|
-
resolved.mid,
|
|
516
|
-
resolved.end.point
|
|
517
|
-
];
|
|
518
|
-
}
|
|
519
|
-
const interaction = editor.interaction;
|
|
520
|
-
scene.drawOverlay({
|
|
521
|
-
zoom: camera.zoom,
|
|
522
|
-
pointerType: editor.pointerType,
|
|
523
|
-
marquee: editor.marquee,
|
|
524
|
-
selection: editor.selection,
|
|
525
|
-
transform: editor.selectionTransform,
|
|
526
|
-
controls: editor.tool === "select",
|
|
527
|
-
hoveredControl: editor.hoveredTransformControl,
|
|
528
|
-
rotationPointer: interaction.type === "rotating" ? interaction.current : null,
|
|
529
|
-
arrowEndpoints,
|
|
530
|
-
ports: editor.connectionPorts,
|
|
531
|
-
hoveredPort: editor.hoveredConnectionPort,
|
|
532
|
-
arrowDraft: null,
|
|
533
|
-
bindTarget: editor.bindTargetFrame ?? editor.arrowDraft?.targetFrame ?? null
|
|
534
|
-
}, current.palette);
|
|
535
|
-
options.drawOverlay?.(overlay, {
|
|
536
|
-
zoom: camera.zoom,
|
|
537
|
-
theme: current,
|
|
538
|
-
scene: renderScene
|
|
539
|
-
});
|
|
540
|
-
drawTransient(overlay, current);
|
|
605
|
+
drawInteraction();
|
|
541
606
|
views.sync({
|
|
542
607
|
ids,
|
|
543
608
|
scene: renderScene,
|
|
@@ -809,6 +874,7 @@ function mountBoardStage(host, options) {
|
|
|
809
874
|
if (active === next) return;
|
|
810
875
|
active = next;
|
|
811
876
|
if (!active) {
|
|
877
|
+
stopMarch();
|
|
812
878
|
cancelScriptPointers();
|
|
813
879
|
cancelAnimationFrame(renderFrame);
|
|
814
880
|
cancelAnimationFrame(tickFrame);
|
|
@@ -836,6 +902,7 @@ function mountBoardStage(host, options) {
|
|
|
836
902
|
toWorld: (clientX, clientY) => screenToWorld(clientX, clientY, rectOf(), renderCamera()),
|
|
837
903
|
destroy() {
|
|
838
904
|
disposed = true;
|
|
905
|
+
stopMarch();
|
|
839
906
|
scriptPointers.clear();
|
|
840
907
|
stopTransforms?.();
|
|
841
908
|
scriptObservers.clear();
|