@neta-art/cohub 11.1.0 → 11.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 (78) hide show
  1. package/dist/board/editor/context.d.ts +1 -0
  2. package/dist/board/editor/context.js +1 -0
  3. package/dist/board/editor/editor.d.ts +1 -0
  4. package/dist/board/editor/editor.js +7 -0
  5. package/dist/board/editor/interaction.d.ts +4 -0
  6. package/dist/board/editor/interaction.js +16 -7
  7. package/dist/board/editor/selection-transform.d.ts +1 -1
  8. package/dist/board/editor/selection-transform.js +13 -2
  9. package/dist/board/export/index.d.ts +2 -0
  10. package/dist/board/export/index.js +1 -0
  11. package/dist/board/export/scene.d.ts +2 -0
  12. package/dist/board/export/scene.js +4 -0
  13. package/dist/board/model/file-preview.d.ts +0 -1
  14. package/dist/board/model/file-preview.js +10 -1
  15. package/dist/board/model/index.d.ts +2 -1
  16. package/dist/board/model/index.js +2 -1
  17. package/dist/board/model/script-api.d.ts +69 -0
  18. package/dist/board/model/script-api.js +77 -0
  19. package/dist/board/render/audio-waveform.d.ts +1 -1
  20. package/dist/board/render/audio-waveform.js +8 -5
  21. package/dist/board/render/card-tag.d.ts +24 -0
  22. package/dist/board/render/card-tag.js +205 -0
  23. package/dist/board/render/index.d.ts +7 -4
  24. package/dist/board/render/index.js +7 -4
  25. package/dist/board/render/media-interaction.d.ts +2 -1
  26. package/dist/board/render/media-interaction.js +14 -6
  27. package/dist/board/render/palette.js +2 -2
  28. package/dist/board/render/renderers/arrow-card-renderer.js +20 -16
  29. package/dist/board/render/renderers/audio-card-renderer.d.ts +8 -1
  30. package/dist/board/render/renderers/audio-card-renderer.js +64 -60
  31. package/dist/board/render/renderers/base-card-renderer.d.ts +1 -26
  32. package/dist/board/render/renderers/base-card-renderer.js +6 -92
  33. package/dist/board/render/renderers/board-renderer-registry.d.ts +8 -0
  34. package/dist/board/render/renderers/board-renderer-registry.js +3 -3
  35. package/dist/board/render/renderers/custom-card-renderer.js +4 -7
  36. package/dist/board/render/renderers/effect-card-renderer.js +0 -5
  37. package/dist/board/render/renderers/file-card-renderer.d.ts +1 -11
  38. package/dist/board/render/renderers/file-card-renderer.js +43 -105
  39. package/dist/board/render/renderers/frame-card-renderer.js +14 -15
  40. package/dist/board/render/renderers/image-card-renderer.js +62 -67
  41. package/dist/board/render/renderers/media-key.d.ts +6 -0
  42. package/dist/board/render/renderers/media-key.js +39 -0
  43. package/dist/board/render/renderers/shape-card-renderer.js +6 -12
  44. package/dist/board/render/renderers/task-card-renderer.d.ts +1 -9
  45. package/dist/board/render/renderers/task-card-renderer.js +82 -56
  46. package/dist/board/render/renderers/unknown-card-renderer.js +17 -16
  47. package/dist/board/render/renderers/video-card-renderer.js +73 -80
  48. package/dist/board/render/stroke.d.ts +1 -1
  49. package/dist/board/render/stroke.js +5 -4
  50. package/dist/board/render/style.d.ts +29 -0
  51. package/dist/board/render/style.js +54 -0
  52. package/dist/board/render/surface.d.ts +34 -0
  53. package/dist/board/render/surface.js +132 -0
  54. package/dist/board/render/text-fit.d.ts +8 -0
  55. package/dist/board/render/text-fit.js +60 -0
  56. package/dist/board/render/text-resolution.d.ts +2 -1
  57. package/dist/board/render/text-resolution.js +4 -1
  58. package/dist/board/stage/index.d.ts +3 -2
  59. package/dist/board/stage/index.js +2 -1
  60. package/dist/board/stage/overlay.d.ts +41 -0
  61. package/dist/board/stage/overlay.js +182 -0
  62. package/dist/board/stage/scene.d.ts +2 -43
  63. package/dist/board/stage/scene.js +23 -131
  64. package/dist/board/stage/stage.d.ts +2 -0
  65. package/dist/board/stage/stage.js +99 -32
  66. package/dist/chunks/app-realtime.js +1697 -0
  67. package/dist/chunks/environment.d.ts +997 -3
  68. package/dist/chunks/http.d.ts +393 -3
  69. package/dist/chunks/http.js +40 -1
  70. package/dist/chunks/websocket.d.ts +4 -0
  71. package/dist/chunks/websocket.js +11 -2
  72. package/dist/http.d.ts +2 -2
  73. package/dist/index.d.ts +4 -323
  74. package/dist/index.js +58 -1660
  75. package/dist/protocol/dist/board-model.js +51 -51
  76. package/dist/protocol/dist/board-schema.d.ts +1 -2
  77. package/dist/protocol/dist/board-schema.js +7 -22
  78. package/package.json +2 -1
