@neta-art/cohub 12.2.0 → 12.3.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 (102) hide show
  1. package/README.md +77 -3
  2. package/dist/board/editor/clipboard.js +1 -1
  3. package/dist/board/editor/creation.js +1 -1
  4. package/dist/board/editor/document.d.ts +3 -1
  5. package/dist/board/editor/document.js +8 -6
  6. package/dist/board/editor/editor.d.ts +7 -3
  7. package/dist/board/editor/editor.js +4 -2
  8. package/dist/board/editor/index.d.ts +3 -3
  9. package/dist/board/editor/index.js +2 -2
  10. package/dist/board/editor/interaction.d.ts +23 -2
  11. package/dist/board/editor/interaction.js +93 -4
  12. package/dist/board/editor/query.d.ts +1 -1
  13. package/dist/board/editor/query.js +3 -7
  14. package/dist/board/editor/selection.js +1 -1
  15. package/dist/board/editor/tool.d.ts +4 -1
  16. package/dist/board/editor/tool.js +6 -1
  17. package/dist/board/export/index.js +1 -1
  18. package/dist/board/export/node/scripts.js +1 -1
  19. package/dist/board/export/scene.js +1 -1
  20. package/dist/board/model/asset-key.d.ts +9 -1
  21. package/dist/board/model/asset-key.js +15 -3
  22. package/dist/board/model/index.d.ts +5 -5
  23. package/dist/board/model/index.js +18 -18
  24. package/dist/board/model/items/create.js +2 -2
  25. package/dist/board/model/items/definition.d.ts +9 -1
  26. package/dist/board/model/items/index.d.ts +2 -2
  27. package/dist/board/model/items/registry.d.ts +2 -1
  28. package/dist/board/model/items/registry.js +1 -0
  29. package/dist/board/model/sampling.js +1 -1
  30. package/dist/board/model/text-metrics.d.ts +21 -1
  31. package/dist/board/model/text-metrics.js +24 -1
  32. package/dist/board/model/themes.d.ts +15 -8
  33. package/dist/board/model/themes.js +15 -10
  34. package/dist/board/render/index.d.ts +8 -6
  35. package/dist/board/render/index.js +7 -5
  36. package/dist/board/render/labels.d.ts +15 -0
  37. package/dist/board/render/labels.js +14 -0
  38. package/dist/board/render/lod.d.ts +9 -1
  39. package/dist/board/render/lod.js +7 -1
  40. package/dist/board/render/media-interaction.d.ts +5 -2
  41. package/dist/board/render/media-interaction.js +15 -3
  42. package/dist/board/render/renderers/arrow-card-renderer.js +7 -5
  43. package/dist/board/render/renderers/audio-card-renderer.js +1 -1
  44. package/dist/board/render/renderers/board-renderer-registry.d.ts +12 -1
  45. package/dist/board/render/renderers/board-renderer-registry.js +9 -4
  46. package/dist/board/render/renderers/draw-card-renderer.js +1 -1
  47. package/dist/board/render/renderers/far-plate.d.ts +4 -1
  48. package/dist/board/render/renderers/far-plate.js +5 -1
  49. package/dist/board/render/renderers/file-card-renderer.js +46 -26
  50. package/dist/board/render/renderers/frame-card-renderer.js +9 -3
  51. package/dist/board/render/renderers/image-card-renderer.js +2 -2
  52. package/dist/board/render/renderers/model-card-renderer.js +5 -5
  53. package/dist/board/render/renderers/shape-card-renderer.js +1 -1
  54. package/dist/board/render/renderers/task-card-renderer.js +2 -2
  55. package/dist/board/render/renderers/video-card-renderer.js +3 -3
  56. package/dist/board/render/script-export.js +1 -1
  57. package/dist/board/render/script-host.js +3 -3
  58. package/dist/board/render/style.d.ts +1 -0
  59. package/dist/board/render/style.js +3 -1
  60. package/dist/board/render/text-edit.d.ts +32 -0
  61. package/dist/board/render/text-edit.js +134 -0
  62. package/dist/board/render/text-fit.d.ts +1 -3
  63. package/dist/board/render/text-fit.js +4 -43
  64. package/dist/board/render/text-measurement.d.ts +12 -2
  65. package/dist/board/render/text-measurement.js +55 -10
  66. package/dist/board/replica/index.d.ts +4 -2
  67. package/dist/board/replica/index.js +28 -17
  68. package/dist/board/replica/indexeddb.d.ts +16 -0
  69. package/dist/board/replica/indexeddb.js +155 -0
  70. package/dist/board/stage/assets.d.ts +16 -4
  71. package/dist/board/stage/assets.js +155 -30
  72. package/dist/board/stage/bench.d.ts +35 -0
  73. package/dist/board/stage/bench.js +153 -0
  74. package/dist/board/stage/device.d.ts +41 -0
  75. package/dist/board/stage/device.js +62 -0
  76. package/dist/board/stage/frame-budget.d.ts +19 -0
  77. package/dist/board/stage/frame-budget.js +69 -0
  78. package/dist/board/stage/idle.d.ts +4 -0
  79. package/dist/board/stage/idle.js +26 -0
  80. package/dist/board/stage/index.d.ts +9 -5
  81. package/dist/board/stage/index.js +5 -2
  82. package/dist/board/stage/presses.d.ts +21 -0
  83. package/dist/board/stage/presses.js +66 -0
  84. package/dist/board/stage/scene.d.ts +4 -0
  85. package/dist/board/stage/scene.js +45 -13
  86. package/dist/board/stage/stage.d.ts +17 -2
  87. package/dist/board/stage/stage.js +314 -95
  88. package/dist/board/stage/texture-pool.d.ts +6 -4
  89. package/dist/board/stage/texture-pool.js +42 -8
  90. package/dist/chunks/environment.d.ts +6 -0
  91. package/dist/chunks/http.d.ts +9 -1
  92. package/dist/chunks/http.js +791 -4
  93. package/dist/index.d.ts +52 -1
  94. package/dist/index.js +65 -733
  95. package/dist/protocol/dist/board-model.d.ts +15 -1
  96. package/dist/protocol/dist/board-model.js +12 -2
  97. package/dist/protocol/dist/board-patch.js +1 -1
  98. package/dist/protocol/dist/board-schema.d.ts +2 -1
  99. package/dist/protocol/dist/board-schema.js +4 -1
  100. package/dist/protocol/dist/core/media.js +4 -0
  101. package/dist/protocol/dist/index.d.ts +2 -2
  102. package/package.json +2 -1
