@neta-art/cohub-cli 10.7.0 → 10.8.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 (36) hide show
  1. package/README.md +4 -4
  2. package/dist/board-kernel.js +194 -109
  3. package/dist/commands/boards/examples.js +16 -16
  4. package/dist/commands/boards.js +1 -1
  5. package/dist/commands/runtime.js +3 -14
  6. package/dist/runtime/harnesses/chained-transcript.d.ts +24 -0
  7. package/dist/runtime/harnesses/chained-transcript.js +62 -0
  8. package/dist/runtime/harnesses/claude-transcript.d.ts +8 -0
  9. package/dist/runtime/harnesses/claude-transcript.js +138 -0
  10. package/dist/runtime/harnesses/claude.js +20 -2
  11. package/dist/runtime/harnesses/codex.js +1 -1
  12. package/dist/runtime/harnesses/cursor-transcript.d.ts +37 -0
  13. package/dist/runtime/harnesses/cursor-transcript.js +183 -0
  14. package/dist/runtime/harnesses/cursor.d.ts +24 -0
  15. package/dist/runtime/harnesses/cursor.js +620 -0
  16. package/dist/runtime/harnesses/index.d.ts +1 -3
  17. package/dist/runtime/harnesses/index.js +4 -10
  18. package/dist/runtime/harnesses/pi.js +1 -1
  19. package/dist/runtime/harnesses/stream-json.d.ts +5 -1
  20. package/dist/runtime/harnesses/stream-json.js +31 -9
  21. package/dist/runtime/harnesses/types.d.ts +10 -5
  22. package/dist/runtime/harnesses/workbuddy-transcript.d.ts +8 -0
  23. package/dist/runtime/harnesses/workbuddy-transcript.js +127 -0
  24. package/dist/runtime/harnesses/workbuddy.d.ts +1 -1
  25. package/dist/runtime/harnesses/workbuddy.js +31 -7
  26. package/dist/runtime/json-rpc.d.ts +2 -0
  27. package/dist/runtime/json-rpc.js +9 -0
  28. package/dist/runtime/native/attach.d.ts +1 -1
  29. package/dist/runtime/native/attach.js +1 -4
  30. package/dist/runtime/native/execution.js +14 -3
  31. package/dist/runtime/native/ingest.d.ts +14 -0
  32. package/dist/runtime/native/ingest.js +51 -5
  33. package/dist/runtime/native/sessions.js +6 -1
  34. package/dist/runtime/native/transcript.d.ts +19 -0
  35. package/dist/runtime/native/transcript.js +33 -2
  36. package/package.json +3 -3
package/README.md CHANGED
@@ -137,8 +137,8 @@ cohub -s home spaces files ls
137
137
 
138
138
  ## Local Runtime
139
139
 
140
- Connect one local workspace to a Space and select Pi, Codex, Claude Code or WorkBuddy per turn.
141
- Claude Code (`claude`) and WorkBuddy (`codebuddy`) run Cohub Turns only; terminal sync covers Pi and Codex.
140
+ Connect one local workspace to a Space and select Pi, Codex, Claude Code, WorkBuddy or Cursor per turn.
141
+ Terminal conversations of every harness sync to the Space; Claude Code (`claude`), WorkBuddy (`codebuddy`) and Cursor (`cursor-agent`) sync read-only.
142
142
  Without `--harness`, every installed harness is tried; one that cannot start is skipped with a warning.
143
143
 
144
144
  To keep using native terminals with ordinary Cohub Chats / Turns:
@@ -181,7 +181,7 @@ are never uploaded automatically. Use `runtime logs --json` to export a failure
181
181
  events carry the local `runtimeId` plus server `requestId` / `traceparent` when a turn has
182
182
  reached the Agent trace.
183
183
 
184
- The Runtime uses WebSockets, native Pi RPC / Codex app-server / Claude Code stream-json, and existing cloud
184
+ The Runtime uses WebSockets, native Pi RPC / Codex app-server / Claude Code and Cursor stream-json, and existing cloud
185
185
  streaming. Local executables and credentials are required. `sandbox up` has been
186
186
  removed. Reconnection and result reconciliation are automatic; no recovery command is needed.
187
187
  When a result genuinely cannot be determined, the affected Chat offers an explicit stop
@@ -319,7 +319,7 @@ Use the filters so a large Board is not read whole:
319
319
  ```bash
320
320
  cohub -s <spaceId> boards get <boardId> --items title,note
321
321
  cohub -s <spaceId> boards get <boardId> --within s1
322
- cohub -s <spaceId> boards get <boardId> --rect 0,0,1600,900
322
+ cohub -s <spaceId> boards get <boardId> --rect 0,0,960,540
323
323
  ```
324
324
 
