@neta-art/cohub-cli 10.2.0 → 10.4.1

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 (35) hide show
  1. package/README.md +40 -2
  2. package/dist/board-export-resources.d.ts +8 -0
  3. package/dist/board-export-resources.js +39 -0
  4. package/dist/board-export.d.ts +6 -0
  5. package/dist/board-export.js +58 -8
  6. package/dist/board-kernel.d.ts +1 -1
  7. package/dist/board-kernel.js +211 -141
  8. package/dist/commands/boards/authoring.js +14 -16
  9. package/dist/commands/boards.d.ts +1 -0
  10. package/dist/commands/boards.js +44 -13
  11. package/dist/commands/rooms-connect.d.ts +17 -0
  12. package/dist/commands/rooms-connect.js +140 -0
  13. package/dist/commands/rooms-rtc-binary.d.ts +2 -0
  14. package/dist/commands/rooms-rtc-binary.js +107 -0
  15. package/dist/commands/rooms-rtc.d.ts +21 -0
  16. package/dist/commands/rooms-rtc.js +171 -0
  17. package/dist/commands/rooms.js +45 -0
  18. package/dist/commands/sandboxd-binary.d.ts +3 -3
  19. package/dist/commands/sandboxd-binary.js +27 -17
  20. package/dist/runtime/harness.d.ts +5 -0
  21. package/dist/runtime/harness.js +29 -3
  22. package/dist/runtime/json-rpc.js +1 -1
  23. package/dist/runtime/launch.js +21 -6
  24. package/dist/runtime/native/attach.js +5 -2
  25. package/dist/runtime/native/codex-link.d.ts +2 -0
  26. package/dist/runtime/native/codex-link.js +3 -1
  27. package/dist/runtime/native/execution.js +3 -3
  28. package/dist/runtime/native/ingest.js +4 -4
  29. package/dist/runtime/native/paths.d.ts +2 -0
  30. package/dist/runtime/native/paths.js +7 -0
  31. package/dist/runtime/native/pi-extension.js +12 -2
  32. package/dist/runtime/native/pi-link.js +21 -4
  33. package/dist/runtime/native/version.js +4 -1
  34. package/dist/runtime/supervisor.js +33 -4
  35. package/package.json +3 -2
@@ -1,4 +1,4 @@
1
- import { $ as groupD8, A as bgr2rgb, B as Bounds, C as Sprite, D as TexturePool, E as RenderGroup, G as TextureStyle, H as Texture, I as BigPool, J as deprecation, K as definedProps, L as GlobalResourceRegistry, M as multiplyHexColors, N as getGlobalBounds, O as InstructionSet, P as FilterEffect, Q as Rectangle, R as warn, S as DOMAdapter, T as Container, U as TextureMatrix, V as updateQuadBounds, W as TextureSource, X as v8_3_4, Y as v8_0_0, Z as uid$1, _ as Cache, a as Geometry, at as extensions, b as ImageSource, c as Filter, d as RendererType, et as Matrix, f as BindGroup, g as getTestContext, h as GlProgram, i as canUseNewCanvasBlendModes, it as ExtensionType, j as multiplyColors, k as getLocalBounds, l as State, m as GpuProgram, n as CanvasPool, nt as eventemitter3_default, o as Buffer$1, p as UniformGroup, q as nextPow2, r as canvasUtils, rt as ObservablePoint, s as BufferUsage, t as getPo2TextureFromSource, tt as Point, u as Shader, v as Ticker, w as ViewContainer, x as CanvasSource, z as Color } from "./board-kernel/getPo2TextureFromSource.js";
1
+ import { $ as groupD8, A as bgr2rgb, B as Bounds, C as Sprite, D as TexturePool, E as RenderGroup, G as TextureStyle, H as Texture, I as BigPool, J as deprecation, K as definedProps, L as GlobalResourceRegistry, M as multiplyHexColors, N as getGlobalBounds, O as InstructionSet, P as FilterEffect, Q as Rectangle, R as warn, S as DOMAdapter, T as Container, U as TextureMatrix, V as updateQuadBounds, W as TextureSource, X as v8_3_4, Y as v8_0_0, Z as uid$1, _ as Cache, a as Geometry, at as extensions, b as ImageSource, c as Filter, d as RendererType, et as Matrix, f as BindGroup, g as getTestContext, h as GlProgram, i as canUseNewCanvasBlendModes, it as ExtensionType, j as multiplyColors, k as getLocalBounds, l as State, m as GpuProgram, n as CanvasPool, nt as eventemitter3_default, o as Buffer, p as UniformGroup, q as nextPow2, r as canvasUtils, rt as ObservablePoint, s as BufferUsage, t as getPo2TextureFromSource, tt as Point, u as Shader, v as Ticker, w as ViewContainer, x as CanvasSource, z as Color } from "./board-kernel/getPo2TextureFromSource.js";
2
2
  import { Worker } from "node:worker_threads";
3
3
  import { fileURLToPath } from "node:url";
4
4
  //#region ../../node_modules/.pnpm/zod@4.6.5/node_modules/zod/v4/core/util.js