@@ -0,0 +1,14 @@
1
+ import { pathPointAt } from "../model/arrow-geometry.js";
2
+ //#region src/board/render/labels.ts
3
+ const FRAME_LABEL_WEIGHT = "600";
4
+ const ARROW_LABEL = {
5
+ fontWeight: "500",
6
+ lineHeight: 1.25,
7
+ padX: .5,
8
+ padY: .25
9
+ };
10
+ function arrowLabelAnchor(arrow, style) {
11
+ return style.trim === void 0 ? arrow.mid : pathPointAt(arrow.path, .5);
12
+ }
13
+ //#endregion
14
+ export { ARROW_LABEL, FRAME_LABEL_WEIGHT, arrowLabelAnchor };
@@ -17,6 +17,14 @@ declare const COMPACT_BOARD_LOD: BoardLod;
17
17
  declare const BOARD_LOD_HYSTERESIS = 0.8;
18
18
  declare function boardCardDetailed(zoom: number): boolean;
19
19
  declare function boardLongEdge(item: BoardSceneItem): number;
20
+ type BoardLodRenderer = {
21
+ lod?: {
22
+ kind: BoardLodKind;
23
+ size?: (item: BoardSceneItem) => number;
24
+ };
25
+ };
26
+ declare function boardLodSize(renderer: BoardLodRenderer, item: BoardSceneItem): number;
27
+ declare function boardLodRatio(renderer: BoardLodRenderer, size: number, zoom: number, lod: BoardLod): number;
20
28
  declare function boardTextSize(item: BoardSceneItem): number;
21
29
  //#endregion
