@neta-art/cohub 9.0.1 → 10.0.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 (178) hide show
  1. package/README.md +100 -103
  2. package/dist/board/editor/align.d.ts +11 -0
  3. package/dist/board/editor/align.js +72 -0
  4. package/dist/board/editor/animation.d.ts +14 -0
  5. package/dist/board/editor/animation.js +172 -0
  6. package/dist/board/editor/camera.d.ts +32 -0
  7. package/dist/board/editor/camera.js +137 -0
  8. package/dist/board/editor/clipboard.d.ts +41 -0
  9. package/dist/board/editor/clipboard.js +122 -0
  10. package/dist/board/editor/connection-ports.d.ts +19 -0
  11. package/dist/board/editor/connection-ports.js +85 -0
  12. package/dist/board/editor/context.d.ts +111 -0
  13. package/dist/board/editor/context.js +30 -0
  14. package/dist/board/editor/copy.d.ts +17 -0
  15. package/dist/board/editor/copy.js +116 -0
  16. package/dist/board/editor/creation.d.ts +47 -0
  17. package/dist/board/editor/creation.js +224 -0
  18. package/dist/board/editor/deletion.d.ts +9 -0
  19. package/dist/board/editor/deletion.js +90 -0
  20. package/dist/board/editor/document-edits.d.ts +22 -0
  21. package/dist/board/editor/document-edits.js +193 -0
  22. package/dist/board/editor/document.d.ts +35 -0
  23. package/dist/board/editor/document.js +255 -0
  24. package/dist/board/editor/editor.d.ts +210 -0
  25. package/dist/board/editor/editor.js +429 -0
  26. package/dist/board/editor/index.d.ts +18 -0
  27. package/dist/board/editor/index.js +12 -0
  28. package/dist/board/editor/interaction.d.ts +115 -0
  29. package/dist/board/editor/interaction.js +584 -0
  30. package/dist/board/editor/query.d.ts +27 -0
  31. package/dist/board/editor/query.js +201 -0
  32. package/dist/board/editor/remote.d.ts +11 -0
  33. package/dist/board/editor/remote.js +55 -0
  34. package/dist/board/editor/selection-transform.d.ts +24 -0
  35. package/dist/board/editor/selection-transform.js +81 -0
  36. package/dist/board/editor/selection.d.ts +33 -0
  37. package/dist/board/editor/selection.js +196 -0
  38. package/dist/board/editor/snapping.d.ts +33 -0
  39. package/dist/board/editor/snapping.js +75 -0
  40. package/dist/board/editor/snapshots.d.ts +27 -0
  41. package/dist/board/editor/snapshots.js +132 -0
  42. package/dist/board/editor/spatial.d.ts +41 -0
  43. package/dist/board/editor/spatial.js +191 -0
  44. package/dist/board/editor/state.d.ts +12 -0
  45. package/dist/board/editor/state.js +49 -0
  46. package/dist/board/editor/tool.d.ts +10 -0
  47. package/dist/board/editor/tool.js +19 -0
  48. package/dist/board/editor/wheel.d.ts +5 -0
  49. package/dist/board/editor/wheel.js +17 -0
  50. package/dist/board/export/index.d.ts +5 -4
  51. package/dist/board/export/index.js +4 -3
  52. package/dist/board/export/node/index.d.ts +55 -0
  53. package/dist/board/{headless → export/node}/index.js +12 -12
  54. package/dist/board/export/node/sketch.d.ts +19 -0
  55. package/dist/board/{headless → export/node}/sketch.js +6 -6
  56. package/dist/board/export/scene.d.ts +6 -4
  57. package/dist/board/export/scene.js +4 -3
  58. package/dist/board/{animation.d.ts → model/animation.d.ts} +4 -4
  59. package/dist/board/{animation.js → model/animation.js} +3 -3
  60. package/dist/board/{core → model}/arrow-geometry.d.ts +2 -2
  61. package/dist/board/{core → model}/arrow-geometry.js +2 -2
  62. package/dist/board/{core → model}/draw-geometry.d.ts +2 -2
  63. package/dist/board/{core → model}/draw-geometry.js +1 -1
  64. package/dist/board/model/draw-input.d.ts +14 -0
  65. package/dist/board/model/draw-input.js +28 -0
  66. package/dist/board/{core → model}/export-assets.d.ts +1 -1
  67. package/dist/board/{core → model}/export-assets.js +1 -1
  68. package/dist/board/{core → model}/export-plan.d.ts +2 -2
  69. package/dist/board/{core → model}/export-plan.js +2 -2
  70. package/dist/board/{core → model}/file-preview.d.ts +1 -1
  71. package/dist/board/{core → model}/file-preview.js +1 -1
  72. package/dist/board/{core → model}/file-snapshot.d.ts +1 -1
  73. package/dist/board/{core → model}/file-snapshot.js +1 -1
  74. package/dist/board/{geometry.d.ts → model/geometry.d.ts} +3 -3
  75. package/dist/board/{geometry.js → model/geometry.js} +1 -1
  76. package/dist/board/{image-key.d.ts → model/image-key.d.ts} +2 -2
  77. package/dist/board/{image-key.js → model/image-key.js} +1 -1
  78. package/dist/board/model/index.d.ts +35 -0
  79. package/dist/board/{index.js → model/index.js} +26 -22
  80. package/dist/board/model/items/builtin.d.ts +5 -0
  81. package/dist/board/model/items/builtin.js +119 -0
  82. package/dist/board/model/items/create.d.ts +59 -0
  83. package/dist/board/model/items/create.js +271 -0
  84. package/dist/board/model/items/definition.d.ts +35 -0
  85. package/dist/board/model/items/definition.js +18 -0
  86. package/dist/board/model/items/id.d.ts +5 -0
  87. package/dist/board/model/items/id.js +11 -0
  88. package/dist/board/model/items/index.d.ts +6 -0
  89. package/dist/board/model/items/index.js +6 -0
  90. package/dist/board/model/items/registry.d.ts +35 -0
  91. package/dist/board/model/items/registry.js +84 -0
  92. package/dist/board/{media-playback.d.ts → model/media-playback.d.ts} +2 -2
  93. package/dist/board/{media-playback.js → model/media-playback.js} +1 -1
  94. package/dist/board/{media.d.ts → model/media.d.ts} +1 -1
  95. package/dist/board/{media.js → model/media.js} +1 -1
  96. package/dist/board/{core → model}/palette.d.ts +1 -1
  97. package/dist/board/{core → model}/palette.js +1 -1
  98. package/dist/board/{presets.d.ts → model/presets.d.ts} +3 -3
  99. package/dist/board/{presets.js → model/presets.js} +1 -1
  100. package/dist/board/{replay.d.ts → model/replay.d.ts} +5 -5
  101. package/dist/board/{replay.js → model/replay.js} +2 -2
  102. package/dist/board/{core → model}/scene.d.ts +1 -1
  103. package/dist/board/{core → model}/scene.js +1 -1
  104. package/dist/board/model/shape-types.d.ts +8 -0
  105. package/dist/board/model/shape-types.js +8 -0
  106. package/dist/board/{task.d.ts → model/task.d.ts} +4 -4
  107. package/dist/board/{task.js → model/task.js} +3 -3
  108. package/dist/board/{core → model}/text-metrics.d.ts +1 -1
  109. package/dist/board/{core → model}/text-metrics.js +1 -1
  110. package/dist/board/{core → model}/tool-styles.d.ts +1 -1
  111. package/dist/board/{core → model}/tool-styles.js +1 -1
  112. package/dist/board/{runtime/board-player.d.ts → player/index.d.ts} +2 -2
  113. package/dist/board/{runtime/board-player.js → player/index.js} +5 -4
  114. package/dist/board/render/board-background.d.ts +1 -1
  115. package/dist/board/render/clip.d.ts +1 -1
  116. package/dist/board/render/index.d.ts +2 -2
  117. package/dist/board/render/index.js +2 -2
  118. package/dist/board/render/media-interaction.d.ts +2 -2
  119. package/dist/board/render/media-interaction.js +1 -1
  120. package/dist/board/render/palette.d.ts +1 -1
  121. package/dist/board/render/palette.js +1 -1
  122. package/dist/board/render/renderers/arrow-card-renderer.js +1 -1
  123. package/dist/board/render/renderers/board-renderer-registry.d.ts +11 -6
  124. package/dist/board/render/renderers/board-renderer-registry.js +15 -15
  125. package/dist/board/render/renderers/draw-card-renderer.js +1 -1
  126. package/dist/board/render/renderers/far-plate.js +1 -1
  127. package/dist/board/render/renderers/file-card-renderer.d.ts +1 -1
  128. package/dist/board/render/renderers/file-card-renderer.js +2 -2
  129. package/dist/board/render/renderers/image-card-renderer.d.ts +1 -1
  130. package/dist/board/render/renderers/shape-card-renderer.js +1 -1
  131. package/dist/board/render/renderers/task-card-renderer.js +1 -1
  132. package/dist/board/render/renderers/text-card-renderer.js +1 -1
  133. package/dist/board/render/renderers/video-card-renderer.d.ts +1 -1
  134. package/dist/board/render/stroke.d.ts +1 -1
  135. package/dist/board/render/stroke.js +1 -1
  136. package/dist/board/render/text-measurement.js +1 -1
  137. package/dist/board/replica/index.d.ts +70 -0
  138. package/dist/board/replica/index.js +276 -0
  139. package/dist/board/stage/assets.d.ts +41 -0
  140. package/dist/board/stage/assets.js +350 -0
  141. package/dist/board/stage/index.d.ts +8 -0
  142. package/dist/board/stage/index.js +8 -0
  143. package/dist/board/stage/lru.d.ts +32 -0
  144. package/dist/board/stage/lru.js +29 -0
  145. package/dist/board/stage/pointer.d.ts +8 -0
  146. package/dist/board/stage/pointer.js +33 -0
  147. package/dist/board/stage/scene.d.ts +85 -0
  148. package/dist/board/stage/scene.js +512 -0
  149. package/dist/board/stage/stage.d.ts +66 -0
  150. package/dist/board/stage/stage.js +568 -0
  151. package/dist/board/stage/texture-pool.d.ts +24 -0
  152. package/dist/board/stage/texture-pool.js +154 -0
  153. package/dist/board/stage/views.d.ts +38 -0
  154. package/dist/board/stage/views.js +93 -0
  155. package/dist/chunks/environment.d.ts +24 -1
  156. package/dist/chunks/http.d.ts +99 -66
  157. package/dist/chunks/http.js +214 -207
  158. package/dist/chunks/websocket.d.ts +1 -1
  159. package/dist/http.d.ts +2 -2
  160. package/dist/index.d.ts +3 -3
  161. package/dist/index.js +20 -4
  162. package/dist/media.d.ts +1 -10
  163. package/dist/media.js +16 -27
  164. package/dist/protocol/dist/board-patch.js +1 -1
  165. package/dist/protocol/dist/board.d.ts +28 -2
  166. package/dist/protocol/dist/board.js +3 -1
  167. package/dist/protocol/dist/core/media.d.ts +5 -0
  168. package/dist/protocol/dist/core/media.js +39 -0
  169. package/dist/protocol/dist/index.d.ts +5 -5
  170. package/dist/types.d.ts +1 -1
  171. package/package.json +20 -8
  172. package/dist/board/core/shape-definition.d.ts +0 -23
  173. package/dist/board/core/shape-definition.js +0 -45
  174. package/dist/board/core/shape-types.d.ts +0 -38
  175. package/dist/board/core/shape-types.js +0 -22
  176. package/dist/board/headless/index.d.ts +0 -55
  177. package/dist/board/headless/sketch.d.ts +0 -19
  178. package/dist/board/index.d.ts +0 -30
