@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
|
@@ -53,6 +53,7 @@ function createBoardEditor(options) {
|
|
|
53
53
|
width: 0,
|
|
54
54
|
height: 0
|
|
55
55
|
},
|
|
56
|
+
controlOutset: 0,
|
|
56
57
|
cameraPolicy: preferences?.cameraPolicy ?? "follow",
|
|
57
58
|
tool: options.initialTool ?? "select",
|
|
58
59
|
toolStyles: preferences?.toolStyles ?? createBoardToolStyles(),
|
|
@@ -339,6 +340,12 @@ function createBoardEditor(options) {
|
|
|
339
340
|
set surfaceSize(value) {
|
|
340
341
|
state.surfaceSize = value;
|
|
341
342
|
},
|
|
343
|
+
get controlOutset() {
|
|
344
|
+
return read("selection", () => state.controlOutset);
|
|
345
|
+
},
|
|
346
|
+
set controlOutset(value) {
|
|
347
|
+
if (value !== state.controlOutset) state.controlOutset = value;
|
|
348
|
+
},
|
|
342
349
|
loadDocument: remote.loadDocument,
|
|
343
350
|
undo: document.undo,
|
|
344
351
|
redo: document.redo,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { clampZoom, normalizeRotation, panBy, rectCenter, rectsIntersect, resizeFrame, rotateFrames, scaleFrames, screenPoint, selectionBounds, worldPoint, zoomAround } from "../model/geometry.js";
|
|
1
|
+
import { clampZoom, frameHandlePosition, normalizeRotation, panBy, rectCenter, rectsIntersect, resizeFrame, rotateFrames, scaleFrames, screenPoint, selectionBounds, worldPoint, zoomAround } from "../model/geometry.js";
|
|
2
2
|
import { resolveSceneArrow } from "../model/arrow-geometry.js";
|
|
3
3
|
import { appendBoardDrawSamples } from "../model/draw-input.js";
|
|
4
4
|
import { createBoardItemId } from "../model/items/id.js";
|
|
@@ -51,8 +51,9 @@ function createInteractionModule(ctx) {
|
|
|
51
51
|
state.hoverPoint,
|
|
52
52
|
state.hoverPointerType,
|
|
53
53
|
state.camera.zoom,
|
|
54
|
-
ctx.selection.transform()
|
|
55
|
-
|
|
54
|
+
ctx.selection.transform(),
|
|
55
|
+
state.controlOutset
|
|
56
|
+
], (interaction, tool, hoverPoint, pointerType, zoom, transform, outset) => interaction.type === "idle" && !pinch && tool === "select" && hoverPoint ? selectionTransformControlAt(transform, hoverPoint, zoom, pointerType, outset) : null);
|
|
56
57
|
function gridSnapSize() {
|
|
57
58
|
const grid = state.base.board.grid;
|
|
58
59
|
return grid?.visible === true ? grid.size ?? 0 : 0;
|
|
@@ -244,7 +245,8 @@ function createInteractionModule(ctx) {
|
|
|
244
245
|
return;
|
|
245
246
|
}
|
|
246
247
|
const bounds = ctx.selection.bounds();
|
|
247
|
-
const
|
|
248
|
+
const transform = ctx.selection.transform();
|
|
249
|
+
const control = selectionTransformControlAt(transform, event.world, camera.zoom, event.pointerType, state.controlOutset);
|
|
248
250
|
if (control?.kind === "rotate" && bounds) {
|
|
249
251
|
const pivot = rectCenter(bounds);
|
|
250
252
|
state.interaction = {
|
|
@@ -257,15 +259,21 @@ function createInteractionModule(ctx) {
|
|
|
257
259
|
};
|
|
258
260
|
return;
|
|
259
261
|
}
|
|
260
|
-
if (control?.kind === "resize" && bounds) {
|
|
262
|
+
if (control?.kind === "resize" && bounds && transform) {
|
|
261
263
|
const origin = ctx.selection.framesFor(ctx.selection.movable());
|
|
262
264
|
const single = origin.size === 1 ? { ...[...origin.values()][0] } : null;
|
|
265
|
+
const edge = frameHandlePosition(transform.frame, control.handle);
|
|
266
|
+
const grab = {
|
|
267
|
+
x: edge.x - event.world.x,
|
|
268
|
+
y: edge.y - event.world.y
|
|
269
|
+
};
|
|
263
270
|
state.interaction = {
|
|
264
271
|
type: "resizing",
|
|
265
272
|
handle: control.handle,
|
|
266
273
|
single,
|
|
267
274
|
bounds,
|
|
268
275
|
origin,
|
|
276
|
+
grab,
|
|
269
277
|
moved: false
|
|
270
278
|
};
|
|
271
279
|
return;
|
|
@@ -349,13 +357,14 @@ function createInteractionModule(ctx) {
|
|
|
349
357
|
moved: true
|
|
350
358
|
};
|
|
351
359
|
const frames = /* @__PURE__ */ new Map();
|
|
360
|
+
const point = worldPoint(event.world.x + gesture.grab.x, event.world.y + gesture.grab.y);
|
|
352
361
|
if (gesture.single) {
|
|
353
362
|
const id = [...gesture.origin.keys()][0];
|
|
354
363
|
const item = state.scene.get(id);
|
|
355
364
|
const keepAspect = item ? registry.capabilities(item).aspectLocked || event.shiftKey : event.shiftKey;
|
|
356
|
-
frames.set(id, resizeFrame(gesture.single, gesture.handle,
|
|
365
|
+
frames.set(id, resizeFrame(gesture.single, gesture.handle, point, void 0, keepAspect));
|
|
357
366
|
} else {
|
|
358
|
-
const scaled = scaleFrames([...gesture.origin.values()], gesture.bounds, gesture.handle,
|
|
367
|
+
const scaled = scaleFrames([...gesture.origin.values()], gesture.bounds, gesture.handle, point);
|
|
359
368
|
[...gesture.origin.keys()].forEach((id, index) => {
|
|
360
369
|
const frame = scaled[index];
|
|
361
370
|
if (frame) frames.set(id, frame);
|
|
@@ -19,6 +19,6 @@ type BoardSelectionTransform = {
|
|
|
19
19
|
canRotate: boolean;
|
|
20
20
|
};
|
|
21
21
|
declare function resolveSelectionTransform(registry: BoardRegistry, items: BoardSceneItem[], bounds: Rect | null): BoardSelectionTransform | null;
|
|
22
|
-
declare function selectionTransformControlAt(
|
|
22
|
+
declare function selectionTransformControlAt(selection: BoardSelectionTransform | null, point: WorldPoint, zoom: number, pointerType: string, outset?: number): BoardTransformControl | null;
|
|
23
23
|
//#endregion
|
|
24
24
|
export { BoardSelectionTransform, BoardTransformControl, resizeCursorForHandle, resolveSelectionTransform, selectionTransformControlAt };
|
|
@@ -51,10 +51,21 @@ function controlHitRadius(pointerType) {
|
|
|
51
51
|
if (pointerType === "pen") return 12;
|
|
52
52
|
return 8;
|
|
53
53
|
}
|
|
54
|
-
function selectionTransformControlAt(
|
|
55
|
-
if (!
|
|
54
|
+
function selectionTransformControlAt(selection, point, zoom, pointerType, outset = 0) {
|
|
55
|
+
if (!selection) return null;
|
|
56
56
|
const screenRadius = controlHitRadius(pointerType);
|
|
57
57
|
const radius = screenRadius / Math.max(zoom, 1e-4);
|
|
58
|
+
const grow = outset / Math.max(zoom, 1e-4);
|
|
59
|
+
const transform = grow > 0 ? {
|
|
60
|
+
...selection,
|
|
61
|
+
frame: {
|
|
62
|
+
...selection.frame,
|
|
63
|
+
x: selection.frame.x - grow,
|
|
64
|
+
y: selection.frame.y - grow,
|
|
65
|
+
width: selection.frame.width + grow * 2,
|
|
66
|
+
height: selection.frame.height + grow * 2
|
|
67
|
+
}
|
|
68
|
+
} : selection;
|
|
58
69
|
if (transform.resizeMode !== "none") for (const handle of CORNER_RESIZE_HANDLES) {
|
|
59
70
|
const position = frameHandlePosition(transform.frame, handle);
|
|
60
71
|
if (Math.hypot(position.x - point.x, position.y - point.y) <= radius) return {
|
|
@@ -6,6 +6,7 @@ import { BoardEvaluation } from "../model/animation.js";
|
|
|
6
6
|
import { BoardAnimationSampler } from "../model/sampling.js";
|
|
7
7
|
import { BoardExportPlan, BoardExportPlanInput, BoardExportRegion } from "../model/export-plan.js";
|
|
8
8
|
import { BoardShapeColors } from "../model/palette.js";
|
|
9
|
+
import { BoardRenderStyle } from "../render/style.js";
|
|
9
10
|
import { BoardCardRendererResolver, BoardRenderPalette, BoardScriptHost } from "../render/renderers/board-renderer-registry.js";
|
|
10
11
|
import "../render/index.js";
|
|
11
12
|
import { BoardExportScene, BoardExportSceneInput, createBoardExportScene } from "./scene.js";
|
|
@@ -29,6 +30,7 @@ type BoardExportOptions = {
|
|
|
29
30
|
background?: BoardExportBackground;
|
|
30
31
|
palette?: Partial<BoardRenderPalette>;
|
|
31
32
|
colors?: BoardShapeColors;
|
|
33
|
+
style?: Partial<BoardRenderStyle>;
|
|
32
34
|
textures?: Map<string, Texture>;
|
|
33
35
|
scripts?: BoardScriptHost;
|
|
34
36
|
assetKey?: (item: BoardSceneItem) => string | null;
|
|
@@ -3,6 +3,7 @@ import "../../protocol/dist/index.js";
|
|
|
3
3
|
import { Rect } from "../model/geometry.js";
|
|
4
4
|
import { BoardScene, BoardSceneItem } from "../model/scene.js";
|
|
5
5
|
import { BoardShapeColors } from "../model/palette.js";
|
|
6
|
+
import { BoardRenderStyle } from "../render/style.js";
|
|
6
7
|
import { BoardCardRendererResolver, BoardRenderContext, BoardRenderPalette } from "../render/renderers/board-renderer-registry.js";
|
|
7
8
|
import "../render/index.js";
|
|
8
9
|
import { Container, Texture } from "pixi.js";
|
|
@@ -18,6 +19,7 @@ type BoardExportSceneInput = {
|
|
|
18
19
|
colorScheme: "dark" | "light";
|
|
19
20
|
palette?: Partial<BoardRenderPalette>;
|
|
20
21
|
colors?: BoardShapeColors;
|
|
22
|
+
style?: Partial<BoardRenderStyle>;
|
|
21
23
|
textures?: Map<string, Texture>;
|
|
22
24
|
scripts?: BoardRenderContext["scripts"];
|
|
23
25
|
assetKey?: (item: BoardSceneItem) => string | null;
|
|
@@ -3,6 +3,7 @@ import { imageAssetKey } from "../model/image-key.js";
|
|
|
3
3
|
import { defaultBoardPalette } from "../render/palette.js";
|
|
4
4
|
import { clippingAncestor, isClippingFrame, syncClipGroup } from "../render/clip.js";
|
|
5
5
|
import { DEFAULT_BOARD_RENDER_LABELS, getBoardCardRenderer } from "../render/renderers/board-renderer-registry.js";
|
|
6
|
+
import { resolveBoardRenderStyle } from "../render/style.js";
|
|
6
7
|
import { Container, Graphics, Sprite, TilingSprite } from "pixi.js";
|
|
7
8
|
//#region src/board/export/scene.ts
|
|
8
9
|
function buildContext(input) {
|
|
@@ -17,12 +18,15 @@ function buildContext(input) {
|
|
|
17
18
|
selectedIds: /* @__PURE__ */ new Set(),
|
|
18
19
|
hoveredId: null,
|
|
19
20
|
resizingIds: /* @__PURE__ */ new Set(),
|
|
21
|
+
liftedIds: /* @__PURE__ */ new Set(),
|
|
20
22
|
palette: {
|
|
21
23
|
...defaultBoardPalette(input.colorScheme),
|
|
22
24
|
...input.palette
|
|
23
25
|
},
|
|
24
26
|
colors: input.colors ?? buildFallbackShapeColors(input.colorScheme),
|
|
25
27
|
colorScheme: input.colorScheme,
|
|
28
|
+
style: resolveBoardRenderStyle(input.colorScheme, input.style),
|
|
29
|
+
themeKey: "export",
|
|
26
30
|
rendererType: "canvas",
|
|
27
31
|
zoom: input.scale,
|
|
28
32
|
reducedMotion: true,
|
|
@@ -7,7 +7,6 @@ type FilePreviewKind = "cover" | "text" | "blank";
|
|
|
7
7
|
* sync with them.
|
|
8
8
|
*/
|
|
9
9
|
declare function filePreviewKind(snapshot: BoardFileSnapshotFacts | undefined): FilePreviewKind;
|
|
10
|
-
/** Short uppercase type label for a file card (`MD`, `JSON`, `FILE`). */
|
|
11
10
|
declare function fileTypeLabel(path: string): string;
|
|
12
11
|
/** Human-readable byte size. Kept as a public helper for custom card renderers. */
|
|
13
12
|
declare function formatFileSize(bytes: number | undefined): string;
|
|
@@ -11,13 +11,22 @@ function filePreviewKind(snapshot) {
|
|
|
11
11
|
if (snapshot.excerpt) return "text";
|
|
12
12
|
return "blank";
|
|
13
13
|
}
|
|
14
|
-
/** Short uppercase type label for a file card (`MD`, `JSON`, `FILE`). */
|
|
15
14
|
function fileTypeLabel(path) {
|
|
15
|
+
if (isWebPageUrl(path)) return "LINK";
|
|
16
16
|
const name = fileBaseName(path);
|
|
17
17
|
const dot = name.lastIndexOf(".");
|
|
18
18
|
if (dot <= 0) return (name.replace(/^\./, "") || "file").toUpperCase();
|
|
19
19
|
return name.slice(dot + 1).toUpperCase();
|
|
20
20
|
}
|
|
21
|
+
function isWebPageUrl(path) {
|
|
22
|
+
if (!/^https?:\/\//i.test(path)) return false;
|
|
23
|
+
try {
|
|
24
|
+
const last = new URL(path).pathname.split("/").filter(Boolean).pop() ?? "";
|
|
25
|
+
return !/\.[a-z\d]{1,8}$/i.test(last);
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
21
30
|
/** Human-readable byte size. Kept as a public helper for custom card renderers. */
|
|
22
31
|
function formatFileSize(bytes) {
|
|
23
32
|
if (bytes === void 0 || !Number.isFinite(bytes) || bytes < 0) return "";
|
|
@@ -7,6 +7,6 @@ declare function drawAudioWaveform(graphics: Graphics, seed: string, rect: {
|
|
|
7
7
|
y: number;
|
|
8
8
|
width: number;
|
|
9
9
|
height: number;
|
|
10
|
-
}, color: number, alpha?: number): void;
|
|
10
|
+
}, color: number, alpha?: number, square?: boolean): void;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { audioWaveformBars, drawAudioWaveform };
|
|
@@ -17,7 +17,7 @@ function audioWaveformBars(seed, count) {
|
|
|
17
17
|
}
|
|
18
18
|
return bars;
|
|
19
19
|
}
|
|
20
|
-
function drawAudioWaveform(graphics, seed, rect, color, alpha = .72) {
|
|
20
|
+
function drawAudioWaveform(graphics, seed, rect, color, alpha = .72, square = false) {
|
|
21
21
|
const count = Math.max(8, Math.min(DEFAULT_BARS, Math.floor(rect.width / 4)));
|
|
22
22
|
const step = rect.width / count;
|
|
23
23
|
const barWidth = Math.max(1.5, step - BAR_GAP);
|
|
@@ -26,11 +26,14 @@ function drawAudioWaveform(graphics, seed, rect, color, alpha = .72) {
|
|
|
26
26
|
const bars = audioWaveformBars(seed, count);
|
|
27
27
|
for (let index = 0; index < count; index += 1) {
|
|
28
28
|
const height = Math.max(2, (bars[index] ?? .4) * maxHeight);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
29
|
+
const x = rect.x + index * step;
|
|
30
|
+
if (square) graphics.rect(x, centerY - height / 2, barWidth, height);
|
|
31
|
+
else graphics.roundRect(x, centerY - height / 2, barWidth, height, barWidth / 2);
|
|
33
32
|
}
|
|
33
|
+
graphics.fill({
|
|
34
|
+
color,
|
|
35
|
+
alpha
|
|
36
|
+
});
|
|
34
37
|
}
|
|
35
38
|
//#endregion
|
|
36
39
|
export { audioWaveformBars, drawAudioWaveform };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BoardRenderContext } from "./renderers/board-renderer-registry.js";
|
|
2
|
+
import { Container, Graphics, Text } from "pixi.js";
|
|
3
|
+
//#region src/board/render/card-tag.d.ts
|
|
4
|
+
type CardGlyph = "image" | "video" | "audio" | "file" | "task";
|
|
5
|
+
declare const CARD_TAG_SIZE = 11;
|
|
6
|
+
declare function formatBoardDuration(ms: number): string;
|
|
7
|
+
declare function drawCardGlyph(graphics: Graphics, glyph: CardGlyph, color: number, pixel: boolean): void;
|
|
8
|
+
type CardTag = {
|
|
9
|
+
root: Container;
|
|
10
|
+
icon: Graphics;
|
|
11
|
+
text: Text | null;
|
|
12
|
+
sig: string;
|
|
13
|
+
resolution: number;
|
|
14
|
+
width: number;
|
|
15
|
+
};
|
|
16
|
+
declare function createCardTag(): CardTag;
|
|
17
|
+
declare function zoomCardTag(tag: CardTag, zoom: number): void;
|
|
18
|
+
declare function syncCardTag(tag: CardTag, input: {
|
|
19
|
+
glyph: CardGlyph;
|
|
20
|
+
text: string;
|
|
21
|
+
width: number;
|
|
22
|
+
}, context: BoardRenderContext): void;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { CARD_TAG_SIZE, CardGlyph, CardTag, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { fitTextToWidth } from "./text-fit.js";
|
|
2
|
+
import { syncTextResolution, textResolutionForZoom } from "./text-resolution.js";
|
|
3
|
+
import { Container, Graphics, Text } from "pixi.js";
|
|
4
|
+
//#region src/board/render/card-tag.ts
|
|
5
|
+
const CARD_TAG_SIZE = 11;
|
|
6
|
+
const ICON = 12;
|
|
7
|
+
const GAP = 5;
|
|
8
|
+
const MAX_GROWTH = 1.25;
|
|
9
|
+
const MIN_READABLE = 7;
|
|
10
|
+
const SECOND = 1e3;
|
|
11
|
+
function formatBoardDuration(ms) {
|
|
12
|
+
const total = Math.max(0, Math.floor(ms / SECOND));
|
|
13
|
+
const hours = Math.floor(total / 3600);
|
|
14
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
15
|
+
const seconds = String(total % 60).padStart(2, "0");
|
|
16
|
+
return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${seconds}` : `${minutes}:${seconds}`;
|
|
17
|
+
}
|
|
18
|
+
const PIXEL_GLYPHS = {
|
|
19
|
+
image: [
|
|
20
|
+
"######",
|
|
21
|
+
"#....#",
|
|
22
|
+
"#.#..#",
|
|
23
|
+
"#.##.#",
|
|
24
|
+
"######",
|
|
25
|
+
"......"
|
|
26
|
+
],
|
|
27
|
+
video: [
|
|
28
|
+
"######",
|
|
29
|
+
"#.#..#",
|
|
30
|
+
"#.##.#",
|
|
31
|
+
"#.#..#",
|
|
32
|
+
"######",
|
|
33
|
+
"......"
|
|
34
|
+
],
|
|
35
|
+
audio: [
|
|
36
|
+
"...#..",
|
|
37
|
+
"...##.",
|
|
38
|
+
"...#.#",
|
|
39
|
+
"...#..",
|
|
40
|
+
".###..",
|
|
41
|
+
".###.."
|
|
42
|
+
],
|
|
43
|
+
file: [
|
|
44
|
+
"####..",
|
|
45
|
+
"#..##.",
|
|
46
|
+
"#...##",
|
|
47
|
+
"#....#",
|
|
48
|
+
"#....#",
|
|
49
|
+
"######"
|
|
50
|
+
],
|
|
51
|
+
task: [
|
|
52
|
+
"..#...",
|
|
53
|
+
".###..",
|
|
54
|
+
"#####.",
|
|
55
|
+
".###..",
|
|
56
|
+
"..#...",
|
|
57
|
+
"......"
|
|
58
|
+
]
|
|
59
|
+
};
|
|
60
|
+
function drawLineGlyph(graphics, glyph, color) {
|
|
61
|
+
const line = {
|
|
62
|
+
color,
|
|
63
|
+
width: 1.25,
|
|
64
|
+
alpha: 1,
|
|
65
|
+
cap: "round",
|
|
66
|
+
join: "round"
|
|
67
|
+
};
|
|
68
|
+
switch (glyph) {
|
|
69
|
+
case "image":
|
|
70
|
+
graphics.roundRect(1, 2, 10, 8, 1.5).stroke(line);
|
|
71
|
+
graphics.moveTo(2.5, 9).lineTo(5, 6).lineTo(7, 8).lineTo(8.5, 6.5).lineTo(10, 8).stroke(line);
|
|
72
|
+
return;
|
|
73
|
+
case "video":
|
|
74
|
+
graphics.roundRect(1, 2.5, 10, 7, 1.5).stroke(line);
|
|
75
|
+
graphics.poly([
|
|
76
|
+
5,
|
|
77
|
+
4.5,
|
|
78
|
+
5,
|
|
79
|
+
7.5,
|
|
80
|
+
7.5,
|
|
81
|
+
6
|
|
82
|
+
]).fill({ color });
|
|
83
|
+
return;
|
|
84
|
+
case "audio":
|
|
85
|
+
for (const [x, height] of [
|
|
86
|
+
[2.5, 3],
|
|
87
|
+
[5, 7],
|
|
88
|
+
[7.5, 9],
|
|
89
|
+
[10, 5]
|
|
90
|
+
]) graphics.moveTo(x, 6 - height / 2).lineTo(x, 6 + height / 2);
|
|
91
|
+
graphics.stroke(line);
|
|
92
|
+
return;
|
|
93
|
+
case "file":
|
|
94
|
+
graphics.poly([
|
|
95
|
+
2.5,
|
|
96
|
+
1,
|
|
97
|
+
7.5,
|
|
98
|
+
1,
|
|
99
|
+
9.5,
|
|
100
|
+
3,
|
|
101
|
+
9.5,
|
|
102
|
+
11,
|
|
103
|
+
2.5,
|
|
104
|
+
11
|
|
105
|
+
]).stroke(line);
|
|
106
|
+
graphics.moveTo(7.5, 1).lineTo(7.5, 3).lineTo(9.5, 3).stroke(line);
|
|
107
|
+
return;
|
|
108
|
+
case "task":
|
|
109
|
+
graphics.poly([
|
|
110
|
+
6,
|
|
111
|
+
1,
|
|
112
|
+
7.3,
|
|
113
|
+
4.7,
|
|
114
|
+
11,
|
|
115
|
+
6,
|
|
116
|
+
7.3,
|
|
117
|
+
7.3,
|
|
118
|
+
6,
|
|
119
|
+
11,
|
|
120
|
+
4.7,
|
|
121
|
+
7.3,
|
|
122
|
+
1,
|
|
123
|
+
6,
|
|
124
|
+
4.7,
|
|
125
|
+
4.7
|
|
126
|
+
]).fill({ color });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function drawCardGlyph(graphics, glyph, color, pixel) {
|
|
131
|
+
if (!pixel) {
|
|
132
|
+
drawLineGlyph(graphics, glyph, color);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
PIXEL_GLYPHS[glyph].forEach((row, y) => {
|
|
136
|
+
for (let x = 0; x < row.length; x += 1) if (row[x] === "#") graphics.rect(x * 2, y * 2, 2, 2);
|
|
137
|
+
});
|
|
138
|
+
graphics.fill({ color });
|
|
139
|
+
}
|
|
140
|
+
function createCardTag() {
|
|
141
|
+
const root = new Container({ label: "card-tag" });
|
|
142
|
+
const icon = new Graphics();
|
|
143
|
+
root.addChild(icon);
|
|
144
|
+
root.visible = false;
|
|
145
|
+
return {
|
|
146
|
+
root,
|
|
147
|
+
icon,
|
|
148
|
+
text: null,
|
|
149
|
+
sig: "",
|
|
150
|
+
resolution: 0,
|
|
151
|
+
width: 0
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function zoomCardTag(tag, zoom) {
|
|
155
|
+
if (!tag.text || !tag.sig) return;
|
|
156
|
+
const scale = Math.min(MAX_GROWTH, Math.max(1, 1 / Math.max(zoom, .01)));
|
|
157
|
+
const span = 17 + tag.text.width;
|
|
158
|
+
tag.root.scale.set(scale);
|
|
159
|
+
tag.root.position.set(0, -18 * scale);
|
|
160
|
+
tag.root.visible = 11 * scale * zoom >= MIN_READABLE && span * scale <= tag.width + .5;
|
|
161
|
+
}
|
|
162
|
+
function syncCardTag(tag, input, context) {
|
|
163
|
+
tag.width = input.width;
|
|
164
|
+
if (!input.text || input.width <= 24) {
|
|
165
|
+
tag.sig = "";
|
|
166
|
+
tag.root.visible = false;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!tag.text) {
|
|
170
|
+
tag.resolution = textResolutionForZoom(context.zoom);
|
|
171
|
+
tag.text = new Text({
|
|
172
|
+
text: "",
|
|
173
|
+
style: {
|
|
174
|
+
fontFamily: context.style.font,
|
|
175
|
+
fontSize: 11,
|
|
176
|
+
fontWeight: "500"
|
|
177
|
+
},
|
|
178
|
+
resolution: tag.resolution,
|
|
179
|
+
roundPixels: true
|
|
180
|
+
});
|
|
181
|
+
tag.root.addChild(tag.text);
|
|
182
|
+
}
|
|
183
|
+
syncTextResolution(tag.text, tag, context.zoom);
|
|
184
|
+
const sig = [
|
|
185
|
+
input.glyph,
|
|
186
|
+
input.text,
|
|
187
|
+
input.width,
|
|
188
|
+
context.themeKey
|
|
189
|
+
].join("|");
|
|
190
|
+
if (sig === tag.sig) {
|
|
191
|
+
zoomCardTag(tag, context.zoom);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
tag.sig = sig;
|
|
195
|
+
const color = context.palette.muted;
|
|
196
|
+
const pixel = context.style.icons === "pixel";
|
|
197
|
+
drawCardGlyph(tag.icon.clear(), input.glyph, color, pixel);
|
|
198
|
+
tag.text.style.fill = color;
|
|
199
|
+
tag.text.style.fontFamily = context.style.font;
|
|
200
|
+
fitTextToWidth(tag.text, input.text, Math.max(1, input.width - ICON - GAP));
|
|
201
|
+
tag.text.position.set(17, (ICON - tag.text.height) / 2);
|
|
202
|
+
zoomCardTag(tag, context.zoom);
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { CARD_TAG_SIZE, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag };
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
import { BoardRenderStyle, BoardSelectionStyle, BoardShadowStyle, defaultBoardRenderStyle, resolveBoardRenderStyle } from "./style.js";
|
|
1
2
|
import { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderLabels, BoardRenderPalette, BoardScriptHost, DEFAULT_BOARD_RENDER_LABELS, createBoardCardRendererResolver, getBoardCardRenderer } from "./renderers/board-renderer-registry.js";
|
|
2
3
|
import { createBoardRenderSampler } from "./sampling.js";
|
|
3
4
|
import { BoardRenderDiagnostic, inspectBoardScene, measureBoardText } from "./diagnostics.js";
|
|
4
5
|
import { BoardBackgroundContext, createBoardBackground, updateBoardBackground } from "./board-background.js";
|
|
6
|
+
import { CARD_TAG_SIZE, CardGlyph, CardTag, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag } from "./card-tag.js";
|
|
5
7
|
import { ClipGroup, clippingAncestor, isClippingFrame, syncClipGroup, traceFrameOutline } from "./clip.js";
|
|
6
8
|
import { BoardCssPaint, parseBoardCssColor, parseBoardCssPaint } from "./css-color.js";
|
|
7
|
-
import { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible } from "./media-interaction.js";
|
|
9
|
+
import { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter } from "./media-interaction.js";
|
|
8
10
|
import { defaultBoardPalette, itemAlpha, itemColor } from "./palette.js";
|
|
9
11
|
import { BoardScriptHostOptions, BrowserBoardScriptHost, createBoardScriptHost } from "./script-host.js";
|
|
10
12
|
import { prepareBrowserBoardScripts } from "./script-export.js";
|
|
11
13
|
import { dashPattern, endAngle, roundRectPoints, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
|
|
14
|
+
import { CardElevation, CardRect, CardSurface, PIXEL_STEP, cardElevation, cardOutlinePoints, cardShadow, containRect, drawCardShadow, drawCardSurface, isBoardCard, traceCardOutline, tracePixelRect } from "./surface.js";
|
|
12
15
|
import { BOARD_EFFECT_MAX_PARTICLES } from "./renderers/effect-card-renderer.js";
|
|
13
|
-
import {
|
|
16
|
+
import { ellipsizeWrappedLines, fitLineWithEllipsis, fitTextToLines, fitTextToWidth } from "./text-fit.js";
|
|
14
17
|
import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
|
|
15
|
-
import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
|
|
18
|
+
import { boardCardDetailed, getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
|
|
16
19
|
import { VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, loadVideoThumbnailTexture, videoTextureNaturalSize, videoThumbnailSize } from "./video-thumbnail.js";
|
|
17
|
-
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, BoardBackgroundContext, BoardCardRenderer, BoardCardRendererResolver, BoardCssPaint, BoardMediaAction, BoardRenderContext, BoardRenderDiagnostic, BoardRenderLabels, BoardRenderPalette, BoardScriptHost, BoardScriptHostOptions, BrowserBoardScriptHost, ClipGroup, DEFAULT_BOARD_RENDER_LABELS,
|
|
20
|
+
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, BoardBackgroundContext, BoardCardRenderer, BoardCardRendererResolver, BoardCssPaint, BoardMediaAction, BoardRenderContext, BoardRenderDiagnostic, BoardRenderLabels, BoardRenderPalette, BoardRenderStyle, BoardScriptHost, BoardScriptHostOptions, BoardSelectionStyle, BoardShadowStyle, BrowserBoardScriptHost, CARD_TAG_SIZE, CardElevation, CardGlyph, CardRect, CardSurface, CardTag, ClipGroup, DEFAULT_BOARD_RENDER_LABELS, PIXEL_STEP, VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, boardCardDetailed, boardMediaActionAt, cardElevation, cardOutlinePoints, cardShadow, clippingAncestor, containRect, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, createCardTag, dashPattern, defaultBoardPalette, defaultBoardRenderStyle, drawCardGlyph, drawCardShadow, drawCardSurface, ellipsizeWrappedLines, endAngle, ensureBoardTextMeasurement, fitLineWithEllipsis, fitTextToLines, fitTextToWidth, formatBoardDuration, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isBoardCard, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, resolveBoardRenderStyle, roundRectPoints, syncCardTag, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceCardOutline, traceFrameOutline, tracePixelRect, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize, zoomCardTag };
|
|
@@ -4,14 +4,17 @@ import { createBoardRenderSampler } from "./sampling.js";
|
|
|
4
4
|
import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
|
|
5
5
|
import { inspectBoardScene, measureBoardText } from "./diagnostics.js";
|
|
6
6
|
import { createBoardBackground, updateBoardBackground } from "./board-background.js";
|
|
7
|
+
import { ellipsizeWrappedLines, fitLineWithEllipsis, fitTextToLines, fitTextToWidth } from "./text-fit.js";
|
|
8
|
+
import { boardCardDetailed, getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
|
|
9
|
+
import { CARD_TAG_SIZE, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag } from "./card-tag.js";
|
|
7
10
|
import { clippingAncestor, isClippingFrame, syncClipGroup, traceFrameOutline } from "./clip.js";
|
|
8
|
-
import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
|
|
9
|
-
import { TASK_CARD_FULL_DETAIL_ZOOM } from "./renderers/task-card-renderer.js";
|
|
10
|
-
import { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible } from "./media-interaction.js";
|
|
11
11
|
import { dashPattern, endAngle, roundRectPoints, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
|
|
12
|
+
import { PIXEL_STEP, cardElevation, cardOutlinePoints, cardShadow, containRect, drawCardShadow, drawCardSurface, isBoardCard, traceCardOutline, tracePixelRect } from "./surface.js";
|
|
13
|
+
import { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter } from "./media-interaction.js";
|
|
12
14
|
import { BOARD_EFFECT_MAX_PARTICLES } from "./renderers/effect-card-renderer.js";
|
|
13
15
|
import { DEFAULT_BOARD_RENDER_LABELS, createBoardCardRendererResolver, getBoardCardRenderer } from "./renderers/board-renderer-registry.js";
|
|
14
16
|
import { createBoardScriptHost } from "./script-host.js";
|
|
15
17
|
import { prepareBrowserBoardScripts } from "./script-export.js";
|
|
18
|
+
import { defaultBoardRenderStyle, resolveBoardRenderStyle } from "./style.js";
|
|
16
19
|
import { VIDEO_THUMBNAIL_MAX_EDGE, loadVideoThumbnailTexture, videoTextureNaturalSize, videoThumbnailSize } from "./video-thumbnail.js";
|
|
17
|
-
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, DEFAULT_BOARD_RENDER_LABELS,
|
|
20
|
+
export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, CARD_TAG_SIZE, DEFAULT_BOARD_RENDER_LABELS, PIXEL_STEP, VIDEO_THUMBNAIL_MAX_EDGE, boardCardDetailed, boardMediaActionAt, cardElevation, cardOutlinePoints, cardShadow, clippingAncestor, containRect, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, createCardTag, dashPattern, defaultBoardPalette, defaultBoardRenderStyle, drawCardGlyph, drawCardShadow, drawCardSurface, ellipsizeWrappedLines, endAngle, ensureBoardTextMeasurement, fitLineWithEllipsis, fitTextToLines, fitTextToWidth, formatBoardDuration, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isBoardCard, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, resolveBoardRenderStyle, roundRectPoints, syncCardTag, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceCardOutline, traceFrameOutline, tracePixelRect, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize, zoomCardTag };
|
|
@@ -9,10 +9,11 @@ declare function mediaPlayBadgeVisible(item: BoardSceneItem, zoom: number, optio
|
|
|
9
9
|
materialized: boolean;
|
|
10
10
|
hasVideoPreview: boolean;
|
|
11
11
|
}): boolean;
|
|
12
|
+
declare function mediaPlayKeyCenter(item: BoardSceneItem): WorldPoint;
|
|
12
13
|
declare function mediaPlayBadgeHit(item: BoardSceneItem, point: WorldPoint, zoom: number): boolean;
|
|
13
14
|
declare function boardMediaActionAt(item: BoardSceneItem, point: WorldPoint, zoom: number, options: {
|
|
14
15
|
materialized: boolean;
|
|
15
16
|
hasVideoPreview: boolean;
|
|
16
17
|
}): BoardMediaAction | null;
|
|
17
18
|
//#endregion
|
|
18
|
-
export { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible };
|
|
19
|
+
export { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter };
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
|
|
2
|
-
import "
|
|
2
|
+
import { degToRad, rotatePointAround, worldPoint } from "../model/geometry.js";
|
|
3
|
+
import { boardCardDetailed } from "./text-resolution.js";
|
|
4
|
+
import { audioKeyCenter } from "./renderers/audio-card-renderer.js";
|
|
3
5
|
//#region src/board/render/media-interaction.ts
|
|
4
6
|
function mediaPlayBadgeVisible(item, zoom, options) {
|
|
5
7
|
if (!options.materialized) return false;
|
|
6
8
|
if (item.type === "video" || item.type === "audio") return true;
|
|
7
|
-
if (item.type !== "task" || zoom
|
|
9
|
+
if (item.type !== "task" || !boardCardDetailed(zoom)) return false;
|
|
8
10
|
const artifact = featuredTaskArtifact(item.props.snapshot.artifacts);
|
|
9
11
|
if (artifact?.type === "audio") return true;
|
|
10
12
|
return artifact?.type === "video" && (Boolean(artifact.previewUrl) || options.hasVideoPreview);
|
|
11
13
|
}
|
|
14
|
+
function mediaPlayKeyCenter(item) {
|
|
15
|
+
const { x, y, width, height, rotation } = item.frame;
|
|
16
|
+
const center = worldPoint(x + width / 2, y + height / 2);
|
|
17
|
+
if (item.type !== "audio") return center;
|
|
18
|
+
const key = audioKeyCenter(width, height);
|
|
19
|
+
return rotatePointAround(worldPoint(x + key.x, y + key.y), center, degToRad(rotation || 0));
|
|
20
|
+
}
|
|
12
21
|
function mediaPlayBadgeHit(item, point, zoom) {
|
|
13
22
|
const radius = 28 / Math.max(zoom, .05);
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
return Math.hypot(point.x - centerX, point.y - centerY) <= radius;
|
|
23
|
+
const center = mediaPlayKeyCenter(item);
|
|
24
|
+
return Math.hypot(point.x - center.x, point.y - center.y) <= radius;
|
|
17
25
|
}
|
|
18
26
|
function boardMediaActionAt(item, point, zoom, options) {
|
|
19
27
|
return mediaPlayBadgeVisible(item, zoom, options) && mediaPlayBadgeHit(item, point, zoom) ? {
|
|
@@ -22,4 +30,4 @@ function boardMediaActionAt(item, point, zoom, options) {
|
|
|
22
30
|
} : null;
|
|
23
31
|
}
|
|
24
32
|
//#endregion
|
|
25
|
-
export { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible };
|
|
33
|
+
export { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter };
|