@neta-art/cohub-cli 10.3.0 → 10.4.2

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.
@@ -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
@@ -6821,20 +6821,20 @@ const CAMERA_PROPERTIES = {
6821
6821
  y: number$1().finite(),
6822
6822
  width: number$1().finite().positive(),
6823
6823
  height: number$1().finite().positive()
6824
- }).strict()])
6824
+ }).strict()]).describe("Item id or world-space rect to fit with 48 px padding; holds until the next focus keyframe.")
6825
6825
  },
6826
6826
  zoom: {
6827
6827
  kind: "number",
6828
- schema: number$1().finite().positive(),
6828
+ schema: number$1().finite().positive().describe("Pixels per world unit; overrides the zoom computed by focus."),
6829
6829
  multiplicative: true
6830
6830
  },
6831
6831
  center: {
6832
6832
  kind: "vec2",
6833
- schema: BoardVec2Schema$1
6833
+ schema: BoardVec2Schema$1.describe("World-space viewport center; overrides focus positioning using the resolved zoom.")
6834
6834
  },
6835
6835
  shake: {
6836
6836
  kind: "number",
6837
- schema: number$1().finite().nonnegative()
6837
+ schema: number$1().finite().nonnegative().describe("Shake amplitude in screen pixels, applied after focus, zoom and center.")
6838
6838
  }
6839
6839
  };