325
325
  Start from an example and write it with `apply`, a JSON Merge Patch where fields
@@ -5650,6 +5650,51 @@ function json(params) {
5650
5650
  return jsonSchema;
5651
5651
  }
5652
5652
  const BOARD_ARROW_STROKE_SIZE = 2.5;
5653
+ const BOARD_DEFAULT_ITEM_SIZE$1 = {
5654
+ width: 240,
5655
+ height: 160
5656
+ };
5657
+ const BOARD_DEFAULT_SIZES$1 = {
5658
+ frame: {
5659
+ width: 480,
5660
+ height: 320
5661
+ },
5662
+ shape: {
5663
+ width: 200,
5664
+ height: 140
5665
+ },
5666
+ image: {
5667
+ width: 480,
5668
+ height: 270
5669
+ },
5670
+ video: {
5671
+ width: 480,
5672
+ height: 270
5673
+ },
5674
+ audio: {
5675
+ width: 320,
5676
+ height: 112
5677
+ },
5678
+ file: {
5679
+ width: 260,
5680
+ height: 132
5681
+ },
5682
+ task: {
5683
+ width: 480,
5684
+ height: 270
5685
+ },
5686
+ effect: {
5687
+ width: 240,
5688
+ height: 240
5689
+ },
5690
+ custom: {
5691
+ width: 480,
5692
+ height: 320
5693
+ }
5694
+ };
5695
+ function boardDefaultSize(type) {
5696
+ return { ...Object.hasOwn(BOARD_DEFAULT_SIZES$1, type) ? BOARD_DEFAULT_SIZES$1[type] : BOARD_DEFAULT_ITEM_SIZE$1 };
5697
+ }
5653
5698
  const BOARD_FONT_STACK = "\"Geist\", system-ui, -apple-system, \"Noto Sans CJK SC\", \"Noto Sans SC\", \"PingFang SC\", \"Microsoft YaHei\", \"Noto Sans Math\", \"Noto Sans Symbols 2\", \"DejaVu Sans\", \"FreeSans\", \"FreeSerif\", sans-serif";
5654
5699
  const BOARD_MONO_FONT_STACK = "\"Geist Mono\", \"Fira Code\", ui-monospace, \"Noto Sans Mono CJK SC\", \"Noto Sans Math\", \"DejaVu Sans\", \"FreeSans\", \"FreeSerif\", monospace";
5655
5700
  //#endregion
@@ -5827,12 +5872,9 @@ const itemEnvelope$1 = {
5827
5872
  locked: boolean().optional(),
5828
5873
  metadata: record(string(), unknown()).optional()
5829
5874
  };
