@neta-art/cohub 11.0.0 → 11.2.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/README.md +31 -5
- package/dist/board/editor/context.d.ts +1 -0
- package/dist/board/editor/context.js +1 -0
- package/dist/board/editor/creation.d.ts +2 -2
- package/dist/board/editor/creation.js +3 -3
- package/dist/board/editor/editor.d.ts +5 -4
- package/dist/board/editor/editor.js +8 -1
- package/dist/board/editor/index.d.ts +2 -2
- package/dist/board/editor/interaction.d.ts +12 -2
- package/dist/board/editor/interaction.js +50 -8
- package/dist/board/editor/selection-transform.d.ts +1 -1
- package/dist/board/editor/selection-transform.js +13 -2
- package/dist/board/editor/snapshots.d.ts +0 -2
- package/dist/board/editor/snapshots.js +4 -20
- 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 +8 -1
- package/dist/board/model/file-preview.d.ts +0 -1
- package/dist/board/model/file-preview.js +10 -1
- package/dist/board/model/image-key.js +1 -1
- package/dist/board/model/index.d.ts +8 -7
- package/dist/board/model/index.js +6 -5
- package/dist/board/model/items/create.d.ts +16 -3
- package/dist/board/model/items/create.js +125 -5
- package/dist/board/model/items/index.d.ts +2 -2
- package/dist/board/model/items/index.js +2 -2
- package/dist/board/model/media-playback.js +1 -1
- package/dist/board/model/overlay-placement.d.ts +25 -0
- package/dist/board/model/overlay-placement.js +35 -0
- package/dist/board/model/replay.d.ts +1 -1
- package/dist/board/model/replay.js +1 -0
- package/dist/board/model/script-hit.d.ts +20 -3
- package/dist/board/model/script-hit.js +81 -24
- package/dist/board/model/script-observers.d.ts +18 -0
- package/dist/board/model/script-observers.js +92 -0
- package/dist/board/model/script-scene.d.ts +1 -1
- package/dist/board/model/script-scene.js +35 -8
- package/dist/board/model/script.d.ts +51 -7
- package/dist/board/render/audio-waveform.d.ts +1 -1
- package/dist/board/render/audio-waveform.js +8 -5
- package/dist/board/render/board-background.js +5 -4
- 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 +8 -5
- package/dist/board/render/index.js +8 -5
- package/dist/board/render/media-interaction.d.ts +2 -1
- package/dist/board/render/media-interaction.js +15 -7
- 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 +25 -2
- package/dist/board/render/renderers/board-renderer-registry.js +11 -4
- package/dist/board/render/renderers/custom-card-renderer.js +6 -8
- 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 -8
- package/dist/board/render/renderers/task-card-renderer.js +286 -111
- 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/script-host.d.ts +1 -1
- package/dist/board/render/script-host.js +53 -28
- package/dist/board/render/script-runtime.js +13 -3
- package/dist/board/render/script-texture.d.ts +11 -0
- package/dist/board/render/script-texture.js +33 -0
- 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/assets.js +1 -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 +4 -43
- package/dist/board/stage/scene.js +38 -130
- package/dist/board/stage/script-pointer.d.ts +1 -0
- package/dist/board/stage/script-pointer.js +8 -2
- package/dist/board/stage/stage.d.ts +13 -3
- package/dist/board/stage/stage.js +217 -53
- package/dist/chunks/environment.d.ts +1037 -8
- package/dist/chunks/environment.js +12 -4
- package/dist/chunks/http.d.ts +51 -2
- package/dist/chunks/http.js +10 -1
- package/dist/chunks/transport.js +1 -1
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +151 -139
- package/dist/protocol/dist/board-model.d.ts +36 -1
- package/dist/protocol/dist/board-model.js +14 -4
- package/dist/protocol/dist/board-task.d.ts +22 -0
- package/dist/{board/model/task.js → protocol/dist/board-task.js} +34 -7
- package/dist/protocol/dist/generation/blocks.d.ts +8 -0
- package/dist/{generation-blocks.js → protocol/dist/generation/blocks.js} +14 -15
- package/dist/protocol/dist/index.d.ts +5 -2
- package/dist/protocol/dist/task/index.d.ts +4 -0
- package/package.json +2 -1
- package/dist/board/model/task.d.ts +0 -11
- package/dist/types.d.ts +0 -36
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { worldPoint } from "../model/geometry.js";
|
|
2
|
+
import { roundRectPoints } from "./stroke.js";
|
|
3
|
+
//#region src/board/render/surface.ts
|
|
4
|
+
const PIXEL_STEP = 2;
|
|
5
|
+
const SHADOW_LAYERS = 5;
|
|
6
|
+
function traceCardOutline(graphics, rect, style, grow = 0) {
|
|
7
|
+
const x = rect.x - grow;
|
|
8
|
+
const y = rect.y - grow;
|
|
9
|
+
const width = Math.max(0, rect.width + grow * 2);
|
|
10
|
+
const height = Math.max(0, rect.height + grow * 2);
|
|
11
|
+
if (style.corner === "pixel") return tracePixelRect(graphics, x, y, width, height, 2);
|
|
12
|
+
const radius = Math.min(Math.max(0, style.radius + grow), width / 2, height / 2);
|
|
13
|
+
return radius > 0 ? graphics.roundRect(x, y, width, height, radius) : graphics.rect(x, y, width, height);
|
|
14
|
+
}
|
|
15
|
+
function tracePixelRect(graphics, x, y, width, height, step) {
|
|
16
|
+
const s = Math.min(step, width / 4, height / 4);
|
|
17
|
+
if (s <= 0) return graphics.rect(x, y, width, height);
|
|
18
|
+
return graphics.poly(pixelRectPoints(x, y, width, height, s).flatMap((point) => [point.x, point.y]));
|
|
19
|
+
}
|
|
20
|
+
function pixelRectPoints(x, y, width, height, s) {
|
|
21
|
+
const r = x + width;
|
|
22
|
+
const b = y + height;
|
|
23
|
+
const p = worldPoint;
|
|
24
|
+
return [
|
|
25
|
+
p(x, y + s * 2),
|
|
26
|
+
p(x + s, y + s * 2),
|
|
27
|
+
p(x + s, y + s),
|
|
28
|
+
p(x + s * 2, y + s),
|
|
29
|
+
p(x + s * 2, y),
|
|
30
|
+
p(r - s * 2, y),
|
|
31
|
+
p(r - s * 2, y + s),
|
|
32
|
+
p(r - s, y + s),
|
|
33
|
+
p(r - s, y + s * 2),
|
|
34
|
+
p(r, y + s * 2),
|
|
35
|
+
p(r, b - s * 2),
|
|
36
|
+
p(r - s, b - s * 2),
|
|
37
|
+
p(r - s, b - s),
|
|
38
|
+
p(r - s * 2, b - s),
|
|
39
|
+
p(r - s * 2, b),
|
|
40
|
+
p(x + s * 2, b),
|
|
41
|
+
p(x + s * 2, b - s),
|
|
42
|
+
p(x + s, b - s),
|
|
43
|
+
p(x + s, b - s * 2),
|
|
44
|
+
p(x, b - s * 2)
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
function cardOutlinePoints(rect, style, grow = 0) {
|
|
48
|
+
const x = rect.x - grow;
|
|
49
|
+
const y = rect.y - grow;
|
|
50
|
+
const width = Math.max(0, rect.width + grow * 2);
|
|
51
|
+
const height = Math.max(0, rect.height + grow * 2);
|
|
52
|
+
if (style.corner === "pixel") {
|
|
53
|
+
const s = Math.min(2, width / 4, height / 4);
|
|
54
|
+
const points = pixelRectPoints(x, y, width, height, Math.max(0, s));
|
|
55
|
+
return [...points, points[0]];
|
|
56
|
+
}
|
|
57
|
+
const radius = Math.min(Math.max(0, style.radius + grow), width / 2, height / 2);
|
|
58
|
+
return roundRectPoints(width, height, radius).map((point) => worldPoint(point.x + x, point.y + y));
|
|
59
|
+
}
|
|
60
|
+
function drawCardShadow(graphics, rect, style, shadow) {
|
|
61
|
+
if (!shadow || shadow.alpha <= 0 || shadow.blur <= 0) return;
|
|
62
|
+
const offset = {
|
|
63
|
+
...rect,
|
|
64
|
+
y: rect.y + shadow.y
|
|
65
|
+
};
|
|
66
|
+
const reach = (index) => (1 - index / SHADOW_LAYERS) ** 2;
|
|
67
|
+
for (let index = SHADOW_LAYERS; index >= 1; index -= 1) {
|
|
68
|
+
const alpha = shadow.alpha * (reach(index - 1) - reach(index));
|
|
69
|
+
traceCardOutline(graphics, offset, style, shadow.blur * index / SHADOW_LAYERS).fill({
|
|
70
|
+
color: shadow.color,
|
|
71
|
+
alpha
|
|
72
|
+
});
|
|
73
|
+
traceCardOutline(graphics, rect, style).cut();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function cardShadow(style, elevation) {
|
|
77
|
+
const shadow = style.shadow;
|
|
78
|
+
if (!shadow || elevation === 0) return null;
|
|
79
|
+
if (elevation === 1) return shadow;
|
|
80
|
+
return {
|
|
81
|
+
...shadow,
|
|
82
|
+
alpha: Math.min(1, shadow.alpha * 1.6),
|
|
83
|
+
blur: shadow.blur * 1.6,
|
|
84
|
+
y: shadow.y * 2 + 4
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function drawCardSurface(graphics, rect, style, surface) {
|
|
88
|
+
drawCardShadow(graphics, rect, style, cardShadow(style, surface.elevation ?? 0));
|
|
89
|
+
traceCardOutline(graphics, rect, style).fill({
|
|
90
|
+
color: surface.fill,
|
|
91
|
+
alpha: surface.fillAlpha ?? 1
|
|
92
|
+
});
|
|
93
|
+
if (surface.border != null) traceCardOutline(graphics, rect, style).stroke({
|
|
94
|
+
color: surface.border,
|
|
95
|
+
width: 1,
|
|
96
|
+
alpha: surface.borderAlpha ?? 1,
|
|
97
|
+
alignment: 1
|
|
98
|
+
});
|
|
99
|
+
return graphics;
|
|
100
|
+
}
|
|
101
|
+
function containRect(width, height, imageWidth, imageHeight) {
|
|
102
|
+
if (width <= 0 || height <= 0 || imageWidth <= 0 || imageHeight <= 0) return {
|
|
103
|
+
x: 0,
|
|
104
|
+
y: 0,
|
|
105
|
+
width: 0,
|
|
106
|
+
height: 0
|
|
107
|
+
};
|
|
108
|
+
const scale = Math.min(width / imageWidth, height / imageHeight);
|
|
109
|
+
return {
|
|
110
|
+
x: (width - imageWidth * scale) / 2,
|
|
111
|
+
y: (height - imageHeight * scale) / 2,
|
|
112
|
+
width: imageWidth * scale,
|
|
113
|
+
height: imageHeight * scale
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const FREE_FORM_TYPES = /* @__PURE__ */ new Set([
|
|
117
|
+
"text",
|
|
118
|
+
"shape",
|
|
119
|
+
"draw",
|
|
120
|
+
"arrow",
|
|
121
|
+
"frame",
|
|
122
|
+
"effect",
|
|
123
|
+
"custom"
|
|
124
|
+
]);
|
|
125
|
+
function cardElevation(context, id) {
|
|
126
|
+
return context.liftedIds.has(id) ? 2 : 1;
|
|
127
|
+
}
|
|
128
|
+
function isBoardCard(item) {
|
|
129
|
+
return !FREE_FORM_TYPES.has(item.type);
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { PIXEL_STEP, cardElevation, cardOutlinePoints, cardShadow, containRect, drawCardShadow, drawCardSurface, isBoardCard, traceCardOutline, tracePixelRect };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Text } from "pixi.js";
|
|
2
|
+
//#region src/board/render/text-fit.d.ts
|
|
3
|
+
declare function ellipsizeWrappedLines(lines: string[], maxLines: number): string;
|
|
4
|
+
declare function fitLineWithEllipsis(line: string, wrapWidth: number, measureWidth: (value: string) => number): string;
|
|
5
|
+
declare function fitTextToLines(text: Text, value: string, maxLines: number, wrapWidth: number): void;
|
|
6
|
+
declare function fitTextToWidth(text: Text, value: string, width: number): void;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { ellipsizeWrappedLines, fitLineWithEllipsis, fitTextToLines, fitTextToWidth };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CanvasTextMetrics } from "pixi.js";
|
|
2
|
+
//#region src/board/render/text-fit.ts
|
|
3
|
+
function ellipsizeWrappedLines(lines, maxLines) {
|
|
4
|
+
if (maxLines <= 0 || lines.length === 0) return "";
|
|
5
|
+
if (lines.length <= maxLines) return lines.join("\n");
|
|
6
|
+
const kept = lines.slice(0, maxLines);
|
|
7
|
+
const lastIndex = kept.length - 1;
|
|
8
|
+
const last = (kept[lastIndex] ?? "").replace(/\s+$/u, "");
|
|
9
|
+
kept[lastIndex] = last ? `${last}…` : "…";
|
|
10
|
+
return kept.join("\n");
|
|
11
|
+
}
|
|
12
|
+
function fitLineWithEllipsis(line, wrapWidth, measureWidth) {
|
|
13
|
+
const trimmed = line.replace(/\s+$/u, "");
|
|
14
|
+
if (!trimmed) return "…";
|
|
15
|
+
const full = `${trimmed}…`;
|
|
16
|
+
if (measureWidth(full) <= wrapWidth) return full;
|
|
17
|
+
let lo = 0;
|
|
18
|
+
let hi = trimmed.length;
|
|
19
|
+
let best = "…";
|
|
20
|
+
while (lo <= hi) {
|
|
21
|
+
const mid = lo + hi >> 1;
|
|
22
|
+
const candidate = mid > 0 ? `${trimmed.slice(0, mid)}…` : "…";
|
|
23
|
+
if (measureWidth(candidate) <= wrapWidth) {
|
|
24
|
+
best = candidate;
|
|
25
|
+
lo = mid + 1;
|
|
26
|
+
} else hi = mid - 1;
|
|
27
|
+
}
|
|
28
|
+
return best;
|
|
29
|
+
}
|
|
30
|
+
function fitTextToLines(text, value, maxLines, wrapWidth) {
|
|
31
|
+
const width = Math.max(1, wrapWidth);
|
|
32
|
+
text.style.wordWrapWidth = width;
|
|
33
|
+
if (!value || maxLines <= 0) {
|
|
34
|
+
if (text.text !== "") text.text = "";
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const charCap = Math.max(maxLines * Math.ceil(width), maxLines * 4);
|
|
38
|
+
const sample = value.length > charCap ? value.slice(0, charCap) : value;
|
|
39
|
+
const metrics = CanvasTextMetrics.measureText(sample, text.style);
|
|
40
|
+
if (!(metrics.lines.length > maxLines || sample !== value)) {
|
|
41
|
+
if (text.text !== value) text.text = value;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const keptCount = Math.min(maxLines, metrics.lines.length);
|
|
45
|
+
const kept = metrics.lines.slice(0, keptCount);
|
|
46
|
+
const lastIndex = kept.length - 1;
|
|
47
|
+
const probe = text.style.clone();
|
|
48
|
+
probe.wordWrap = false;
|
|
49
|
+
kept[lastIndex] = fitLineWithEllipsis(kept[lastIndex] ?? "", width, (candidate) => CanvasTextMetrics.measureText(candidate, probe).width);
|
|
50
|
+
const fitted = kept.join("\n");
|
|
51
|
+
if (text.text !== fitted) text.text = fitted;
|
|
52
|
+
}
|
|
53
|
+
function fitTextToWidth(text, value, width) {
|
|
54
|
+
text.style.wordWrap = false;
|
|
55
|
+
const measure = (candidate) => CanvasTextMetrics.measureText(candidate, text.style).width;
|
|
56
|
+
const fitted = !value || measure(value) <= width ? value : fitLineWithEllipsis(value, width, measure);
|
|
57
|
+
if (text.text !== fitted) text.text = fitted;
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { ellipsizeWrappedLines, fitLineWithEllipsis, fitTextToLines, fitTextToWidth };
|
|
@@ -2,6 +2,7 @@ import { Text } from "pixi.js";
|
|
|
2
2
|
//#region src/board/render/text-resolution.d.ts
|
|
3
3
|
declare function getBoardResolution(): number;
|
|
4
4
|
declare function textZoomBucket(zoom: number): number;
|
|
5
|
+
declare function boardCardDetailed(zoom: number): boolean;
|
|
5
6
|
/** Effective text resolution for a given camera zoom. */
|
|
6
7
|
declare function textResolutionForZoom(zoom: number): number;
|
|
7
8
|
/** Update a Pixi text texture only when zoom crosses a resolution bucket. */
|
|
@@ -13,4 +14,4 @@ declare function syncTextWrapWidth(text: Text, state: {
|
|
|
13
14
|
wrapWidth: number;
|
|
14
15
|
}, width: number, defer: boolean): void;
|
|
15
16
|
//#endregion
|
|
16
|
-
export { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
|
|
17
|
+
export { boardCardDetailed, getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
|
|
@@ -21,6 +21,9 @@ function textZoomBucket(zoom) {
|
|
|
21
21
|
for (const bucket of TEXT_ZOOM_BUCKETS) if (clamped <= bucket) return bucket;
|
|
22
22
|
return TEXT_ZOOM_BUCKETS[TEXT_ZOOM_BUCKETS.length - 1] ?? 4;
|
|
23
23
|
}
|
|
24
|
+
function boardCardDetailed(zoom) {
|
|
25
|
+
return textZoomBucket(zoom) >= 1;
|
|
26
|
+
}
|
|
24
27
|
/** Effective text resolution for a given camera zoom. */
|
|
25
28
|
function textResolutionForZoom(zoom) {
|
|
26
29
|
const bucket = textZoomBucket(zoom);
|
|
@@ -40,4 +43,4 @@ function syncTextWrapWidth(text, state, width, defer) {
|
|
|
40
43
|
state.wrapWidth = width;
|
|
41
44
|
}
|
|
42
45
|
//#endregion
|
|
43
|
-
export { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
|
|
46
|
+
export { boardCardDetailed, getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mediaPreviewCandidates } from "../../media.js";
|
|
2
2
|
import { parseBoardMediaSource } from "../../protocol/dist/board-model.js";
|
|
3
|
-
import { featuredTaskArtifact } from "
|
|
3
|
+
import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
|
|
4
4
|
import { boardImageKeySource, imageAssetKey } from "../model/image-key.js";
|
|
5
5
|
import { loadVideoThumbnailTexture, videoTextureNaturalSize } from "../render/video-thumbnail.js";
|
|
6
6
|
import { DEFAULT_LRU_BUDGET, selectLruEvictions } from "./lru.js";
|
|
@@ -2,7 +2,8 @@ import { DEFAULT_LRU_BUDGET, LruBudget, LruEntry, selectLruEvictions } from "./l
|
|
|
2
2
|
import { BoardImageTexturePool, boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTexture, unloadBoardImageTexture } from "./texture-pool.js";
|
|
3
3
|
import { BoardAssetManager, BoardAssetManagerOptions, boardAssetKey, createBoardAssetManager } from "./assets.js";
|
|
4
4
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
5
|
-
import { BoardLiveStroke, BoardScene, BoardSceneNode,
|
|
5
|
+
import { BoardLiveStroke, BoardScene, BoardSceneNode, SceneSyncInput, createBoardScene } from "./scene.js";
|
|
6
6
|
import { BoardItemView, BoardItemViewContext, BoardItemViewInstance, BoardItemViewLayer, createBoardItemViewLayer } from "./views.js";
|
|
7
7
|
import { BoardStage, BoardStageOptions, BoardStagePointerPhase, BoardStageTheme, mountBoardStage } from "./stage.js";
|
|
8
|
-
|
|
8
|
+
import { BoardOverlayInput, drawBoardOverlay, expandFrame } from "./overlay.js";
|
|
9
|
+
export { BoardAssetManager, BoardAssetManagerOptions, BoardImageTexturePool, BoardItemView, BoardItemViewContext, BoardItemViewInstance, BoardItemViewLayer, BoardLiveStroke, BoardOverlayInput, BoardScene, BoardSceneNode, BoardStage, BoardStageOptions, BoardStagePointerPhase, BoardStageTheme, DEFAULT_LRU_BUDGET, LruBudget, LruEntry, SceneSyncInput, boardAssetKey, boardImageTexturePool, createBoardAssetManager, createBoardImageTexturePool, createBoardItemViewLayer, createBoardScene, drawBoardOverlay, expandFrame, loadBoardImageTexture, mountBoardStage, selectLruEvictions, toBoardPointerEvent, unloadBoardImageTexture };
|
|
@@ -3,6 +3,7 @@ import { boardImageTexturePool, createBoardImageTexturePool, loadBoardImageTextu
|
|
|
3
3
|
import { boardAssetKey, createBoardAssetManager } from "./assets.js";
|
|
4
4
|
import { toBoardPointerEvent } from "./pointer.js";
|
|
5
5
|
import { createBoardScene } from "./scene.js";
|
|
6
|
+
import { drawBoardOverlay, expandFrame } from "./overlay.js";
|
|
6
7
|
import { createBoardItemViewLayer } from "./views.js";
|
|
7
8
|
import { mountBoardStage } from "./stage.js";
|
|
8
|
-
export { DEFAULT_LRU_BUDGET, boardAssetKey, boardImageTexturePool, createBoardAssetManager, createBoardImageTexturePool, createBoardItemViewLayer, createBoardScene, loadBoardImageTexture, mountBoardStage, selectLruEvictions, toBoardPointerEvent, unloadBoardImageTexture };
|
|
9
|
+
export { DEFAULT_LRU_BUDGET, boardAssetKey, boardImageTexturePool, createBoardAssetManager, createBoardImageTexturePool, createBoardItemViewLayer, createBoardScene, drawBoardOverlay, expandFrame, loadBoardImageTexture, mountBoardStage, selectLruEvictions, toBoardPointerEvent, unloadBoardImageTexture };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BoardFrame } from "../../protocol/dist/board-layout.js";
|
|
2
|
+
import { Rect, WorldPoint } from "../model/geometry.js";
|
|
3
|
+
import { BoardScene, BoardSceneItem } from "../model/scene.js";
|
|
4
|
+
import "../model/index.js";
|
|
5
|
+
import { BoardSelectionTransform, BoardTransformControl } from "../editor/selection-transform.js";
|
|
6
|
+
import "../editor/index.js";
|
|
7
|
+
import { BoardRenderStyle } from "../render/style.js";
|
|
8
|
+
import { BoardRenderPalette } from "../render/renderers/board-renderer-registry.js";
|
|
9
|
+
import "../render/index.js";
|
|
10
|
+
import { Graphics } from "pixi.js";
|
|
11
|
+
//#region src/board/stage/overlay.d.ts
|
|
12
|
+
type BoardOverlayInput = {
|
|
13
|
+
zoom: number;
|
|
14
|
+
pointerType: string;
|
|
15
|
+
palette: BoardRenderPalette;
|
|
16
|
+
style: BoardRenderStyle;
|
|
17
|
+
scene: BoardScene;
|
|
18
|
+
hovered: BoardSceneItem | null;
|
|
19
|
+
selected: readonly BoardSceneItem[];
|
|
20
|
+
march: number;
|
|
21
|
+
marquee: Rect | null;
|
|
22
|
+
transform: BoardSelectionTransform | null;
|
|
23
|
+
controls: boolean;
|
|
24
|
+
hoveredControl: BoardTransformControl | null;
|
|
25
|
+
rotationPointer: WorldPoint | null;
|
|
26
|
+
arrowEndpoints?: readonly WorldPoint[];
|
|
27
|
+
ports?: ReadonlyArray<{
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
radius: number;
|
|
31
|
+
}>;
|
|
32
|
+
hoveredPort?: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
} | null;
|
|
36
|
+
bindTarget?: BoardFrame | null;
|
|
37
|
+
};
|
|
38
|
+
declare function expandFrame(frame: BoardFrame, by: number): BoardFrame;
|
|
39
|
+
declare function drawBoardOverlay(graphics: Graphics, input: BoardOverlayInput): void;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { BoardOverlayInput, drawBoardOverlay, expandFrame };
|
|
@@ -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
|
|
@@ -24,46 +20,11 @@ type SceneSyncInput = {
|
|
|
24
20
|
visibleIds: Set<string> | null;
|
|
25
21
|
pinnedIds: Set<string>;
|
|
26
22
|
globalSig: string;
|
|
23
|
+
staleIds?: ReadonlySet<string>;
|
|
27
24
|
structureVersion: number;
|
|
28
25
|
geometryVersion: number;
|
|
29
26
|
gestureActive: boolean;
|
|
30
27
|
};
|
|
31
|
-
type SceneOverlayInput = {
|
|
32
|
-
zoom: number;
|
|
33
|
-
pointerType: string;
|
|
34
|
-
marquee: Rect | null;
|
|
35
|
-
selection: string[];
|
|
36
|
-
transform: BoardSelectionTransform | null;
|
|
37
|
-
controls: boolean;
|
|
38
|
-
hoveredControl: BoardTransformControl | null;
|
|
39
|
-
rotationPointer: WorldPoint | null;
|
|
40
|
-
arrowEndpoints?: Array<{
|
|
41
|
-
x: number;
|
|
42
|
-
y: number;
|
|
43
|
-
}>;
|
|
44
|
-
ports?: ReadonlyArray<{
|
|
45
|
-
x: number;
|
|
46
|
-
y: number;
|
|
47
|
-
radius: number;
|
|
48
|
-
}>;
|
|
49
|
-
hoveredPort?: {
|
|
50
|
-
x: number;
|
|
51
|
-
y: number;
|
|
52
|
-
} | null;
|
|
53
|
-
arrowDraft?: {
|
|
54
|
-
from: {
|
|
55
|
-
x: number;
|
|
56
|
-
y: number;
|
|
57
|
-
};
|
|
58
|
-
to: {
|
|
59
|
-
x: number;
|
|
60
|
-
y: number;
|
|
61
|
-
};
|
|
62
|
-
size: number;
|
|
63
|
-
targetFrame: BoardFrame | null;
|
|
64
|
-
} | null;
|
|
65
|
-
bindTarget?: BoardFrame | null;
|
|
66
|
-
};
|
|
67
28
|
type BoardSceneNode = {
|
|
68
29
|
item: BoardSceneItem;
|
|
69
30
|
container: Container;
|
|
@@ -72,7 +33,7 @@ type BoardScene = {
|
|
|
72
33
|
sync: (input: SceneSyncInput) => void;
|
|
73
34
|
getNode: (itemId: string) => BoardSceneNode | null;
|
|
74
35
|
readonly animated: boolean;
|
|
75
|
-
|
|
36
|
+
tick: (clock: number) => number | null;
|
|
76
37
|
destroy: (context: BoardRenderContext) => void;
|
|
77
38
|
};
|
|
78
39
|
declare function createBoardScene(options: {
|
|
@@ -82,4 +43,4 @@ declare function createBoardScene(options: {
|
|
|
82
43
|
onFarLayerFrame?: () => void;
|
|
83
44
|
}): BoardScene;
|
|
84
45
|
//#endregion
|
|
85
|
-
export { BoardLiveStroke, BoardScene, BoardSceneNode,
|
|
46
|
+
export { BoardLiveStroke, BoardScene, BoardSceneNode, SceneSyncInput, createBoardScene };
|