@neta-art/cohub 10.0.0 → 11.0.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 (129) hide show
  1. package/README.md +11 -2
  2. package/dist/board/editor/animation.d.ts +3 -0
  3. package/dist/board/editor/animation.js +16 -1
  4. package/dist/board/editor/clipboard.js +7 -0
  5. package/dist/board/editor/copy.js +23 -18
  6. package/dist/board/editor/creation.d.ts +5 -1
  7. package/dist/board/editor/creation.js +2 -1
  8. package/dist/board/editor/deletion.js +3 -3
  9. package/dist/board/editor/document.d.ts +8 -2
  10. package/dist/board/editor/document.js +54 -7
  11. package/dist/board/editor/editor.d.ts +18 -3
  12. package/dist/board/editor/editor.js +11 -1
  13. package/dist/board/editor/query.d.ts +5 -5
  14. package/dist/board/editor/query.js +14 -12
  15. package/dist/board/editor/selection.js +2 -1
  16. package/dist/board/export/index.d.ts +25 -4
  17. package/dist/board/export/index.js +45 -12
  18. package/dist/board/export/node/index.d.ts +5 -4
  19. package/dist/board/export/node/index.js +16 -11
  20. package/dist/board/export/node/scripts.d.ts +16 -0
  21. package/dist/board/export/node/scripts.js +258 -0
  22. package/dist/board/export/scene.d.ts +2 -1
  23. package/dist/board/export/scene.js +20 -6
  24. package/dist/board/model/animation.d.ts +4 -1
  25. package/dist/board/model/animation.js +34 -7
  26. package/dist/board/model/authoring.d.ts +20 -0
  27. package/dist/board/model/authoring.js +185 -0
  28. package/dist/board/model/export-plan.js +1 -0
  29. package/dist/board/model/file-preview.js +1 -1
  30. package/dist/board/model/file-snapshot.js +3 -2
  31. package/dist/board/model/image-key.js +6 -2
  32. package/dist/board/model/index.d.ts +13 -6
  33. package/dist/board/model/index.js +14 -8
  34. package/dist/board/model/items/builtin.js +2 -2
  35. package/dist/board/model/items/create.d.ts +4 -2
  36. package/dist/board/model/items/create.js +6 -5
  37. package/dist/board/model/items/index.d.ts +2 -2
  38. package/dist/board/model/items/index.js +2 -2
  39. package/dist/board/model/media-playback.d.ts +2 -1
  40. package/dist/board/model/media-playback.js +14 -4
  41. package/dist/board/model/media.js +7 -1
  42. package/dist/board/model/sampling.d.ts +19 -0
  43. package/dist/board/model/sampling.js +96 -0
  44. package/dist/board/model/script-example.d.ts +4 -0
  45. package/dist/board/model/script-example.js +21 -0
  46. package/dist/board/model/script-hit.d.ts +10 -0
  47. package/dist/board/model/script-hit.js +52 -0
  48. package/dist/board/model/script-index.d.ts +7 -0
  49. package/dist/board/model/script-index.js +15 -0
  50. package/dist/board/model/script-scene.d.ts +24 -0
  51. package/dist/board/model/script-scene.js +267 -0
  52. package/dist/board/model/script.d.ts +105 -0
  53. package/dist/board/model/script.js +1 -0
  54. package/dist/board/render/css-color.d.ts +6 -1
  55. package/dist/board/render/css-color.js +34 -20
  56. package/dist/board/render/diagnostics.d.ts +20 -0
  57. package/dist/board/render/diagnostics.js +117 -0
  58. package/dist/board/render/index.d.ts +9 -6
  59. package/dist/board/render/index.js +9 -6
  60. package/dist/board/render/palette.d.ts +2 -1
  61. package/dist/board/render/palette.js +6 -2
  62. package/dist/board/render/renderers/arrow-card-renderer.js +3 -2
  63. package/dist/board/render/renderers/audio-card-renderer.js +2 -1
  64. package/dist/board/render/renderers/board-renderer-registry.d.ts +10 -5
  65. package/dist/board/render/renderers/board-renderer-registry.js +3 -3
  66. package/dist/board/render/renderers/custom-card-renderer.d.ts +5 -0
  67. package/dist/board/render/renderers/{sketch-card-renderer.js → custom-card-renderer.js} +16 -15
  68. package/dist/board/render/renderers/draw-card-renderer.js +3 -2
  69. package/dist/board/render/renderers/effect-card-renderer.js +22 -5
  70. package/dist/board/render/renderers/frame-card-renderer.js +30 -14
  71. package/dist/board/render/renderers/image-card-renderer.js +43 -4
  72. package/dist/board/render/renderers/shape-card-renderer.js +10 -27
  73. package/dist/board/render/renderers/text-card-renderer.js +5 -3
  74. package/dist/board/render/renderers/unknown-card-renderer.js +1 -0
  75. package/dist/board/render/renderers/video-card-renderer.js +2 -1
  76. package/dist/board/render/sampling.d.ts +22 -0
  77. package/dist/board/render/sampling.js +21 -0
  78. package/dist/board/render/script-export.d.ts +11 -0
  79. package/dist/board/render/script-export.js +86 -0
  80. package/dist/board/render/script-host.d.ts +31 -0
  81. package/dist/board/render/script-host.js +421 -0
  82. package/dist/board/render/script-modules.d.ts +2 -0
  83. package/dist/board/render/script-modules.js +2 -0
  84. package/dist/board/render/script-runtime.d.ts +29 -0
  85. package/dist/board/render/script-runtime.js +213 -0
  86. package/dist/board/render/script-sandbox.d.ts +4 -0
  87. package/dist/board/render/script-sandbox.js +173 -0
  88. package/dist/board/render/stroke.d.ts +2 -1
  89. package/dist/board/render/stroke.js +22 -1
  90. package/dist/board/render/text-measurement.js +1 -1
  91. package/dist/board/stage/assets.js +6 -1
  92. package/dist/board/stage/maintenance.d.ts +5 -0
  93. package/dist/board/stage/maintenance.js +11 -0
  94. package/dist/board/stage/scene.js +5 -2
  95. package/dist/board/stage/script-pointer.d.ts +19 -0
  96. package/dist/board/stage/script-pointer.js +35 -0
  97. package/dist/board/stage/stage.d.ts +10 -0
  98. package/dist/board/stage/stage.js +249 -43
  99. package/dist/board/stage/texture-pool.js +2 -3
  100. package/dist/chunks/environment.d.ts +266 -23
  101. package/dist/chunks/host-bridge.d.ts +2 -2
  102. package/dist/chunks/http.d.ts +88 -7
  103. package/dist/chunks/http.js +155 -63
  104. package/dist/chunks/transport.js +3 -1
  105. package/dist/chunks/websocket.d.ts +9 -1
  106. package/dist/chunks/websocket.js +16 -2
  107. package/dist/host-bridge.js +2 -1
  108. package/dist/http.d.ts +2 -2
  109. package/dist/index.d.ts +182 -4
  110. package/dist/index.js +1374 -126
  111. package/dist/protocol/dist/board-assets.d.ts +1 -0
  112. package/dist/protocol/dist/board-constants.js +2 -2
  113. package/dist/protocol/dist/board-model.d.ts +201 -11
  114. package/dist/protocol/dist/board-model.js +33 -14
  115. package/dist/protocol/dist/board-patch.js +2 -1
  116. package/dist/protocol/dist/board-schema.d.ts +4 -2
  117. package/dist/protocol/dist/board-schema.js +18 -4
  118. package/dist/protocol/dist/board-script-modules.d.ts +25 -0
  119. package/dist/protocol/dist/board-script-modules.js +127 -0
  120. package/dist/protocol/dist/display/index.d.ts +1 -0
  121. package/dist/protocol/dist/index.d.ts +4 -2
  122. package/dist/protocol/dist/realtime/types.d.ts +1 -0
  123. package/dist/protocol/dist/rtc/index.d.ts +1 -0
  124. package/package.json +2 -1
  125. package/dist/board/export/node/sketch.d.ts +0 -19
  126. package/dist/board/export/node/sketch.js +0 -146
  127. package/dist/board/render/renderers/sketch-card-renderer.d.ts +0 -5
  128. package/dist/board/render/sketch-host.d.ts +0 -14
  129. package/dist/board/render/sketch-host.js +0 -216