5830
- const boxed$1 = (width, height) => ({
5875
+ const boxed$1 = (size) => ({
5831
5876
  ...itemEnvelope$1,
5832
- size: BoardSizeSchema$1.default({
5833
- width,
5834
- height
5835
- })
5877
+ size: BoardSizeSchema$1.default({ ...size })
5836
5878
  });
5837
5879
  const unsized$1 = { ...itemEnvelope$1 };
5838
5880
  const fontSize$1 = finite$1.min(2).max(512);
@@ -5850,7 +5892,7 @@ const BOARD_SHAPE_GEOMETRIES$1 = [
5850
5892
  "path"
5851
5893
  ];
5852
5894
  const BoardFrameItemSchema$1 = object({
5853
- ...boxed$1(480, 320),
5895
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.frame),
5854
5896
  type: literal("frame"),
5855
5897
  props: object({
5856
5898
  label: string().max(280).default(""),
@@ -5874,7 +5916,7 @@ const BoardTextItemSchema$1 = object({
5874
5916
  }).strict().prefault({})
5875
5917
  }).strict();
5876
5918
  const BoardShapeItemSchema$1 = object({
5877
- ...boxed$1(240, 160),
5919
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.shape),
5878
5920
  type: literal("shape"),
5879
5921
  props: object({
5880
5922
  geometry: _enum(BOARD_SHAPE_GEOMETRIES$1).default("rectangle"),
@@ -5972,7 +6014,7 @@ const BoardCropSchema$1 = object({
5972
6014
  h: ratio$1
5973
6015
  }).strict().describe("Region shown, as fractions of the image. Without it the whole image fits inside size.");
5974
6016
  const BoardImageItemSchema$1 = object({
5975
- ...boxed$1(640, 360),
6017
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.image),
5976
6018
  type: literal("image"),
5977
6019
  props: object({
5978
6020
  ...mediaProps$1,
@@ -5980,17 +6022,17 @@ const BoardImageItemSchema$1 = object({
5980
6022
  }).strict()
5981
6023
  }).strict();
5982
6024
  const BoardVideoItemSchema$1 = object({
5983
- ...boxed$1(640, 360),
6025
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.video),
5984
6026
  type: literal("video"),
5985
6027
  props: object(timedFileProps$1).strict()
5986
6028
  }).strict();
5987
6029
  const BoardAudioItemSchema$1 = object({
5988
- ...boxed$1(480, 96),
6030
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.audio),
5989
6031
  type: literal("audio"),
5990
6032
  props: object(timedFileProps$1).strict()
5991
6033
  }).strict();
5992
6034
  const BoardFileItemSchema$1 = object({
5993
- ...boxed$1(360, 220),
6035
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.file),
5994
6036
  type: literal("file"),
5995
6037
  props: object(fileProps$1).strict()
5996
6038
  }).strict();
@@ -6047,7 +6089,7 @@ const BoardTaskSnapshotSchema$1 = object({
6047
6089
  updatedAt: string().optional()
6048
6090
  }).strict();
6049
6091
  const BoardTaskItemSchema$1 = object({
6050
- ...boxed$1(420, 240),
6092
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.task),
6051
6093
  type: literal("task"),
6052
6094
  props: object({
6053
6095
  taskRunId: string().min(1).max(160).describe("The generation run shown; the server keeps the card's status and results up to date."),
@@ -6062,7 +6104,7 @@ const BOARD_EFFECT_KINDS$1 = [
6062
6104
  "glow"
6063
6105
  ];
6064
6106
  const BoardEffectItemSchema$1 = object({
6065
- ...boxed$1(240, 240),
6107
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.effect),
6066
6108
  type: literal("effect"),
6067
6109
  props: object({
6068
6110
  kind: _enum(BOARD_EFFECT_KINDS$1),
@@ -6079,18 +6121,14 @@ const BoardEffectItemSchema$1 = object({
6079
6121
  }).strict()
6080
6122
  }).strict();
6081
6123
  const BoardCustomItemSchema$1 = object({
6082
- ...boxed$1(480, 320),
6124
+ ...boxed$1(BOARD_DEFAULT_SIZES$1.custom),
6083
6125
  type: literal("custom"),
6084
6126
  script: BoardScriptSchema$1,
6085
6127
  props: object({}).strict().default({})
6086
6128
  }).strict();
6087
6129
  const BOARD_EXTENSION_TYPE_PATTERN$1 = /^[a-z][a-z0-9]*(?:\.[a-z0-9-]+)+$/;
6088
6130
  const BoardExtensionItemSchema$1 = object({
6089
- ...itemEnvelope$1,
6090
- size: BoardSizeSchema$1.default({
6091
- width: 240,
6092
- height: 160
6093
- }),
6131
+ ...boxed$1(BOARD_DEFAULT_ITEM_SIZE$1),
6094
6132
  type: string().regex(BOARD_EXTENSION_TYPE_PATTERN$1),
6095
6133
  props: record(string(), unknown()).default({})
6096
6134
  }).strict();
@@ -6540,8 +6578,7 @@ function createBoardLayout(initialLookup, dependents) {
6540
6578
  } else box = {
6541
6579
  x: 0,
6542
6580
  y: 0,
6543
- width: 240,
6544
- height: 160
6581
+ ...boardDefaultSize(item.type)
6545
6582
  };
6546
6583
  boxes.set(id, box);
6547
6584
  return box;
@@ -27968,6 +28005,19 @@ function traceCardOutline(graphics, rect, style, grow = 0) {
27968
28005
  const radius = Math.min(Math.max(0, style.radius + grow), width / 2, height / 2);
27969
28006
  return radius > 0 ? graphics.roundRect(x, y, width, height, radius) : graphics.rect(x, y, width, height);
27970
28007
  }
28008
+ function fillCardPicture(graphics, texture, picture, style, crop) {
28009
+ const sampling = texture.source.style;
28010
+ if (sampling.addressMode !== "mirror-repeat") {
28011
+ sampling.addressMode = "mirror-repeat";
28012
+ sampling.update();
28013
+ }
28014
+ const matrix = crop ? new Matrix(1 / crop.w, 0, 0, 1 / crop.h, -crop.x / crop.w, -crop.y / crop.h) : void 0;
28015
+ return traceCardOutline(graphics, picture, style).fill({
28016
+ texture,
28017
+ textureSpace: "local",
28018
+ ...matrix ? { matrix } : {}
28019
+ });
28020
+ }
27971
28021
  function tracePixelRect(graphics, x, y, width, height, step) {
27972
28022
  const s = Math.min(step, width / 4, height / 4);
27973
28023
  if (s <= 0) return graphics.rect(x, y, width, height);
@@ -28303,6 +28353,9 @@ const audioCardRenderer = {
28303
28353
  update: (container, item, context) => {
28304
28354
  if (item.type === "audio") sync$13(container, item, context);
28305
28355
  },
28356
+ resolution: (container, item, context) => {
28357
+ if (item.type === "audio") sync$13(container, item, context);
28358
+ },
28306
28359
  renderFar: (graphics, item, context) => {
28307
28360
  drawFarPlate(graphics, item.frame, {
28308
28361
  fill: context.palette.surface,
@@ -28351,7 +28404,7 @@ function sync$12(container, item, context) {
28351
28404
  const elevation = cardElevation(context, item.id);
28352
28405
  const sig = [
28353
28406
  key ?? "",
28354
- texture ? `${texture.width}x${texture.height}` : "none",
28407
+ texture?.uid ?? "none",
28355
28408
  failed,
28356
28409
  width,
28357
28410
  height,
@@ -28420,6 +28473,9 @@ const modelCardRenderer = {
28420
28473
  update: (container, item, context) => {
28421
28474
  if (item.type === "file") sync$12(container, item, context);
28422
28475
  },
28476
+ resolution: (container, item, context) => {
28477
+ if (item.type === "file") sync$12(container, item, context);
28478
+ },
28423
28479
  zoom: (container, zoom) => {
28424
28480
  const tag = partsByContainer$12.get(container)?.tag;
28425
28481
  if (tag) zoomCardTag(tag, zoom);
@@ -28560,6 +28616,10 @@ const arrowCardRenderer = {
28560
28616
  update: (container, item, context) => {
28561
28617
  if (item.type === "arrow") sync$11(container, item, context);
28562
28618
  },
28619
+ resolution: (container, _item, context) => {
28620
+ const parts = partsByContainer$11.get(container);
28621
+ if (parts) syncTextResolution(parts.label, parts, context.zoom);
28622
+ },
28563
28623
  renderFar: (graphics, item, context) => {
28564
28624
  if (item.type !== "arrow") return;
28565
28625
  const arrow = item;
@@ -28969,7 +29029,7 @@ function sync$8(container, item, context) {
28969
29029
  height,
28970
29030
  detail,
28971
29031
  elevation,
28972
- texture ? `${texture.width}x${texture.height}` : "none",
29032
+ texture?.uid ?? "none",
28973
29033
  coverFailed,
28974
29034
  fileState,
28975
29035
  kind,
@@ -29154,6 +29214,9 @@ const fileCardRenderer = {
29154
29214
  update: (container, item, context) => {
29155
29215
  if (item.type === "file") sync$8(container, item, context);
29156
29216
  },
29217
+ resolution: (container, item, context) => {
29218
+ if (item.type === "file") sync$8(container, item, context);
29219
+ },
29157
29220
  zoom: (container, zoom) => {
29158
29221
  const tag = partsByContainer$8.get(container)?.tag;
29159
29222
  if (tag) zoomCardTag(tag, zoom);
@@ -29266,6 +29329,10 @@ const frameCardRenderer = {
29266
29329
  update: (container, item, context) => {
29267
29330
  if (item.type === "frame") sync$7(container, item, context);
29268
29331
  },
29332
+ resolution: (container, _item, context) => {
29333
+ const parts = partsByContainer$7.get(container);
29334
+ if (parts) syncTextResolution(parts.label, parts, context.zoom);
29335
+ },
29269
29336
  renderFar: (graphics, item, context) => {
29270
29337
  if (item.type !== "frame" || item.props.decoration === "none") return;
29271
29338
  drawFarPlate(graphics, item.frame, {
@@ -29472,6 +29539,10 @@ const shapeCardRenderer = {
29472
29539
  update: (container, item, context) => {
29473
29540
  if (item.type === "shape") sync$6(container, item, context);
29474
29541
  },
29542
+ resolution: (container, _item, context) => {
29543
+ const parts = partsByContainer$6.get(container);
29544
+ if (parts) syncTextResolution(parts.label, parts, context.zoom);
29545
+ },
29475
29546
  renderFar: (graphics, item, context) => {
29476
29547
  if (item.type !== "shape") return;
29477
29548
  drawFarPlate(graphics, item.frame, {
@@ -29499,8 +29570,7 @@ function sync$5(container, item, context) {
29499
29570
  positionShell(parts.root, item);
29500
29571
  syncTextResolution(parts.label, parts.label, context.zoom);
29501
29572
  const { width, height } = item.frame;
29502
- const resolution = Math.min(4, Math.max(.25, context.zoom * getBoardResolution()));
29503
- const texture = context.scripts?.frame(item, context.time, resolution) ?? null;
29573
+ const texture = context.scripts?.frame(item, context.time) ?? null;
29504
29574
  const error = context.scripts?.error(item.id) ?? null;
29505
29575
  parts.sprite.visible = Boolean(texture);
29506
29576
  const display = texture ?? Texture.EMPTY;
@@ -29565,6 +29635,10 @@ const customCardRenderer = {
29565
29635
  update: (container, item, context) => {
29566
29636
  if (item.type === "custom") sync$5(container, item, context);
29567
29637
  },
29638
+ resolution: (container, _item, context) => {
29639
+ const parts = partsByContainer$5.get(container);
29640
+ if (parts) syncTextResolution(parts.label, parts.label, context.zoom);
29641
+ },
29568
29642
  renderFar: (graphics, item, context) => {
29569
29643
  drawFarPlate(graphics, item.frame, {
29570
29644
  fill: context.palette.muted,
@@ -29592,28 +29666,6 @@ function cropRect(crop) {
29592
29666
  h
29593
29667
  } : null;
29594
29668
  }
29595
- function showTexture(parts, texture, crop) {
29596
- const rect = texture ? cropRect(crop) : null;
29597
- const key = rect ? `${rect.x}|${rect.y}|${rect.w}|${rect.h}` : "";
29598
- const previous = parts.cropped;
29599
- const reusable = previous && parts.cropSource === texture && parts.cropKey === key;
29600
- if (texture && rect && !reusable) {
29601
- const { frame } = texture;
29602
- const width = Math.max(1, frame.width * rect.w);
29603
- const height = Math.max(1, frame.height * rect.h);
29604
- const x = frame.x + Math.min(frame.width * rect.x, frame.width - width);
29605
- const y = frame.y + Math.min(frame.height * rect.y, frame.height - height);
29606
- parts.cropped = new Texture({
29607
- source: texture.source,
29608
- frame: new Rectangle(x, y, width, height)
29609
- });
29610
- } else if (!rect) parts.cropped = null;
29611
- parts.cropSource = rect ? texture : null;
29612
- parts.cropKey = key;
29613
- const display = parts.cropped ?? texture ?? Texture.EMPTY;
29614
- if (parts.sprite.texture !== display) parts.sprite.texture = display;
29615
- if (previous && previous !== parts.cropped) previous.destroy(false);
29616
- }
29617
29669
  const PRINT_MARGIN = 6;
29618
29670
  function sync$4(container, item, context) {
29619
29671
  const parts = partsByContainer$4.get(container);
@@ -29628,7 +29680,7 @@ function sync$4(container, item, context) {
29628
29680
  key ?? "",
29629
29681
  width,
29630
29682
  height,
29631
- texture ? `${texture.width}x${texture.height}` : "none",
29683
+ texture?.uid ?? "none",
29632
29684
  JSON.stringify(item.props.crop ?? null),
29633
29685
  failed,
29634
29686
  elevation,
@@ -29636,8 +29688,7 @@ function sync$4(container, item, context) {
29636
29688
  ].join("|");
29637
29689
  if (sig === parts.sig) return;
29638
29690
  parts.sig = sig;
29639
- showTexture(parts, texture, item.props.crop);
29640
- const display = parts.sprite.texture;
29691
+ const crop = cropRect(item.props.crop);
29641
29692
  const framed = context.style.media === "framed";
29642
29693
  const margin = framed ? Math.min(PRINT_MARGIN, width / 8, height / 8) : 0;
29643
29694
  const area = {
@@ -29646,7 +29697,7 @@ function sync$4(container, item, context) {
29646
29697
  width: width - margin * 2,
29647
29698
  height: height - margin * 2
29648
29699
  };
29649
- const fitted = texture ? containRect(area.width, area.height, display.width, display.height) : {
29700
+ const fitted = texture ? containRect(area.width, area.height, texture.width * (crop?.w ?? 1), texture.height * (crop?.h ?? 1)) : {
29650
29701
  ...area,
29651
29702
  x: 0,
29652
29703
  y: 0
@@ -29663,16 +29714,11 @@ function sync$4(container, item, context) {
29663
29714
  width: picture.width + margin * 2,
29664
29715
  height: picture.height + margin * 2
29665
29716
  } : picture;
29666
- parts.sprite.visible = Boolean(texture);
29667
- if (texture) {
29668
- parts.sprite.position.set(picture.x, picture.y);
29669
- parts.sprite.width = picture.width;
29670
- parts.sprite.height = picture.height;
29671
- }
29672
- traceCardOutline(parts.mask.clear(), picture, framed ? {
29717
+ parts.picture.clear();
29718
+ if (texture) fillCardPicture(parts.picture, texture, picture, framed ? {
29673
29719
  corner: "round",
29674
29720
  radius: 0
29675
- } : context.style).fill({ color: 16777215 });
29721
+ } : context.style, crop);
29676
29722
  parts.placeholder.clear();
29677
29723
  if (texture && !framed) return;
29678
29724
  drawCardSurface(parts.placeholder, card, context.style, {
@@ -29699,20 +29745,14 @@ const imageCardRenderer = {
29699
29745
  canRender: (item) => item.type === "image",
29700
29746
  create: (item, context) => {
29701
29747
  const root = new Container();
29702
- const sprite = new Sprite(Texture.EMPTY);
29703
29748
  const placeholder = new Graphics();
29704
- const mask = new Graphics();
29705
- root.addChild(placeholder, sprite, mask);
29706
- sprite.mask = mask;
29749
+ const picture = new Graphics();
29750
+ root.addChild(placeholder, picture);
29707
29751
  partsByContainer$4.set(root, {
29708
29752
  root,
29709
- sprite,
29710
29753
  placeholder,
29711
- mask,
29712
- sig: "",
29713
- cropped: null,
29714
- cropSource: null,
29715
- cropKey: ""
29754
+ picture,
29755
+ sig: ""
29716
29756
  });
29717
29757
  if (item.type === "image") sync$4(root, item, context);
29718
29758
  return root;
@@ -29727,9 +29767,7 @@ const imageCardRenderer = {
29727
29767
  });
29728
29768
  },
29729
29769
  destroy: (container) => {
29730
- const parts = partsByContainer$4.get(container);
29731
- parts?.root.destroy({ children: true });
29732
- parts?.cropped?.destroy(false);
29770
+ partsByContainer$4.get(container)?.root.destroy({ children: true });
29733
29771
  partsByContainer$4.delete(container);
29734
29772
  }
29735
29773
  };
@@ -29985,7 +30023,7 @@ function sync$3(container, item, context) {
29985
30023
  if (key !== parts.assetKey) {
29986
30024
  if (parts.assetKey) context.releaseTexture(parts.assetKey);
29987
30025
  parts.assetKey = key;
29988
- if (key) context.acquireTexture(key);
30026
+ if (key) context.acquireTexture(key, item);
29989
30027
  }
29990
30028
  const texture = key ? context.getTexture(key) : null;
29991
30029
  const previewFailed = Boolean(key && !texture && context.hasError(key));
@@ -30058,7 +30096,7 @@ function sync$3(container, item, context) {
30058
30096
  artifact?.type ?? "none",
30059
30097
  artifact?.id ?? "none",
30060
30098
  item.props.taskRunId,
30061
- texture ? `${texture.width}x${texture.height}` : "none",
30099
+ texture?.uid ?? "none",
30062
30100
  context.themeKey
30063
30101
  ].join("|");
30064
30102
  if (visualSig !== parts.visualSig) {
@@ -30227,6 +30265,9 @@ const taskCardRenderer = {
30227
30265
  const parts = partsByContainer$3.get(container);
30228
30266
  return parts ? advance(parts, clock) : null;
30229
30267
  },
30268
+ resolution: (container, item, context) => {
30269
+ if (item.type === "task") sync$3(container, item, context);
30270
+ },
30230
30271
  zoom: (container, zoom) => {
30231
30272
  const tag = partsByContainer$3.get(container)?.tag;
30232
30273
  if (tag) zoomCardTag(tag, zoom);
@@ -30337,6 +30378,10 @@ const textCardRenderer = {
30337
30378
  update: (container, item, context) => {
30338
30379
  if (item.type === "text") sync$2(container, item, context);
30339
30380
  },
30381
+ resolution: (container, _item, context) => {
30382
+ const parts = partsByContainer$2.get(container);
30383
+ if (parts) syncTextResolution(parts.body, parts, context.zoom * Math.max(1, parts.body.scale.x));
30384
+ },
30340
30385
  renderFar: (graphics, item, context) => {
30341
30386
  if (item.type !== "text") return;
30342
30387
  drawFarPlate(graphics, item.frame, {
@@ -30429,6 +30474,10 @@ const unknownCardRenderer = {
30429
30474
  update: (container, item, context) => {
30430
30475
  sync$1(container, item, context);
30431
30476
  },
30477
+ resolution: (container, _item, context) => {
30478
+ const parts = partsByContainer$1.get(container);
30479
+ if (parts) syncTextResolution(parts.label, parts, context.zoom);
30480
+ },
30432
30481
  renderFar: (graphics, item, context) => {
30433
30482
  drawFarPlate(graphics, item.frame, {
30434
30483
  fill: context.palette.muted,
@@ -30466,7 +30515,7 @@ function sync(container, item, context) {
30466
30515
  const elevation = cardElevation(context, item.id);
30467
30516
  const sig = [
30468
30517
  key ?? "",
30469
- texture ? `${texture.width}x${texture.height}` : "none",
30518
+ texture?.uid ?? "none",
30470
30519
  width,
30471
30520
  height,
30472
30521
  elevation,
@@ -30496,17 +30545,18 @@ function sync(container, item, context) {
30496
30545
  elevation
30497
30546
  });
30498
30547
  if (framed) drawFilmRails(parts.plate, width, height, rail, context.palette.bg);
30499
- traceCardOutline(parts.mask.clear(), framed ? screen : rect, framed ? {
30500
- corner: "round",
30501
- radius: 1
30502
- } : context.style).fill({ color: 16777215 });
30503
- if (texture && parts.sprite.texture !== texture) parts.sprite.texture = texture;
30504
- parts.sprite.visible = Boolean(texture);
30548
+ parts.picture.clear();
30505
30549
  if (texture) {
30506
30550
  const fitted = containRect(screen.width, screen.height, texture.width, texture.height);
30507
- parts.sprite.position.set(screen.x + fitted.x, screen.y + fitted.y);
30508
- parts.sprite.width = fitted.width;
30509
- parts.sprite.height = fitted.height;
30551
+ const picture = {
30552
+ ...fitted,
30553
+ x: screen.x + fitted.x,
30554
+ y: screen.y + fitted.y
30555
+ };
30556
+ fillCardPicture(parts.picture, texture, picture, framed ? {
30557
+ corner: "round",
30558
+ radius: 1
30559
+ } : context.style);
30510
30560
  }
30511
30561
  parts.chrome.clear();
30512
30562
  if (boardCardDetailed(context.zoom)) drawMediaPlayKey(parts.chrome, screen.x + screen.width / 2, screen.y + screen.height / 2, Math.min(18, Math.min(screen.width, screen.height) * .16), context);
@@ -30531,17 +30581,14 @@ const videoCardRenderer = {
30531
30581
  create: (item, context) => {
30532
30582
  const root = new Container();
30533
30583
  const plate = new Graphics();
30534
- const sprite = new Sprite(Texture.EMPTY);
30535
- const mask = new Graphics();
30584
+ const picture = new Graphics();
30536
30585
  const chrome = new Graphics();
30537
30586
  const tag = createCardTag();
30538
- root.addChild(plate, sprite, mask, chrome, tag.root);
30539
- sprite.mask = mask;
30587
+ root.addChild(plate, picture, chrome, tag.root);
30540
30588
  partsByContainer.set(root, {
30541
30589
  root,
30542
30590
  plate,
30543
- sprite,
30544
- mask,
30591
+ picture,
30545
30592
  chrome,
30546
30593
  tag,
30547
30594
  sig: ""
@@ -30552,6 +30599,9 @@ const videoCardRenderer = {
30552
30599
  update: (container, item, context) => {
30553
30600
  if (item.type === "video") sync(container, item, context);
30554
30601
  },
30602
+ resolution: (container, item, context) => {
30603
+ if (item.type === "video") sync(container, item, context);
30604
+ },
30555
30605
  zoom: (container, zoom) => {
30556
30606
  const tag = partsByContainer.get(container)?.tag;
30557
30607
  if (tag) zoomCardTag(tag, zoom);
@@ -30606,6 +30656,48 @@ function createBoardCardRendererResolver(renderers = []) {
30606
30656
  }
30607
30657
  /** The built-in renderers alone. */
30608
30658
  const getBoardCardRenderer = createBoardCardRendererResolver();
30659
+ const BOARD_DEFAULT_ITEM_SIZE = {
30660
+ width: 240,
30661
+ height: 160
30662
+ };
30663
+ const BOARD_DEFAULT_SIZES = {
30664
+ frame: {
30665
+ width: 480,
30666
+ height: 320
30667
+ },
30668
+ shape: {
30669
+ width: 200,
30670
+ height: 140
30671
+ },
30672
+ image: {
30673
+ width: 480,
30674
+ height: 270
30675
+ },
30676
+ video: {
30677
+ width: 480,
30678
+ height: 270
30679
+ },
30680
+ audio: {
30681
+ width: 320,
30682
+ height: 112
30683
+ },
30684
+ file: {
30685
+ width: 260,
30686
+ height: 132
30687
+ },
30688
+ task: {
30689
+ width: 480,
30690
+ height: 270
30691
+ },
30692
+ effect: {
30693
+ width: 240,
30694
+ height: 240
30695
+ },
30696
+ custom: {
30697
+ width: 480,
30698
+ height: 320
30699
+ }
30700
+ };
30609
30701
  //#endregion
30610
30702
  //#region ../protocol/dist/board-url.js
30611
30703
  const BOARD_REMOTE_URL_MAX_LENGTH = 4096;
@@ -30781,12 +30873,9 @@ const itemEnvelope = {
30781
30873
  locked: boolean().optional(),
30782
30874
  metadata: record(string(), unknown()).optional()
30783
30875
  };
30784
- const boxed = (width, height) => ({
30876
+ const boxed = (size) => ({
30785
30877
  ...itemEnvelope,
30786
- size: BoardSizeSchema.default({
30787
- width,
30788
- height
30789
- })
30878
+ size: BoardSizeSchema.default({ ...size })
30790
30879
  });
30791
30880
  const unsized = { ...itemEnvelope };
30792
30881
  const fontSize = finite.min(2).max(512);
@@ -30804,7 +30893,7 @@ const BOARD_SHAPE_GEOMETRIES = [
30804
30893
  "path"
30805
30894
  ];
30806
30895
  object({
30807
- ...boxed(480, 320),
30896
+ ...boxed(BOARD_DEFAULT_SIZES.frame),
30808
30897
  type: literal("frame"),
30809
30898
  props: object({
30810
30899
  label: string().max(280).default(""),
@@ -30828,7 +30917,7 @@ object({
30828
30917
  }).strict().prefault({})
30829
30918
  }).strict();
30830
30919
  object({
30831
- ...boxed(240, 160),
30920
+ ...boxed(BOARD_DEFAULT_SIZES.shape),
30832
30921
  type: literal("shape"),
30833
30922
  props: object({
30834
30923
  geometry: _enum(BOARD_SHAPE_GEOMETRIES).default("rectangle"),
@@ -30926,7 +31015,7 @@ const BoardCropSchema = object({
30926
31015
  h: ratio
30927
31016
  }).strict().describe("Region shown, as fractions of the image. Without it the whole image fits inside size.");
30928
31017
  object({
30929
- ...boxed(640, 360),
31018
+ ...boxed(BOARD_DEFAULT_SIZES.image),
30930
31019
  type: literal("image"),
30931
31020
  props: object({
30932
31021
  ...mediaProps,
@@ -30934,17 +31023,17 @@ object({
30934
31023
  }).strict()
30935
31024
  }).strict();
30936
31025
  object({
30937
- ...boxed(640, 360),
31026
+ ...boxed(BOARD_DEFAULT_SIZES.video),
30938
31027
  type: literal("video"),
30939
31028
  props: object(timedFileProps).strict()
30940
31029
  }).strict();
30941
31030
  object({
30942
- ...boxed(480, 96),
31031
+ ...boxed(BOARD_DEFAULT_SIZES.audio),
30943
31032
  type: literal("audio"),
30944
31033
  props: object(timedFileProps).strict()
30945
31034
  }).strict();
30946
31035
  object({
30947
- ...boxed(360, 220),
31036
+ ...boxed(BOARD_DEFAULT_SIZES.file),
30948
31037
  type: literal("file"),
30949
31038
  props: object(fileProps).strict()
30950
31039
  }).strict();
@@ -31001,7 +31090,7 @@ const BoardTaskSnapshotSchema = object({
31001
31090
  updatedAt: string().optional()
31002
31091
  }).strict();
31003
31092
  object({
31004
- ...boxed(420, 240),
31093
+ ...boxed(BOARD_DEFAULT_SIZES.task),
31005
31094
  type: literal("task"),
31006
31095
  props: object({
31007
31096
  taskRunId: string().min(1).max(160).describe("The generation run shown; the server keeps the card's status and results up to date."),
@@ -31016,7 +31105,7 @@ const BOARD_EFFECT_KINDS = [
31016
31105
  "glow"
31017
31106
  ];
31018
31107
  object({
31019
- ...boxed(240, 240),
31108
+ ...boxed(BOARD_DEFAULT_SIZES.effect),
31020
31109
  type: literal("effect"),
31021
31110
  props: object({
31022
31111
  kind: _enum(BOARD_EFFECT_KINDS),
@@ -31033,18 +31122,14 @@ object({
31033
31122
  }).strict()
31034
31123
  }).strict();
31035
31124
  object({
31036
- ...boxed(480, 320),
31125
+ ...boxed(BOARD_DEFAULT_SIZES.custom),
31037
31126
  type: literal("custom"),
31038
31127
  script: BoardScriptSchema,
31039
31128
  props: object({}).strict().default({})
31040
31129
  }).strict();
31041
31130
  const BOARD_EXTENSION_TYPE_PATTERN = /^[a-z][a-z0-9]*(?:\.[a-z0-9-]+)+$/;
31042
31131
  object({
31043
- ...itemEnvelope,
31044
- size: BoardSizeSchema.default({
31045
- width: 240,
31046
- height: 160
31047
- }),
31132
+ ...boxed(BOARD_DEFAULT_ITEM_SIZE),
31048
31133
  type: string().regex(BOARD_EXTENSION_TYPE_PATTERN),
31049
31134
  props: record(string(), unknown()).default({})
31050
31135
  }).strict();