@@ -0,0 +1,119 @@
1
+ import { degToRad, frameContainsPoint, itemBounds, rectCenter, rectContainsPoint, rotatePointAround, worldPoint } from "../geometry.js";
2
+ import { computeDrawBounds, distanceToStroke } from "../draw-geometry.js";
3
+ import { defineBoardItem } from "./definition.js";
4
+ //#region src/board/model/items/builtin.ts
5
+ function frameLocalPoint(item, point) {
6
+ const center = rectCenter(item.frame);
7
+ const rotated = item.frame.rotation ? rotatePointAround(point, center, -degToRad(item.frame.rotation)) : point;
8
+ return worldPoint(rotated.x - item.frame.x, rotated.y - item.frame.y);
9
+ }
10
+ const textItem = defineBoardItem({
11
+ type: "text",
12
+ capabilities: {
13
+ canEdit: true,
14
+ aspectLocked: true
15
+ }
16
+ });
17
+ const imageItem = defineBoardItem({
18
+ type: "image",
19
+ capabilities: { aspectLocked: true }
20
+ });
21
+ const videoItem = defineBoardItem({
22
+ type: "video",
23
+ capabilities: {
24
+ canRotate: false,
25
+ aspectLocked: true
26
+ }
27
+ });
28
+ const audioItem = defineBoardItem({
29
+ type: "audio",
30
+ capabilities: { canRotate: false }
31
+ });
32
+ const fileItem = defineBoardItem({
33
+ type: "file",
34
+ capabilities: { canRotate: false }
35
+ });
36
+ const taskItem = defineBoardItem({
37
+ type: "task",
38
+ capabilities: {
39
+ canRotate: false,
40
+ aspectLocked: true
41
+ }
42
+ });
43
+ const effectItem = defineBoardItem({
44
+ type: "effect",
45
+ capabilities: {
46
+ canConnect: false,
47
+ canSnap: false
48
+ }
49
+ });
50
+ const sketchItem = defineBoardItem({ type: "sketch" });
51
+ const frameItem = defineBoardItem({
52
+ type: "frame",
53
+ capabilities: {
54
+ canEdit: true,
55
+ canRotate: false,
56
+ canConnect: false
57
+ }
58
+ });
59
+ const arrowItem = defineBoardItem({
60
+ type: "arrow",
61
+ capabilities: {
62
+ canResize: false,
63
+ canRotate: false,
64
+ canEdit: true,
65
+ canConnect: false,
66
+ canSnap: false
67
+ }
68
+ });
69
+ function shapeContainsLocal(item, local) {
70
+ const w = item.frame.width;
71
+ const h = item.frame.height;
72
+ const nx = local.x / w * 2 - 1;
73
+ const ny = local.y / h * 2 - 1;
74
+ switch (item.props.geometry) {
75
+ case "ellipse": return nx * nx + ny * ny <= 1;
76
+ case "diamond": return Math.abs(nx) + Math.abs(ny) <= 1;
77
+ case "triangle": return ny >= -1 && ny <= 1 && Math.abs(nx) <= (ny + 1) / 2;
78
+ default: return local.x >= 0 && local.x <= w && local.y >= 0 && local.y <= h;
79
+ }
80
+ }
81
+ const builtinBoardItems = [
82
+ frameItem,
83
+ textItem,
84
+ defineBoardItem({
85
+ type: "shape",
86
+ capabilities: { canEdit: true },
87
+ hitTest: (item, point) => {
88
+ if (item.type !== "shape" || !frameContainsPoint(item.frame, point)) return false;
89
+ return shapeContainsLocal(item, frameLocalPoint(item, point));
90
+ }
91
+ }),
92
+ defineBoardItem({
93
+ type: "draw",
94
+ capabilities: {
95
+ aspectLocked: true,
96
+ canConnect: false
97
+ },
98
+ hitTest: (item, point) => {
99
+ if (item.type !== "draw" || !rectContainsPoint(itemBounds(item.frame), point, 8)) return false;
100
+ const draw = item;
101
+ const width = draw.style.strokeWidth ?? 4;
102
+ const box = computeDrawBounds(draw.props.points, width);
103
+ const scale = item.frame.width / Math.max(1e-4, box.width);
104
+ const local = frameLocalPoint(item, point);
105
+ const inPoints = worldPoint(box.x + local.x / scale, box.y + local.y / scale);
106
+ return distanceToStroke(draw.props.points, inPoints) <= Math.max(6, width) / Math.min(1, scale);
107
+ }
108
+ }),
109
+ arrowItem,
110
+ imageItem,
111
+ videoItem,
112
+ audioItem,
113
+ fileItem,
114
+ taskItem,
115
+ effectItem,
116
+ sketchItem
117
+ ];
118
+ //#endregion
119
+ export { builtinBoardItems };
@@ -0,0 +1,59 @@
1
+ import { BoardItem, BoardMediaSnapshot, BoardTaskSnapshot } from "../../../protocol/dist/board-model.js";
2
+ import "../../../protocol/dist/index.js";
3
+ import { BoardFileSnapshotFacts } from "../file-snapshot.js";
4
+ import { ShapeKind } from "../shape-types.js";
5
+ //#region src/board/model/items/create.d.ts
6
+ type BoardItemEntry = {
7
+ id: string;
8
+ item: BoardItem;
9
+ };
10
+ declare function mediaFrameSize(naturalWidth?: number | null, naturalHeight?: number | null, maxEdge?: number, fallback?: {
11
+ width: number;
12
+ height: number;
13
+ }): {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ declare function createFileNodeForPath(path: string, x: number, y: number, snapshot?: BoardMediaSnapshot & BoardFileSnapshotFacts, id?: string): BoardItemEntry;
18
+ declare function createTaskBoardItem(taskRunId: string, snapshot: BoardTaskSnapshot, x: number, y: number, metadata?: Record<string, unknown>, id?: string): BoardItemEntry;
19
+ declare function createTextBoardItem(text: string, x: number, y: number, color?: string, id?: string): BoardItemEntry;
20
+ declare function createShapeBoardItem(geometry: ShapeKind, x: number, y: number, color?: string, id?: string, box?: {
21
+ x: number;
22
+ y: number;
23
+ width: number;
24
+ height: number;
25
+ }): BoardItemEntry;
26
+ declare function createFrameBoardItem(x: number, y: number, color?: string, label?: string, id?: string, box?: {
27
+ x: number;
28
+ y: number;
29
+ width: number;
30
+ height: number;
31
+ }): BoardItemEntry;
32
+ type BoardAppMetadata = {
33
+ appId: string;
34
+ ref: string;
35
+ url: string;
36
+ name: string;
37
+ icon?: string;
38
+ };
39
+ declare function createAppBoardItem(app: BoardAppMetadata, x: number, y: number, id?: string): BoardItemEntry;
40
+ declare function createDrawBoardItem(worldPoints: Array<{
41
+ x: number;
42
+ y: number;
43
+ p: number;
44
+ }>, color: string, size?: number, id?: string): BoardItemEntry;
45
+ declare function createArrowBoardItem(start: {
46
+ x: number;
47
+ y: number;
48
+ }, end: {
49
+ x: number;
50
+ y: number;
51
+ } | {
52
+ item: string;
53
+ }, color: string, id?: string, size?: number, startEnd?: {
54
+ item: string;
55
+ anchor?: unknown;
56
+ }): BoardItemEntry;
57
+ declare function titleForBoardItem(value: BoardItem): string;
58
+ //#endregion
59
+ export { BoardAppMetadata, BoardItemEntry, createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem, mediaFrameSize, titleForBoardItem };
@@ -0,0 +1,271 @@
1
+ import { parseBoardItem } from "../../../protocol/dist/board-model.js";
2
+ import { pointsRect } from "../../../protocol/dist/board-layout.js";
3
+ import { quantizeDrawCoordinate } from "../draw-input.js";
4
+ import { fileStem } from "../file-snapshot.js";
5
+ import { filePreviewKind } from "../file-preview.js";
6
+ import { DEFAULT_BOARD_TOOL_STYLES } from "../tool-styles.js";
7
+ import { featuredTaskArtifact } from "../task.js";
8
+ import { getMediaResourceTitle, inferBoardMediaKind } from "../media.js";
9
+ import { createBoardItemId } from "./id.js";
10
+ //#region src/board/model/items/create.ts
11
+ const DEFAULT_MEDIA_SIZE = {
12
+ width: 320,
13
+ height: 200
14
+ };
15
+ const DEFAULT_TASK_SIZE = {
16
+ width: 300,
17
+ height: 188
18
+ };
19
+ const DEFAULT_TASK_MEDIA_SIZE = {
20
+ width: 320,
21
+ height: 180
22
+ };
23
+ const DEFAULT_VIDEO_SIZE = {
24
+ width: 320,
25
+ height: 180
26
+ };
27
+ const DEFAULT_AUDIO_SIZE = {
28
+ width: 320,
29
+ height: 112
30
+ };
31
+ const DEFAULT_FILE_SIZE = {
32
+ width: 260,
33
+ height: 132
34
+ };
35
+ const DEFAULT_FILE_COVER_SIZE = {
36
+ width: 260,
37
+ height: 208
38
+ };
39
+ const DEFAULT_SHAPE_SIZE = {
40
+ width: 200,
41
+ height: 140
42
+ };
43
+ const DEFAULT_FRAME_SIZE = {
44
+ width: 480,
45
+ height: 320
46
+ };
47
+ function item(value) {
48
+ const parsed = parseBoardItem(value);
49
+ if (!parsed.ok) throw new Error(`Invalid Board item: ${parsed.diagnostics[0]?.path}: ${parsed.diagnostics[0]?.message}`);
50
+ return parsed.item;
51
+ }
52
+ function centered(x, y, size) {
53
+ return {
54
+ position: {
55
+ x: x - size.width / 2,
56
+ y: y - size.height / 2
57
+ },
58
+ size: { ...size }
59
+ };
60
+ }
61
+ function mediaFrameSize(naturalWidth, naturalHeight, maxEdge = 480, fallback = DEFAULT_MEDIA_SIZE) {
62
+ if (!naturalWidth || !naturalHeight || !Number.isFinite(naturalWidth) || !Number.isFinite(naturalHeight) || naturalWidth <= 0 || naturalHeight <= 0) return { ...fallback };
63
+ const scale = Math.min(1, maxEdge / Math.max(naturalWidth, naturalHeight));
64
+ return {
65
+ width: Math.max(24, naturalWidth * scale),
66
+ height: Math.max(24, naturalHeight * scale)
67
+ };
68
+ }
69
+ function cleanSnapshot(snapshot) {
70
+ return Object.fromEntries(Object.entries(snapshot).filter(([, value]) => value !== void 0));
71
+ }
72
+ function createFileNodeForPath(path, x, y, snapshot = {}, id = createBoardItemId()) {
73
+ const kind = inferBoardMediaKind(path, snapshot.mimeType);
74
+ const title = snapshot.title ?? (kind === "file" || kind === "text" ? fileStem(path) : getMediaResourceTitle(path));
75
+ if (kind === "image" || kind === "video") {
76
+ const size = mediaFrameSize(snapshot.naturalWidth, snapshot.naturalHeight, 480, kind === "video" ? DEFAULT_VIDEO_SIZE : DEFAULT_MEDIA_SIZE);
77
+ const mimeType = snapshot.mimeType ?? (kind === "video" ? "video/*" : void 0);
78
+ return {
79
+ id,
80
+ item: item({
81
+ type: kind,
82
+ ...centered(x, y, size),
83
+ props: {
84
+ src: path,
85
+ snapshot: cleanSnapshot({
86
+ ...snapshot,
87
+ title,
88
+ mimeType
89
+ })
90
+ }
91
+ })
92
+ };
93
+ }
94
+ if (kind === "audio") return {
95
+ id,
96
+ item: item({
97
+ type: "audio",
98
+ ...centered(x, y, DEFAULT_AUDIO_SIZE),
99
+ props: {
100
+ src: path,
101
+ snapshot: cleanSnapshot({
102
+ ...snapshot,
103
+ title,
104
+ mimeType: snapshot.mimeType ?? "audio/*"
105
+ })
106
+ }
107
+ })
108
+ };
109
+ return {
110
+ id,
111
+ item: item({
112
+ type: "file",
113
+ ...centered(x, y, filePreviewKind(snapshot) === "cover" ? DEFAULT_FILE_COVER_SIZE : DEFAULT_FILE_SIZE),
114
+ props: {
115
+ src: path,
116
+ snapshot: cleanSnapshot({
117
+ ...snapshot,
118
+ title
119
+ })
120
+ }
121
+ })
122
+ };
123
+ }
124
+ function createTaskBoardItem(taskRunId, snapshot, x, y, metadata, id = createBoardItemId()) {
125
+ const artifact = featuredTaskArtifact(snapshot.artifacts);
126
+ const visual = artifact?.type === "image" || artifact?.type === "video" ? artifact : null;
127
+ return {
128
+ id,
129
+ item: item({
130
+ type: "task",
131
+ ...centered(x, y, visual ? mediaFrameSize(visual.naturalWidth, visual.naturalHeight, 480, DEFAULT_TASK_MEDIA_SIZE) : artifact?.type === "audio" ? DEFAULT_TASK_MEDIA_SIZE : DEFAULT_TASK_SIZE),
132
+ ...metadata ? { metadata } : {},
133
+ props: {
134
+ taskRunId,
135
+ snapshot
136
+ }
137
+ })
138
+ };
139
+ }
140
+ function createTextBoardItem(text, x, y, color = DEFAULT_BOARD_TOOL_STYLES.text.color, id = createBoardItemId()) {
141
+ return {
142
+ id,
143
+ item: item({
144
+ type: "text",
145
+ position: {
146
+ x,
147
+ y
148
+ },
149
+ ...color === "neutral" ? {} : { style: { fill: color } },
150
+ props: { text }
151
+ })
152
+ };
153
+ }
154
+ function createShapeBoardItem(geometry, x, y, color = DEFAULT_BOARD_TOOL_STYLES.shape.color, id = createBoardItemId(), box) {
155
+ return {
156
+ id,
157
+ item: item({
158
+ type: "shape",
159
+ ...box ? {
160
+ position: {
161
+ x: box.x,
162
+ y: box.y
163
+ },
164
+ size: {
165
+ width: box.width,
166
+ height: box.height
167
+ }
168
+ } : centered(x, y, DEFAULT_SHAPE_SIZE),
169
+ style: { stroke: color },
170
+ props: { geometry }
171
+ })
172
+ };
173
+ }
174
+ function createFrameBoardItem(x, y, color = DEFAULT_BOARD_TOOL_STYLES.frame.color, label = "Frame", id = createBoardItemId(), box) {
175
+ return {
176
+ id,
177
+ item: item({
178
+ type: "frame",
179
+ ...box ? {
180
+ position: {
181
+ x: box.x,
182
+ y: box.y
183
+ },
184
+ size: {
185
+ width: box.width,
186
+ height: box.height
187
+ }
188
+ } : centered(x, y, DEFAULT_FRAME_SIZE),
189
+ ...color === "neutral" ? {} : { style: { stroke: color } },
190
+ props: { label }
191
+ })
192
+ };
193
+ }
194
+ function createAppBoardItem(app, x, y, id = createBoardItemId()) {
195
+ return {
196
+ id,
197
+ item: {
198
+ ...createFrameBoardItem(x, y, "brand", app.name, id).item,
199
+ metadata: { cohubApp: app }
200
+ }
201
+ };
202
+ }
203
+ function createDrawBoardItem(worldPoints, color, size = 4, id = createBoardItemId()) {
204
+ const origin = pointsRect(worldPoints);
205
+ const points = worldPoints.map((point) => ({
206
+ x: quantizeDrawCoordinate(point.x - origin.x),
207
+ y: quantizeDrawCoordinate(point.y - origin.y),
208
+ p: point.p
209
+ }));
210
+ return {
211
+ id,
212
+ item: item({
213
+ type: "draw",
214
+ position: {
215
+ x: origin.x,
216
+ y: origin.y
217
+ },
218
+ style: {
219
+ stroke: color,
220
+ strokeWidth: size
221
+ },
222
+ props: { points }
223
+ })
224
+ };
225
+ }
226
+ function createArrowBoardItem(start, end, color, id = createBoardItemId(), size = DEFAULT_BOARD_TOOL_STYLES.arrow.size, startEnd) {
227
+ const local = (point) => ({
228
+ x: point.x - start.x,
229
+ y: point.y - start.y
230
+ });
231
+ return {
232
+ id,
233
+ item: item({
234
+ type: "arrow",
235
+ position: {
236
+ x: start.x,
237
+ y: start.y
238
+ },
239
+ style: {
240
+ stroke: color,
241
+ strokeWidth: size
242
+ },
243
+ props: {
244
+ start: startEnd ?? {
245
+ x: 0,
246
+ y: 0
247
+ },
248
+ end: "item" in end ? end : local(end)
249
+ }
250
+ })
251
+ };
252
+ }
253
+ function titleForBoardItem(value) {
254
+ switch (value.type) {
255
+ case "text": return value.props.text.split("\n")[0] || "Text";
256
+ case "shape": return value.props.text.split("\n")[0] || value.props.geometry;
257
+ case "draw": return "Drawing";
258
+ case "arrow": return value.props.label || "Arrow";
259
+ case "frame": return value.props.label || "Frame";
260
+ case "image":
261
+ case "video":
262
+ case "audio": return value.props.snapshot?.title ?? getMediaResourceTitle(value.props.src);
263
+ case "file": return value.props.snapshot?.title ?? fileStem(value.props.src);
264
+ case "task": return value.props.snapshot.title;
265
+ case "effect": return value.props.kind;
266
+ case "sketch": return value.props.src.split("/").pop() ?? "Sketch";
267
+ default: return value.type;
268
+ }
269
+ }
270
+ //#endregion
271
+ export { createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem, mediaFrameSize, titleForBoardItem };
@@ -0,0 +1,35 @@
1
+ import { BoardExtensionItem, BoardItemType } from "../../../protocol/dist/board-model.js";
2
+ import "../../../protocol/dist/index.js";
3
+ import { Rect, WorldPoint } from "../geometry.js";
4
+ import { BoardSceneItem } from "../scene.js";
5
+ import { z } from "zod";
6
+ //#region src/board/model/items/definition.d.ts
7
+ type BoardItemCapabilities = {
8
+ canMove: boolean;
9
+ canResize: boolean;
10
+ aspectLocked: boolean;
11
+ canRotate: boolean;
12
+ canEdit: boolean;
13
+ canConnect: boolean;
14
+ canSnap: boolean;
15
+ canLock: boolean;
16
+ };
17
+ type BoardItemResizeMode = "none" | "uniform" | "free";
18
+ declare const FULL_CAPABILITIES: BoardItemCapabilities;
19
+ type BoardItemDefinition<Props extends Record<string, unknown> = Record<string, unknown>> = {
20
+ type: BoardItemType;
21
+ capabilities?: Partial<BoardItemCapabilities>;
22
+ size?: {
23
+ width: number;
24
+ height: number;
25
+ };
26
+ props?: z.ZodType<Props>;
27
+ bounds?: (item: BoardSceneItem) => Rect;
28
+ hitTest?: (item: BoardSceneItem, point: WorldPoint) => boolean;
29
+ };
30
+ type BoardItemOf<Definition> = Definition extends BoardItemDefinition<infer Props> ? BoardExtensionItem & {
31
+ props: Props;
32
+ } : never;
33
+ declare function defineBoardItem<Props extends Record<string, unknown>>(definition: BoardItemDefinition<Props>): BoardItemDefinition<Props>;
34
+ //#endregion
35
+ export { BoardItemCapabilities, BoardItemDefinition, BoardItemOf, BoardItemResizeMode, FULL_CAPABILITIES, defineBoardItem };
@@ -0,0 +1,18 @@
1
+ import { BOARD_EXTENSION_TYPE_PATTERN, isBuiltinItemType } from "../../../protocol/dist/board-model.js";
2
+ //#region src/board/model/items/definition.ts
3
+ const FULL_CAPABILITIES = {
4
+ canMove: true,
5
+ canResize: true,
6
+ aspectLocked: false,
7
+ canRotate: true,
8
+ canEdit: false,
9
+ canConnect: true,
10
+ canSnap: true,
11
+ canLock: true
12
+ };
13
+ function defineBoardItem(definition) {
14
+ if (!isBuiltinItemType(definition.type) && !BOARD_EXTENSION_TYPE_PATTERN.test(definition.type)) throw new Error(`Board item type "${definition.type}" must be namespaced, like "acme.order".`);
15
+ return definition;
16
+ }
17
+ //#endregion
18
+ export { FULL_CAPABILITIES, defineBoardItem };
@@ -0,0 +1,5 @@
1
+ //#region src/board/model/items/id.d.ts
2
+ declare function createBoardEntityId(): string;
3
+ declare function createBoardItemId(): string;
4
+ //#endregion
5
+ export { createBoardEntityId, createBoardItemId };
@@ -0,0 +1,11 @@
1
+ //#region src/board/model/items/id.ts
2
+ function createBoardEntityId() {
3
+ const cryptoRef = globalThis.crypto;
4
+ if (cryptoRef && typeof cryptoRef.randomUUID === "function") return cryptoRef.randomUUID();
5
+ return `c-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
6
+ }
7
+ function createBoardItemId() {
8
+ return `item_${createBoardEntityId()}`;
9
+ }
10
+ //#endregion
11
+ export { createBoardEntityId, createBoardItemId };
@@ -0,0 +1,6 @@
1
+ import { BoardItemCapabilities, BoardItemDefinition, BoardItemOf, BoardItemResizeMode, FULL_CAPABILITIES, defineBoardItem } from "./definition.js";
2
+ import { builtinBoardItems } from "./builtin.js";
3
+ import { BoardAppMetadata, BoardItemEntry, createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem, mediaFrameSize, titleForBoardItem } from "./create.js";
4
+ import { createBoardEntityId, createBoardItemId } from "./id.js";
5
+ import { BoardItemInput, BoardRegistry, createBoardRegistry, defaultBoardRegistry } from "./registry.js";
6
+ export { BoardAppMetadata, BoardItemCapabilities, BoardItemDefinition, BoardItemEntry, BoardItemInput, BoardItemOf, BoardItemResizeMode, BoardRegistry, FULL_CAPABILITIES, builtinBoardItems, createAppBoardItem, createArrowBoardItem, createBoardEntityId, createBoardItemId, createBoardRegistry, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem, defaultBoardRegistry, defineBoardItem, mediaFrameSize, titleForBoardItem };
@@ -0,0 +1,6 @@
1
+ import { FULL_CAPABILITIES, defineBoardItem } from "./definition.js";
2
+ import { builtinBoardItems } from "./builtin.js";
3
+ import { createBoardEntityId, createBoardItemId } from "./id.js";
4
+ import { createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem, mediaFrameSize, titleForBoardItem } from "./create.js";
5
+ import { createBoardRegistry, defaultBoardRegistry } from "./registry.js";
6
+ export { FULL_CAPABILITIES, builtinBoardItems, createAppBoardItem, createArrowBoardItem, createBoardEntityId, createBoardItemId, createBoardRegistry, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem, defaultBoardRegistry, defineBoardItem, mediaFrameSize, titleForBoardItem };
@@ -0,0 +1,35 @@
1
+ import { BoardItem } from "../../../protocol/dist/board-model.js";
2
+ import "../../../protocol/dist/index.js";
3
+ import { Rect, WorldPoint } from "../geometry.js";
4
+ import { BoardSceneItem } from "../scene.js";
5
+ import { BoardItemCapabilities, BoardItemDefinition, BoardItemResizeMode } from "./definition.js";
6
+ import { BoardItemEntry } from "./create.js";
7
+ //#region src/board/model/items/registry.d.ts
8
+ type BoardItemInput = {
9
+ props?: Record<string, unknown>;
10
+ at: WorldPoint;
11
+ size?: {
12
+ width: number;
13
+ height: number;
14
+ };
15
+ id?: string;
16
+ };
17
+ type BoardRegistry = {
18
+ readonly definitions: readonly BoardItemDefinition[];
19
+ get(type: string): BoardItemDefinition | undefined;
20
+ capabilities(item: Pick<BoardItem, "type">): BoardItemCapabilities;
21
+ resizeMode(item: Pick<BoardItem, "type">): BoardItemResizeMode;
22
+ bounds(item: BoardSceneItem): Rect;
23
+ hitTest(item: BoardSceneItem, point: WorldPoint): boolean;
24
+ validateProps(type: string, props: unknown): {
25
+ ok: true;
26
+ } | {
27
+ ok: false;
28
+ message: string;
29
+ };
30
+ create(type: string, input: BoardItemInput): BoardItemEntry;
31
+ };
32
+ declare function createBoardRegistry(definitions?: Iterable<BoardItemDefinition>): BoardRegistry;
33
+ declare const defaultBoardRegistry: BoardRegistry;
34
+ //#endregion
35
+ export { BoardItemInput, BoardRegistry, createBoardRegistry, defaultBoardRegistry };
@@ -0,0 +1,84 @@
1
+ import { hasAuthoredSize, parseBoardItem } from "../../../protocol/dist/board-model.js";
2
+ import { frameContainsPoint, itemBounds } from "../geometry.js";
3
+ import { FULL_CAPABILITIES } from "./definition.js";
4
+ import { builtinBoardItems } from "./builtin.js";
5
+ import { createBoardItemId } from "./id.js";
6
+ //#region src/board/model/items/registry.ts
7
+ const DEFAULT_ITEM_SIZE = {
8
+ width: 240,
9
+ height: 160
10
+ };
11
+ function createBoardRegistry(definitions = []) {
12
+ const byType = /* @__PURE__ */ new Map();
13
+ for (const definition of [...builtinBoardItems, ...definitions]) byType.set(definition.type, definition);
14
+ const capabilities = /* @__PURE__ */ new Map();
15
+ function capabilitiesOf(type) {
16
+ let value = capabilities.get(type);
17
+ if (!value) {
18
+ value = {
19
+ ...FULL_CAPABILITIES,
20
+ ...byType.get(type)?.capabilities
21
+ };
22
+ capabilities.set(type, value);
23
+ }
24
+ return value;
25
+ }
26
+ function validateProps(type, props) {
27
+ const schema = byType.get(type)?.props;
28
+ if (!schema) return { ok: true };
29
+ const parsed = schema.safeParse(props);
30
+ if (parsed.success) return { ok: true };
31
+ const issue = parsed.error.issues[0];
32
+ return {
33
+ ok: false,
34
+ message: `${type} props${issue?.path.length ? `.${issue.path.join(".")}` : ""}: ${issue?.message ?? "invalid"}`
35
+ };
36
+ }
37
+ return {
38
+ definitions: [...byType.values()],
39
+ get: (type) => byType.get(type),
40
+ capabilities: (item) => capabilitiesOf(item.type),
41
+ resizeMode(item) {
42
+ const value = capabilitiesOf(item.type);
43
+ if (!value.canResize) return "none";
44
+ return value.aspectLocked ? "uniform" : "free";
45
+ },
46
+ bounds: (item) => byType.get(item.type)?.bounds?.(item) ?? itemBounds(item.frame),
47
+ hitTest(item, point) {
48
+ const hitTest = byType.get(item.type)?.hitTest;
49
+ return hitTest ? hitTest(item, point) : frameContainsPoint(item.frame, point);
50
+ },
51
+ validateProps,
52
+ create(type, { props = {}, at, size, id = createBoardItemId() }) {
53
+ const valid = validateProps(type, props);
54
+ if (!valid.ok) throw new Error(valid.message);
55
+ const box = size ?? byType.get(type)?.size ?? DEFAULT_ITEM_SIZE;
56
+ const placement = hasAuthoredSize(type) ? {
57
+ position: {
58
+ x: at.x - box.width / 2,
59
+ y: at.y - box.height / 2
60
+ },
61
+ size: { ...box }
62
+ } : { position: {
63
+ x: at.x,
64
+ y: at.y
65
+ } };
66
+ const parsed = parseBoardItem({
67
+ type,
68
+ ...placement,
69
+ props
70
+ });
71
+ if (!parsed.ok) {
72
+ const diagnostic = parsed.diagnostics[0];
73
+ throw new Error(`Invalid ${type} item: ${diagnostic?.path}: ${diagnostic?.message}`);
74
+ }
75
+ return {
76
+ id,
77
+ item: parsed.item
78
+ };
79
+ }
80
+ };
81
+ }
82
+ const defaultBoardRegistry = createBoardRegistry();
83
+ //#endregion
84
+ export { createBoardRegistry, defaultBoardRegistry };
@@ -1,5 +1,5 @@
1
- import { BoardSceneItem } from "./core/scene.js";
2
- //#region src/board/media-playback.d.ts
1
+ import { BoardSceneItem } from "./scene.js";
2
+ //#region src/board/model/media-playback.d.ts
3
3
  type BoardAssetSource = {
4
4
  resolveFileUrl: (path: string) => Promise<string | null>;
5
5
  resolvePlaybackUrl?: (path: string) => Promise<string | null>;
@@ -1,5 +1,5 @@
1
1
  import { rankedTaskArtifacts } from "./task.js";
2
- //#region src/board/media-playback.ts
2
+ //#region src/board/model/media-playback.ts
3
3
  const resolvedUrls = /* @__PURE__ */ new Map();
4
4
  const sourceIds = /* @__PURE__ */ new WeakMap();
5
5
  const URL_CACHE_LIMIT = 64;
@@ -1,4 +1,4 @@
1
- //#region src/board/media.d.ts
1
+ //#region src/board/model/media.d.ts
2
2
  type BoardMediaKind = "image" | "video" | "audio" | "text" | "file";
3
3
  declare function getMediaExtension(value: string): string;
4
4
  declare function inferBoardMediaKind(value: string, mimeType?: string | null): BoardMediaKind;