@@ -5772,10 +5772,10 @@ const colorString$1 = string().max(120).refine(isBoardColorString$1, "expected a
5772
5772
  const BoardColorSchema$1 = union([colorString$1, object({
5773
5773
  light: colorString$1,
5774
5774
  dark: colorString$1
5775
- }).strict()]);
5775
+ }).strict()]).describe("Palette token (follows the theme), CSS color, or { light, dark }.");
5776
5776
  const BoardStyleSchema$1 = object({
5777
5777
  fill: BoardColorSchema$1.optional(),
5778
- fillOpacity: ratio$1.optional(),
5778
+ fillOpacity: ratio$1.optional().describe("Fill opacity, independent of the item's opacity."),
5779
5779
  stroke: BoardColorSchema$1.optional(),
5780
5780
  strokeWidth: finite$1.min(0).max(64).optional(),
5781
5781
  dash: _enum([
@@ -5783,8 +5783,8 @@ const BoardStyleSchema$1 = object({
5783
5783
  "dashed",
5784
5784
  "dotted"
5785
5785
  ]).optional(),
5786
- radius: nonNegative$1.optional(),
5787
- trim: ratio$1.optional()
5786
+ radius: nonNegative$1.optional().describe("Corner radius in board units."),
5787
+ trim: ratio$1.optional().describe("Fraction of the outline drawn; animate 0 → 1 to draw it in.")
5788
5788
  }).strict();
5789
5789
  function isSafeBoardSourcePath$1(value) {
5790
5790
  return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !/^[a-z][a-z\d+.-]*:/i.test(value.trimStart()) && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
@@ -5800,18 +5800,18 @@ function parseBoardMediaSource$1(src) {
5800
5800
  value: src
5801
5801
  } : null;
5802
5802
  }
5803
- const BoardMediaSourceSchema$1 = string().max(4096).refine((src) => parseBoardMediaSource$1(src) !== null, "src must be a public HTTP(S) URL or relative Space file path").describe("Prefer a stable public HTTP(S) URL; relative Space file paths remain supported.");
5803
+ const BoardMediaSourceSchema$1 = string().max(4096).refine((src) => parseBoardMediaSource$1(src) !== null, "src must be a public HTTP(S) URL or relative Space file path").describe("Prefer a public HTTP(S) URL; a Space path relative to the workspace root also works.");
5804
5804
  const BoardScriptSchema$1 = object({
5805
- src: srcSchema$1.refine((path) => /\.m?js$/.test(path), "script src must be a .js or .mjs module"),
5806
- params: record(string(), json()).default({}),
5807
- targets: record(string().min(1).max(80), BoardIdSchema$1).default({}),
5808
- seed: string().min(1).max(160).optional(),
5805
+ src: srcSchema$1.refine((path) => /\.m?js$/.test(path), "script src must be a .js or .mjs module").describe("A .js or .mjs Space file."),
5806
+ params: record(string(), json()).default({}).describe("Values the module reads as params."),
5807
+ targets: record(string().min(1).max(80), BoardIdSchema$1).default({}).describe("Named items the module can reach, as ctx.targets."),
5808
+ seed: string().min(1).max(160).optional().describe("Seed for ctx.random(); defaults to the item id."),
5809
5809
  enabled: boolean().default(true)
5810
- }).strict();
5810
+ }).strict().describe("A JavaScript module attached to the item; `cohub boards schema script` lists its API.");
5811
5811
  const itemEnvelope$1 = {
5812
5812
  script: BoardScriptSchema$1.optional(),
5813
- parent: BoardIdSchema$1.optional(),
5814
- z: finite$1.optional(),
5813
+ parent: BoardIdSchema$1.optional().describe("A frame id; position becomes relative to the frame, and the item moves with it."),
5814
+ z: finite$1.optional().describe("Order among siblings; new items go on top."),
5815
5815
  position: BoardVec2Schema$1.default({
5816
5816
  x: 0,
5817
5817
  y: 0
@@ -5821,7 +5821,7 @@ const itemEnvelope$1 = {
5821
5821
  origin: BoardVec2Schema$1.default({
5822
5822
  x: .5,
5823
5823
  y: .5
5824
- }),
5824
+ }).describe("Rotation and scale origin as fractions of the size."),
5825
5825
  opacity: ratio$1.default(1),
5826
5826
  style: BoardStyleSchema$1.default({}),
5827
5827
  locked: boolean().optional(),
@@ -5854,8 +5854,8 @@ const BoardFrameItemSchema$1 = object({
5854
5854
  type: literal("frame"),
5855
5855
  props: object({
5856
5856
  label: string().max(280).default(""),
5857
- decoration: _enum(["frame", "none"]).optional(),
5858
- clip: boolean().default(true)
5857
+ decoration: _enum(["frame", "none"]).optional().describe("none hides the title, border and background: an invisible group whose tracks move every child."),
5858
+ clip: boolean().default(true).describe("Hide children outside the frame.")
5859
5859
  }).strict().prefault({})
5860
5860
  }).strict();
5861
5861
  const BoardTextItemSchema$1 = object({
@@ -5865,12 +5865,12 @@ const BoardTextItemSchema$1 = object({
5865
5865
  text: string().default(""),
5866
5866
  fontSize: fontSize$1.default(24),
5867
5867
  fontWeight: finite$1.min(100).max(900).default(500),
5868
- font: string().min(1).max(120).default("sans"),
5868
+ font: string().min(1).max(120).default("sans").describe("sans, serif, mono, or a font family name."),
5869
5869
  align: _enum(BOARD_TEXT_ALIGNS$1).default("left"),
5870
5870
  lineHeight: finite$1.min(.5).max(4).default(4 / 3),
5871
- width: finite$1.positive().optional(),
5872
- reveal: ratio$1.default(1),
5873
- caret: boolean().default(false)
5871
+ width: finite$1.positive().optional().describe("Wrap width; the height follows the text."),
5872
+ reveal: ratio$1.default(1).describe("Fraction of the text shown; animate 0 → 1 to type it."),
5873
+ caret: boolean().default(false).describe("Blink a caret while the text is being revealed.")
5874
5874
  }).strict().prefault({})
5875
5875
  }).strict();
5876
5876
  const BoardShapeItemSchema$1 = object({
@@ -5878,7 +5878,7 @@ const BoardShapeItemSchema$1 = object({
5878
5878
  type: literal("shape"),
5879
5879
  props: object({
5880
5880
  geometry: _enum(BOARD_SHAPE_GEOMETRIES$1).default("rectangle"),
5881
- path: string().min(1).max(2e5).optional(),
5881
+ path: string().min(1).max(2e5).optional().describe("SVG path data for path geometry, fitted to the item size."),
5882
5882
  text: string().default(""),
5883
5883
  fontSize: fontSize$1.default(20),
5884
5884
  align: _enum(BOARD_TEXT_ALIGNS$1).default("center")
@@ -5890,12 +5890,12 @@ const BoardShapeItemSchema$1 = object({
5890
5890
  const BoardDrawPointSchema$1 = object({
5891
5891
  x: finite$1,
5892
5892
  y: finite$1,
5893
- p: ratio$1.default(.5)
5893
+ p: ratio$1.default(.5).describe("Pen pressure.")
5894
5894
  }).strict();
5895
5895
  const BoardDrawItemSchema$1 = object({
5896
5896
  ...unsized$1,
5897
5897
  type: literal("draw"),
5898
- props: object({ points: array(BoardDrawPointSchema$1).min(1).max(1e5) }).strict()
5898
+ props: object({ points: array(BoardDrawPointSchema$1).min(1).max(1e5).describe("Stroke points relative to position; the size follows them.") }).strict()
5899
5899
  }).strict();
5900
5900
  const BoardArrowEndSchema$1 = union([BoardVec2Schema$1, object({
5901
5901
  item: BoardIdSchema$1,
@@ -5914,9 +5914,9 @@ const BoardArrowEndSchema$1 = union([BoardVec2Schema$1, object({
5914
5914
  x: ratio$1,
5915
5915
  y: ratio$1
5916
5916
  }).strict()
5917
- ]).default("auto"),
5917
+ ]).default("auto").describe("auto uses the side facing the other end; or a side with an offset, or a point as fractions of the item."),
5918
5918
  port: string().min(1).max(120).optional()
5919
- }).strict()]);
5919
+ }).strict()]).describe("A point relative to position, or { item } to follow that item.");
5920
5920
  const BOARD_ARROW_ROUTES$1 = [
5921
5921
  "straight",
5922
5922
  "curve",
@@ -5929,13 +5929,13 @@ const BoardArrowItemSchema$1 = object({
5929
5929
  start: BoardArrowEndSchema$1,
5930
5930
  end: BoardArrowEndSchema$1,
5931
5931
  route: _enum(BOARD_ARROW_ROUTES$1).default("straight"),
5932
- bend: finite$1.min(-.85).max(.85).default(0),
5933
- waypoints: array(BoardVec2Schema$1).max(64).default([]),
5932
+ bend: finite$1.min(-.85).max(.85).default(0).describe("Sideways curve as a fraction of the length; the sign picks the side."),
5933
+ waypoints: array(BoardVec2Schema$1).max(64).default([]).describe("Points the route passes through."),
5934
5934
  arrowStart: boolean().default(false),
5935
5935
  arrowEnd: boolean().default(true),
5936
5936
  label: string().max(280).default(""),
5937
5937
  fontSize: fontSize$1.default(14),
5938
- relation: string().max(64).regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/, "relation must be a lowercase slug").optional()
5938
+ relation: string().max(64).regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/, "relation must be a lowercase slug").optional().describe("What the arrow means, as a slug such as triggers or depends-on.")
5939
5939
  }).strict()
5940
5940
  }).strict();
5941
5941
  const fileProps$1 = {
@@ -5951,26 +5951,26 @@ const fileProps$1 = {
5951
5951
  excerpt: string().optional(),
5952
5952
  coverPath: string().optional(),
5953
5953
  coverUrl: string().optional()
5954
- }).strict().optional()
5954
+ }).strict().describe("Cached facts for an instant preview; filled in when the media loads.").optional()
5955
5955
  };