@@ -0,0 +1,205 @@
1
+ import { fitTextToWidth } from "./text-fit.js";
2
+ import { syncTextResolution, textResolutionForZoom } from "./text-resolution.js";
3
+ import { Container, Graphics, Text } from "pixi.js";
4
+ //#region src/board/render/card-tag.ts
5
+ const CARD_TAG_SIZE = 11;
6
+ const ICON = 12;
7
+ const GAP = 5;
8
+ const MAX_GROWTH = 1.25;
9
+ const MIN_READABLE = 7;
10
+ const SECOND = 1e3;
11
+ function formatBoardDuration(ms) {
12
+ const total = Math.max(0, Math.floor(ms / SECOND));
13
+ const hours = Math.floor(total / 3600);
14
+ const minutes = Math.floor(total % 3600 / 60);
15
+ const seconds = String(total % 60).padStart(2, "0");
16
+ return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${seconds}` : `${minutes}:${seconds}`;
17
+ }
18
+ const PIXEL_GLYPHS = {
19
+ image: [
20
+ "######",
21
+ "#....#",
22
+ "#.#..#",
23
+ "#.##.#",
24
+ "######",
25
+ "......"
26
+ ],
27
+ video: [
28
+ "######",
29
+ "#.#..#",
30
+ "#.##.#",
31
+ "#.#..#",
32
+ "######",
33
+ "......"
34
+ ],
35
+ audio: [
36
+ "...#..",
37
+ "...##.",
38
+ "...#.#",
39
+ "...#..",
40
+ ".###..",
41
+ ".###.."
42
+ ],
43
+ file: [
44
+ "####..",
45
+ "#..##.",
46
+ "#...##",
47
+ "#....#",
48
+ "#....#",
49
+ "######"
50
+ ],
51
+ task: [
52
+ "..#...",
53
+ ".###..",
54
+ "#####.",
55
+ ".###..",
56
+ "..#...",
57
+ "......"
58
+ ]
59
+ };
60
+ function drawLineGlyph(graphics, glyph, color) {
61
+ const line = {
62
+ color,
63
+ width: 1.25,
64
+ alpha: 1,
65
+ cap: "round",
66
+ join: "round"
67
+ };
68
+ switch (glyph) {
69
+ case "image":
70
+ graphics.roundRect(1, 2, 10, 8, 1.5).stroke(line);
71
+ graphics.moveTo(2.5, 9).lineTo(5, 6).lineTo(7, 8).lineTo(8.5, 6.5).lineTo(10, 8).stroke(line);
72
+ return;
73
+ case "video":
74
+ graphics.roundRect(1, 2.5, 10, 7, 1.5).stroke(line);
75
+ graphics.poly([
76
+ 5,
77
+ 4.5,
78
+ 5,
79
+ 7.5,
80
+ 7.5,
81
+ 6
82
+ ]).fill({ color });
83
+ return;
84
+ case "audio":
85
+ for (const [x, height] of [
86
+ [2.5, 3],
87
+ [5, 7],
88
+ [7.5, 9],
89
+ [10, 5]
90
+ ]) graphics.moveTo(x, 6 - height / 2).lineTo(x, 6 + height / 2);
91
+ graphics.stroke(line);
92
+ return;
93
+ case "file":
94
+ graphics.poly([
95
+ 2.5,
96
+ 1,
97
+ 7.5,
98
+ 1,
99
+ 9.5,
100
+ 3,
101
+ 9.5,
102
+ 11,
103
+ 2.5,
104
+ 11
105
+ ]).stroke(line);
106
+ graphics.moveTo(7.5, 1).lineTo(7.5, 3).lineTo(9.5, 3).stroke(line);
107
+ return;
108
+ case "task":
109
+ graphics.poly([
110
+ 6,
111
+ 1,
112
+ 7.3,
113
+ 4.7,
114
+ 11,
115
+ 6,
116
+ 7.3,
117
+ 7.3,
118
+ 6,
119
+ 11,
120
+ 4.7,
121
+ 7.3,
122
+ 1,
123
+ 6,
124
+ 4.7,
125
+ 4.7
126
+ ]).fill({ color });
127
+ return;
128
+ }
129
+ }
130
+ function drawCardGlyph(graphics, glyph, color, pixel) {
131
+ if (!pixel) {
132
+ drawLineGlyph(graphics, glyph, color);
133
+ return;
134
+ }
135
+ PIXEL_GLYPHS[glyph].forEach((row, y) => {
136
+ for (let x = 0; x < row.length; x += 1) if (row[x] === "#") graphics.rect(x * 2, y * 2, 2, 2);
137
+ });
138
+ graphics.fill({ color });
139
+ }
140
+ function createCardTag() {
141
+ const root = new Container({ label: "card-tag" });
142
+ const icon = new Graphics();
143
+ root.addChild(icon);
144
+ root.visible = false;
145
+ return {
146
+ root,
147
+ icon,
148
+ text: null,
149
+ sig: "",
150
+ resolution: 0,
151
+ width: 0
152
+ };
153
+ }
154
+ function zoomCardTag(tag, zoom) {
155
+ if (!tag.text || !tag.sig) return;
156
+ const scale = Math.min(MAX_GROWTH, Math.max(1, 1 / Math.max(zoom, .01)));
157
+ const span = 17 + tag.text.width;
158
+ tag.root.scale.set(scale);
159
+ tag.root.position.set(0, -18 * scale);
160
+ tag.root.visible = 11 * scale * zoom >= MIN_READABLE && span * scale <= tag.width + .5;
161
+ }
162
+ function syncCardTag(tag, input, context) {
163
+ tag.width = input.width;
164
+ if (!input.text || input.width <= 24) {
165
+ tag.sig = "";
166
+ tag.root.visible = false;
167
+ return;
168
+ }
169
+ if (!tag.text) {
170
+ tag.resolution = textResolutionForZoom(context.zoom);
171
+ tag.text = new Text({
172
+ text: "",
173
+ style: {
174
+ fontFamily: context.style.font,
175
+ fontSize: 11,
176
+ fontWeight: "500"
177
+ },
178
+ resolution: tag.resolution,
179
+ roundPixels: true
180
+ });
181
+ tag.root.addChild(tag.text);
182
+ }
183
+ syncTextResolution(tag.text, tag, context.zoom);
184
+ const sig = [
185
+ input.glyph,
186
+ input.text,
187
+ input.width,
188
+ context.themeKey
189
+ ].join("|");
190
+ if (sig === tag.sig) {
191
+ zoomCardTag(tag, context.zoom);
192
+ return;
193
+ }
194
+ tag.sig = sig;
195
+ const color = context.palette.muted;
196
+ const pixel = context.style.icons === "pixel";
197
+ drawCardGlyph(tag.icon.clear(), input.glyph, color, pixel);
198
+ tag.text.style.fill = color;
199
+ tag.text.style.fontFamily = context.style.font;
200
+ fitTextToWidth(tag.text, input.text, Math.max(1, input.width - ICON - GAP));
201
+ tag.text.position.set(17, (ICON - tag.text.height) / 2);
202
+ zoomCardTag(tag, context.zoom);
203
+ }
204
+ //#endregion
205
+ export { CARD_TAG_SIZE, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag };
@@ -1,17 +1,20 @@
1
+ import { BoardRenderStyle, BoardSelectionStyle, BoardShadowStyle, defaultBoardRenderStyle, resolveBoardRenderStyle } from "./style.js";
1
2
  import { BoardCardRenderer, BoardCardRendererResolver, BoardRenderContext, BoardRenderLabels, BoardRenderPalette, BoardScriptHost, DEFAULT_BOARD_RENDER_LABELS, createBoardCardRendererResolver, getBoardCardRenderer } from "./renderers/board-renderer-registry.js";
2
3
  import { createBoardRenderSampler } from "./sampling.js";
3
4
  import { BoardRenderDiagnostic, inspectBoardScene, measureBoardText } from "./diagnostics.js";
4
5
  import { BoardBackgroundContext, createBoardBackground, updateBoardBackground } from "./board-background.js";
6
+ import { CARD_TAG_SIZE, CardGlyph, CardTag, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag } from "./card-tag.js";
5
7
  import { ClipGroup, clippingAncestor, isClippingFrame, syncClipGroup, traceFrameOutline } from "./clip.js";
6
8
  import { BoardCssPaint, parseBoardCssColor, parseBoardCssPaint } from "./css-color.js";
7
- import { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible } from "./media-interaction.js";
9
+ import { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter } from "./media-interaction.js";
8
10
  import { defaultBoardPalette, itemAlpha, itemColor } from "./palette.js";
9
11
  import { BoardScriptHostOptions, BrowserBoardScriptHost, createBoardScriptHost } from "./script-host.js";
10
12
  import { prepareBrowserBoardScripts } from "./script-export.js";
11
13
  import { dashPattern, endAngle, roundRectPoints, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
14
+ import { CardElevation, CardRect, CardSurface, PIXEL_STEP, cardElevation, cardOutlinePoints, cardShadow, containRect, drawCardShadow, drawCardSurface, isBoardCard, traceCardOutline, tracePixelRect } from "./surface.js";
12
15
  import { BOARD_EFFECT_MAX_PARTICLES } from "./renderers/effect-card-renderer.js";
13
- import { TASK_CARD_FULL_DETAIL_ZOOM } from "./renderers/task-card-renderer.js";
16
+ import { ellipsizeWrappedLines, fitLineWithEllipsis, fitTextToLines, fitTextToWidth } from "./text-fit.js";
14
17
  import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
15
- import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
18
+ import { boardCardDetailed, getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
16
19
  import { VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, loadVideoThumbnailTexture, videoTextureNaturalSize, videoThumbnailSize } from "./video-thumbnail.js";
17
- export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, BoardBackgroundContext, BoardCardRenderer, BoardCardRendererResolver, BoardCssPaint, BoardMediaAction, BoardRenderContext, BoardRenderDiagnostic, BoardRenderLabels, BoardRenderPalette, BoardScriptHost, BoardScriptHostOptions, BrowserBoardScriptHost, ClipGroup, DEFAULT_BOARD_RENDER_LABELS, TASK_CARD_FULL_DETAIL_ZOOM, VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, boardMediaActionAt, clippingAncestor, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, dashPattern, defaultBoardPalette, endAngle, ensureBoardTextMeasurement, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, roundRectPoints, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceFrameOutline, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize };
20
+ export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, BoardBackgroundContext, BoardCardRenderer, BoardCardRendererResolver, BoardCssPaint, BoardMediaAction, BoardRenderContext, BoardRenderDiagnostic, BoardRenderLabels, BoardRenderPalette, BoardRenderStyle, BoardScriptHost, BoardScriptHostOptions, BoardSelectionStyle, BoardShadowStyle, BrowserBoardScriptHost, CARD_TAG_SIZE, CardElevation, CardGlyph, CardRect, CardSurface, CardTag, ClipGroup, DEFAULT_BOARD_RENDER_LABELS, PIXEL_STEP, VIDEO_THUMBNAIL_MAX_EDGE, VideoNaturalSize, boardCardDetailed, boardMediaActionAt, cardElevation, cardOutlinePoints, cardShadow, clippingAncestor, containRect, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, createCardTag, dashPattern, defaultBoardPalette, defaultBoardRenderStyle, drawCardGlyph, drawCardShadow, drawCardSurface, ellipsizeWrappedLines, endAngle, ensureBoardTextMeasurement, fitLineWithEllipsis, fitTextToLines, fitTextToWidth, formatBoardDuration, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isBoardCard, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, resolveBoardRenderStyle, roundRectPoints, syncCardTag, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceCardOutline, traceFrameOutline, tracePixelRect, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize, zoomCardTag };
@@ -4,14 +4,17 @@ import { createBoardRenderSampler } from "./sampling.js";
4
4
  import { BOARD_FONT_STACKS, ensureBoardTextMeasurement, installBoardTextMeasurement } from "./text-measurement.js";
5
5
  import { inspectBoardScene, measureBoardText } from "./diagnostics.js";
6
6
  import { createBoardBackground, updateBoardBackground } from "./board-background.js";
7
+ import { ellipsizeWrappedLines, fitLineWithEllipsis, fitTextToLines, fitTextToWidth } from "./text-fit.js";
8
+ import { boardCardDetailed, getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
9
+ import { CARD_TAG_SIZE, createCardTag, drawCardGlyph, formatBoardDuration, syncCardTag, zoomCardTag } from "./card-tag.js";
7
10
  import { clippingAncestor, isClippingFrame, syncClipGroup, traceFrameOutline } from "./clip.js";
8
- import { getBoardResolution, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket } from "./text-resolution.js";
9
- import { TASK_CARD_FULL_DETAIL_ZOOM } from "./renderers/task-card-renderer.js";
10
- import { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible } from "./media-interaction.js";
11
11
  import { dashPattern, endAngle, roundRectPoints, traceArrowhead, tracePolyline, trimPolyline } from "./stroke.js";
12
+ import { PIXEL_STEP, cardElevation, cardOutlinePoints, cardShadow, containRect, drawCardShadow, drawCardSurface, isBoardCard, traceCardOutline, tracePixelRect } from "./surface.js";
13
+ import { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter } from "./media-interaction.js";
12
14
  import { BOARD_EFFECT_MAX_PARTICLES } from "./renderers/effect-card-renderer.js";
13
15
  import { DEFAULT_BOARD_RENDER_LABELS, createBoardCardRendererResolver, getBoardCardRenderer } from "./renderers/board-renderer-registry.js";
14
16
  import { createBoardScriptHost } from "./script-host.js";
15
17
  import { prepareBrowserBoardScripts } from "./script-export.js";
18
+ import { defaultBoardRenderStyle, resolveBoardRenderStyle } from "./style.js";
16
19
  import { VIDEO_THUMBNAIL_MAX_EDGE, loadVideoThumbnailTexture, videoTextureNaturalSize, videoThumbnailSize } from "./video-thumbnail.js";
17
- export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, DEFAULT_BOARD_RENDER_LABELS, TASK_CARD_FULL_DETAIL_ZOOM, VIDEO_THUMBNAIL_MAX_EDGE, boardMediaActionAt, clippingAncestor, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, dashPattern, defaultBoardPalette, endAngle, ensureBoardTextMeasurement, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, roundRectPoints, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceFrameOutline, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize };
20
+ export { BOARD_EFFECT_MAX_PARTICLES, BOARD_FONT_STACKS, CARD_TAG_SIZE, DEFAULT_BOARD_RENDER_LABELS, PIXEL_STEP, VIDEO_THUMBNAIL_MAX_EDGE, boardCardDetailed, boardMediaActionAt, cardElevation, cardOutlinePoints, cardShadow, clippingAncestor, containRect, createBoardBackground, createBoardCardRendererResolver, createBoardRenderSampler, createBoardScriptHost, createCardTag, dashPattern, defaultBoardPalette, defaultBoardRenderStyle, drawCardGlyph, drawCardShadow, drawCardSurface, ellipsizeWrappedLines, endAngle, ensureBoardTextMeasurement, fitLineWithEllipsis, fitTextToLines, fitTextToWidth, formatBoardDuration, getBoardCardRenderer, getBoardResolution, inspectBoardScene, installBoardTextMeasurement, isBoardCard, isClippingFrame, itemAlpha, itemColor, loadVideoThumbnailTexture, measureBoardText, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter, parseBoardCssColor, parseBoardCssPaint, prepareBrowserBoardScripts, resolveBoardRenderStyle, roundRectPoints, syncCardTag, syncClipGroup, syncTextResolution, syncTextWrapWidth, textResolutionForZoom, textZoomBucket, traceArrowhead, traceCardOutline, traceFrameOutline, tracePixelRect, tracePolyline, trimPolyline, updateBoardBackground, videoTextureNaturalSize, videoThumbnailSize, zoomCardTag };
@@ -9,10 +9,11 @@ declare function mediaPlayBadgeVisible(item: BoardSceneItem, zoom: number, optio
9
9
  materialized: boolean;
10
10
  hasVideoPreview: boolean;
11
11
  }): boolean;
12
+ declare function mediaPlayKeyCenter(item: BoardSceneItem): WorldPoint;
12
13
  declare function mediaPlayBadgeHit(item: BoardSceneItem, point: WorldPoint, zoom: number): boolean;
13
14
  declare function boardMediaActionAt(item: BoardSceneItem, point: WorldPoint, zoom: number, options: {
14
15
  materialized: boolean;
15
16
  hasVideoPreview: boolean;
16
17
  }): BoardMediaAction | null;
17
18
  //#endregion
18
- export { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible };
19
+ export { BoardMediaAction, boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter };
@@ -1,19 +1,27 @@
1
1
  import { featuredTaskArtifact } from "../../protocol/dist/board-task.js";
2
- import "./renderers/task-card-renderer.js";
2
+ import { degToRad, rotatePointAround, worldPoint } from "../model/geometry.js";
3
+ import { boardCardDetailed } from "./text-resolution.js";
4
+ import { audioKeyCenter } from "./renderers/audio-card-renderer.js";
3
5
  //#region src/board/render/media-interaction.ts
4
6
  function mediaPlayBadgeVisible(item, zoom, options) {
5
7
  if (!options.materialized) return false;
6
8
  if (item.type === "video" || item.type === "audio") return true;
7
- if (item.type !== "task" || zoom < .55) return false;
9
+ if (item.type !== "task" || !boardCardDetailed(zoom)) return false;
8
10
  const artifact = featuredTaskArtifact(item.props.snapshot.artifacts);
9
11
  if (artifact?.type === "audio") return true;
10
12
  return artifact?.type === "video" && (Boolean(artifact.previewUrl) || options.hasVideoPreview);
11
13
  }
14
+ function mediaPlayKeyCenter(item) {
15
+ const { x, y, width, height, rotation } = item.frame;
16
+ const center = worldPoint(x + width / 2, y + height / 2);
17
+ if (item.type !== "audio") return center;
18
+ const key = audioKeyCenter(width, height);
19
+ return rotatePointAround(worldPoint(x + key.x, y + key.y), center, degToRad(rotation || 0));
20
+ }
12
21
  function mediaPlayBadgeHit(item, point, zoom) {
13
22
  const radius = 28 / Math.max(zoom, .05);
14
- const centerX = item.frame.x + item.frame.width / 2;
15
- const centerY = item.frame.y + item.frame.height / 2;
16
- return Math.hypot(point.x - centerX, point.y - centerY) <= radius;
23
+ const center = mediaPlayKeyCenter(item);
24
+ return Math.hypot(point.x - center.x, point.y - center.y) <= radius;
17
25
  }
18
26
  function boardMediaActionAt(item, point, zoom, options) {
19
27
  return mediaPlayBadgeVisible(item, zoom, options) && mediaPlayBadgeHit(item, point, zoom) ? {
@@ -22,4 +30,4 @@ function boardMediaActionAt(item, point, zoom, options) {
22
30
  } : null;
23
31
  }
24
32
  //#endregion
25
- export { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible };
33
+ export { boardMediaActionAt, mediaPlayBadgeHit, mediaPlayBadgeVisible, mediaPlayKeyCenter };
@@ -14,8 +14,8 @@ const DARK = {
14
14
  legendary: 16096779
15
15
  };
16
16
  const LIGHT = {
17
- bg: 16777215,
18
- surface: 16053493,
17
+ bg: 16251129,
18
+ surface: 16777215,
19
19
  hover: 15329772,
20
20
  border: 14079706,
21
21
  brand: 15222030,
@@ -2,8 +2,9 @@ import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
2
  import { pathPointAt, resolveSceneArrow } from "../../model/arrow-geometry.js";
3
3
  import { itemAlpha, itemColor } from "../palette.js";
4
4
  import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
5
- import { drawFarStroke } from "./far-plate.js";
6
5
  import { dashPattern, endAngle, traceArrowhead, tracePolyline, trimPolyline } from "../stroke.js";
6
+ import { drawCardSurface } from "../surface.js";
7
+ import { drawFarStroke } from "./far-plate.js";
7
8
  import { Container, Graphics, Text } from "pixi.js";
8
9
  //#region src/board/render/renderers/arrow-card-renderer.ts
9
10
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -14,8 +15,6 @@ function sync(container, item, context) {
14
15
  parts.root.rotation = 0;
15
16
  parts.root.scale.set(1);
16
17
  const { props, style } = item;
17
- const selected = context.selectedIds.has(item.id);
18
- const hovered = context.hoveredId === item.id;
19
18
  parts.line.alpha = itemAlpha(context, style.stroke);
20
19
  const color = itemColor(context, style.stroke, "brand");
21
20
  const size = style.strokeWidth ?? 2.5;
@@ -24,8 +23,6 @@ function sync(container, item, context) {
24
23
  const path = trimPolyline(resolved.path, style.trim);
25
24
  const lineSig = [
26
25
  path.map((point) => `${point.x},${point.y}`).join(";"),
27
- selected,
28
- hovered,
29
26
  color,
30
27
  size,
31
28
  style.dash,
@@ -35,11 +32,11 @@ function sync(container, item, context) {
35
32
  if (lineSig !== parts.lineSig) {
36
33
  parts.lineSig = lineSig;
37
34
  parts.line.clear();
38
- const width = selected ? size + 1 : size;
35
+ const width = size;
39
36
  const stroke = {
40
37
  color,
41
38
  width,
42
- alpha: selected || hovered ? 1 : .92,
39
+ alpha: .92,
43
40
  cap: "round",
44
41
  join: "round"
45
42
  };
@@ -63,22 +60,29 @@ function sync(container, item, context) {
63
60
  }
64
61
  const labelSig = [
65
62
  props.label,
66
- context.palette.text,
67
- context.palette.bg,
68
- props.fontSize
63
+ props.fontSize,
64
+ context.themeKey
69
65
  ].join("|");
70
66
  if (labelSig !== parts.labelSig) {
71
67
  parts.labelSig = labelSig;
72
68
  parts.label.text = props.label;
73
69
  parts.label.style.fill = context.palette.text;
74
70
  parts.label.style.fontSize = props.fontSize;
75
- const padX = props.fontSize * .4;
76
- const padY = props.fontSize * .15;
71
+ const padX = props.fontSize * .5;
72
+ const padY = props.fontSize * .25;
77
73
  const { width, height } = parts.label;
78
- parts.backdrop.clear();
79
- parts.backdrop.roundRect(-width / 2 - padX, -height / 2 - padY, width + padX * 2, height + padY * 2, props.fontSize * .3).fill({
80
- color: context.palette.bg,
81
- alpha: .92
74
+ const plate = {
75
+ x: -width / 2 - padX,
76
+ y: -height / 2 - padY,
77
+ width: width + padX * 2,
78
+ height: height + padY * 2
79
+ };
80
+ drawCardSurface(parts.backdrop.clear(), plate, {
81
+ ...context.style,
82
+ radius: Math.min(context.style.radius, plate.height / 2)
83
+ }, {
84
+ fill: context.palette.surface,
85
+ border: context.palette.border
82
86
  });
83
87
  }
84
88
  const visible = props.label.length > 0 && (style.trim === void 0 || style.trim >= .5);
@@ -1,5 +1,12 @@
1
1
  import { BoardCardRenderer } from "./board-renderer-registry.js";
2
2
  //#region src/board/render/renderers/audio-card-renderer.d.ts
3
+ declare const AUDIO_PADDING = 14;
4
+ declare const AUDIO_KEY_RADIUS = 16;
5
+ declare function audioKeyCenter(width: number, height: number): {
6
+ x: number;
7
+ y: number;
8
+ radius: number;
9
+ };
3
10
  declare const audioCardRenderer: BoardCardRenderer;
4
11
  //#endregion
5
- export { audioCardRenderer };
12
+ export { AUDIO_KEY_RADIUS, AUDIO_PADDING, audioCardRenderer, audioKeyCenter };
@@ -1,70 +1,80 @@
1
- import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
1
  import { getMediaResourceTitle } from "../../model/media.js";
2
+ import { fitTextToWidth } from "../text-fit.js";
3
+ import { boardCardDetailed, syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
4
+ import { formatBoardDuration } from "../card-tag.js";
3
5
  import { drawAudioWaveform } from "../audio-waveform.js";
4
- import { syncTextResolution, syncTextWrapWidth, textResolutionForZoom } from "../text-resolution.js";
6
+ import { cardElevation, drawCardSurface } from "../surface.js";
5
7
  import { positionShell } from "./base-card-renderer.js";
6
8
  import { drawFarPlate } from "./far-plate.js";
9
+ import { drawMediaPlayKey } from "./media-key.js";
7
10
  import { Container, Graphics, Text } from "pixi.js";
8
11
  //#region src/board/render/renderers/audio-card-renderer.ts
9
- const RADIUS = 4;
10
- const PLAY_RADIUS = 18;
12
+ const AUDIO_PADDING = 14;
13
+ const AUDIO_KEY_RADIUS = 16;
14
+ const GAP = 12;
15
+ const WAVE_HEIGHT = 16;
11
16
  const partsByContainer = /* @__PURE__ */ new WeakMap();
17
+ function audioKeyCenter(width, height) {
18
+ const radius = Math.min(16, Math.max(8, height / 2 - 8), width / 4);
19
+ return {
20
+ x: 14 + radius,
21
+ y: height / 2,
22
+ radius
23
+ };
24
+ }
12
25
  function sync(container, item, context) {
13
26
  const parts = partsByContainer.get(container);
14
27
  if (!parts) return;
15
28
  positionShell(parts.root, item);
16
29
  const { width, height } = item.frame;
17
- const selected = context.selectedIds.has(item.id);
18
- const hovered = context.hoveredId === item.id;
19
30
  const title = item.props.snapshot?.title ?? getMediaResourceTitle(item.props.src);
20
- syncTextResolution(parts.label, parts, context.zoom);
21
- if (parts.label.text !== title) parts.label.text = title;
22
- syncTextWrapWidth(parts.label, parts, Math.max(1, width - 24), false);
23
- parts.label.position.set(12, Math.max(8, height - parts.label.height - 8));
31
+ const durationMs = item.props.snapshot?.durationMs;
32
+ const detailed = boardCardDetailed(context.zoom);
33
+ syncTextResolution(parts.title, parts, context.zoom);
34
+ const elevation = cardElevation(context, item.id);
24
35
  const sig = [
25
36
  width,
26
37
  height,
27
- selected,
28
- hovered,
29
38
  title,
30
- parts.label.height,
31
- context.palette.surface,
32
- context.palette.brand,
33
- context.palette.border,
34
- context.palette.muted,
35
- context.palette.text
39
+ durationMs ?? "",
40
+ detailed,
41
+ elevation,
42
+ item.props.src,
43
+ item.props.snapshot?.mtimeMs ?? "",
44
+ context.themeKey
36
45
  ].join("|");
37
46
  if (sig === parts.sig) return;
38
47
  parts.sig = sig;
39
- parts.plate.clear().roundRect(0, 0, width, height, RADIUS).fill({
40
- color: context.palette.surface,
41
- alpha: .98
42
- }).roundRect(0, 0, width, height, RADIUS).stroke({
43
- color: selected ? context.palette.brand : hovered ? context.palette.muted : context.palette.border,
44
- width: selected ? 2 : 1,
45
- alpha: selected ? .96 : .84
46
- });
47
- const labelBand = Math.min(height * .38, parts.label.height + 16);
48
- const waveformInset = Math.min(24, width * .08);
49
- parts.waveform.clear();
50
- drawAudioWaveform(parts.waveform, `${item.props.src}:${item.props.snapshot?.mtimeMs ?? "unknown"}`, {
51
- x: waveformInset,
52
- y: 10,
53
- width: Math.max(1, width - waveformInset * 2),
54
- height: Math.max(8, height - labelBand - 16)
55
- }, context.palette.brand);
56
- const cx = width / 2;
57
- const cy = Math.max(24, (height - labelBand) / 2);
58
- parts.chrome.clear().circle(cx, cy, PLAY_RADIUS).fill({
59
- color: selected ? context.palette.brand : context.palette.surface,
60
- alpha: .92
61
- });
62
- const triangle = PLAY_RADIUS * .58;
63
- parts.chrome.moveTo(cx - triangle * .35, cy - triangle * .62).lineTo(cx - triangle * .35, cy + triangle * .62).lineTo(cx + triangle * .72, cy).closePath().fill({
64
- color: context.palette.text,
65
- alpha: .96
48
+ drawCardSurface(parts.plate.clear(), {
49
+ x: 0,
50
+ y: 0,
51
+ width,
52
+ height
53
+ }, context.style, {
54
+ fill: context.palette.surface,
55
+ border: context.palette.border,
56
+ borderAlpha: .85,
57
+ elevation
66
58
  });
67
- parts.label.style.fill = context.palette.text;
59
+ const key = audioKeyCenter(width, height);
60
+ drawMediaPlayKey(parts.plate, key.x, key.y, key.radius, context, true);
61
+ const left = key.x + key.radius + GAP;
62
+ const room = width - 14 - left;
63
+ parts.title.visible = detailed && room > 24;
64
+ parts.wave.clear();
65
+ if (!parts.title.visible) return;
66
+ parts.title.style.fontFamily = context.style.font;
67
+ parts.title.style.fill = context.palette.text;
68
+ fitTextToWidth(parts.title, durationMs ? `${title} · ${formatBoardDuration(durationMs)}` : title, room);
69
+ const withWave = height >= 56;
70
+ const top = (height - (parts.title.height + (withWave ? 22 : 0))) / 2;
71
+ parts.title.position.set(left, top);
72
+ if (withWave) drawAudioWaveform(parts.wave, `${item.props.src}:${item.props.snapshot?.mtimeMs ?? "unknown"}`, {
73
+ x: left,
74
+ y: top + parts.title.height + 6,
75
+ width: room,
76
+ height: WAVE_HEIGHT
77
+ }, context.palette.muted, .45, context.style.corner === "pixel");
68
78
  }
69
79
  const audioCardRenderer = {
70
80
  id: "audio-card",
@@ -72,30 +82,24 @@ const audioCardRenderer = {
72
82
  create: (item, context) => {
73
83
  const root = new Container();
74
84
  const plate = new Graphics();
75
- const waveform = new Graphics();
76
- const chrome = new Graphics();
85
+ const wave = new Graphics();
77
86
  const resolution = textResolutionForZoom(context.zoom);
78
- const label = new Text({
87
+ const title = new Text({
79
88
  text: "",
80
89
  style: {
81
- fill: context.palette.text,
82
- fontFamily: BOARD_FONT_STACK,
83
- fontSize: 12,
84
- fontWeight: "500",
85
- wordWrap: true
90
+ fontSize: 13,
91
+ fontWeight: "600"
86
92
  },
87
93
  resolution,
88
94
  roundPixels: true
89
95
  });
90
- root.addChild(plate, waveform, chrome, label);
96
+ root.addChild(plate, wave, title);
91
97
  partsByContainer.set(root, {
92
98
  root,
93
99
  plate,
94
- waveform,
95
- chrome,
96
- label,
100
+ wave,
101
+ title,
97
102
  sig: "",
98
- wrapWidth: 0,
99
103
  resolution
100
104
  });
101
105
  if (item.type === "audio") sync(root, item, context);
@@ -118,4 +122,4 @@ const audioCardRenderer = {
118
122
  }
119
123
  };
120
124
  //#endregion
121
- export { audioCardRenderer };
125
+ export { AUDIO_KEY_RADIUS, AUDIO_PADDING, audioCardRenderer, audioKeyCenter };
@@ -1,35 +1,10 @@
1
1
  import { BoardFrame } from "../../../protocol/dist/board-layout.js";
2
2
  import "../../../protocol/dist/index.js";
3
- import { BoardRenderPalette } from "./board-renderer-registry.js";
4
3
  import { Container, Text } from "pixi.js";
5
4
  //#region src/board/render/renderers/base-card-renderer.d.ts
6
- declare const CARD_RADIUS = 10;
7
- declare const CARD_PADDING = 12;
8
- declare const FOOTER_HEIGHT = 34;
9
5
  declare function createLabel(text: string, style: Record<string, unknown>): Text;
10
- type CardShellState = {
11
- width: number;
12
- height: number;
13
- selected: boolean;
14
- hovered: boolean;
15
- accent: number;
16
- title: string;
17
- };
18
- type CardShell = {
19
- root: Container;
20
- content: Container;
21
- contentRect: () => {
22
- x: number;
23
- y: number;
24
- width: number;
25
- height: number;
26
- };
27
- update: (state: CardShellState, palette: BoardRenderPalette, footer: boolean) => void;
28
- destroy: () => void;
29
- };
30
- declare function createCardShell(): CardShell;
31
6
  declare function positionShell(root: Container, item: {
32
7
  frame: BoardFrame;
33
8
  }): void;
34
9
  //#endregion
35
- export { CARD_PADDING, CARD_RADIUS, CardShell, CardShellState, FOOTER_HEIGHT, createCardShell, createLabel, positionShell };
10
+ export { createLabel, positionShell };