@neta-art/cohub 11.1.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/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/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 +100 -33
- package/dist/chunks/environment.d.ts +997 -3
- package/dist/chunks/http.d.ts +48 -1
- package/dist/chunks/http.js +8 -0
- package/dist/http.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/package.json +2 -1
|
@@ -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 };
|
|
@@ -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
|
|
@@ -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 };
|