@@ -1,8 +1,9 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
+ import { itemAlpha, itemColor } from "../palette.js";
2
3
  import { syncTextResolution } from "../text-resolution.js";
3
4
  import { createLabel, positionShell } from "./base-card-renderer.js";
4
5
  import { drawFarPlate } from "./far-plate.js";
5
- import { itemColor } from "../palette.js";
6
+ import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
6
7
  import { Container, Graphics } from "pixi.js";
7
8
  //#region src/board/render/renderers/frame-card-renderer.ts
8
9
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -16,13 +17,15 @@ function sync(container, item, context) {
16
17
  const selected = context.selectedIds.has(item.id);
17
18
  const hovered = context.hoveredId === item.id;
18
19
  const fill = itemColor(context, style.fill ?? style.stroke, "neutral", "fill");
19
- const fillAlpha = style.fillOpacity ?? (style.fill ? 1 : DEFAULT_WASH);
20
+ const fillAlpha = (style.fillOpacity ?? (style.fill ? 1 : DEFAULT_WASH)) * itemAlpha(context, style.fill ?? style.stroke);
20
21
  const baseStroke = itemColor(context, style.stroke, "neutral");
21
- const stroke = selected ? context.palette.brand : baseStroke;
22
- const alpha = selected ? 1 : hovered ? .85 : .55;
22
+ const stroke = selected ? context.palette.brand : hovered && style.stroke ? context.palette.muted : baseStroke;
23
+ const alpha = (selected || style.stroke ? 1 : hovered ? .85 : .55) * itemAlpha(context, style.stroke);
23
24
  const radius = style.radius ?? 4;
24
25
  const strokeWidth = style.strokeWidth ?? 1.5;
26
+ const decorated = props.decoration !== "none";
25
27
  const visualSig = [
28
+ props.decoration,
26
29
  frame.width,
27
30
  frame.height,
28
31
  selected,
@@ -30,23 +33,36 @@ function sync(container, item, context) {
30
33
  fill,
31
34
  fillAlpha,
32
35
  stroke,
36
+ alpha,
33
37
  radius,
34
- strokeWidth
38
+ strokeWidth,
39
+ style.dash,
40
+ style.trim
35
41
  ].join("|");
36
42
  if (visualSig !== parts.visualSig) {
37
43
  parts.visualSig = visualSig;
38
44
  parts.box.clear();
39
- parts.box.roundRect(0, 0, frame.width, frame.height, radius).fill({
45
+ if (decorated) parts.box.roundRect(0, 0, frame.width, frame.height, radius).fill({
40
46
  color: fill,
41
47
  alpha: fillAlpha
42
48
  });
43
- if (strokeWidth > 0 || selected) parts.box.roundRect(0, 0, frame.width, frame.height, radius).stroke({
44
- color: stroke,
45
- width: selected ? Math.max(2, strokeWidth) : strokeWidth,
46
- alpha
47
- });
49
+ const width = selected ? Math.max(2, strokeWidth) : strokeWidth;
50
+ const pattern = decorated ? dashPattern(style.dash, width) : null;
51
+ const trim = decorated ? style.trim ?? 1 : 1;
52
+ if ((decorated && strokeWidth > 0 || selected || hovered) && trim > 0) {
53
+ if (pattern || trim < 1) tracePolyline(parts.box, trimPolyline(roundRectPoints(frame.width, frame.height, radius), trim), pattern);
54
+ else parts.box.roundRect(0, 0, frame.width, frame.height, radius);
55
+ parts.box.stroke({
56
+ color: stroke,
57
+ width,
58
+ alpha,
59
+ cap: pattern || trim < 1 ? "round" : "butt",
60
+ join: "round"
61
+ });
62
+ }
48
63
  }
49
- const title = locked ? `🔒 ${props.label || "Frame"}` : props.label || "Frame";
64
+ parts.label.visible = decorated;
65
+ const title = locked ? `🔒 ${props.label}`.trim() : props.label;
50
66
  const labelColor = style.stroke ? baseStroke : context.palette.muted;
51
67
  const textSig = [title, labelColor].join("|");
52
68
  if (textSig !== parts.textSig) {
@@ -84,10 +100,10 @@ const frameCardRenderer = {
84
100
  if (item.type === "frame") sync(container, item, context);
85
101
  },
86
102
  renderFar: (graphics, item, context) => {
87
- if (item.type !== "frame") return;
103
+ if (item.type !== "frame" || item.props.decoration === "none") return;
88
104
  drawFarPlate(graphics, item.frame, {
89
105
  fill: itemColor(context, item.style.fill ?? item.style.stroke, "neutral", "fill"),
90
- fillAlpha: Math.max(.07, item.style.fillOpacity ?? (item.style.fill ? 1 : 0))
106
+ fillAlpha: Math.max(.07, item.style.fillOpacity ?? (item.style.fill ? 1 : 0)) * itemAlpha(context, item.style.fill ?? item.style.stroke)
91
107
  });
92
108
  },
93
109
  destroy: (container) => {
@@ -1,6 +1,6 @@
1
1
  import { positionShell } from "./base-card-renderer.js";
2
2
  import { drawFarPlate } from "./far-plate.js";
3
- import { Container, Graphics, Sprite, Texture } from "pixi.js";
3
+ import { Container, Graphics, Rectangle, Sprite, Texture } from "pixi.js";
4
4
  //#region src/board/render/renderers/image-card-renderer.ts
5
5
  const partsByContainer = /* @__PURE__ */ new WeakMap();
6
6
  const RADIUS = 2;
@@ -15,6 +15,39 @@ function layoutContain(sprite, width, height) {
15
15
  sprite.x = (width - sprite.width) / 2;
16
16
  sprite.y = (height - sprite.height) / 2;
17
17
  }
18
+ function cropRect(crop) {
19
+ if (!crop) return null;
20
+ const w = Math.min(crop.w, 1 - crop.x);
21
+ const h = Math.min(crop.h, 1 - crop.y);
22
+ return w > 0 && h > 0 ? {
23
+ x: crop.x,
24
+ y: crop.y,
25
+ w,
26
+ h
27
+ } : null;
28
+ }
29
+ function showTexture(parts, texture, crop) {
30
+ const rect = texture ? cropRect(crop) : null;
31
+ const key = rect ? `${rect.x}|${rect.y}|${rect.w}|${rect.h}` : "";
32
+ const previous = parts.cropped;
33
+ const reusable = previous && parts.cropSource === texture && parts.cropKey === key;
34
+ if (texture && rect && !reusable) {
35
+ const { frame } = texture;
36
+ const width = Math.max(1, frame.width * rect.w);
37
+ const height = Math.max(1, frame.height * rect.h);
38
+ const x = frame.x + Math.min(frame.width * rect.x, frame.width - width);
39
+ const y = frame.y + Math.min(frame.height * rect.y, frame.height - height);
40
+ parts.cropped = new Texture({
41
+ source: texture.source,
42
+ frame: new Rectangle(x, y, width, height)
43
+ });
44
+ } else if (!rect) parts.cropped = null;
45
+ parts.cropSource = rect ? texture : null;
46
+ parts.cropKey = key;
47
+ const display = parts.cropped ?? texture ?? Texture.EMPTY;
48
+ if (parts.sprite.texture !== display) parts.sprite.texture = display;
49
+ if (previous && previous !== parts.cropped) previous.destroy(false);
50
+ }
18
51
  function sync(container, item, context) {
19
52
  const parts = partsByContainer.get(container);
20
53
  if (!parts) return;
@@ -33,12 +66,13 @@ function sync(container, item, context) {
33
66
  selected,
34
67
  hovered,
35
68
  texId,
69
+ JSON.stringify(item.props.crop ?? null),
36
70
  failed,
37
71
  context.colorScheme
38
72
  ].join("|");
39
73
  if (sig === parts.sig) return;
40
74
  parts.sig = sig;
41
- if (texture && parts.sprite.texture !== texture) parts.sprite.texture = texture;
75
+ showTexture(parts, texture, item.props.crop);
42
76
  parts.sprite.visible = Boolean(texture);
43
77
  if (texture) layoutContain(parts.sprite, width, height);
44
78
  parts.placeholder.clear();
@@ -98,7 +132,10 @@ const imageCardRenderer = {
98
132
  root,
99
133
  sprite,
100
134
  placeholder,
101
- sig: ""
135
+ sig: "",
136
+ cropped: null,
137
+ cropSource: null,
138
+ cropKey: ""
102
139
  });
103
140
  if (item.type === "image") sync(root, item, context);
104
141
  return root;
@@ -118,7 +155,9 @@ const imageCardRenderer = {
118
155
  });
119
156
  },
120
157
  destroy: (container) => {
121
- partsByContainer.get(container)?.root.destroy({ children: true });
158
+ const parts = partsByContainer.get(container);
159
+ parts?.root.destroy({ children: true });
160
+ parts?.cropped?.destroy(false);
122
161
  partsByContainer.delete(container);
123
162
  }
124
163
  };
@@ -1,10 +1,10 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
2
  import { worldPoint } from "../../model/geometry.js";
3
+ import { itemAlpha, itemColor } from "../palette.js";
3
4
  import { syncTextResolution, syncTextWrapWidth, textResolutionForZoom } from "../text-resolution.js";
4
5
  import { positionShell } from "./base-card-renderer.js";
5
6
  import { drawFarPlate } from "./far-plate.js";
6
- import { itemColor } from "../palette.js";
7
- import { dashPattern, tracePolyline, trimPolyline } from "../stroke.js";
7
+ import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
8
8
  import { Container, Graphics, GraphicsPath, Matrix, Text } from "pixi.js";
9
9
  //#region src/board/render/renderers/shape-card-renderer.ts
10
10
  const DEFAULT_ROUNDED_RADIUS = 8;
@@ -86,27 +86,7 @@ function outlinePoints(geometry, width, height, radius) {
86
86
  p(0, height),
87
87
  p(width / 2, 0)
88
88
  ];
89
- default: {
90
- const r = Math.min(radius, width / 2, height / 2);
91
- if (r <= 0) return [
92
- p(0, 0),
93
- p(width, 0),
94
- p(width, height),
95
- p(0, height),
96
- p(0, 0)
97
- ];
98
- const corner = (cx, cy, from) => Array.from({ length: 9 }, (_, index) => {
99
- const angle = from + index / 8 * (Math.PI / 2);
100
- return p(cx + Math.cos(angle) * r, cy + Math.sin(angle) * r);
101
- });
102
- return [
103
- ...corner(width - r, r, -Math.PI / 2),
104
- ...corner(width - r, height - r, 0),
105
- ...corner(r, height - r, Math.PI / 2),
106
- ...corner(r, r, Math.PI),
107
- p(width - r, 0)
108
- ];
109
- }
89
+ default: return roundRectPoints(width, height, radius);
110
90
  }
111
91
  }
112
92
  function sync(container, item, context) {
@@ -119,7 +99,8 @@ function sync(container, item, context) {
119
99
  const hovered = context.hoveredId === item.id;
120
100
  const stroke = itemColor(context, style.stroke ?? style.fill, "brand");
121
101
  const fill = itemColor(context, style.fill ?? style.stroke, "brand", "fill");
122
- const fillOpacity = style.fillOpacity ?? (style.fill ? 1 : 0);
102
+ const fillOpacity = (style.fillOpacity ?? (style.fill ? 1 : 0)) * itemAlpha(context, style.fill ?? style.stroke);
103
+ const strokeAlpha = itemAlpha(context, style.stroke ?? style.fill);
123
104
  const strokeWidth = style.strokeWidth ?? 1.75;
124
105
  syncTextResolution(parts.label, parts, context.zoom);
125
106
  const visualSig = [
@@ -132,6 +113,7 @@ function sync(container, item, context) {
132
113
  fill,
133
114
  fillOpacity,
134
115
  stroke,
116
+ strokeAlpha,
135
117
  strokeWidth,
136
118
  style.dash,
137
119
  style.radius,
@@ -159,12 +141,13 @@ function sync(container, item, context) {
159
141
  parts.shape.stroke({
160
142
  color: hovered && !selected ? context.palette.muted : stroke,
161
143
  width: lineWidth,
162
- alpha: selected ? 1 : hovered ? .95 : .85,
144
+ alpha: (selected ? 1 : hovered ? .95 : .85) * strokeAlpha,
163
145
  cap: partial || pattern ? "round" : "butt",
164
146
  join: "round"
165
147
  });
166
148
  }
167
149
  }
150
+ parts.label.alpha = itemAlpha(context, style.stroke);
168
151
  const label = itemColor(context, style.stroke, "neutral", "label");
169
152
  const textSig = [
170
153
  props.text,
@@ -226,9 +209,9 @@ const shapeCardRenderer = {
226
209
  if (item.type !== "shape") return;
227
210
  drawFarPlate(graphics, item.frame, {
228
211
  fill: itemColor(context, item.style.fill ?? item.style.stroke, "brand", "fill"),
229
- fillAlpha: Math.max(.18, item.style.fillOpacity ?? 0),
212
+ fillAlpha: Math.max(.18, item.style.fillOpacity ?? 0) * itemAlpha(context, item.style.fill ?? item.style.stroke),
230
213
  accent: itemColor(context, item.style.stroke, "brand"),
231
- accentAlpha: .9
214
+ accentAlpha: .9 * itemAlpha(context, item.style.stroke)
232
215
  });
233
216
  },
234
217
  destroy: (container) => {
@@ -1,10 +1,10 @@
1
1
  import { layoutBoardText } from "../../../protocol/dist/board-layout.js";
2
2
  import { boardFontFamily } from "../../model/text-metrics.js";
3
+ import { itemAlpha, itemColor } from "../palette.js";
4
+ import { BOARD_FONT_STACKS } from "../text-measurement.js";
3
5
  import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
4
6
  import { positionShell } from "./base-card-renderer.js";
5
7
  import { drawFarPlate } from "./far-plate.js";
6
- import { itemColor } from "../palette.js";
7
- import { BOARD_FONT_STACKS } from "../text-measurement.js";
8
8
  import { Container, Graphics, Text } from "pixi.js";
9
9
  //#region src/board/render/renderers/text-card-renderer.ts
10
10
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -33,6 +33,8 @@ function sync(container, item, context) {
33
33
  parts.body.anchor.set(anchorX, 0);
34
34
  parts.body.position.set(item.frame.width * anchorX, 0);
35
35
  syncTextResolution(parts.body, parts, context.zoom * Math.max(1, previewScale));
36
+ parts.body.alpha = itemAlpha(context, item.style.fill);
37
+ parts.caret.alpha = parts.body.alpha;
36
38
  const ink = itemColor(context, item.style.fill, "neutral");
37
39
  const text = revealed(props.text, props.reveal);
38
40
  const contentSig = [
@@ -93,7 +95,7 @@ const textCardRenderer = {
93
95
  if (item.type !== "text") return;
94
96
  drawFarPlate(graphics, item.frame, {
95
97
  fill: itemColor(context, item.style.fill, "neutral"),
96
- fillAlpha: .35
98
+ fillAlpha: .35 * itemAlpha(context, item.style.fill)
97
99
  });
98
100
  },
99
101
  destroy: (container) => {
@@ -54,6 +54,7 @@ const unknownCardRenderer = {
54
54
  create: (item, context) => {
55
55
  const root = new Container();
56
56
  const box = new Graphics();
57
+ box.context.batchMode = "batch";
57
58
  const resolution = textResolutionForZoom(context.zoom);
58
59
  const label = new Text({
59
60
  text: "",
@@ -1,4 +1,5 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
+ import { getMediaResourceTitle } from "../../model/media.js";
2
3
  import { syncTextResolution, syncTextWrapWidth, textResolutionForZoom } from "../text-resolution.js";
3
4
  import { positionShell } from "./base-card-renderer.js";
4
5
  import { drawFarPlate } from "./far-plate.js";
@@ -23,7 +24,7 @@ function sync(container, item, context) {
23
24
  const selected = context.selectedIds.has(item.id);
24
25
  const hovered = context.hoveredId === item.id;
25
26
  const resizing = context.resizingIds.has(item.id);
26
- const title = item.props.snapshot?.title ?? item.props.src.split("/").pop() ?? "Video";
27
+ const title = item.props.snapshot?.title ?? getMediaResourceTitle(item.props.src);
27
28
  const key = context.assetKey(item);
28
29
  const texture = key ? context.getTexture(key) : null;
29
30
  const failed = Boolean(key && !texture && context.hasError(key));
@@ -0,0 +1,22 @@
1
+ import { BoardDocument } from "../../protocol/dist/board-model.js";
2
+ import { BoardFrame } from "../../protocol/dist/board-layout.js";
3
+ import "../../protocol/dist/index.js";
4
+ import { BoardScene } from "../model/scene.js";
5
+ import { BoardEvaluation } from "../model/animation.js";
6
+ import { BoardShapeColors } from "../model/palette.js";
7
+ import { BoardRenderPalette } from "./renderers/board-renderer-registry.js";
8
+ //#region src/board/render/sampling.d.ts
9
+ declare function createBoardRenderSampler(document: BoardDocument, animation: string | null, options?: {
10
+ colorScheme?: "dark" | "light";
11
+ colors?: BoardShapeColors;
12
+ palette?: Partial<BoardRenderPalette>;
13
+ }): {
14
+ frameAt(id: string, time: number): BoardFrame | undefined;
15
+ at(time: number): {
16
+ document: BoardDocument;
17
+ evaluation: BoardEvaluation;
18
+ scene: BoardScene;
19
+ };
20
+ };
21
+ //#endregion
22
+ export { createBoardRenderSampler };
@@ -0,0 +1,21 @@
1
+ import { createBoardAnimationSampler } from "../model/sampling.js";
2
+ import { buildFallbackShapeColors } from "../model/palette.js";
3
+ import { defaultBoardPalette, itemAlpha, itemColor } from "./palette.js";
4
+ //#region src/board/render/sampling.ts
5
+ function createBoardRenderSampler(document, animation, options = {}) {
6
+ const colorScheme = options.colorScheme ?? "dark";
7
+ const context = {
8
+ colorScheme,
9
+ colors: options.colors ?? buildFallbackShapeColors(colorScheme),
10
+ palette: {
11
+ ...defaultBoardPalette(colorScheme),
12
+ ...options.palette
13
+ }
14
+ };
15
+ return createBoardAnimationSampler(document, animation, { resolveColor: (color) => ({
16
+ color: itemColor(context, color, "brand", "fill"),
17
+ alpha: itemAlpha(context, color)
18
+ }) });
19
+ }
20
+ //#endregion
21
+ export { createBoardRenderSampler };
@@ -0,0 +1,11 @@
1
+ import { BoardDocument } from "../../protocol/dist/board-model.js";
2
+ import "../../protocol/dist/index.js";
3
+ import { BoardScriptSnapshot } from "../model/script.js";
4
+ import { BoardScriptHost } from "./renderers/board-renderer-registry.js";
5
+ //#region src/board/render/script-export.d.ts
6
+ declare function prepareBrowserBoardScripts(document: BoardDocument, time: number, readModule: (path: string) => Promise<string>, snapshots?: Readonly<Record<string, BoardScriptSnapshot>>): Promise<{
7
+ host: BoardScriptHost;
8
+ dispose(): void;
9
+ }>;
10
+ //#endregion
11
+ export { prepareBrowserBoardScripts };
@@ -0,0 +1,86 @@
1
+ import { composeBoardScriptItems } from "../model/script-scene.js";
2
+ import { buildBoardScene } from "../model/scene.js";
3
+ import { createBoardScriptHost } from "./script-host.js";
4
+ //#region src/board/render/script-export.ts
5
+ async function prepareBrowserBoardScripts(document, time, readModule, snapshots) {
6
+ if (!Number.isFinite(time) || time < 0 || time > 6e5) throw new Error("Script export time must be between 0 and 600000 ms.");
7
+ const element = globalThis.document.createElement("div");
8
+ element.style.cssText = "position:fixed;left:-10000px;top:0;width:1px;height:1px;visibility:hidden;pointer-events:none";
9
+ globalThis.document.body.appendChild(element);
10
+ const outputs = /* @__PURE__ */ new Map();
11
+ const scene = buildBoardScene(document);
12
+ const ids = Object.entries(document.items).filter(([, item]) => item.script?.enabled).map(([id]) => id);
13
+ let schedule = () => {};
14
+ const host = createBoardScriptHost({
15
+ host: element,
16
+ readModule,
17
+ export: true,
18
+ snapshots,
19
+ timeoutMs: 15e3,
20
+ onFrame: () => schedule(),
21
+ onOutput(id, output) {
22
+ if (output) outputs.set(id, output);
23
+ else outputs.delete(id);
24
+ }
25
+ });
26
+ const dispose = () => {
27
+ schedule = () => {};
28
+ host.destroy();
29
+ element.remove();
30
+ };
31
+ try {
32
+ await new Promise((resolve, reject) => {
33
+ const timer = setTimeout(() => reject(/* @__PURE__ */ new Error("Script export preparation timed out.")), 2e4);
34
+ let queued = false;
35
+ const sync = () => {
36
+ queued = false;
37
+ try {
38
+ host.sync(document, scene, {
39
+ x: 0,
40
+ y: 0,
41
+ zoom: 1
42
+ }, time, false);
43
+ for (const id of ids) {
44
+ const error = host.error(id);
45
+ if (error) throw new Error(error);
46
+ }
47
+ if (ids.every((id) => host.ready?.(id))) {
48
+ clearTimeout(timer);
49
+ schedule = () => {};
50
+ resolve();
51
+ }
52
+ } catch (error) {
53
+ clearTimeout(timer);
54
+ schedule = () => {};
55
+ reject(error);
56
+ }
57
+ };
58
+ schedule = () => {
59
+ if (!queued) {
60
+ queued = true;
61
+ queueMicrotask(sync);
62
+ }
63
+ };
64
+ schedule();
65
+ });
66
+ return {
67
+ host: {
68
+ ...host,
69
+ document(input) {
70
+ const items = { ...input.items };
71
+ for (const [id, item] of composeBoardScriptItems(input, buildBoardScene(input), outputs)) items[id] = item;
72
+ return {
73
+ ...input,
74
+ items
75
+ };
76
+ }
77
+ },
78
+ dispose
79
+ };
80
+ } catch (error) {
81
+ dispose();
82
+ throw error;
83
+ }
84
+ }
85
+ //#endregion
86
+ export { prepareBrowserBoardScripts };
@@ -0,0 +1,31 @@
1
+ import { BoardDocument } from "../../protocol/dist/board-model.js";
2
+ import "../../protocol/dist/index.js";
3
+ import { BoardScriptEvent, BoardScriptOutput, BoardScriptSnapshot } from "../model/script.js";
4
+ import { BoardViewport } from "../model/geometry.js";
5
+ import { BoardScene } from "../model/scene.js";
6
+ import { BoardScriptHost } from "./renderers/board-renderer-registry.js";
7
+ //#region src/board/render/script-host.d.ts
8
+ type BoardScriptHostOptions = {
9
+ readModule(path: string): Promise<string>;
10
+ host: HTMLElement;
11
+ onFrame(): void;
12
+ onOutput(id: string, output: BoardScriptOutput | null): void;
13
+ capabilities?: Record<string, (id: string, input: unknown) => Promise<unknown>>;
14
+ timeoutMs?: number;
15
+ export?: boolean;
16
+ snapshots?: Readonly<Record<string, BoardScriptSnapshot>>;
17
+ };
18
+ type BrowserBoardScriptHost = BoardScriptHost & {
19
+ sync(document: BoardDocument, scene: BoardScene, camera: BoardViewport, time: number, interactive: boolean, visibleIds?: ReadonlySet<string> | null): void;
20
+ event(id: string, event: BoardScriptEvent): void;
21
+ output(id: string): BoardScriptOutput | null;
22
+ snapshot(id: string): Promise<BoardScriptSnapshot | null>;
23
+ restore(id: string, snapshot: BoardScriptSnapshot): void;
24
+ fault(id: string, message: string): void;
25
+ readonly animated: boolean;
26
+ invalidate(src?: string): void;
27
+ destroy(): void;
28
+ };
29
+ declare function createBoardScriptHost(options: BoardScriptHostOptions): BrowserBoardScriptHost;
30
+ //#endregion
31
+ export { BoardScriptHostOptions, BrowserBoardScriptHost, createBoardScriptHost };