5956
5956
  const BoardMediaOriginSchema$1 = object({
5957
5957
  taskRunId: string().min(1).max(160),
5958
5958
  artifactId: string().min(1).max(240)
5959
- }).strict();
5959
+ }).strict().describe("The generation run and result this media came from.");
5960
5960
  const mediaProps$1 = {
5961
5961
  ...fileProps$1,
5962
5962
  origin: BoardMediaOriginSchema$1.optional()
5963
5963
  };
5964
5964
  const timedFileProps$1 = {
5965
5965
  ...mediaProps$1,
5966
- time: nonNegative$1.optional()
5966
+ time: nonNegative$1.optional().describe("Playback position in milliseconds; animate props.time to play it on a timeline.")
5967
5967
  };
5968
5968
  const BoardCropSchema$1 = object({
5969
5969
  x: ratio$1,
5970
5970
  y: ratio$1,
5971
5971
  w: ratio$1,
5972
5972
  h: ratio$1
5973
- }).strict();
5973
+ }).strict().describe("Region shown, as fractions of the image. Without it the whole image fits inside size.");
5974
5974
  const BoardImageItemSchema$1 = object({
5975
5975
  ...boxed$1(640, 360),
5976
5976
  type: literal("image"),
@@ -6050,8 +6050,8 @@ const BoardTaskItemSchema$1 = object({
6050
6050
  ...boxed$1(420, 240),
6051
6051
  type: literal("task"),
6052
6052
  props: object({
6053
- taskRunId: string().min(1).max(160),
6054
- snapshot: BoardTaskSnapshotSchema$1.describe("Maintained by the server for tasks in the Board's Space; writers may omit it.")
6053
+ taskRunId: string().min(1).max(160).describe("The generation run shown; the server keeps the card's status and results up to date."),
6054
+ snapshot: BoardTaskSnapshotSchema$1.describe("Maintained by the server; omit it.")
6055
6055
  }).strict()
6056
6056
  }).strict();
6057
6057
  const BOARD_EFFECT_KINDS$1 = [
@@ -6074,8 +6074,8 @@ const BoardEffectItemSchema$1 = object({
6074
6074
  spread: finite$1.min(0).max(360).default(360),
6075
6075
  gravity: finite$1.default(0),
6076
6076
  intensity: nonNegative$1.max(10).default(1),
6077
- follow: BoardIdSchema$1.optional(),
6078
- seed: string().min(1).max(160).optional()
6077
+ follow: BoardIdSchema$1.optional().describe("trail: the item to follow."),
6078
+ seed: string().min(1).max(160).optional().describe("Makes procedural motion repeatable.")
6079
6079
  }).strict()
6080
6080
  }).strict();
6081
6081
  const BoardCustomItemSchema$1 = object({
@@ -6177,21 +6177,21 @@ function isBoardEase$1(value) {
6177
6177
  }
6178
6178
  const BoardEaseSchema$1 = string().max(120).refine(isBoardEase$1, "expected a CSS easing: linear, ease-in-out, cubic-bezier(…) or steps(…)");
6179
6179
  const BoardKeyframeSchema$1 = object({
6180
- at: nonNegative$1,
6180
+ at: nonNegative$1.describe("Time in milliseconds."),
6181
6181
  value: unknown(),
6182
- ease: BoardEaseSchema$1.optional()
6182
+ ease: BoardEaseSchema$1.optional().describe("Easing into this keyframe: a CSS keyword, cubic-bezier() or steps(); defaults to linear.")
6183
6183
  }).strict();
6184
6184
  const BoardTrackSchema$1 = object({
6185
- target: string().min(1).max(160),
6186
- property: string().min(1).max(200),
6187
- keyframes: array(BoardKeyframeSchema$1).min(1).max(1e5),
6188
- composite: _enum(["replace", "add"]).default("replace"),
6185
+ target: string().min(1).max(160).describe("An item id, camera, or another animation id (its time property nests that timeline)."),
6186
+ property: string().min(1).max(200).describe("JSON path in the target, e.g. position, opacity, style.fill, props.text."),
6187
+ keyframes: array(BoardKeyframeSchema$1).min(1).max(1e5).describe("Ordered by at. The value holds before the first and after the last keyframe."),
6188
+ composite: _enum(["replace", "add"]).default("replace").describe("replace: the latest-started track wins. add: positions and rotations add; scale and opacity multiply."),
6189
6189
  interpolation: _enum([
6190
6190
  "auto",
6191
6191
  "step",
6192
6192
  "spline"
6193
- ]).default("auto"),
6194
- orient: boolean().optional()
6193
+ ]).default("auto").describe("auto blends numbers, vectors and colors and steps other values; spline smooths position paths."),
6194
+ orient: boolean().optional().describe("Rotate the item along its path.")
6195
6195
  }).strict().superRefine((track, context) => {
6196
6196
  for (let index = 1; index < track.keyframes.length; index += 1) if ((track.keyframes[index]?.at ?? 0) < (track.keyframes[index - 1]?.at ?? 0)) context.addIssue({
6197
6197
  code: "custom",
@@ -6206,20 +6206,20 @@ const BoardTrackSchema$1 = object({
6206
6206
  const BoardMarkerSchema$1 = object({
6207
6207
  at: nonNegative$1,
6208
6208
  label: string().max(280).optional(),
6209
- pause: boolean().optional()
6209
+ pause: boolean().optional().describe("Hold shared playback here until next.")
6210
6210
  }).strict();
6211
6211
  const BoardAnimationHeaderSchema$1 = object({
6212
6212
  name: string().max(280).optional(),
6213
- duration: finite$1.positive().max(864e5),
6213
+ duration: finite$1.positive().max(864e5).describe("Milliseconds; a new animation without it ends at its last keyframe."),
6214
6214
  play: _enum([
6215
6215
  "manual",
6216
6216
  "auto",
6217
6217
  "always"
6218
- ]).default("manual"),
6218
+ ]).default("manual").describe("manual: only shared playback starts it. auto: plays when the Board opens. always: loops continuously."),
6219
6219
  delay: nonNegative$1.default(0),
6220
6220
  loop: boolean().default(false),
6221
- end: _enum(["hold", "reset"]).default("hold"),
6222
- markers: array(BoardMarkerSchema$1).max(1e4).default([])
6221
+ end: _enum(["hold", "reset"]).default("hold").describe("hold keeps the last frame; reset returns to the start."),
6222
+ markers: array(BoardMarkerSchema$1).max(1e4).default([]).describe("Labeled points on this timeline; a nested animation's markers do not carry over.")
6223
6223
  }).strict();
6224
6224
  BoardAnimationHeaderSchema$1.extend({ tracks: record(BoardKeySchema$1, BoardTrackSchema$1).default({}) }).strict();
6225
6225
  const BOARD_ENTER_PRESETS$1 = [
@@ -6237,7 +6237,7 @@ const BoardBackgroundSchema$1 = object({
6237
6237
  "image"
6238
6238
  ]).default("dots"),
6239
6239
  color: BoardColorSchema$1.optional(),
6240
- imageUrl: string().url().max(4096).optional(),
6240
+ imageUrl: string().url().max(4096).optional().describe("Background image for kind image."),
6241
6241
  fit: _enum([
6242
6242
  "cover",
6243
6243
  "contain",
@@ -6249,13 +6249,13 @@ const BoardSettingsSchema$1 = object({
6249
6249
  title: string().min(1).max(255).optional(),
6250
6250
  background: BoardBackgroundSchema$1.default({ kind: "dots" }),
6251
6251
  grid: object({
6252
- visible: boolean().default(false),
6252
+ visible: boolean().default(false).describe("Show the grid overlay."),
6253
6253
  size: finite$1.min(4).max(512).default(24)
6254
6254
  }).strict().optional(),
6255
6255
  enter: object({
6256
6256
  preset: _enum(BOARD_ENTER_PRESETS$1),
6257
6257
  duration: finite$1.positive().max(1e4).optional()
6258
- }).strict().optional()
6258
+ }).strict().optional().describe("Motion for items as they are added.")
6259
6259
  }).strict();
6260
6260
  const patchObject$1 = record(string(), unknown());
6261
6261
  const BoardPatchSchema$1 = object({
@@ -12305,13 +12305,13 @@ const placeHolderBufferData = /* @__PURE__ */ new Float32Array(1);
12305
12305
  const placeHolderIndexData = /* @__PURE__ */ new Uint32Array(1);
12306
12306
  var BatchGeometry = class extends Geometry {
12307
12307
  constructor() {
12308
- const attributeBuffer = new Buffer$1({
12308
+ const attributeBuffer = new Buffer({
12309
12309
  data: placeHolderBufferData,
12310
12310
  label: "attribute-batch-buffer",
12311
12311
  usage: BufferUsage.VERTEX | BufferUsage.COPY_DST,
12312
12312
  shrinkToFit: false
12313
12313
  });
12314
- const indexBuffer = new Buffer$1({
12314
+ const indexBuffer = new Buffer({
12315
12315
  data: placeHolderIndexData,
12316
12316
  label: "index-batch-buffer",
12317
12317
  usage: BufferUsage.INDEX | BufferUsage.COPY_DST,
@@ -18288,19 +18288,19 @@ const _MeshGeometry = class _MeshGeometry extends Geometry {
18288
18288
  3
18289
18289
  ]);
18290
18290
  const shrinkToFit = options.shrinkBuffersToFit;
18291
- const positionBuffer = new Buffer$1({
18291
+ const positionBuffer = new Buffer({
18292
18292
  data: positions,
18293
18293
  label: "attribute-mesh-positions",
18294
18294
  shrinkToFit,
18295
18295
  usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
18296
18296
  });
18297
- const uvBuffer = new Buffer$1({
18297
+ const uvBuffer = new Buffer({
18298
18298
  data: uvs,
18299
18299
  label: "attribute-mesh-uvs",
18300
18300
  shrinkToFit,
18301
18301
  usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
18302
18302
  });
18303
- const indexBuffer = new Buffer$1({
18303
+ const indexBuffer = new Buffer({
18304
18304
  data: indices,
18305
18305
  label: "index-mesh-buffer",
18306
18306
  shrinkToFit,
@@ -27161,12 +27161,24 @@ function measureBoardText(props) {
27161
27161
  missingCharacters: characters
27162
27162
  };
27163
27163
  }
27164
- function inspectBoardScene(scene, limit = 100) {
27164
+ function inspectBoardItems(scene, ids) {
27165
+ return {
27166
+ diagnostics: inspectBoardScene(scene, 100, ids),
27167
+ items: scene.items.filter((item) => !ids || ids.has(item.id)).map((item) => ({
27168
+ id: item.id,
27169
+ bounds: item.frame,
27170
+ worldBounds: itemBounds(item.frame),
27171
+ opacity: scene.opacity(item.id),
27172
+ ...item.type === "text" ? { text: measureBoardText(item.props) } : {}
27173
+ }))
27174
+ };
27175
+ }
27176
+ function inspectBoardScene(scene, limit = 100, ids) {
27165
27177
  ensureBoardTextMeasurement();
27166
27178
  const diagnostics = [];
27167
27179
  const texts = scene.items.filter((item) => item.type === "text" && scene.opacity(item.id) > .01);
27168
27180
  const push = (entry) => {
27169
- if (diagnostics.length < limit) diagnostics.push(entry);
27181
+ if (diagnostics.length < limit && (!ids || ids.has(entry.item) || Boolean(entry.related && ids.has(entry.related)))) diagnostics.push(entry);
27170
27182
  };
27171
27183
  for (const item of scene.items) {
27172
27184
  if (diagnostics.length >= limit) break;
@@ -27178,7 +27190,7 @@ function inspectBoardScene(scene, limit = 100) {
27178
27190
  let glyphBudget = 256;
27179
27191
  for (const item of texts) {
27180
27192
  if (diagnostics.length >= limit || item.type !== "text") break;
27181
- if (glyphBudget-- > 0) {
27193
+ if ((!ids || ids.has(item.id)) && glyphBudget-- > 0) {
27182
27194
  const measured = measureBoardText(item.props);
27183
27195
  if (measured.missingCharacters) push({
27184
27196
  code: "glyph-unavailable",
@@ -27217,6 +27229,7 @@ function inspectBoardScene(scene, limit = 100) {
27217
27229
  const b = sorted[j];
27218
27230
  if (!b) continue;
27219
27231
  if (b.rect.x >= a.rect.x + a.rect.width) break;
27232
+ if (ids && !ids.has(a.item.id) && !ids.has(b.item.id)) continue;
27220
27233
  if (++comparisons > 1e4) return diagnostics;
27221
27234
  if (a.item.parent === b.item.parent && rectsIntersect(a.rect, b.rect)) push({
27222
27235
  code: "text-overlap",
@@ -30341,10 +30354,10 @@ const colorString = string().max(120).refine(isBoardColorString, "expected a pal
30341
30354
  const BoardColorSchema = union([colorString, object({
30342
30355
  light: colorString,
30343
30356
  dark: colorString
30344
- }).strict()]);
30357
+ }).strict()]).describe("Palette token (follows the theme), CSS color, or { light, dark }.");
30345
30358
  const BoardStyleSchema = object({
30346
30359
  fill: BoardColorSchema.optional(),
30347
- fillOpacity: ratio.optional(),
30360
+ fillOpacity: ratio.optional().describe("Fill opacity, independent of the item's opacity."),
30348
30361
  stroke: BoardColorSchema.optional(),
30349
30362
  strokeWidth: finite.min(0).max(64).optional(),
30350
30363
  dash: _enum([
@@ -30352,8 +30365,8 @@ const BoardStyleSchema = object({
30352
30365
  "dashed",
30353
30366
  "dotted"
30354
30367
  ]).optional(),
30355
- radius: nonNegative.optional(),
30356
- trim: ratio.optional()
30368
+ radius: nonNegative.optional().describe("Corner radius in board units."),
30369
+ trim: ratio.optional().describe("Fraction of the outline drawn; animate 0 → 1 to draw it in.")
30357
30370
  }).strict();
30358
30371
  function isSafeBoardSourcePath(value) {
30359
30372
  return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !/^[a-z][a-z\d+.-]*:/i.test(value.trimStart()) && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
@@ -30369,18 +30382,18 @@ function parseBoardMediaSource(src) {
30369
30382
  value: src
30370
30383
  } : null;
30371
30384
  }
30372
- const BoardMediaSourceSchema = string().max(4096).refine((src) => parseBoardMediaSource(src) !== null, "src must be a public HTTP(S) URL or relative Space file path").describe("Prefer a stable public HTTP(S) URL; relative Space file paths remain supported.");
30385
+ const BoardMediaSourceSchema = string().max(4096).refine((src) => parseBoardMediaSource(src) !== null, "src must be a public HTTP(S) URL or relative Space file path").describe("Prefer a public HTTP(S) URL; a Space path relative to the workspace root also works.");
30373
30386
  const BoardScriptSchema = object({
30374
- src: srcSchema.refine((path) => /\.m?js$/.test(path), "script src must be a .js or .mjs module"),
30375
- params: record(string(), json()).default({}),
30376
- targets: record(string().min(1).max(80), BoardIdSchema).default({}),
30377
- seed: string().min(1).max(160).optional(),
30387
+ src: srcSchema.refine((path) => /\.m?js$/.test(path), "script src must be a .js or .mjs module").describe("A .js or .mjs Space file."),
30388
+ params: record(string(), json()).default({}).describe("Values the module reads as params."),
30389
+ targets: record(string().min(1).max(80), BoardIdSchema).default({}).describe("Named items the module can reach, as ctx.targets."),
30390
+ seed: string().min(1).max(160).optional().describe("Seed for ctx.random(); defaults to the item id."),
30378
30391
  enabled: boolean().default(true)
30379
- }).strict();
30392
+ }).strict().describe("A JavaScript module attached to the item; `cohub boards schema script` lists its API.");
30380
30393
  const itemEnvelope = {
30381
30394
  script: BoardScriptSchema.optional(),
30382
- parent: BoardIdSchema.optional(),
30383
- z: finite.optional(),
30395
+ parent: BoardIdSchema.optional().describe("A frame id; position becomes relative to the frame, and the item moves with it."),
30396
+ z: finite.optional().describe("Order among siblings; new items go on top."),
30384
30397
  position: BoardVec2Schema.default({
30385
30398
  x: 0,
30386
30399
  y: 0
@@ -30390,7 +30403,7 @@ const itemEnvelope = {
30390
30403
  origin: BoardVec2Schema.default({
30391
30404
  x: .5,
30392
30405
  y: .5
30393
- }),
30406
+ }).describe("Rotation and scale origin as fractions of the size."),
30394
30407
  opacity: ratio.default(1),
30395
30408
  style: BoardStyleSchema.default({}),
30396
30409
  locked: boolean().optional(),
@@ -30423,8 +30436,8 @@ object({
30423
30436
  type: literal("frame"),
30424
30437
  props: object({
30425
30438
  label: string().max(280).default(""),
30426
- decoration: _enum(["frame", "none"]).optional(),
30427
- clip: boolean().default(true)
30439
+ decoration: _enum(["frame", "none"]).optional().describe("none hides the title, border and background: an invisible group whose tracks move every child."),
30440
+ clip: boolean().default(true).describe("Hide children outside the frame.")
30428
30441
  }).strict().prefault({})
30429
30442
  }).strict();
30430
30443
  object({
@@ -30434,12 +30447,12 @@ object({
30434
30447
  text: string().default(""),
30435
30448
  fontSize: fontSize.default(24),
30436
30449
  fontWeight: finite.min(100).max(900).default(500),
30437
- font: string().min(1).max(120).default("sans"),
30450
+ font: string().min(1).max(120).default("sans").describe("sans, serif, mono, or a font family name."),
30438
30451
  align: _enum(BOARD_TEXT_ALIGNS).default("left"),
30439
30452
  lineHeight: finite.min(.5).max(4).default(4 / 3),
30440
- width: finite.positive().optional(),
30441
- reveal: ratio.default(1),
30442
- caret: boolean().default(false)
30453
+ width: finite.positive().optional().describe("Wrap width; the height follows the text."),
30454
+ reveal: ratio.default(1).describe("Fraction of the text shown; animate 0 → 1 to type it."),
30455
+ caret: boolean().default(false).describe("Blink a caret while the text is being revealed.")
30443
30456
  }).strict().prefault({})
30444
30457
  }).strict();
30445
30458
  object({
@@ -30447,7 +30460,7 @@ object({
30447
30460
  type: literal("shape"),
30448
30461
  props: object({
30449
30462
  geometry: _enum(BOARD_SHAPE_GEOMETRIES).default("rectangle"),
30450
- path: string().min(1).max(2e5).optional(),
30463
+ path: string().min(1).max(2e5).optional().describe("SVG path data for path geometry, fitted to the item size."),
30451
30464
  text: string().default(""),
30452
30465
  fontSize: fontSize.default(20),
30453
30466
  align: _enum(BOARD_TEXT_ALIGNS).default("center")
@@ -30459,12 +30472,12 @@ object({
30459
30472
  const BoardDrawPointSchema = object({
30460
30473
  x: finite,
30461
30474
  y: finite,
30462
- p: ratio.default(.5)
30475
+ p: ratio.default(.5).describe("Pen pressure.")
30463
30476
  }).strict();
30464
30477
  object({
30465
30478
  ...unsized,
30466
30479
  type: literal("draw"),
30467
- props: object({ points: array(BoardDrawPointSchema).min(1).max(1e5) }).strict()
30480
+ props: object({ points: array(BoardDrawPointSchema).min(1).max(1e5).describe("Stroke points relative to position; the size follows them.") }).strict()
30468
30481
  }).strict();
30469
30482
  const BoardArrowEndSchema = union([BoardVec2Schema, object({
30470
30483
  item: BoardIdSchema,
@@ -30483,9 +30496,9 @@ const BoardArrowEndSchema = union([BoardVec2Schema, object({
30483
30496
  x: ratio,
30484
30497
  y: ratio
30485
30498
  }).strict()
30486
- ]).default("auto"),
30499
+ ]).default("auto").describe("auto uses the side facing the other end; or a side with an offset, or a point as fractions of the item."),
30487
30500
  port: string().min(1).max(120).optional()
30488
- }).strict()]);
30501
+ }).strict()]).describe("A point relative to position, or { item } to follow that item.");
30489
30502
  const BOARD_ARROW_ROUTES = [
30490
30503
  "straight",
30491
30504
  "curve",
@@ -30498,13 +30511,13 @@ object({
30498
30511
  start: BoardArrowEndSchema,
30499
30512
  end: BoardArrowEndSchema,
30500
30513
  route: _enum(BOARD_ARROW_ROUTES).default("straight"),
30501
- bend: finite.min(-.85).max(.85).default(0),
30502
- waypoints: array(BoardVec2Schema).max(64).default([]),
30514
+ bend: finite.min(-.85).max(.85).default(0).describe("Sideways curve as a fraction of the length; the sign picks the side."),
30515
+ waypoints: array(BoardVec2Schema).max(64).default([]).describe("Points the route passes through."),
30503
30516
  arrowStart: boolean().default(false),
30504
30517
  arrowEnd: boolean().default(true),
30505
30518
  label: string().max(280).default(""),
30506
30519
  fontSize: fontSize.default(14),
30507
- relation: string().max(64).regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/, "relation must be a lowercase slug").optional()
30520
+ relation: string().max(64).regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/, "relation must be a lowercase slug").optional().describe("What the arrow means, as a slug such as triggers or depends-on.")
30508
30521
  }).strict()
30509
30522
  }).strict();
30510
30523
  const fileProps = {
@@ -30520,26 +30533,26 @@ const fileProps = {
30520
30533
  excerpt: string().optional(),
30521
30534
  coverPath: string().optional(),
30522
30535
  coverUrl: string().optional()
30523
- }).strict().optional()
30536
+ }).strict().describe("Cached facts for an instant preview; filled in when the media loads.").optional()
30524
30537
  };
30525
30538
  const BoardMediaOriginSchema = object({
30526
30539
  taskRunId: string().min(1).max(160),
30527
30540
  artifactId: string().min(1).max(240)
30528
- }).strict();
30541
+ }).strict().describe("The generation run and result this media came from.");
30529
30542
  const mediaProps = {
30530
30543
  ...fileProps,
30531
30544
  origin: BoardMediaOriginSchema.optional()
30532
30545
  };
30533
30546
  const timedFileProps = {
30534
30547
  ...mediaProps,
30535
- time: nonNegative.optional()
30548
+ time: nonNegative.optional().describe("Playback position in milliseconds; animate props.time to play it on a timeline.")
30536
30549
  };
30537
30550
  const BoardCropSchema = object({
30538
30551
  x: ratio,
30539
30552
  y: ratio,
30540
30553
  w: ratio,
30541
30554
  h: ratio
30542
- }).strict();
30555
+ }).strict().describe("Region shown, as fractions of the image. Without it the whole image fits inside size.");
30543
30556
  object({
30544
30557
  ...boxed(640, 360),
30545
30558
  type: literal("image"),
@@ -30619,8 +30632,8 @@ object({
30619
30632
  ...boxed(420, 240),
30620
30633
  type: literal("task"),
30621
30634
  props: object({
30622
- taskRunId: string().min(1).max(160),
30623
- snapshot: BoardTaskSnapshotSchema.describe("Maintained by the server for tasks in the Board's Space; writers may omit it.")
30635
+ taskRunId: string().min(1).max(160).describe("The generation run shown; the server keeps the card's status and results up to date."),
30636
+ snapshot: BoardTaskSnapshotSchema.describe("Maintained by the server; omit it.")
30624
30637
  }).strict()
30625
30638
  }).strict();
30626
30639
  const BOARD_EFFECT_KINDS = [
@@ -30643,8 +30656,8 @@ object({
30643
30656
  spread: finite.min(0).max(360).default(360),
30644
30657
  gravity: finite.default(0),
30645
30658
  intensity: nonNegative.max(10).default(1),
30646
- follow: BoardIdSchema.optional(),
30647
- seed: string().min(1).max(160).optional()
30659
+ follow: BoardIdSchema.optional().describe("trail: the item to follow."),
30660
+ seed: string().min(1).max(160).optional().describe("Makes procedural motion repeatable.")
30648
30661
  }).strict()
30649
30662
  }).strict();
30650
30663
  object({
@@ -30684,21 +30697,21 @@ function isBoardEase(value) {
30684
30697
  }
30685
30698
  const BoardEaseSchema = string().max(120).refine(isBoardEase, "expected a CSS easing: linear, ease-in-out, cubic-bezier(…) or steps(…)");
30686
30699
  const BoardKeyframeSchema = object({
30687
- at: nonNegative,
30700
+ at: nonNegative.describe("Time in milliseconds."),
30688
30701
  value: unknown(),
30689
- ease: BoardEaseSchema.optional()
30702
+ ease: BoardEaseSchema.optional().describe("Easing into this keyframe: a CSS keyword, cubic-bezier() or steps(); defaults to linear.")
30690
30703
  }).strict();
30691
30704
  const BoardTrackSchema = object({
30692
- target: string().min(1).max(160),
30693
- property: string().min(1).max(200),
30694
- keyframes: array(BoardKeyframeSchema).min(1).max(1e5),
30695
- composite: _enum(["replace", "add"]).default("replace"),
30705
+ target: string().min(1).max(160).describe("An item id, camera, or another animation id (its time property nests that timeline)."),
30706
+ property: string().min(1).max(200).describe("JSON path in the target, e.g. position, opacity, style.fill, props.text."),
30707
+ keyframes: array(BoardKeyframeSchema).min(1).max(1e5).describe("Ordered by at. The value holds before the first and after the last keyframe."),
30708
+ composite: _enum(["replace", "add"]).default("replace").describe("replace: the latest-started track wins. add: positions and rotations add; scale and opacity multiply."),
30696
30709
  interpolation: _enum([
30697
30710
  "auto",
30698
30711
  "step",
30699
30712
  "spline"
30700
- ]).default("auto"),
30701
- orient: boolean().optional()
30713
+ ]).default("auto").describe("auto blends numbers, vectors and colors and steps other values; spline smooths position paths."),
30714
+ orient: boolean().optional().describe("Rotate the item along its path.")
30702
30715
  }).strict().superRefine((track, context) => {
30703
30716
  for (let index = 1; index < track.keyframes.length; index += 1) if ((track.keyframes[index]?.at ?? 0) < (track.keyframes[index - 1]?.at ?? 0)) context.addIssue({
30704
30717
  code: "custom",
@@ -30713,20 +30726,20 @@ const BoardTrackSchema = object({
30713
30726
  const BoardMarkerSchema = object({
30714
30727
  at: nonNegative,
30715
30728
  label: string().max(280).optional(),
30716
- pause: boolean().optional()
30729
+ pause: boolean().optional().describe("Hold shared playback here until next.")
30717
30730
  }).strict();
30718
30731
  object({
30719
30732
  name: string().max(280).optional(),
30720
- duration: finite.positive().max(864e5),
30733
+ duration: finite.positive().max(864e5).describe("Milliseconds; a new animation without it ends at its last keyframe."),
30721
30734
  play: _enum([
30722
30735
  "manual",
30723
30736
  "auto",
30724
30737
  "always"
30725
- ]).default("manual"),
30738
+ ]).default("manual").describe("manual: only shared playback starts it. auto: plays when the Board opens. always: loops continuously."),
30726
30739
  delay: nonNegative.default(0),
30727
30740
  loop: boolean().default(false),
30728
- end: _enum(["hold", "reset"]).default("hold"),
30729
- markers: array(BoardMarkerSchema).max(1e4).default([])
30741
+ end: _enum(["hold", "reset"]).default("hold").describe("hold keeps the last frame; reset returns to the start."),
30742
+ markers: array(BoardMarkerSchema).max(1e4).default([]).describe("Labeled points on this timeline; a nested animation's markers do not carry over.")
30730
30743
  }).strict().extend({ tracks: record(BoardKeySchema, BoardTrackSchema).default({}) }).strict();
30731
30744
  const BOARD_ENTER_PRESETS = [
30732
30745
  "fade-in",
@@ -30743,7 +30756,7 @@ const BoardBackgroundSchema = object({
30743
30756
  "image"
30744
30757
  ]).default("dots"),
30745
30758
  color: BoardColorSchema.optional(),
30746
- imageUrl: string().url().max(4096).optional(),
30759
+ imageUrl: string().url().max(4096).optional().describe("Background image for kind image."),
30747
30760
  fit: _enum([
30748
30761
  "cover",
30749
30762
  "contain",
@@ -30755,13 +30768,13 @@ object({
30755
30768
  title: string().min(1).max(255).optional(),
30756
30769
  background: BoardBackgroundSchema.default({ kind: "dots" }),
30757
30770
  grid: object({
30758
- visible: boolean().default(false),
30771
+ visible: boolean().default(false).describe("Show the grid overlay."),
30759
30772
  size: finite.min(4).max(512).default(24)
30760
30773
  }).strict().optional(),
30761
30774
  enter: object({
30762
30775
  preset: _enum(BOARD_ENTER_PRESETS),
30763
30776
  duration: finite.positive().max(1e4).optional()
30764
- }).strict().optional()
30777
+ }).strict().optional().describe("Motion for items as they are added.")
30765
30778
  }).strict();
30766
30779
  const patchObject = record(string(), unknown());
30767
30780
  object({
@@ -31338,13 +31351,24 @@ function renderBoardExport(renderer, input, options = {}) {
31338
31351
  backgroundImage: options.backgroundImage
31339
31352
  });
31340
31353
  try {
31341
- const canvas = renderer.extract.canvas({
31354
+ const texture = renderer.textureGenerator.generateTexture({
31342
31355
  target: exportScene.root,
31343
31356
  frame: new Rectangle(0, 0, plan.width, plan.height),
31344
31357
  resolution: 1,
31345
31358
  antialias: true,
31346
31359
  clearColor: options.background === "transparent" ? void 0 : palette.bg
31347
31360
  });
31361
+ let canvas;
31362
+ const surface = renderer.type === RendererType.CANVAS ? texture.source.resource : void 0;
31363
+ try {
31364
+ canvas = renderer.texture.generateCanvas(texture);
31365
+ } finally {
31366
+ texture.destroy(true);
31367
+ if (surface) {
31368
+ surface.width = 1;
31369
+ surface.height = 1;
31370
+ }
31371
+ }
31348
31372
  const warnings = [];
31349
31373
  if (plan.clamped) warnings.push({
31350
31374
  kind: "scale-clamped",
@@ -31369,11 +31393,55 @@ function renderBoardExport(renderer, input, options = {}) {
31369
31393
  }
31370
31394
  }
31371
31395
  //#endregion
31396
+ //#region ../sdk/src/board/export/node/fonts.ts
31397
+ function initializeBoardFonts(registry, fonts) {
31398
+ for (const font of fonts) registry.registerFromPath(font.path, font.family);
31399
+ if (!registry.setAlias) return;
31400
+ for (const [alias, candidates] of Object.entries({
31401
+ "system-ui": [
31402
+ "Noto Sans",
31403
+ "DejaVu Sans",
31404
+ "Liberation Sans",
31405
+ "FreeSans",
31406
+ "Helvetica Neue",
31407
+ "Arial",
31408
+ "Segoe UI"
31409
+ ],
31410
+ "sans-serif": [
31411
+ "Geist",
31412
+ "Noto Sans",
31413
+ "DejaVu Sans",
31414
+ "Liberation Sans",
31415
+ "FreeSans",
31416
+ "Arial"
31417
+ ],
31418
+ serif: [
31419
+ "Noto Serif",
31420
+ "DejaVu Serif",
31421
+ "Liberation Serif",
31422
+ "FreeSerif",
31423
+ "Times New Roman"
31424
+ ],
31425
+ monospace: [
31426
+ "Geist Mono",
31427
+ "DejaVu Sans Mono",
31428
+ "Liberation Mono",
31429
+ "FreeMono",
31430
+ "Courier New"
31431
+ ]
31432
+ })) {
31433
+ if (registry.has(alias)) continue;
31434
+ const family = candidates.find((name) => registry.has(name));
31435
+ if (family) registry.setAlias(family, alias);
31436
+ }
31437
+ }
31438
+ //#endregion
31372
31439
  //#region ../sdk/src/board/export/node/scripts.ts
31373
31440
  const WORKER = `
31374
31441
  const {parentPort, workerData} = require('node:worker_threads');
31375
31442
  const {createContext, Script, SourceTextModule} = require('node:vm');
31376
- const {createCanvas} = require(workerData.canvasModule);
31443
+ const {createCanvas, GlobalFonts} = require(workerData.canvasModule);
31444
+ (${initializeBoardFonts.toString()})(GlobalFonts, workerData.fonts);
31377
31445
  const instances = new Map();
31378
31446
  const graphs = new Map();
31379
31447
  const update = new Script('__runtime.update(JSON.parse(__inputJson)); __runtime.step(__time)');
@@ -31382,7 +31450,10 @@ const output = new Script('JSON.stringify(__runtime.output())');
31382
31450
  const dispose = new Script('__runtime.destroy()');
31383
31451
  function release(id) {
31384
31452
  const entry=instances.get(id);instances.delete(id);
31385
- if(entry) {try {dispose.runInContext(entry.context,{timeout:workerData.timeout});} catch {}}
31453
+ if(entry) {
31454
+ try {dispose.runInContext(entry.context,{timeout:workerData.timeout});} catch {}
31455
+ if(entry.canvas) {entry.canvas.width=1;entry.canvas.height=1;}
31456
+ }
31386
31457
  }
31387
31458
  async function create(data) {
31388
31459
  const context = createContext({console:{log(){},warn(){},error(){}},structuredClone},{codeGeneration:{strings:false,wasm:false}});
@@ -31432,22 +31503,29 @@ parentPort.on('message',async data=>{
31432
31503
  const width=Math.max(1,Math.round(data.input.width*data.scale));
31433
31504
  const height=Math.max(1,Math.round(data.input.height*data.scale));
31434
31505
  const canvas=entry.canvas??=createCanvas(width,height);
31435
- canvas.width=width;canvas.height=height;
31436
- const drawing=canvas.getContext('2d');drawing.scale(data.scale,data.scale);
31437
- context.__drawing=drawing;draw.runInContext(context,{timeout:workerData.timeout});
31438
- png=canvas.toBuffer('image/png');delete context.__drawing;
31506
+ if(canvas.width!==width || canvas.height!==height) {canvas.width=width;canvas.height=height;}
31507
+ const drawing=canvas.getContext('2d');drawing.reset();drawing.scale(data.scale,data.scale);
31508
+ context.__drawing=drawing;
31509
+ try {
31510
+ draw.runInContext(context,{timeout:workerData.timeout});
31511
+ png=canvas.toBuffer('image/png');
31512
+ } finally {
31513
+ delete context.__drawing;canvas.width=1;canvas.height=1;
31514
+ }
31439
31515
  }
31440
31516
  parentPort.postMessage({png,output:JSON.parse(output.runInContext(context,{timeout:workerData.timeout}))});
31441
- } catch(error) { instances.delete(data.input?.id);parentPort.postMessage({error:String(error.stack||error)}); }
31517
+ } catch(error) { release(data.input?.id);parentPort.postMessage({error:String(error.stack||error)}); }
31442
31518
  });
31443
31519
  `;
31444
31520
  function createNodeBoardScriptHost(renderer, options) {
31521
+ const concurrency = options.concurrency ?? 4;
31522
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1 || concurrency > 4) throw new Error("Script concurrency must be an integer between 1 and 4.");
31445
31523
  const textures = /* @__PURE__ */ new Map();
31446
31524
  const outputs = /* @__PURE__ */ new Map();
31447
31525
  const errors = /* @__PURE__ */ new Map();
31448
31526
  const sources = /* @__PURE__ */ new Map();
31449
31527
  const modules = createBoardScriptModuleReader(options.readModule, options.readTimeoutMs);
31450
- const lanes = Array.from({ length: 4 }, () => ({
31528
+ const lanes = Array.from({ length: concurrency }, () => ({
31451
31529
  worker: null,
31452
31530
  tail: Promise.resolve(),
31453
31531
  graphs: /* @__PURE__ */ new Set()
@@ -31488,6 +31566,7 @@ function createNodeBoardScriptHost(renderer, options) {
31488
31566
  workerData: {
31489
31567
  runtime: createScriptRuntime.toString(),
31490
31568
  timeout,
31569
+ fonts: renderer.fonts ?? [],
31491
31570
  canvasModule: fileURLToPath(import.meta.resolve("@napi-rs/canvas"))
31492
31571
  }
31493
31572
  });
@@ -31621,20 +31700,6 @@ function createNodeBoardScriptHost(renderer, options) {
31621
31700
  }
31622
31701
  //#endregion
31623
31702
  //#region ../sdk/src/board/export/node/index.ts
31624
- const SYSTEM_UI_FAMILIES = [
31625
- "Noto Sans",
31626
- "DejaVu Sans",
31627
- "Liberation Sans",
31628
- "FreeSans",
31629
- "Helvetica Neue",
31630
- "Arial",
31631
- "Segoe UI"
31632
- ];
31633
- function aliasSystemUi(fonts) {
31634
- if (!fonts.setAlias || fonts.has("system-ui")) return;
31635
- const family = SYSTEM_UI_FAMILIES.find((name) => fonts.has(name));
31636
- if (family) fonts.setAlias(family, "system-ui");
31637
- }
31638
31703
  const WEBGL_PROBE_STUB = {
31639
31704
  getShaderPrecisionFormat: () => ({
31640
31705
  precision: 1,
@@ -31681,8 +31746,11 @@ function installAnimationFrameShim() {
31681
31746
  async function createNodeBoardRenderer(options = {}) {
31682
31747
  const canvasModule = options.canvasModule ?? await loadCanvasModule();
31683
31748
  extensions.add(CanvasGraphicsPipe, GraphicsPipe, CanvasGraphicsContextSystem, GraphicsContextSystem, CanvasRendererTextSystem, CanvasTextSystem, CanvasTextPipe);
31684
- for (const font of options.fonts ?? []) canvasModule.GlobalFonts.registerFromPath(font.path, font.family ?? "Geist");
31685
- aliasSystemUi(canvasModule.GlobalFonts);
31749
+ const fonts = (options.fonts ?? []).map((font) => ({
31750
+ ...font,
31751
+ family: font.family ?? "Geist"
31752
+ }));
31753
+ initializeBoardFonts(canvasModule.GlobalFonts, fonts);
31686
31754
  function createCanvas(width = 1, height = 1) {
31687
31755
  const canvas = canvasModule.createCanvas(Math.max(1, Math.ceil(width)), Math.max(1, Math.ceil(height)));
31688
31756
  const nativeGetContext = canvas.getContext.bind(canvas);
@@ -31719,14 +31787,16 @@ async function createNodeBoardRenderer(options = {}) {
31719
31787
  return {
31720
31788
  renderer,
31721
31789
  canvasModule,
31722
- decodeImage: async (bytes, mimeType = "image/png") => {
31790
+ fonts,
31791
+ decodeImage: async (bytes) => {
31723
31792
  const image = new canvasModule.Image();
31724
- const base64 = Buffer.from(bytes).toString("base64");
31725
31793
  await new Promise((resolve, reject) => {
31726
31794
  image.onload = () => resolve();
31727
31795
  image.onerror = (error) => reject(error);
31728
- image.src = `data:${mimeType};base64,${base64}`;
31796
+ image.src = bytes;
31729
31797
  });
31798
+ image.onload = void 0;
31799
+ image.onerror = void 0;
31730
31800
  return new Texture({ source: new ImageSource({
31731
31801
  resource: image,
31732
31802
  width: image.width,
@@ -31760,7 +31830,7 @@ function exportBoardImageBytes(renderer, document, options = {}) {
31760
31830
  try {
31761
31831
  const bytes = format === "png" ? canvas.toBuffer(MIME.png) : canvas.toBuffer(MIME[format], Math.round(quality * 100));
31762
31832
  return {
31763
- bytes: new Uint8Array(bytes),
31833
+ bytes: new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength),
31764
31834
  plan: result.plan,
31765
31835
  warnings: result.warnings,
31766
31836
  format
@@ -31771,4 +31841,4 @@ function exportBoardImageBytes(renderer, document, options = {}) {
31771
31841
  }
31772
31842
  }
31773
31843
  //#endregion
31774
- export { BOARD_SCRIPT_DRAWING_EXAMPLE, boardDocumentAt, boardImageKeySource, boardPathTrack, boardSequence, boardSequencePatch, buildBoardScene, createBoardAnimationSampler, createBoardRenderSampler, createNodeBoardRenderer, createNodeBoardScriptHost, exportBoardImageBytes, hasBoardScripts, imageAssetKey, inspectBoardScene, measureBoardText, parseBoardDocument, planBoardExport, prepareBoardExport, selectBoardExportAssets };
31844
+ export { BOARD_SCRIPT_DRAWING_EXAMPLE, boardDocumentAt, boardImageKeySource, boardPathTrack, boardSequence, boardSequencePatch, buildBoardScene, createBoardAnimationSampler, createBoardRenderSampler, createNodeBoardRenderer, createNodeBoardScriptHost, exportBoardImageBytes, hasBoardScripts, imageAssetKey, inspectBoardItems, inspectBoardScene, measureBoardText, parseBoardDocument, planBoardExport, prepareBoardExport, selectBoardExportAssets };