@neta-art/cohub 11.0.0 → 11.2.0

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