@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
|
@@ -1,91 +1,94 @@
|
|
|
1
|
-
import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
1
|
import { getMediaResourceTitle } from "../../model/media.js";
|
|
3
|
-
import {
|
|
2
|
+
import { boardCardDetailed } from "../text-resolution.js";
|
|
3
|
+
import { createCardTag, formatBoardDuration, syncCardTag, zoomCardTag } from "../card-tag.js";
|
|
4
|
+
import { cardElevation, containRect, drawCardSurface, traceCardOutline } from "../surface.js";
|
|
4
5
|
import { positionShell } from "./base-card-renderer.js";
|
|
5
6
|
import { drawFarPlate } from "./far-plate.js";
|
|
6
|
-
import {
|
|
7
|
+
import { drawMediaPlayKey } from "./media-key.js";
|
|
8
|
+
import { Container, Graphics, Sprite, Texture } from "pixi.js";
|
|
7
9
|
//#region src/board/render/renderers/video-card-renderer.ts
|
|
8
10
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const scale = Math.min(width / texture.width, height / texture.height);
|
|
14
|
-
sprite.width = texture.width * scale;
|
|
15
|
-
sprite.height = texture.height * scale;
|
|
16
|
-
sprite.x = (width - sprite.width) / 2;
|
|
17
|
-
sprite.y = (height - sprite.height) / 2;
|
|
11
|
+
const FILM_RAIL = 10;
|
|
12
|
+
const FILM_HOLE = 4;
|
|
13
|
+
function videoTitle(item) {
|
|
14
|
+
return item.props.snapshot?.title ?? getMediaResourceTitle(item.props.src);
|
|
18
15
|
}
|
|
19
16
|
function sync(container, item, context) {
|
|
20
17
|
const parts = partsByContainer.get(container);
|
|
21
18
|
if (!parts) return;
|
|
22
19
|
positionShell(parts.root, item);
|
|
23
20
|
const { width, height } = item.frame;
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
21
|
+
const title = videoTitle(item);
|
|
22
|
+
const durationMs = item.props.snapshot?.durationMs;
|
|
23
|
+
const duration = durationMs ? formatBoardDuration(durationMs) : "";
|
|
24
|
+
syncCardTag(parts.tag, {
|
|
25
|
+
glyph: "video",
|
|
26
|
+
text: duration ? `${title} · ${duration}` : title,
|
|
27
|
+
width
|
|
28
|
+
}, context);
|
|
28
29
|
const key = context.assetKey(item);
|
|
29
30
|
const texture = key ? context.getTexture(key) : null;
|
|
30
|
-
const
|
|
31
|
-
syncTextResolution(parts.label, parts, context.zoom);
|
|
32
|
-
if (parts.label.text !== title) parts.label.text = title;
|
|
33
|
-
syncTextWrapWidth(parts.label, parts, Math.max(1, width - 16), resizing);
|
|
34
|
-
parts.label.position.set(8, Math.max(8, height - parts.label.height - 8));
|
|
31
|
+
const elevation = cardElevation(context, item.id);
|
|
35
32
|
const sig = [
|
|
36
33
|
key ?? "",
|
|
37
34
|
texture ? `${texture.width}x${texture.height}` : "none",
|
|
38
35
|
width,
|
|
39
36
|
height,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
title,
|
|
44
|
-
item.props.time ?? 0,
|
|
45
|
-
parts.label.height,
|
|
46
|
-
context.palette.surface,
|
|
47
|
-
context.palette.brand,
|
|
48
|
-
context.palette.muted,
|
|
49
|
-
context.palette.border,
|
|
50
|
-
context.palette.hover,
|
|
51
|
-
context.palette.text
|
|
37
|
+
elevation,
|
|
38
|
+
boardCardDetailed(context.zoom),
|
|
39
|
+
context.themeKey
|
|
52
40
|
].join("|");
|
|
53
41
|
if (sig === parts.sig) return;
|
|
54
42
|
parts.sig = sig;
|
|
43
|
+
const framed = context.style.media === "framed";
|
|
44
|
+
const rect = {
|
|
45
|
+
x: 0,
|
|
46
|
+
y: 0,
|
|
47
|
+
width,
|
|
48
|
+
height
|
|
49
|
+
};
|
|
50
|
+
const rail = framed ? Math.min(FILM_RAIL, height / 6) : 0;
|
|
51
|
+
const screen = {
|
|
52
|
+
x: framed ? rail * .6 : 0,
|
|
53
|
+
y: rail,
|
|
54
|
+
width: width - (framed ? rail * 1.2 : 0),
|
|
55
|
+
height: height - rail * 2
|
|
56
|
+
};
|
|
57
|
+
drawCardSurface(parts.plate.clear(), rect, context.style, {
|
|
58
|
+
fill: framed ? context.palette.text : context.palette.surface,
|
|
59
|
+
border: framed ? null : context.palette.border,
|
|
60
|
+
borderAlpha: .85,
|
|
61
|
+
elevation
|
|
62
|
+
});
|
|
63
|
+
if (framed) drawFilmRails(parts.plate, width, height, rail, context.palette.bg);
|
|
64
|
+
traceCardOutline(parts.mask.clear(), framed ? screen : rect, framed ? {
|
|
65
|
+
corner: "round",
|
|
66
|
+
radius: 1
|
|
67
|
+
} : context.style).fill({ color: 16777215 });
|
|
55
68
|
if (texture && parts.sprite.texture !== texture) parts.sprite.texture = texture;
|
|
56
69
|
parts.sprite.visible = Boolean(texture);
|
|
57
|
-
if (texture) layoutContain(parts.sprite, width, height);
|
|
58
|
-
parts.mask.clear().roundRect(0, 0, width, height, RADIUS).fill({ color: 16777215 });
|
|
59
|
-
parts.plate.clear().roundRect(0, 0, width, height, RADIUS).fill({
|
|
60
|
-
color: context.palette.surface,
|
|
61
|
-
alpha: .96
|
|
62
|
-
});
|
|
63
|
-
parts.chrome.clear();
|
|
64
70
|
if (texture) {
|
|
65
|
-
const
|
|
66
|
-
parts.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
71
|
+
const fitted = containRect(screen.width, screen.height, texture.width, texture.height);
|
|
72
|
+
parts.sprite.position.set(screen.x + fitted.x, screen.y + fitted.y);
|
|
73
|
+
parts.sprite.width = fitted.width;
|
|
74
|
+
parts.sprite.height = fitted.height;
|
|
70
75
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
color:
|
|
85
|
-
|
|
86
|
-
alpha: selected ? .95 : .85
|
|
76
|
+
parts.chrome.clear();
|
|
77
|
+
if (boardCardDetailed(context.zoom)) drawMediaPlayKey(parts.chrome, screen.x + screen.width / 2, screen.y + screen.height / 2, Math.min(18, Math.min(screen.width, screen.height) * .16), context);
|
|
78
|
+
}
|
|
79
|
+
function drawFilmRails(graphics, width, height, rail, hole) {
|
|
80
|
+
const size = Math.min(FILM_HOLE, rail * .45);
|
|
81
|
+
const step = size * 2.5;
|
|
82
|
+
const count = Math.max(1, Math.floor((width - step) / step));
|
|
83
|
+
const start = (width - (count - 1) * step - size) / 2;
|
|
84
|
+
for (let index = 0; index < count; index += 1) {
|
|
85
|
+
const x = start + index * step;
|
|
86
|
+
graphics.rect(x, (rail - size) / 2, size, size).rect(x, height - rail + (rail - size) / 2, size, size);
|
|
87
|
+
}
|
|
88
|
+
graphics.fill({
|
|
89
|
+
color: hole,
|
|
90
|
+
alpha: .9
|
|
87
91
|
});
|
|
88
|
-
parts.label.style.fill = texture ? context.palette.text : context.palette.muted;
|
|
89
92
|
}
|
|
90
93
|
const videoCardRenderer = {
|
|
91
94
|
id: "video-card",
|
|
@@ -96,20 +99,8 @@ const videoCardRenderer = {
|
|
|
96
99
|
const sprite = new Sprite(Texture.EMPTY);
|
|
97
100
|
const mask = new Graphics();
|
|
98
101
|
const chrome = new Graphics();
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
text: "",
|
|
102
|
-
style: {
|
|
103
|
-
fill: context.palette.muted,
|
|
104
|
-
fontFamily: BOARD_FONT_STACK,
|
|
105
|
-
fontSize: 12,
|
|
106
|
-
fontWeight: "500",
|
|
107
|
-
wordWrap: true
|
|
108
|
-
},
|
|
109
|
-
resolution,
|
|
110
|
-
roundPixels: true
|
|
111
|
-
});
|
|
112
|
-
root.addChild(plate, sprite, mask, chrome, label);
|
|
102
|
+
const tag = createCardTag();
|
|
103
|
+
root.addChild(plate, sprite, mask, chrome, tag.root);
|
|
113
104
|
sprite.mask = mask;
|
|
114
105
|
partsByContainer.set(root, {
|
|
115
106
|
root,
|
|
@@ -117,10 +108,8 @@ const videoCardRenderer = {
|
|
|
117
108
|
sprite,
|
|
118
109
|
mask,
|
|
119
110
|
chrome,
|
|
120
|
-
|
|
121
|
-
sig: ""
|
|
122
|
-
wrapWidth: 0,
|
|
123
|
-
resolution
|
|
111
|
+
tag,
|
|
112
|
+
sig: ""
|
|
124
113
|
});
|
|
125
114
|
if (item.type === "video") sync(root, item, context);
|
|
126
115
|
return root;
|
|
@@ -128,6 +117,10 @@ const videoCardRenderer = {
|
|
|
128
117
|
update: (container, item, context) => {
|
|
129
118
|
if (item.type === "video") sync(container, item, context);
|
|
130
119
|
},
|
|
120
|
+
zoom: (container, zoom) => {
|
|
121
|
+
const tag = partsByContainer.get(container)?.tag;
|
|
122
|
+
if (tag) zoomCardTag(tag, zoom);
|
|
123
|
+
},
|
|
131
124
|
renderFar: (graphics, item, context) => {
|
|
132
125
|
drawFarPlate(graphics, item.frame, {
|
|
133
126
|
fill: context.palette.surface,
|
|
@@ -5,7 +5,7 @@ import { Graphics } from "pixi.js";
|
|
|
5
5
|
//#region src/board/render/stroke.d.ts
|
|
6
6
|
declare function dashPattern(dash: BoardDash | undefined, width: number): [number, number] | null;
|
|
7
7
|
declare function trimPolyline(path: readonly WorldPoint[], trim: number | undefined): readonly WorldPoint[];
|
|
8
|
-
declare function tracePolyline(graphics: Graphics, path: readonly WorldPoint[], pattern: [number, number] | null, closed?: boolean): void;
|
|
8
|
+
declare function tracePolyline(graphics: Graphics, path: readonly WorldPoint[], pattern: [number, number] | null, closed?: boolean, offset?: number): void;
|
|
9
9
|
declare function traceArrowhead(graphics: Graphics, tip: WorldPoint, angle: number, size: number): void;
|
|
10
10
|
declare function endAngle(path: readonly WorldPoint[], atStart: boolean): number | null;
|
|
11
11
|
declare function roundRectPoints(width: number, height: number, radius: number): WorldPoint[];
|
|
@@ -31,7 +31,7 @@ function trimPolyline(path, trim) {
|
|
|
31
31
|
}
|
|
32
32
|
return out;
|
|
33
33
|
}
|
|
34
|
-
function tracePolyline(graphics, path, pattern, closed = false) {
|
|
34
|
+
function tracePolyline(graphics, path, pattern, closed = false, offset = 0) {
|
|
35
35
|
const points = closed && path.length > 2 ? [...path, path[0]] : path;
|
|
36
36
|
if (points.length < 2) return;
|
|
37
37
|
if (!pattern) {
|
|
@@ -45,9 +45,10 @@ function tracePolyline(graphics, path, pattern, closed = false) {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
const [dash, gap] = pattern;
|
|
48
|
-
|
|
49
|
-
let
|
|
50
|
-
let
|
|
48
|
+
const phase = Math.max(0, offset) % (dash + gap);
|
|
49
|
+
let penDown = phase < dash;
|
|
50
|
+
let fresh = penDown;
|
|
51
|
+
let remaining = penDown ? dash - phase : dash + gap - phase;
|
|
51
52
|
for (let index = 1; index < points.length; index += 1) {
|
|
52
53
|
const a = points[index - 1];
|
|
53
54
|
const b = points[index];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/board/render/style.d.ts
|
|
2
|
+
type BoardRenderStyle = {
|
|
3
|
+
corner: "round" | "pixel";
|
|
4
|
+
radius: number;
|
|
5
|
+
shadow: BoardShadowStyle | null;
|
|
6
|
+
selection: BoardSelectionStyle;
|
|
7
|
+
media: "plain" | "framed";
|
|
8
|
+
progress: "shimmer" | "mosaic";
|
|
9
|
+
icons: "line" | "pixel";
|
|
10
|
+
motion: "smooth" | "stepped";
|
|
11
|
+
font: string;
|
|
12
|
+
};
|
|
13
|
+
type BoardShadowStyle = {
|
|
14
|
+
color: number;
|
|
15
|
+
alpha: number;
|
|
16
|
+
blur: number;
|
|
17
|
+
y: number;
|
|
18
|
+
};
|
|
19
|
+
type BoardSelectionStyle = {
|
|
20
|
+
width: number;
|
|
21
|
+
outset: number;
|
|
22
|
+
dash: number;
|
|
23
|
+
handle: "circle" | "square";
|
|
24
|
+
handleSize: number;
|
|
25
|
+
};
|
|
26
|
+
declare function defaultBoardRenderStyle(mode: "dark" | "light"): BoardRenderStyle;
|
|
27
|
+
declare function resolveBoardRenderStyle(mode: "dark" | "light", style?: Partial<BoardRenderStyle>): BoardRenderStyle;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { BoardRenderStyle, BoardSelectionStyle, BoardShadowStyle, defaultBoardRenderStyle, resolveBoardRenderStyle };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BOARD_FONT_STACK } from "../../protocol/dist/board-constants.js";
|
|
2
|
+
//#region src/board/render/style.ts
|
|
3
|
+
const BASE = {
|
|
4
|
+
corner: "round",
|
|
5
|
+
radius: 6,
|
|
6
|
+
selection: {
|
|
7
|
+
width: 1.5,
|
|
8
|
+
outset: 3,
|
|
9
|
+
dash: 0,
|
|
10
|
+
handle: "square",
|
|
11
|
+
handleSize: 8
|
|
12
|
+
},
|
|
13
|
+
media: "plain",
|
|
14
|
+
progress: "shimmer",
|
|
15
|
+
icons: "line",
|
|
16
|
+
motion: "smooth",
|
|
17
|
+
font: BOARD_FONT_STACK
|
|
18
|
+
};
|
|
19
|
+
const SHADOW = {
|
|
20
|
+
light: {
|
|
21
|
+
color: 1447704,
|
|
22
|
+
alpha: .06,
|
|
23
|
+
blur: 12,
|
|
24
|
+
y: 2
|
|
25
|
+
},
|
|
26
|
+
dark: {
|
|
27
|
+
color: 0,
|
|
28
|
+
alpha: .24,
|
|
29
|
+
blur: 12,
|
|
30
|
+
y: 2
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function defaultBoardRenderStyle(mode) {
|
|
34
|
+
return {
|
|
35
|
+
...BASE,
|
|
36
|
+
selection: { ...BASE.selection },
|
|
37
|
+
shadow: { ...SHADOW[mode] }
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function resolveBoardRenderStyle(mode, style) {
|
|
41
|
+
const base = defaultBoardRenderStyle(mode);
|
|
42
|
+
if (!style) return base;
|
|
43
|
+
return {
|
|
44
|
+
...base,
|
|
45
|
+
...style,
|
|
46
|
+
selection: {
|
|
47
|
+
...base.selection,
|
|
48
|
+
...style.selection
|
|
49
|
+
},
|
|
50
|
+
shadow: style.shadow === void 0 ? base.shadow : style.shadow
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { defaultBoardRenderStyle, resolveBoardRenderStyle };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { WorldPoint } from "../model/geometry.js";
|
|
2
|
+
import { BoardRenderStyle, BoardShadowStyle } from "./style.js";
|
|
3
|
+
import { Graphics } from "pixi.js";
|
|
4
|
+
//#region src/board/render/surface.d.ts
|
|
5
|
+
declare const PIXEL_STEP = 2;
|
|
6
|
+
type CardRect = {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
};
|
|
12
|
+
declare function traceCardOutline(graphics: Graphics, rect: CardRect, style: Pick<BoardRenderStyle, "corner" | "radius">, grow?: number): Graphics;
|
|
13
|
+
declare function tracePixelRect(graphics: Graphics, x: number, y: number, width: number, height: number, step: number): Graphics;
|
|
14
|
+
declare function cardOutlinePoints(rect: CardRect, style: Pick<BoardRenderStyle, "corner" | "radius">, grow?: number): WorldPoint[];
|
|
15
|
+
declare function drawCardShadow(graphics: Graphics, rect: CardRect, style: Pick<BoardRenderStyle, "corner" | "radius">, shadow: BoardShadowStyle | null): void;
|
|
16
|
+
type CardSurface = {
|
|
17
|
+
fill: number;
|
|
18
|
+
fillAlpha?: number;
|
|
19
|
+
border?: number | null;
|
|
20
|
+
borderAlpha?: number;
|
|
21
|
+
elevation?: CardElevation;
|
|
22
|
+
};
|
|
23
|
+
type CardElevation = 0 | 1 | 2;
|
|
24
|
+
declare function cardShadow(style: BoardRenderStyle, elevation: CardElevation): BoardShadowStyle | null;
|
|
25
|
+
declare function drawCardSurface(graphics: Graphics, rect: CardRect, style: BoardRenderStyle, surface: CardSurface): Graphics;
|
|
26
|
+
declare function containRect(width: number, height: number, imageWidth: number, imageHeight: number): CardRect;
|
|
27
|
+
declare function cardElevation(context: {
|
|
28
|
+
liftedIds: ReadonlySet<string>;
|
|
29
|
+
}, id: string): CardElevation;
|
|
30
|
+
declare function isBoardCard(item: {
|
|
31
|
+
type: string;
|
|
32
|
+
}): boolean;
|
|
33
|
+
//#endregion
|
|
34
|
+
export { CardElevation, CardRect, CardSurface, PIXEL_STEP, cardElevation, cardOutlinePoints, cardShadow, containRect, drawCardShadow, drawCardSurface, isBoardCard, traceCardOutline, tracePixelRect };
|
|
@@ -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 };
|