6840
6840
  const ANIMATION_PROPERTIES = { time: {
@@ -7338,6 +7338,86 @@ function parseBoardDocument(input) {
7338
7338
  } : result;
7339
7339
  }
7340
7340
  //#endregion
7341
+ //#region ../protocol/src/board.ts
7342
+ const BOARD_EXTENSION = ".board";
7343
+ const BOARD_MIME_TYPE = "application/json";
7344
+ object({
7345
+ kind: literal("cohub.board.manifest"),
7346
+ version: literal(1),
7347
+ boardId: string().uuid(),
7348
+ title: string().min(1).max(255)
7349
+ });
7350
+ const BOARD_READ_SECTIONS = ["items", "animations"];
7351
+ const rectSchema = object({
7352
+ x: number$1().finite(),
7353
+ y: number$1().finite(),
7354
+ width: number$1().finite().positive(),
7355
+ height: number$1().finite().positive()
7356
+ }).strict();
7357
+ object({
7358
+ only: array(_enum(BOARD_READ_SECTIONS)).optional(),
7359
+ rect: rectSchema.optional(),
7360
+ within: BoardIdSchema$1.optional(),
7361
+ items: array(string().min(1)).max(5e4).optional(),
7362
+ animations: array(string().min(1)).max(1e4).optional(),
7363
+ limit: number$1().int().min(1).max(5e4).optional(),
7364
+ cursor: string().min(1).optional()
7365
+ }).strict().refine((read) => !(read.cursor && (read.rect || read.within)), {
7366
+ path: ["cursor"],
7367
+ message: "cursor pages by id, so it cannot be combined with rect or within"
7368
+ });
7369
+ object({
7370
+ patch: BoardPatchSchema$1,
7371
+ replace: boolean().default(false),
7372
+ cascade: boolean().default(false),
7373
+ dryRun: boolean().default(false),
7374
+ baseVersion: number$1().int().nonnegative().optional(),
7375
+ mutationId: string().min(1).max(160).optional(),
7376
+ clientId: string().min(1).max(160).optional()
7377
+ }).strict();
7378
+ object({
7379
+ path: string().min(1),
7380
+ title: string().min(1).max(255).optional(),
7381
+ document: BoardPatchSchema$1.optional(),
7382
+ mutationId: string().max(160).optional()
7383
+ }).strict();
7384
+ object({
7385
+ before: number$1().int().positive().optional(),
7386
+ limit: number$1().int().min(1).max(500).default(200)
7387
+ }).strict();
7388
+ const commandId = string().min(1).max(160);
7389
+ discriminatedUnion("type", [
7390
+ object({
7391
+ commandId,
7392
+ type: literal("play"),
7393
+ animationId: string().min(1),
7394
+ position: number$1().finite().nonnegative().optional(),
7395
+ timeScale: number$1().finite().positive().max(4).optional(),
7396
+ seed: string().min(1).max(160).optional()
7397
+ }).strict(),
7398
+ object({
7399
+ commandId,
7400
+ type: literal("pause")
7401
+ }).strict(),
7402
+ object({
7403
+ commandId,
7404
+ type: literal("resume")
7405
+ }).strict(),
7406
+ object({
7407
+ commandId,
7408
+ type: literal("seek"),
7409
+ position: number$1().finite().nonnegative()
7410
+ }).strict(),
7411
+ object({
7412
+ commandId,
7413
+ type: literal("next")
7414
+ }).strict(),
7415
+ object({
7416
+ commandId,
7417
+ type: literal("stop")
7418
+ }).strict()
7419
+ ]);
7420
+ //#endregion
7341
7421
  //#region ../protocol/src/board-task.ts
7342
7422
  function artifactScore(artifact) {
7343
7423
  const kind = {
@@ -7374,6 +7454,117 @@ function taskArtifactPreviewUrl(artifact) {
7374
7454
  if (artifact?.type === "video" || artifact?.type === "audio") return artifact.previewUrl;
7375
7455
  }
7376
7456
  //#endregion
7457
+ //#region ../protocol/src/fs/mime.ts
7458
+ const MIME_BY_EXTENSION = {
7459
+ ".txt": "text/plain",
7460
+ ".md": "text/markdown",
7461
+ ".markdown": "text/markdown",
7462
+ ".json": "application/json",
7463
+ ".jsonl": "application/x-ndjson",
7464
+ ".csv": "text/csv",
7465
+ [BOARD_EXTENSION]: BOARD_MIME_TYPE,
7466
+ ".js": "text/javascript",
7467
+ ".mjs": "text/javascript",
7468
+ ".cjs": "text/javascript",
7469
+ ".ts": "text/typescript",
7470
+ ".tsx": "text/tsx",
7471
+ ".jsx": "text/jsx",
7472
+ ".svelte": "text/x-svelte",
7473
+ ".css": "text/css",
7474
+ ".scss": "text/x-scss",
7475
+ ".html": "text/html",
7476
+ ".htm": "text/html",
7477
+ ".xml": "application/xml",
7478
+ ".yaml": "application/yaml",
7479
+ ".yml": "application/yaml",
7480
+ ".toml": "application/toml",
7481
+ ".ini": "text/plain",
7482
+ ".env": "text/plain",
7483
+ ".sh": "text/x-shellscript",
7484
+ ".bash": "text/x-shellscript",
7485
+ ".py": "text/x-python",
7486
+ ".go": "text/x-go",
7487
+ ".rs": "text/x-rust",
7488
+ ".java": "text/x-java-source",
7489
+ ".c": "text/x-c",
7490
+ ".h": "text/x-c",
7491
+ ".cpp": "text/x-c++src",
7492
+ ".hpp": "text/x-c++hdr",
7493
+ ".sql": "application/sql",
7494
+ ".svg": "image/svg+xml",
7495
+ ".png": "image/png",
7496
+ ".jpg": "image/jpeg",
7497
+ ".jpeg": "image/jpeg",
7498
+ ".gif": "image/gif",
7499
+ ".webp": "image/webp",
7500
+ ".avif": "image/avif",
7501
+ ".bmp": "image/bmp",
7502
+ ".ico": "image/x-icon",
7503
+ ".mp4": "video/mp4",
7504
+ ".webm": "video/webm",
7505
+ ".mov": "video/quicktime",
7506
+ ".m4v": "video/x-m4v",
7507
+ ".ogv": "video/ogg",
7508
+ ".avi": "video/x-msvideo",
7509
+ ".mp3": "audio/mpeg",
7510
+ ".wav": "audio/wav",
7511
+ ".ogg": "audio/ogg",
7512
+ ".m4a": "audio/mp4",
7513
+ ".aac": "audio/aac",
7514
+ ".flac": "audio/flac",
7515
+ ".opus": "audio/ogg",
7516
+ ".glb": "model/gltf-binary",
7517
+ ".gltf": "model/gltf+json",
7518
+ ".stl": "model/stl",
7519
+ ".obj": "model/obj",
7520
+ ".3mf": "model/3mf",
7521
+ ".usdz": "model/vnd.usdz+zip",
7522
+ ".mtl": "text/plain",
7523
+ ".pdf": "application/pdf",
7524
+ ".wasm": "application/wasm",
7525
+ ".woff": "font/woff",
7526
+ ".woff2": "font/woff2",
7527
+ ".zip": "application/zip",
7528
+ ".gz": "application/gzip",
7529
+ ".tar": "application/x-tar",
7530
+ ".rar": "application/vnd.rar",
7531
+ ".7z": "application/x-7z-compressed",
7532
+ ".exe": "application/x-msdownload",
7533
+ ".dmg": "application/x-apple-diskimage",
7534
+ ".deb": "application/vnd.debian.binary-package",
7535
+ ".rpm": "application/x-rpm"
7536
+ };
7537
+ const MIME_BY_FILENAME = {
7538
+ dockerfile: "text/x-dockerfile",
7539
+ makefile: "text/x-makefile"
7540
+ };
7541
+ function mimeTypeForPath(path) {
7542
+ const name = (path.split(/[\\/]/).pop() ?? "").toLowerCase();
7543
+ const byName = Object.hasOwn(MIME_BY_FILENAME, name) ? MIME_BY_FILENAME[name] : void 0;
7544
+ if (byName) return byName;
7545
+ const dot = name.lastIndexOf(".");
7546
+ const byExtension = dot > 0 ? MIME_BY_EXTENSION[name.slice(dot)] : void 0;
7547
+ if (byExtension) return byExtension;
7548
+ return name.startsWith(".") ? "text/plain" : null;
7549
+ }
7550
+ function normalizeMime$1(mimeType) {
7551
+ if (!mimeType) return null;
7552
+ return mimeType.split(";")[0]?.trim().toLowerCase() || null;
7553
+ }
7554
+ const MODEL_FORMAT_BY_MIME = {
7555
+ "model/gltf-binary": "glb",
7556
+ "model/gltf+json": "gltf",
7557
+ "model/stl": "stl",
7558
+ "model/obj": "obj"
7559
+ };
7560
+ function modelFormatForPath(path, mimeType) {
7561
+ const format = (mime) => {
7562
+ const key = normalizeMime$1(mime);
7563
+ return key && Object.hasOwn(MODEL_FORMAT_BY_MIME, key) ? MODEL_FORMAT_BY_MIME[key] : void 0;
7564
+ };
7565
+ return format(mimeTypeForPath(path)) ?? format(mimeType) ?? null;
7566
+ }
7567
+ //#endregion
7377
7568
  //#region ../sdk/src/board/model/script-index.ts
7378
7569
  const scriptsByItems = /* @__PURE__ */ new WeakMap();
7379
7570
  function boardScriptItems(document) {
@@ -8470,7 +8661,6 @@ function boardSequence(document, chapters) {
8470
8661
  const chapterIndex = String(index).padStart(String(chapters.length).length, "0");
8471
8662
  for (const [id, track] of Object.entries(animation.tracks)) {
8472
8663
  if (document.animations[track.target]) throw new Error(`Chapter ${chapter.animation} contains nested clocks; compose its leaf animations instead.`);
8473
- if (chapter.frame && track.target === "camera" && track.property === "focus") continue;
8474
8664
  tracks[`chapter-${chapterIndex}-${ranks.get(id)}`] = {
8475
8665
  ...track,
8476
8666
  keyframes: track.keyframes.map((keyframe) => ({
@@ -8501,7 +8691,7 @@ function boardSequence(document, chapters) {
8501
8691
  }] : []
8502
8692
  ]
8503
8693
  };
8504
- tracks[`camera-${index}`] = {
8694
+ tracks[`chapter-${chapterIndex}--camera`] = {
8505
8695
  target: "camera",
8506
8696
  property: "focus",
8507
8697
  composite: "replace",
@@ -9192,6 +9382,14 @@ function getMediaResourceTitle(value) {
9192
9382
  return value.split("/").filter(Boolean).pop() || value;
9193
9383
  }
9194
9384
  }
9385
+ function boardModelFormat(src, mimeType) {
9386
+ const source = parseBoardMediaSource$1(src);
9387
+ if (!source) return null;
9388
+ return modelFormatForPath(source.kind === "url" ? source.value.split(/[?#]/, 1)[0] ?? "" : source.value, mimeType);
9389
+ }
9390
+ function isBoardModelItem(item) {
9391
+ return item?.type === "file" && boardModelFormat(item.props.src, item.props.snapshot?.mimeType) !== null;
9392
+ }
9195
9393
  //#endregion
9196
9394
  //#region ../sdk/src/board/model/file-snapshot.ts
9197
9395
  /**
@@ -12305,13 +12503,13 @@ const placeHolderBufferData = /* @__PURE__ */ new Float32Array(1);
12305
12503
  const placeHolderIndexData = /* @__PURE__ */ new Uint32Array(1);
12306
12504
  var BatchGeometry = class extends Geometry {
12307
12505
  constructor() {
12308
- const attributeBuffer = new Buffer$1({
12506
+ const attributeBuffer = new Buffer({
12309
12507
  data: placeHolderBufferData,
12310
12508
  label: "attribute-batch-buffer",
12311
12509
  usage: BufferUsage.VERTEX | BufferUsage.COPY_DST,
12312
12510
  shrinkToFit: false
12313
12511
  });
12314
- const indexBuffer = new Buffer$1({
12512
+ const indexBuffer = new Buffer({
12315
12513
  data: placeHolderIndexData,
12316
12514
  label: "index-batch-buffer",
12317
12515
  usage: BufferUsage.INDEX | BufferUsage.COPY_DST,
@@ -18288,19 +18486,19 @@ const _MeshGeometry = class _MeshGeometry extends Geometry {
18288
18486
  3
18289
18487
  ]);
18290
18488
  const shrinkToFit = options.shrinkBuffersToFit;
18291
- const positionBuffer = new Buffer$1({
18489
+ const positionBuffer = new Buffer({
18292
18490
  data: positions,
18293
18491
  label: "attribute-mesh-positions",
18294
18492
  shrinkToFit,
18295
18493
  usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
18296
18494
  });
18297
- const uvBuffer = new Buffer$1({
18495
+ const uvBuffer = new Buffer({
18298
18496
  data: uvs,
18299
18497
  label: "attribute-mesh-uvs",
18300
18498
  shrinkToFit,
18301
18499
  usage: BufferUsage.VERTEX | BufferUsage.COPY_DST
18302
18500
  });
18303
- const indexBuffer = new Buffer$1({
18501
+ const indexBuffer = new Buffer({
18304
18502
  data: indices,
18305
18503
  label: "index-mesh-buffer",
18306
18504
  shrinkToFit,
@@ -27161,12 +27359,24 @@ function measureBoardText(props) {
27161
27359
  missingCharacters: characters
27162
27360
  };
27163
27361
  }
27164
- function inspectBoardScene(scene, limit = 100) {
27362
+ function inspectBoardItems(scene, ids) {
27363
+ return {
27364
+ diagnostics: inspectBoardScene(scene, 100, ids),
27365
+ items: scene.items.filter((item) => !ids || ids.has(item.id)).map((item) => ({
27366
+ id: item.id,
27367
+ bounds: item.frame,
27368
+ worldBounds: itemBounds(item.frame),
27369
+ opacity: scene.opacity(item.id),
27370
+ ...item.type === "text" ? { text: measureBoardText(item.props) } : {}
27371
+ }))
27372
+ };
27373
+ }
27374
+ function inspectBoardScene(scene, limit = 100, ids) {
27165
27375
  ensureBoardTextMeasurement();
27166
27376
  const diagnostics = [];
27167
27377
  const texts = scene.items.filter((item) => item.type === "text" && scene.opacity(item.id) > .01);
27168
27378
  const push = (entry) => {
27169
- if (diagnostics.length < limit) diagnostics.push(entry);
27379
+ if (diagnostics.length < limit && (!ids || ids.has(entry.item) || Boolean(entry.related && ids.has(entry.related)))) diagnostics.push(entry);
27170
27380
  };
27171
27381
  for (const item of scene.items) {
27172
27382
  if (diagnostics.length >= limit) break;
@@ -27178,7 +27388,7 @@ function inspectBoardScene(scene, limit = 100) {
27178
27388
  let glyphBudget = 256;
27179
27389
  for (const item of texts) {
27180
27390
  if (diagnostics.length >= limit || item.type !== "text") break;
27181
- if (glyphBudget-- > 0) {
27391
+ if ((!ids || ids.has(item.id)) && glyphBudget-- > 0) {
27182
27392
  const measured = measureBoardText(item.props);
27183
27393
  if (measured.missingCharacters) push({
27184
27394
  code: "glyph-unavailable",
@@ -27217,6 +27427,7 @@ function inspectBoardScene(scene, limit = 100) {
27217
27427
  const b = sorted[j];
27218
27428
  if (!b) continue;
27219
27429
  if (b.rect.x >= a.rect.x + a.rect.width) break;
27430
+ if (ids && !ids.has(a.item.id) && !ids.has(b.item.id)) continue;
27220
27431
  if (++comparisons > 1e4) return diagnostics;
27221
27432
  if (a.item.parent === b.item.parent && rectsIntersect(a.rect, b.rect)) push({
27222
27433
  code: "text-overlap",
@@ -27359,6 +27570,14 @@ const PIXEL_GLYPHS = {
27359
27570
  ".###..",
27360
27571
  ".###.."
27361
27572
  ],
27573
+ model: [
27574
+ "..#...",
27575
+ ".###..",
27576
+ "#####.",
27577
+ "#####.",
27578
+ ".###..",
27579
+ "..#..."
27580
+ ],
27362
27581
  file: [
27363
27582
  "####..",
27364
27583
  "#..##.",
@@ -27409,6 +27628,9 @@ function drawLineGlyph(graphics, glyph, color) {
27409
27628
  ]) graphics.moveTo(x, 6 - height / 2).lineTo(x, 6 + height / 2);
27410
27629
  graphics.stroke(line);
27411
27630
  return;
27631
+ case "model":
27632
+ traceCube(graphics, 6, 6, 4.5).stroke(line);
27633
+ return;
27412
27634
  case "file":
27413
27635
  graphics.poly([
27414
27636
  2.5,
@@ -27446,6 +27668,25 @@ function drawLineGlyph(graphics, glyph, color) {
27446
27668
  return;
27447
27669
  }
27448
27670
  }
27671
+ function traceCube(graphics, cx, cy, radius) {
27672
+ const dx = radius * .866;
27673
+ const dy = radius / 2;
27674
+ graphics.poly([
27675
+ cx,
27676
+ cy - radius,
27677
+ cx + dx,
27678
+ cy - dy,
27679
+ cx + dx,
27680
+ cy + dy,
27681
+ cx,
27682
+ cy + radius,
27683
+ cx - dx,
27684
+ cy + dy,
27685
+ cx - dx,
27686
+ cy - dy
27687
+ ]).moveTo(cx - dx, cy - dy).lineTo(cx, cy).lineTo(cx + dx, cy - dy).moveTo(cx, cy).lineTo(cx, cy + radius);
27688
+ return graphics;
27689
+ }
27449
27690
  function drawCardGlyph(graphics, glyph, color, pixel) {
27450
27691
  if (!pixel) {
27451
27692
  drawLineGlyph(graphics, glyph, color);
@@ -27944,9 +28185,28 @@ function drawMediaPlayKey(graphics, cx, cy, radius, context, solid = false) {
27944
28185
  alpha: .96
27945
28186
  });
27946
28187
  }
28188
+ function drawModelKey(graphics, cx, cy, radius, context) {
28189
+ if (context.style.corner === "pixel") {
28190
+ const size = Math.round(radius * 1.6);
28191
+ tracePixelRect(graphics, Math.round(cx - size / 2), Math.round(cy - size / 2), size, size, 2).fill({
28192
+ color: MEDIA_SCRIM,
28193
+ alpha: .62
28194
+ });
28195
+ } else graphics.circle(cx, cy, radius).fill({
28196
+ color: MEDIA_SCRIM,
28197
+ alpha: .45
28198
+ });
28199
+ traceCube(graphics, cx, cy, radius * .5).stroke({
28200
+ color: MEDIA_INK,
28201
+ width: Math.max(1, radius / 10),
28202
+ alpha: .96,
28203
+ join: "round",
28204
+ cap: "round"
28205
+ });
28206
+ }
27947
28207
  const GAP$1 = 12;
27948
28208
  const WAVE_HEIGHT = 16;
27949
- const partsByContainer$12 = /* @__PURE__ */ new WeakMap();
28209
+ const partsByContainer$13 = /* @__PURE__ */ new WeakMap();
27950
28210
  function audioKeyCenter(width, height) {
27951
28211
  const radius = Math.min(16, Math.max(8, height / 2 - 8), width / 4);
27952
28212
  return {
@@ -27955,8 +28215,8 @@ function audioKeyCenter(width, height) {
27955
28215
  radius
27956
28216
  };
27957
28217
  }
27958
- function sync$12(container, item, context) {
27959
- const parts = partsByContainer$12.get(container);
28218
+ function sync$13(container, item, context) {
28219
+ const parts = partsByContainer$13.get(container);
27960
28220
  if (!parts) return;
27961
28221
  positionShell(parts.root, item);
27962
28222
  const { width, height } = item.frame;
@@ -28027,7 +28287,7 @@ const audioCardRenderer = {
28027
28287
  roundPixels: true
28028
28288
  });
28029
28289
  root.addChild(plate, wave, title);
28030
- partsByContainer$12.set(root, {
28290
+ partsByContainer$13.set(root, {
28031
28291
  root,
28032
28292
  plate,
28033
28293
  wave,
@@ -28035,11 +28295,11 @@ const audioCardRenderer = {
28035
28295
  sig: "",
28036
28296
  resolution
28037
28297
  });
28038
- if (item.type === "audio") sync$12(root, item, context);
28298
+ if (item.type === "audio") sync$13(root, item, context);
28039
28299
  return root;
28040
28300
  },
28041
28301
  update: (container, item, context) => {
28042
- if (item.type === "audio") sync$12(container, item, context);
28302
+ if (item.type === "audio") sync$13(container, item, context);
28043
28303
  },
28044
28304
  renderFar: (graphics, item, context) => {
28045
28305
  drawFarPlate(graphics, item.frame, {
@@ -28049,6 +28309,127 @@ const audioCardRenderer = {
28049
28309
  accentAlpha: .72
28050
28310
  });
28051
28311
  },
28312
+ destroy: (container) => {
28313
+ partsByContainer$13.get(container)?.root.destroy({ children: true });
28314
+ partsByContainer$13.delete(container);
28315
+ }
28316
+ };
28317
+ //#endregion
28318
+ //#region ../sdk/src/board/render/renderers/model-card-renderer.ts
28319
+ const partsByContainer$12 = /* @__PURE__ */ new WeakMap();
28320
+ const INSET_RATIO = .06;
28321
+ const KEY_RADIUS = 14;
28322
+ const KEY_MARGIN = 10;
28323
+ function modelKeyCenter(width, height) {
28324
+ const radius = Math.max(8, Math.min(KEY_RADIUS, Math.min(width, height) * .12));
28325
+ const margin = Math.min(KEY_MARGIN, radius * .7);
28326
+ return {
28327
+ x: width - margin - radius,
28328
+ y: height - margin - radius,
28329
+ radius
28330
+ };
28331
+ }
28332
+ function sync$12(container, item, context) {
28333
+ const parts = partsByContainer$12.get(container);
28334
+ if (!parts) return;
28335
+ positionShell(parts.root, item);
28336
+ const { width, height } = item.frame;
28337
+ const { src, snapshot } = item.props;
28338
+ const meta = fileMetaLine(src, snapshot?.size);
28339
+ const title = snapshot?.title || fileStem(src);
28340
+ syncCardTag(parts.tag, {
28341
+ glyph: "model",
28342
+ text: `${title} · ${meta}`,
28343
+ width
28344
+ }, context);
28345
+ const key = context.assetKey(item);
28346
+ const texture = key ? context.getTexture(key) : null;
28347
+ const failed = Boolean(key && !texture && context.hasError(key));
28348
+ const detailed = boardCardDetailed(context.zoom);
28349
+ const elevation = cardElevation(context, item.id);
28350
+ const sig = [
28351
+ key ?? "",
28352
+ texture ? `${texture.width}x${texture.height}` : "none",
28353
+ failed,
28354
+ width,
28355
+ height,
28356
+ elevation,
28357
+ detailed,
28358
+ context.themeKey
28359
+ ].join("|");
28360
+ if (sig === parts.sig) return;
28361
+ parts.sig = sig;
28362
+ drawCardSurface(parts.plate.clear(), {
28363
+ x: 0,
28364
+ y: 0,
28365
+ width,
28366
+ height
28367
+ }, context.style, {
28368
+ fill: context.palette.surface,
28369
+ border: context.palette.border,
28370
+ borderAlpha: .85,
28371
+ elevation
28372
+ });
28373
+ if (texture && parts.sprite.texture !== texture) parts.sprite.texture = texture;
28374
+ parts.sprite.visible = Boolean(texture);
28375
+ if (texture) {
28376
+ const inset = Math.min(width, height) * INSET_RATIO;
28377
+ const fitted = containRect(width - inset * 2, height - inset * 2, texture.width, texture.height);
28378
+ parts.sprite.position.set(inset + fitted.x, inset + fitted.y);
28379
+ parts.sprite.width = fitted.width;
28380
+ parts.sprite.height = fitted.height;
28381
+ } else {
28382
+ const radius = Math.max(6, Math.min(28, Math.min(width, height) * .14));
28383
+ traceCube(parts.plate, width / 2, height / 2, radius).stroke({
28384
+ color: context.palette.muted,
28385
+ width: 1.5,
28386
+ alpha: failed ? .35 : .6,
28387
+ join: "round",
28388
+ cap: "round"
28389
+ });
28390
+ }
28391
+ parts.chrome.clear();
28392
+ if (detailed) {
28393
+ const center = modelKeyCenter(width, height);
28394
+ drawModelKey(parts.chrome, center.x, center.y, center.radius, context);
28395
+ }
28396
+ }
28397
+ const modelCardRenderer = {
28398
+ id: "model-card",
28399
+ canRender: (item) => isBoardModelItem(item),
28400
+ create: (item, context) => {
28401
+ const root = new Container();
28402
+ const plate = new Graphics();
28403
+ const sprite = new Sprite(Texture.EMPTY);
28404
+ const chrome = new Graphics();
28405
+ const tag = createCardTag();
28406
+ root.addChild(plate, sprite, chrome, tag.root);
28407
+ partsByContainer$12.set(root, {
28408
+ root,
28409
+ plate,
28410
+ sprite,
28411
+ chrome,
28412
+ tag,
28413
+ sig: ""
28414
+ });
28415
+ if (item.type === "file") sync$12(root, item, context);
28416
+ return root;
28417
+ },
28418
+ update: (container, item, context) => {
28419
+ if (item.type === "file") sync$12(container, item, context);
28420
+ },
28421
+ zoom: (container, zoom) => {
28422
+ const tag = partsByContainer$12.get(container)?.tag;
28423
+ if (tag) zoomCardTag(tag, zoom);
28424
+ },
28425
+ renderFar: (graphics, item, context) => {
28426
+ drawFarPlate(graphics, item.frame, {
28427
+ fill: context.palette.surface,
28428
+ fillAlpha: .96,
28429
+ accent: context.palette.legendary,
28430
+ accentAlpha: .45
28431
+ });
28432
+ },
28052
28433
  destroy: (container) => {
28053
28434
  partsByContainer$12.get(container)?.root.destroy({ children: true });
28054
28435
  partsByContainer$12.delete(container);
@@ -30199,6 +30580,7 @@ const builtinCardRenderers = [
30199
30580
  imageCardRenderer,
30200
30581
  videoCardRenderer,
30201
30582
  audioCardRenderer,
30583
+ modelCardRenderer,
30202
30584
  fileCardRenderer,
30203
30585
  taskCardRenderer,
30204
30586
  shapeCardRenderer,
@@ -31338,13 +31720,24 @@ function renderBoardExport(renderer, input, options = {}) {
31338
31720
  backgroundImage: options.backgroundImage
31339
31721
  });
31340
31722
  try {
31341
- const canvas = renderer.extract.canvas({
31723
+ const texture = renderer.textureGenerator.generateTexture({
31342
31724
  target: exportScene.root,
31343
31725
  frame: new Rectangle(0, 0, plan.width, plan.height),
31344
31726
  resolution: 1,
31345
31727
  antialias: true,
31346
31728
  clearColor: options.background === "transparent" ? void 0 : palette.bg
31347
31729
  });
31730
+ let canvas;
31731
+ const surface = renderer.type === RendererType.CANVAS ? texture.source.resource : void 0;
31732
+ try {
31733
+ canvas = renderer.texture.generateCanvas(texture);
31734
+ } finally {
31735
+ texture.destroy(true);
31736
+ if (surface) {
31737
+ surface.width = 1;
31738
+ surface.height = 1;
31739
+ }
31740
+ }
31348
31741
  const warnings = [];
31349
31742
  if (plan.clamped) warnings.push({
31350
31743
  kind: "scale-clamped",
@@ -31369,11 +31762,55 @@ function renderBoardExport(renderer, input, options = {}) {
31369
31762
  }
31370
31763
  }
31371
31764
  //#endregion
31765
+ //#region ../sdk/src/board/export/node/fonts.ts
31766
+ function initializeBoardFonts(registry, fonts) {
31767
+ for (const font of fonts) registry.registerFromPath(font.path, font.family);
31768
+ if (!registry.setAlias) return;
31769
+ for (const [alias, candidates] of Object.entries({
31770
+ "system-ui": [
31771
+ "Noto Sans",
31772
+ "DejaVu Sans",
31773
+ "Liberation Sans",
31774
+ "FreeSans",
31775
+ "Helvetica Neue",
31776
+ "Arial",
31777
+ "Segoe UI"
31778
+ ],
31779
+ "sans-serif": [
31780
+ "Geist",
31781
+ "Noto Sans",
31782
+ "DejaVu Sans",
31783
+ "Liberation Sans",
31784
+ "FreeSans",
31785
+ "Arial"
31786
+ ],
31787
+ serif: [
31788
+ "Noto Serif",
31789
+ "DejaVu Serif",
31790
+ "Liberation Serif",
31791
+ "FreeSerif",
31792
+ "Times New Roman"
31793
+ ],
31794
+ monospace: [
31795
+ "Geist Mono",
31796
+ "DejaVu Sans Mono",
31797
+ "Liberation Mono",
31798
+ "FreeMono",
31799
+ "Courier New"
31800
+ ]
31801
+ })) {
31802
+ if (registry.has(alias)) continue;
31803
+ const family = candidates.find((name) => registry.has(name));
31804
+ if (family) registry.setAlias(family, alias);
31805
+ }
31806
+ }
31807
+ //#endregion
31372
31808
  //#region ../sdk/src/board/export/node/scripts.ts
31373
31809
  const WORKER = `
31374
31810
  const {parentPort, workerData} = require('node:worker_threads');
31375
31811
  const {createContext, Script, SourceTextModule} = require('node:vm');
31376
- const {createCanvas} = require(workerData.canvasModule);
31812
+ const {createCanvas, GlobalFonts} = require(workerData.canvasModule);
31813
+ (${initializeBoardFonts.toString()})(GlobalFonts, workerData.fonts);
31377
31814
  const instances = new Map();
31378
31815
  const graphs = new Map();
31379
31816
  const update = new Script('__runtime.update(JSON.parse(__inputJson)); __runtime.step(__time)');
@@ -31382,7 +31819,10 @@ const output = new Script('JSON.stringify(__runtime.output())');
31382
31819
  const dispose = new Script('__runtime.destroy()');
31383
31820
  function release(id) {
31384
31821
  const entry=instances.get(id);instances.delete(id);
31385
- if(entry) {try {dispose.runInContext(entry.context,{timeout:workerData.timeout});} catch {}}
31822
+ if(entry) {
31823
+ try {dispose.runInContext(entry.context,{timeout:workerData.timeout});} catch {}
31824
+ if(entry.canvas) {entry.canvas.width=1;entry.canvas.height=1;}
31825
+ }
31386
31826
  }
31387
31827
  async function create(data) {
31388
31828
  const context = createContext({console:{log(){},warn(){},error(){}},structuredClone},{codeGeneration:{strings:false,wasm:false}});
@@ -31432,22 +31872,29 @@ parentPort.on('message',async data=>{
31432
31872
  const width=Math.max(1,Math.round(data.input.width*data.scale));
31433
31873
  const height=Math.max(1,Math.round(data.input.height*data.scale));
31434
31874
  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;
31875
+ if(canvas.width!==width || canvas.height!==height) {canvas.width=width;canvas.height=height;}
31876
+ const drawing=canvas.getContext('2d');drawing.reset();drawing.scale(data.scale,data.scale);
31877
+ context.__drawing=drawing;
31878
+ try {
31879
+ draw.runInContext(context,{timeout:workerData.timeout});
31880
+ png=canvas.toBuffer('image/png');
31881
+ } finally {
31882
+ delete context.__drawing;canvas.width=1;canvas.height=1;
31883
+ }
31439
31884
  }
31440
31885
  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)}); }
31886
+ } catch(error) { release(data.input?.id);parentPort.postMessage({error:String(error.stack||error)}); }
31442
31887
  });
31443
31888
  `;
31444
31889
  function createNodeBoardScriptHost(renderer, options) {
31890
+ const concurrency = options.concurrency ?? 4;
31891
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1 || concurrency > 4) throw new Error("Script concurrency must be an integer between 1 and 4.");
31445
31892
  const textures = /* @__PURE__ */ new Map();
31446
31893
  const outputs = /* @__PURE__ */ new Map();
31447
31894
  const errors = /* @__PURE__ */ new Map();
31448
31895
  const sources = /* @__PURE__ */ new Map();
31449
31896
  const modules = createBoardScriptModuleReader(options.readModule, options.readTimeoutMs);
31450
- const lanes = Array.from({ length: 4 }, () => ({
31897
+ const lanes = Array.from({ length: concurrency }, () => ({
31451
31898
  worker: null,
31452
31899
  tail: Promise.resolve(),
31453
31900
  graphs: /* @__PURE__ */ new Set()
@@ -31488,6 +31935,7 @@ function createNodeBoardScriptHost(renderer, options) {
31488
31935
  workerData: {
31489
31936
  runtime: createScriptRuntime.toString(),
31490
31937
  timeout,
31938
+ fonts: renderer.fonts ?? [],
31491
31939
  canvasModule: fileURLToPath(import.meta.resolve("@napi-rs/canvas"))
31492
31940
  }
31493
31941
  });
@@ -31621,20 +32069,6 @@ function createNodeBoardScriptHost(renderer, options) {
31621
32069
  }
31622
32070
  //#endregion
31623
32071
  //#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
32072
  const WEBGL_PROBE_STUB = {
31639
32073
  getShaderPrecisionFormat: () => ({
31640
32074
  precision: 1,
@@ -31681,8 +32115,11 @@ function installAnimationFrameShim() {
31681
32115
  async function createNodeBoardRenderer(options = {}) {
31682
32116
  const canvasModule = options.canvasModule ?? await loadCanvasModule();
31683
32117
  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);
32118
+ const fonts = (options.fonts ?? []).map((font) => ({
32119
+ ...font,
32120
+ family: font.family ?? "Geist"
32121
+ }));
32122
+ initializeBoardFonts(canvasModule.GlobalFonts, fonts);
31686
32123
  function createCanvas(width = 1, height = 1) {
31687
32124
  const canvas = canvasModule.createCanvas(Math.max(1, Math.ceil(width)), Math.max(1, Math.ceil(height)));
31688
32125
  const nativeGetContext = canvas.getContext.bind(canvas);
@@ -31719,14 +32156,16 @@ async function createNodeBoardRenderer(options = {}) {
31719
32156
  return {
31720
32157
  renderer,
31721
32158
  canvasModule,
31722
- decodeImage: async (bytes, mimeType = "image/png") => {
32159
+ fonts,
32160
+ decodeImage: async (bytes) => {
31723
32161
  const image = new canvasModule.Image();
31724
- const base64 = Buffer.from(bytes).toString("base64");
31725
32162
  await new Promise((resolve, reject) => {
31726
32163
  image.onload = () => resolve();
31727
32164
  image.onerror = (error) => reject(error);
31728
- image.src = `data:${mimeType};base64,${base64}`;
32165
+ image.src = bytes;
31729
32166
  });
32167
+ image.onload = void 0;
32168
+ image.onerror = void 0;
31730
32169
  return new Texture({ source: new ImageSource({
31731
32170
  resource: image,
31732
32171
  width: image.width,
@@ -31760,7 +32199,7 @@ function exportBoardImageBytes(renderer, document, options = {}) {
31760
32199
  try {
31761
32200
  const bytes = format === "png" ? canvas.toBuffer(MIME.png) : canvas.toBuffer(MIME[format], Math.round(quality * 100));
31762
32201
  return {
31763
- bytes: new Uint8Array(bytes),
32202
+ bytes: new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength),
31764
32203
  plan: result.plan,
31765
32204
  warnings: result.warnings,
31766
32205
  format
@@ -31771,4 +32210,4 @@ function exportBoardImageBytes(renderer, document, options = {}) {
31771
32210
  }
31772
32211
  }
31773
32212
  //#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 };
32213
+ 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 };