22
- export { BOARD_LOD_HYSTERESIS, BoardLod, BoardLodKind, COMPACT_BOARD_LOD, DEFAULT_BOARD_LOD, boardCardDetailed, boardLongEdge, boardTextSize };
30
+ export { BOARD_LOD_HYSTERESIS, BoardLod, BoardLodKind, COMPACT_BOARD_LOD, DEFAULT_BOARD_LOD, boardCardDetailed, boardLodRatio, boardLodSize, boardLongEdge, boardTextSize };
@@ -18,10 +18,16 @@ function boardCardDetailed(zoom) {
18
18
  function boardLongEdge(item) {
19
19
  return Math.max(item.frame.width, item.frame.height);
20
20
  }
21
+ function boardLodSize(renderer, item) {
22
+ return renderer.lod?.size?.(item) ?? boardLongEdge(item);
23
+ }
24
+ function boardLodRatio(renderer, size, zoom, lod) {
25
+ return size * zoom / lod[renderer.lod?.kind ?? "card"];
26
+ }
21
27
  function boardTextSize(item) {
22
28
  if (item.type !== "text") return boardLongEdge(item);
23
29
  const scale = item.frame.width / Math.max(1e-4, layoutBoardText(item.props).width);
24
30
  return clampBoardFontSize(item.props.fontSize ?? 24) * scale;
25
31
  }
26
32
  //#endregion
27
- export { BOARD_LOD_HYSTERESIS, COMPACT_BOARD_LOD, DEFAULT_BOARD_LOD, boardCardDetailed, boardLongEdge, boardTextSize };
33
+ export { BOARD_LOD_HYSTERESIS, COMPACT_BOARD_LOD, DEFAULT_BOARD_LOD, boardCardDetailed, boardLodRatio, boardLodSize, boardLongEdge, boardTextSize };
@@ -1,8 +1,10 @@
1
1
  import { WorldPoint } from "../model/geometry.js";
2
2
  import { BoardSceneItem } from "../model/scene.js";
3
+ import { BoardCardHit, BoardRenderContext } from "./renderers/board-renderer-registry.js";
3
4
  //#region src/board/render/media-interaction.d.ts
5
+ declare const BOARD_PLAY_MEDIA = "play-media";
4
6
  type BoardMediaAction = {
5
- action: "play-media";
7
+ action: typeof BOARD_PLAY_MEDIA;
6
8
  itemId: string;
7
9
  };
8
10
  declare function mediaPlayBadgeVisible(item: BoardSceneItem, zoom: number, options: {
@@ -15,5 +17,6 @@ declare function boardMediaActionAt(item: BoardSceneItem, point: WorldPoint, zoo
15
17
  materialized: boolean;
16
18
  hasVideoPreview: boolean;
17
19
  }): BoardMediaAction | null;
20
+ declare function mediaPlayHit(item: BoardSceneItem, point: WorldPoint, context: BoardRenderContext): BoardCardHit | null;
18
21
  //#endregion
19
- export { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter };
22
+ export { BOARD_PLAY_MEDIA, BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayHit, mediaPlayKeyCenter };
@@ -1,10 +1,11 @@
1
1
  import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
2
2
  import { degToRad, rotatePointAround, worldPoint } from "../model/geometry.js";
3
- import { isBoardModelItem } from "../model/media.js";
4
3
  import { boardCardDetailed } from "./lod.js";
4
+ import { isBoardModelItem } from "../model/media.js";
5
5
  import { audioKeyCenter } from "./renderers/audio-card-renderer.js";
6
6
  import { modelKeyCenter } from "./renderers/model-card-renderer.js";
7
7
  //#region src/board/render/media-interaction.ts
8
+ const BOARD_PLAY_MEDIA = "play-media";
8
9
  function mediaPlayBadgeVisible(item, zoom, options) {
9
10
  if (!options.materialized) return false;
10
11
  if (item.type === "video" || item.type === "audio") return true;
@@ -34,9 +35,20 @@ function mediaPlayBadgeHit(item, point, zoom) {
34
35
  }
35
36
  function boardMediaActionAt(item, point, zoom, options) {
36
37
  return mediaPlayBadgeVisible(item, zoom, options) && mediaPlayBadgeHit(item, point, zoom) ? {
37
- action: "play-media",
38
+ action: BOARD_PLAY_MEDIA,
38
39
  itemId: item.id
39
40
  } : null;
40
41
  }
42
+ const PLAY_HIT = {
43
+ action: BOARD_PLAY_MEDIA,
44
+ cursor: "pointer"
45
+ };
46
+ function mediaPlayHit(item, point, context) {
47
+ const key = context.assetKey(item);
48
+ return boardMediaActionAt(item, point, context.zoom, {
49
+ materialized: true,
50
+ hasVideoPreview: Boolean(key && context.getTexture(key))
51
+ }) ? PLAY_HIT : null;
52
+ }
41
53
  //#endregion
42
- export { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter };
54
+ export { BOARD_PLAY_MEDIA, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayHit, mediaPlayKeyCenter };
@@ -1,10 +1,11 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
- import { pathPointAt, resolveSceneArrow } from "../../model/arrow-geometry.js";
3
2
  import { itemAlpha, itemColor } from "../palette.js";
3
+ import { resolveSceneArrow } from "../../model/arrow-geometry.js";
4
4
  import { syncTextResolution } from "../text-resolution.js";
5
5
  import { dashPattern, endAngle, traceArrowhead, tracePolyline, trimPolyline } from "../stroke.js";
6
6
  import { drawCardSurface } from "../surface.js";
7
7
  import { drawFarStroke } from "./far-plate.js";
8
+ import { ARROW_LABEL, arrowLabelAnchor } from "../labels.js";
8
9
  import { Container, Graphics, Text } from "pixi.js";
9
10
  //#region src/board/render/renderers/arrow-card-renderer.ts
10
11
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -67,8 +68,9 @@ function sync(container, item, context) {
67
68
  parts.label.text = props.label;
68
69
  parts.label.style.fill = context.palette.text;
69
70
  parts.label.style.fontSize = props.fontSize;
70
- const padX = props.fontSize * .5;
71
- const padY = props.fontSize * .25;
71
+ parts.label.style.lineHeight = props.fontSize * ARROW_LABEL.lineHeight;
72
+ const padX = props.fontSize * ARROW_LABEL.padX;
73
+ const padY = props.fontSize * ARROW_LABEL.padY;
72
74
  const { width, height } = parts.label;
73
75
  const plate = {
74
76
  x: -width / 2 - padX,
@@ -88,7 +90,7 @@ function sync(container, item, context) {
88
90
  const visible = props.label.length > 0 && (style.trim === void 0 || style.trim >= .5);
89
91
  parts.label.visible = visible;
90
92
  parts.backdrop.visible = visible;
91
- const mid = style.trim === void 0 ? resolved.mid : pathPointAt(resolved.path, .5);
93
+ const mid = arrowLabelAnchor(resolved, style);
92
94
  parts.label.position.set(mid.x, mid.y);
93
95
  parts.backdrop.position.set(mid.x, mid.y);
94
96
  }
@@ -104,7 +106,7 @@ const arrowCardRenderer = {
104
106
  fill: 16777215,
105
107
  fontFamily: BOARD_FONT_STACK,
106
108
  fontSize: 14,
107
- fontWeight: "500"
109
+ fontWeight: ARROW_LABEL.fontWeight
108
110
  },
109
111
  resolution: context.textResolution,
110
112
  roundPixels: true
@@ -1,8 +1,8 @@
1
- import { getMediaResourceTitle } from "../../model/media.js";
2
1
  import { boardCardDetailed } from "../lod.js";
3
2
  import { fitTextToWidth } from "../text-fit.js";
4
3
  import { syncTextResolution } from "../text-resolution.js";
5
4
  import { formatBoardDuration } from "../card-tag.js";
5
+ import { getMediaResourceTitle } from "../../model/media.js";
6
6
  import { drawAudioWaveform } from "../audio-waveform.js";
7
7
  import { cardElevation, drawCardSurface } from "../surface.js";
8
8
  import { positionShell } from "./base-card-renderer.js";
@@ -1,6 +1,7 @@
1
1
  import { BoardCustomItem, BoardDocument, BoardSettings, BoardTaskItem } from "../../../protocol/dist/board-model.js";
2
2
  import { BoardFrame } from "../../../protocol/dist/board-layout.js";
3
3
  import "../../../protocol/dist/index.js";
4
+ import { WorldPoint } from "../../model/geometry.js";
4
5
  import { BoardScene, BoardSceneItem, SceneItem } from "../../model/scene.js";
5
6
  import { BoardShapeColors } from "../../model/palette.js";
6
7
  import { BoardRenderStyle } from "../style.js";
@@ -64,9 +65,18 @@ type BoardRenderContext = {
64
65
  assetKey: (item: BoardSceneItem) => string | null;
65
66
  getTexture: (key: string) => Texture | null;
66
67
  hasError: (key: string) => boolean;
68
+ assetTone?: (key: string) => number | null;
67
69
  fileState: (path: string) => "ok" | "missing" | "unavailable";
68
70
  acquireTexture: (key: string, item: BoardSceneItem) => void;
69
71
  releaseTexture: (key: string) => void;
72
+ hoveredHit?: {
73
+ itemId: string;
74
+ action: string;
75
+ } | null;
76
+ };
77
+ type BoardCardHit = {
78
+ action: string;
79
+ cursor?: string;
70
80
  };
71
81
  type BoardCardRenderer = {
72
82
  id: string;
@@ -89,6 +99,7 @@ type BoardCardRenderer = {
89
99
  kind: BoardLodKind;
90
100
  size?: (item: BoardSceneItem) => number;
91
101
  };
102
+ hitAt?: (item: BoardSceneItem, point: WorldPoint, context: BoardRenderContext) => BoardCardHit | null;
92
103
  renderFar?: (graphics: Graphics, item: BoardSceneItem, context: BoardRenderContext) => void;
93
104
  destroy?: (container: Container, context: BoardRenderContext) => void;
94
105
  };
@@ -101,4 +112,4 @@ declare function createBoardCardRendererResolver(renderers?: Iterable<BoardCardR
101
112
  /** The built-in renderers alone. */
102
113
  declare const getBoardCardRenderer: BoardCardRendererResolver;
103
114
  //#endregion
104
- export { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderLabels, BoardRenderPalette, BoardScriptHost, DEFAULT_BOARD_RENDER_LABELS, createBoardCardRendererResolver, getBoardCardRenderer };
115
+ export { BoardCardHit, BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderLabels, BoardRenderPalette, BoardScriptHost, DEFAULT_BOARD_RENDER_LABELS, createBoardCardRendererResolver, getBoardCardRenderer };
@@ -1,6 +1,7 @@
1
1
  import { ensureBoardTextMeasurement } from "../text-measurement.js";
2
2
  import { audioCardRenderer } from "./audio-card-renderer.js";
3
3
  import { modelCardRenderer } from "./model-card-renderer.js";
4
+ import { mediaPlayHit } from "../media-interaction.js";
4
5
  import { arrowCardRenderer } from "./arrow-card-renderer.js";
5
6
  import { drawCardRenderer } from "./draw-card-renderer.js";
6
7
  import { effectCardRenderer } from "./effect-card-renderer.js";
@@ -21,14 +22,18 @@ const DEFAULT_BOARD_RENDER_LABELS = {
21
22
  previewUnavailable: "Preview unavailable",
22
23
  noPreview: "No preview"
23
24
  };
25
+ const playable = (renderer) => ({
26
+ ...renderer,
27
+ hitAt: mediaPlayHit
28
+ });
24
29
  const builtinCardRenderers = [
25
30
  textCardRenderer,
26
31
  imageCardRenderer,
27
- videoCardRenderer,
28
- audioCardRenderer,
29
- modelCardRenderer,
32
+ playable(videoCardRenderer),
33
+ playable(audioCardRenderer),
34
+ playable(modelCardRenderer),
30
35
  fileCardRenderer,
31
- taskCardRenderer,
36
+ playable(taskCardRenderer),
32
37
  shapeCardRenderer,
33
38
  drawCardRenderer,
34
39
  arrowCardRenderer,
@@ -1,8 +1,8 @@
1
1
  import { applyMatrix } from "../../../protocol/dist/board-layout.js";
2
- import { computeDrawBounds, createStrokeRibbonBuilder, trimDrawPoints } from "../../model/draw-geometry.js";
3
2
  import { itemAlpha, itemColor } from "../palette.js";
4
3
  import { positionShell } from "./base-card-renderer.js";
5
4
  import { drawFarStroke, farStrokeSamples } from "./far-plate.js";
5
+ import { computeDrawBounds, createStrokeRibbonBuilder, trimDrawPoints } from "../../model/draw-geometry.js";
6
6
  import { Container, Graphics, Mesh, MeshGeometry, Texture } from "pixi.js";
7
7
  //#region src/board/render/renderers/draw-card-renderer.ts
8
8
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -1,5 +1,7 @@
1
1
  import { BoardFrame } from "../../../protocol/dist/board-layout.js";
2
2
  import "../../../protocol/dist/index.js";
3
+ import { BoardSceneItem } from "../../model/scene.js";
4
+ import { BoardRenderContext } from "./board-renderer-registry.js";
3
5
  import { Graphics } from "pixi.js";
4
6
  //#region src/board/render/renderers/far-plate.d.ts
5
7
  type LocalRect = {
@@ -14,6 +16,7 @@ type FarPlateStyle = {
14
16
  accent?: number;
15
17
  accentAlpha?: number;
16
18
  };
19
+ declare function farPictureTone(item: BoardSceneItem, context: BoardRenderContext): number | undefined;
17
20
  declare function drawFarPlate(graphics: Graphics, frame: BoardFrame, style: FarPlateStyle): void;
18
21
  declare function drawFarBars(graphics: Graphics, frame: BoardFrame, bars: readonly LocalRect[], style: {
19
22
  color: number;
@@ -29,4 +32,4 @@ declare function drawFarStroke(graphics: Graphics, points: ReadonlyArray<{
29
32
  alpha?: number;
30
33
  }): void;
31
34
  //#endregion
32
- export { FarPlateStyle, drawFarBars, drawFarPlate, drawFarStroke, farStrokeSamples };
35
+ export { FarPlateStyle, drawFarBars, drawFarPlate, drawFarStroke, farPictureTone, farStrokeSamples };
@@ -32,6 +32,10 @@ function traceLocal(graphics, frame, rect) {
32
32
  const d = corner(x, y + height);
33
33
  graphics.moveTo(a.x, a.y).lineTo(b.x, b.y).lineTo(c.x, c.y).lineTo(d.x, d.y).closePath();
34
34
  }
35
+ function farPictureTone(item, context) {
36
+ const key = context.assetKey(item);
37
+ return (key ? context.assetTone?.(key) : null) ?? void 0;
38
+ }
35
39
  function drawFarPlate(graphics, frame, style) {
36
40
  if (frame.width <= 0 || frame.height <= 0) return;
37
41
  traceLocal(graphics, frame, {
@@ -89,4 +93,4 @@ function drawFarStroke(graphics, points, style) {
89
93
  });
90
94
  }
91
95
  //#endregion
92
- export { drawFarBars, drawFarPlate, drawFarStroke, farStrokeSamples };
96
+ export { drawFarBars, drawFarPlate, drawFarStroke, farPictureTone, farStrokeSamples };
@@ -1,13 +1,14 @@
1
1
  import { BOARD_FONT_STACK, BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
- import { fileCategory, fileStem } from "../../model/file-snapshot.js";
3
- import { fileCategoryAccent, fileMetaLine, filePreviewKind, fileTypeLabel } from "../../model/file-preview.js";
2
+ import { boardItemPreview } from "../../../protocol/dist/board-model.js";
4
3
  import { boardCardDetailed } from "../lod.js";
5
4
  import { fitTextToLines } from "../text-fit.js";
6
5
  import { syncTextResolution } from "../text-resolution.js";
7
6
  import { createCardTag, syncCardTag, zoomCardTag } from "../card-tag.js";
8
7
  import { cardElevation, containRect, drawCardSurface, traceCardOutline } from "../surface.js";
9
8
  import { positionShell } from "./base-card-renderer.js";
10
- import { drawFarPlate } from "./far-plate.js";
9
+ import { drawFarPlate, farPictureTone } from "./far-plate.js";
10
+ import { fileCategory, fileStem } from "../../model/file-snapshot.js";
11
+ import { fileCategoryAccent, fileMetaLine, filePreviewKind, fileTypeLabel } from "../../model/file-preview.js";
11
12
  import { Container, Graphics, Sprite, Text, Texture } from "pixi.js";
12
13
  //#region src/board/render/renderers/file-card-renderer.ts
13
14
  const PADDING = 10;
@@ -27,8 +28,34 @@ const partsByContainer = /* @__PURE__ */ new WeakMap();
27
28
  function detailFor(zoom) {
28
29
  return boardCardDetailed(zoom) ? "full" : "title";
29
30
  }
30
- function coverHeight(item, height) {
31
- if (filePreviewKind(item.props.snapshot) !== "cover") return 0;
31
+ function viewOf(item) {
32
+ if (item.type === "file") {
33
+ const { src, snapshot } = item.props;
34
+ return {
35
+ src,
36
+ title: snapshot?.title || fileStem(src),
37
+ excerpt: snapshot?.excerpt ?? "",
38
+ kind: filePreviewKind(snapshot),
39
+ meta: fileMetaLine(src, snapshot?.size),
40
+ mark: fileTypeLabel(src),
41
+ category: fileCategory(src, snapshot?.mimeType)
42
+ };
43
+ }
44
+ const preview = boardItemPreview(item);
45
+ if (!preview) return null;
46
+ const label = (item.type.split(".").pop() ?? item.type).toUpperCase();
47
+ return {
48
+ src: preview.src ?? null,
49
+ title: preview.title || item.type,
50
+ excerpt: preview.excerpt ?? "",
51
+ kind: preview.cover ? "cover" : preview.excerpt ? "text" : "blank",
52
+ meta: label,
53
+ mark: label,
54
+ category: preview.src ? fileCategory(preview.src) : "other"
55
+ };
56
+ }
57
+ function coverHeight(view, height) {
58
+ if (view.kind !== "cover") return 0;
32
59
  const ideal = Math.round(height * COVER_RATIO);
33
60
  return Math.max(0, Math.min(ideal, height - MIN_BODY_FOR_COVER));
34
61
  }
@@ -47,31 +74,31 @@ function syncClip(parts, rect, style) {
47
74
  }
48
75
  function sync(container, item, context) {
49
76
  const parts = partsByContainer.get(container);
50
- if (!parts) return;
51
- syncContent(parts, item, context);
77
+ const view = viewOf(item);
78
+ if (!parts || !view) return;
79
+ syncContent(parts, item, view, context);
52
80
  for (const text of [
53
81
  parts.title,
54
82
  parts.excerpt,
55
83
  parts.typeMark
56
84
  ]) syncTextResolution(text, context.textResolution);
57
85
  }
58
- function syncContent(parts, item, context) {
86
+ function syncContent(parts, item, view, context) {
59
87
  positionShell(parts.root, item);
60
88
  const { width, height } = item.frame;
61
89
  const detail = detailFor(context.zoom);
62
90
  const elevation = cardElevation(context, item.id);
63
91
  syncCardTag(parts.tag, {
64
92
  glyph: "file",
65
- text: fileMetaLine(item.props.src, item.props.snapshot?.size),
93
+ text: view.meta,
66
94
  width
67
95
  }, context);
68
96
  const key = context.assetKey(item);
69
97
  const texture = key ? context.getTexture(key) : null;
70
98
  const coverFailed = Boolean(key && !texture && context.hasError(key));
71
- const fileState = context.fileState(item.props.src);
72
- const band = coverHeight(item, height);
73
- const kind = filePreviewKind(item.props.snapshot);
74
- const category = fileCategory(item.props.src, item.props.snapshot?.mimeType);
99
+ const fileState = view.src ? context.fileState(view.src) : "ok";
100
+ const band = coverHeight(view, height);
101
+ const { kind, category, title, excerpt, mark } = view;
75
102
  const accent = fileCategoryAccent(category, context.palette);
76
103
  const visualSig = [
77
104
  key ?? "",
@@ -130,9 +157,6 @@ function syncContent(parts, item, context) {
130
157
  parts.excerpt.visible = detail === "full";
131
158
  parts.typeMark.visible = kind === "blank";
132
159
  }
133
- const title = item.props.snapshot?.title || fileStem(item.props.src);
134
- const excerpt = item.props.snapshot?.excerpt ?? "";
135
- const mark = fileTypeLabel(item.props.src);
136
160
  const innerWidth = Math.max(1, width - 20);
137
161
  const showTypeMark = kind === "blank";
138
162
  const textSig = [
@@ -188,7 +212,7 @@ function syncContent(parts, item, context) {
188
212
  }
189
213
  const fileCardRenderer = {
190
214
  id: "file-card",
191
- canRender: (item) => item.type === "file",
215
+ canRender: (item) => item.type === "file" || boardItemPreview(item) !== null,
192
216
  create: (item, context) => {
193
217
  const root = new Container();
194
218
  const plate = new Graphics();
@@ -252,23 +276,19 @@ const fileCardRenderer = {
252
276
  visualSig: "",
253
277
  textSig: ""
254
278
  });
255
- if (item.type === "file") sync(root, item, context);
279
+ sync(root, item, context);
256
280
  return root;
257
281
  },
258
- update: (container, item, context) => {
259
- if (item.type === "file") sync(container, item, context);
260
- },
261
- resolution: (container, item, context) => {
262
- if (item.type === "file") sync(container, item, context);
263
- },
282
+ update: sync,
283
+ resolution: sync,
264
284
  zoom: (container, zoom) => {
265
285
  const tag = partsByContainer.get(container)?.tag;
266
286
  if (tag) zoomCardTag(tag, zoom);
267
287
  },
268
288
  renderFar: (graphics, item, context) => {
269
- const category = fileCategory(item.type === "file" ? item.props.src : "", item.type === "file" ? item.props.snapshot?.mimeType : void 0);
289
+ const category = viewOf(item)?.category ?? "other";
270
290
  drawFarPlate(graphics, item.frame, {
271
- fill: context.palette.surface,
291
+ fill: farPictureTone(item, context) ?? context.palette.surface,
272
292
  fillAlpha: .96,
273
293
  accent: fileCategoryAccent(category, context.palette),
274
294
  accentAlpha: .45
@@ -1,9 +1,11 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
2
  import { itemAlpha, itemColor } from "../palette.js";
3
+ import { BOARD_FRAME_LABEL_LINE_HEIGHT, frameLabelOffset } from "../../model/text-metrics.js";
3
4
  import { syncTextResolution } from "../text-resolution.js";
4
5
  import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
5
6
  import { createLabel, positionShell } from "./base-card-renderer.js";
6
7
  import { drawFarPlate } from "./far-plate.js";
8
+ import "../labels.js";
7
9
  import { Container, Graphics } from "pixi.js";
8
10
  //#region src/board/render/renderers/frame-card-renderer.ts
9
11
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -57,19 +59,24 @@ function sync(container, item, context) {
57
59
  parts.label.visible = decorated;
58
60
  const title = locked ? `🔒 ${props.label}`.trim() : props.label;
59
61
  const labelColor = style.stroke ? baseStroke : context.palette.muted;
62
+ const size = context.style.frameLabelSize;
60
63
  const textSig = [
61
64
  title,
62
65
  labelColor,
63
- context.style.font
66
+ context.style.font,
67
+ size
64
68
  ].join("|");
65
69
  if (textSig !== parts.textSig) {
66
70
  parts.textSig = textSig;
67
71
  parts.label.text = title;
68
72
  parts.label.style.fill = labelColor;
69
73
  parts.label.style.fontFamily = context.style.font;
74
+ parts.label.style.fontSize = size;
75
+ parts.label.style.lineHeight = size * BOARD_FRAME_LABEL_LINE_HEIGHT;
76
+ const offset = frameLabelOffset(size);
77
+ parts.label.position.set(offset.x, offset.y);
70
78
  }
71
79
  syncTextResolution(parts.label, context.textResolution);
72
- parts.label.position.set(4, -18);
73
80
  }
74
81
  const frameCardRenderer = {
75
82
  id: "frame-card",
@@ -80,7 +87,6 @@ const frameCardRenderer = {
80
87
  const label = createLabel("", {
81
88
  fill: context.palette.muted,
82
89
  fontFamily: BOARD_FONT_STACK,
83
- fontSize: 12,
84
90
  fontWeight: "600"
85
91
  }, context.textResolution);
86
92
  root.addChild(box, label);
@@ -1,6 +1,6 @@
1
1
  import { cardElevation, containRect, drawCardSurface, fillCardPicture } from "../surface.js";
2
2
  import { positionShell } from "./base-card-renderer.js";
3
- import { drawFarPlate } from "./far-plate.js";
3
+ import { drawFarPlate, farPictureTone } from "./far-plate.js";
4
4
  import { Container, Graphics } from "pixi.js";
5
5
  //#region src/board/render/renderers/image-card-renderer.ts
6
6
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -137,7 +137,7 @@ const imageCardRenderer = {
137
137
  lod: { kind: "media" },
138
138
  renderFar: (graphics, item, context) => {
139
139
  drawFarPlate(graphics, item.frame, {
140
- fill: context.palette.hover,
140
+ fill: farPictureTone(item, context) ?? context.palette.hover,
141
141
  fillAlpha: FAR_ALPHA
142
142
  });
143
143
  },
@@ -1,12 +1,12 @@
1
- import { isBoardModelItem } from "../../model/media.js";
2
- import { fileStem } from "../../model/file-snapshot.js";
3
- import { fileMetaLine } from "../../model/file-preview.js";
4
1
  import { boardCardDetailed } from "../lod.js";
5
2
  import { createCardTag, syncCardTag, traceCube, zoomCardTag } from "../card-tag.js";
3
+ import { isBoardModelItem } from "../../model/media.js";
6
4
  import { cardElevation, containRect, drawCardSurface } from "../surface.js";
7
5
  import { positionShell } from "./base-card-renderer.js";
8
- import { drawFarPlate } from "./far-plate.js";
6
+ import { drawFarPlate, farPictureTone } from "./far-plate.js";
9
7
  import { drawModelKey } from "./media-key.js";
8
+ import { fileStem } from "../../model/file-snapshot.js";
9
+ import { fileMetaLine } from "../../model/file-preview.js";
10
10
  import { Container, Graphics, Sprite, Texture } from "pixi.js";
11
11
  //#region src/board/render/renderers/model-card-renderer.ts
12
12
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -120,7 +120,7 @@ const modelCardRenderer = {
120
120
  },
121
121
  renderFar: (graphics, item, context) => {
122
122
  drawFarPlate(graphics, item.frame, {
123
- fill: context.palette.surface,
123
+ fill: farPictureTone(item, context) ?? context.palette.surface,
124
124
  fillAlpha: .96,
125
125
  accent: context.palette.legendary,
126
126
  accentAlpha: .45
@@ -1,6 +1,6 @@
1
1
  import { worldPoint } from "../../model/geometry.js";
2
- import { layoutShapeLabel } from "../../model/text-metrics.js";
3
2
  import { itemAlpha, itemColor } from "../palette.js";
3
+ import { layoutShapeLabel } from "../../model/text-metrics.js";
4
4
  import { boardTextStyle, cssBaselineShift } from "../text-measurement.js";
5
5
  import { syncTextResolution } from "../text-resolution.js";
6
6
  import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
@@ -7,7 +7,7 @@ import { createCardTag, formatBoardDuration, syncCardTag, zoomCardTag } from "..
7
7
  import { drawAudioWaveform } from "../audio-waveform.js";
8
8
  import { cardElevation, containRect, drawCardSurface, traceCardOutline } from "../surface.js";
9
9
  import { positionShell } from "./base-card-renderer.js";
10
- import { drawFarPlate } from "./far-plate.js";
10
+ import { drawFarPlate, farPictureTone } from "./far-plate.js";
11
11
  import { Container, FillGradient, Graphics, Sprite, Text } from "pixi.js";
12
12
  //#region src/board/render/renderers/task-card-renderer.ts
13
13
  const PADDING = 12;
@@ -512,7 +512,7 @@ const taskCardRenderer = {
512
512
  const { status } = liveStatus(item, context);
513
513
  const active = status === "failed" || status === "running" || status === "pending";
514
514
  drawFarPlate(graphics, item.frame, {
515
- fill: context.palette.surface,
515
+ fill: farPictureTone(item, context) ?? context.palette.surface,
516
516
  fillAlpha: .82,
517
517
  ...active ? {
518
518
  accent: statusColor(status, context),
@@ -1,9 +1,9 @@
1
- import { getMediaResourceTitle } from "../../model/media.js";
2
1
  import { boardCardDetailed } from "../lod.js";
3
2
  import { createCardTag, formatBoardDuration, syncCardTag, zoomCardTag } from "../card-tag.js";
3
+ import { getMediaResourceTitle } from "../../model/media.js";
4
4
  import { cardElevation, containRect, drawCardSurface, fillCardPicture } from "../surface.js";
5
5
  import { positionShell } from "./base-card-renderer.js";
6
- import { drawFarPlate } from "./far-plate.js";
6
+ import { drawFarPlate, farPictureTone } from "./far-plate.js";
7
7
  import { drawMediaPlayKey } from "./media-key.js";
8
8
  import { Container, Graphics } from "pixi.js";
9
9
  //#region src/board/render/renderers/video-card-renderer.ts
@@ -125,7 +125,7 @@ const videoCardRenderer = {
125
125
  lod: { kind: "media" },
126
126
  renderFar: (graphics, item, context) => {
127
127
  drawFarPlate(graphics, item.frame, {
128
- fill: context.palette.surface,
128
+ fill: farPictureTone(item, context) ?? context.palette.surface,
129
129
  fillAlpha: .96,
130
130
  accent: context.palette.brand,
131
131
  accentAlpha: .75
@@ -1,5 +1,5 @@
1
- import { composeBoardScriptItems } from "../model/script-scene.js";
2
1
  import { buildBoardScene } from "../model/scene.js";
2
+ import { composeBoardScriptItems } from "../model/script-scene.js";
3
3
  import { createBoardScriptHost } from "./script-host.js";
4
4
  //#region src/board/render/script-export.ts
5
5
  async function prepareBrowserBoardScripts(document, time, readModule, snapshots) {
@@ -1,9 +1,9 @@
1
- import { boardScriptItems } from "../model/script-index.js";
2
- import { boardScriptScope } from "../model/script-scene.js";
3
- import { boardScriptHitVisible, resolveBoardScriptHit } from "../model/script-hit.js";
4
1
  import { getBoardResolution } from "./text-resolution.js";
5
2
  import { BoardScriptTexture } from "./script-texture.js";
6
3
  import { scriptResolution } from "./script-resolution.js";
4
+ import { boardScriptItems } from "../model/script-index.js";
5
+ import { boardScriptScope } from "../model/script-scene.js";
6
+ import { boardScriptHitVisible, resolveBoardScriptHit } from "../model/script-hit.js";
7
7
  import { createBoardScriptModuleReader, loadBoardScriptSources } from "../../protocol/dist/board-script-modules.js";
8
8
  import { boardScriptFrameSource } from "./script-sandbox.js";
9
9
  //#region src/board/render/script-host.ts
@@ -9,6 +9,7 @@ type BoardRenderStyle = {
9
9
  icons: "line" | "pixel";
10
10
  motion: "smooth" | "stepped";
11
11
  font: string;
12
+ frameLabelSize: number;
12
13
  };
13
14
  type BoardShadowStyle = {
14
15
  color: number;
@@ -1,4 +1,5 @@
1
1
  import { BOARD_FONT_STACK } from "../../protocol/dist/board-constants.js";
2
+ import "../model/text-metrics.js";
2
3
  //#region src/board/render/style.ts
3
4
  const BASE = {
4
5
  corner: "round",
@@ -14,7 +15,8 @@ const BASE = {
14
15
  progress: "shimmer",
15
16
  icons: "line",
16
17
  motion: "smooth",
17
- font: BOARD_FONT_STACK
18
+ font: BOARD_FONT_STACK,
19
+ frameLabelSize: 12
18
20
  };
19
21
  const SHADOW = {
20
22
  light: {
@@ -0,0 +1,32 @@
1
+ import { BoardViewport } from "../model/geometry.js";
2
+ import { BoardScene, BoardSceneItem } from "../model/scene.js";
3
+ import { BoardRenderStyle } from "./style.js";
4
+ import { BoardTextStyle } from "./text-measurement.js";
5
+ //#region src/board/render/text-edit.d.ts
6
+ /** A content-box textarea at left/top, transformed by `rotate(rotation deg) scale(scale)` about its center. */
7
+ type BoardTextEditLayout = {
8
+ left: number;
9
+ top: number;
10
+ width: number;
11
+ height: number;
12
+ padding: {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ scale: number;
17
+ rotation: number;
18
+ font: BoardTextStyle;
19
+ lineHeight: number;
20
+ textAlign: "left" | "center" | "right";
21
+ whiteSpace: "pre" | "pre-wrap";
22
+ live: boolean;
23
+ };
24
+ type BoardTextEditInput = {
25
+ viewport: BoardViewport;
26
+ scene: BoardScene;
27
+ style?: Partial<Pick<BoardRenderStyle, "font" | "frameLabelSize">>;
28
+ draft: string;
29
+ };
30
+ declare function boardTextEditLayout(item: BoardSceneItem, { viewport, scene, style, draft }: BoardTextEditInput): BoardTextEditLayout | null;
31
+ //#endregion
32
+ export { BoardTextEditInput, BoardTextEditLayout, boardTextEditLayout };