@neta-art/cohub 9.0.1 → 11.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 (240) hide show
  1. package/README.md +108 -102
  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 +17 -0
  5. package/dist/board/editor/animation.js +187 -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 +129 -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 +121 -0
  16. package/dist/board/editor/creation.d.ts +51 -0
  17. package/dist/board/editor/creation.js +225 -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 +41 -0
  23. package/dist/board/editor/document.js +302 -0
  24. package/dist/board/editor/editor.d.ts +225 -0
  25. package/dist/board/editor/editor.js +439 -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 +203 -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 +197 -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 +28 -6
  51. package/dist/board/export/index.js +47 -13
  52. package/dist/board/export/node/index.d.ts +56 -0
  53. package/dist/board/{headless → export/node}/index.js +26 -21
  54. package/dist/board/export/node/scripts.d.ts +16 -0
  55. package/dist/board/export/node/scripts.js +258 -0
  56. package/dist/board/export/scene.d.ts +8 -5
  57. package/dist/board/export/scene.js +24 -9
  58. package/dist/board/{animation.d.ts → model/animation.d.ts} +8 -5
  59. package/dist/board/{animation.js → model/animation.js} +37 -10
  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/model/authoring.d.ts +20 -0
  63. package/dist/board/model/authoring.js +185 -0
  64. package/dist/board/{core → model}/draw-geometry.d.ts +2 -2
  65. package/dist/board/{core → model}/draw-geometry.js +1 -1
  66. package/dist/board/model/draw-input.d.ts +14 -0
  67. package/dist/board/model/draw-input.js +28 -0
  68. package/dist/board/{core → model}/export-assets.d.ts +1 -1
  69. package/dist/board/{core → model}/export-assets.js +1 -1
  70. package/dist/board/{core → model}/export-plan.d.ts +2 -2
  71. package/dist/board/{core → model}/export-plan.js +3 -2
  72. package/dist/board/{core → model}/file-preview.d.ts +1 -1
  73. package/dist/board/{core → model}/file-preview.js +2 -2
  74. package/dist/board/{core → model}/file-snapshot.d.ts +1 -1
  75. package/dist/board/{core → model}/file-snapshot.js +4 -3
  76. package/dist/board/{geometry.d.ts → model/geometry.d.ts} +3 -3
  77. package/dist/board/{geometry.js → model/geometry.js} +1 -1
  78. package/dist/board/{image-key.d.ts → model/image-key.d.ts} +2 -2
  79. package/dist/board/{image-key.js → model/image-key.js} +7 -3
  80. package/dist/board/model/index.d.ts +42 -0
  81. package/dist/board/model/index.js +39 -0
  82. package/dist/board/model/items/builtin.d.ts +5 -0
  83. package/dist/board/model/items/builtin.js +119 -0
  84. package/dist/board/model/items/create.d.ts +61 -0
  85. package/dist/board/model/items/create.js +272 -0
  86. package/dist/board/model/items/definition.d.ts +35 -0
  87. package/dist/board/model/items/definition.js +18 -0
  88. package/dist/board/model/items/id.d.ts +5 -0
  89. package/dist/board/model/items/id.js +11 -0
  90. package/dist/board/model/items/index.d.ts +6 -0
  91. package/dist/board/model/items/index.js +6 -0
  92. package/dist/board/model/items/registry.d.ts +35 -0
  93. package/dist/board/model/items/registry.js +84 -0
  94. package/dist/board/{media-playback.d.ts → model/media-playback.d.ts} +4 -3
  95. package/dist/board/{media-playback.js → model/media-playback.js} +15 -5
  96. package/dist/board/{media.d.ts → model/media.d.ts} +1 -1
  97. package/dist/board/{media.js → model/media.js} +8 -2
  98. package/dist/board/{core → model}/palette.d.ts +1 -1
  99. package/dist/board/{core → model}/palette.js +1 -1
  100. package/dist/board/{presets.d.ts → model/presets.d.ts} +3 -3
  101. package/dist/board/{presets.js → model/presets.js} +1 -1
  102. package/dist/board/{replay.d.ts → model/replay.d.ts} +5 -5
  103. package/dist/board/{replay.js → model/replay.js} +2 -2
  104. package/dist/board/model/sampling.d.ts +19 -0
  105. package/dist/board/model/sampling.js +96 -0
  106. package/dist/board/{core → model}/scene.d.ts +1 -1
  107. package/dist/board/{core → model}/scene.js +1 -1
  108. package/dist/board/model/script-example.d.ts +4 -0
  109. package/dist/board/model/script-example.js +21 -0
  110. package/dist/board/model/script-hit.d.ts +10 -0
  111. package/dist/board/model/script-hit.js +52 -0
  112. package/dist/board/model/script-index.d.ts +7 -0
  113. package/dist/board/model/script-index.js +15 -0
  114. package/dist/board/model/script-scene.d.ts +24 -0
  115. package/dist/board/model/script-scene.js +267 -0
  116. package/dist/board/model/script.d.ts +105 -0
  117. package/dist/board/model/script.js +1 -0
  118. package/dist/board/model/shape-types.d.ts +8 -0
  119. package/dist/board/model/shape-types.js +8 -0
  120. package/dist/board/{task.d.ts → model/task.d.ts} +4 -4
  121. package/dist/board/{task.js → model/task.js} +3 -3
  122. package/dist/board/{core → model}/text-metrics.d.ts +1 -1
  123. package/dist/board/{core → model}/text-metrics.js +1 -1
  124. package/dist/board/{core → model}/tool-styles.d.ts +1 -1
  125. package/dist/board/{core → model}/tool-styles.js +1 -1
  126. package/dist/board/{runtime/board-player.d.ts → player/index.d.ts} +2 -2
  127. package/dist/board/{runtime/board-player.js → player/index.js} +5 -4
  128. package/dist/board/render/board-background.d.ts +1 -1
  129. package/dist/board/render/clip.d.ts +1 -1
  130. package/dist/board/render/css-color.d.ts +6 -1
  131. package/dist/board/render/css-color.js +34 -20
  132. package/dist/board/render/diagnostics.d.ts +20 -0
  133. package/dist/board/render/diagnostics.js +117 -0
  134. package/dist/board/render/index.d.ts +9 -6
  135. package/dist/board/render/index.js +10 -7
  136. package/dist/board/render/media-interaction.d.ts +2 -2
  137. package/dist/board/render/media-interaction.js +1 -1
  138. package/dist/board/render/palette.d.ts +3 -2
  139. package/dist/board/render/palette.js +7 -3
  140. package/dist/board/render/renderers/arrow-card-renderer.js +4 -3
  141. package/dist/board/render/renderers/audio-card-renderer.js +2 -1
  142. package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -10
  143. package/dist/board/render/renderers/board-renderer-registry.js +17 -17
  144. package/dist/board/render/renderers/custom-card-renderer.d.ts +5 -0
  145. package/dist/board/render/renderers/{sketch-card-renderer.js → custom-card-renderer.js} +16 -15
  146. package/dist/board/render/renderers/draw-card-renderer.js +4 -3
  147. package/dist/board/render/renderers/effect-card-renderer.js +22 -5
  148. package/dist/board/render/renderers/far-plate.js +1 -1
  149. package/dist/board/render/renderers/file-card-renderer.d.ts +1 -1
  150. package/dist/board/render/renderers/file-card-renderer.js +2 -2
  151. package/dist/board/render/renderers/frame-card-renderer.js +30 -14
  152. package/dist/board/render/renderers/image-card-renderer.d.ts +1 -1
  153. package/dist/board/render/renderers/image-card-renderer.js +43 -4
  154. package/dist/board/render/renderers/shape-card-renderer.js +11 -28
  155. package/dist/board/render/renderers/task-card-renderer.js +1 -1
  156. package/dist/board/render/renderers/text-card-renderer.js +6 -4
  157. package/dist/board/render/renderers/unknown-card-renderer.js +1 -0
  158. package/dist/board/render/renderers/video-card-renderer.d.ts +1 -1
  159. package/dist/board/render/renderers/video-card-renderer.js +2 -1
  160. package/dist/board/render/sampling.d.ts +22 -0
  161. package/dist/board/render/sampling.js +21 -0
  162. package/dist/board/render/script-export.d.ts +11 -0
  163. package/dist/board/render/script-export.js +86 -0
  164. package/dist/board/render/script-host.d.ts +31 -0
  165. package/dist/board/render/script-host.js +421 -0
  166. package/dist/board/render/script-modules.d.ts +2 -0
  167. package/dist/board/render/script-modules.js +2 -0
  168. package/dist/board/render/script-runtime.d.ts +29 -0
  169. package/dist/board/render/script-runtime.js +213 -0
  170. package/dist/board/render/script-sandbox.d.ts +4 -0
  171. package/dist/board/render/script-sandbox.js +173 -0
  172. package/dist/board/render/stroke.d.ts +3 -2
  173. package/dist/board/render/stroke.js +23 -2
  174. package/dist/board/render/text-measurement.js +2 -2
  175. package/dist/board/replica/index.d.ts +70 -0
  176. package/dist/board/replica/index.js +276 -0
  177. package/dist/board/stage/assets.d.ts +41 -0
  178. package/dist/board/stage/assets.js +355 -0
  179. package/dist/board/stage/index.d.ts +8 -0
  180. package/dist/board/stage/index.js +8 -0
  181. package/dist/board/stage/lru.d.ts +32 -0
  182. package/dist/board/stage/lru.js +29 -0
  183. package/dist/board/stage/maintenance.d.ts +5 -0
  184. package/dist/board/stage/maintenance.js +11 -0
  185. package/dist/board/stage/pointer.d.ts +8 -0
  186. package/dist/board/stage/pointer.js +33 -0
  187. package/dist/board/stage/scene.d.ts +85 -0
  188. package/dist/board/stage/scene.js +515 -0
  189. package/dist/board/stage/script-pointer.d.ts +19 -0
  190. package/dist/board/stage/script-pointer.js +35 -0
  191. package/dist/board/stage/stage.d.ts +76 -0
  192. package/dist/board/stage/stage.js +774 -0
  193. package/dist/board/stage/texture-pool.d.ts +24 -0
  194. package/dist/board/stage/texture-pool.js +153 -0
  195. package/dist/board/stage/views.d.ts +38 -0
  196. package/dist/board/stage/views.js +93 -0
  197. package/dist/chunks/environment.d.ts +289 -23
  198. package/dist/chunks/host-bridge.d.ts +2 -2
  199. package/dist/chunks/http.d.ts +185 -71
  200. package/dist/chunks/http.js +368 -269
  201. package/dist/chunks/transport.js +3 -1
  202. package/dist/chunks/websocket.d.ts +9 -1
  203. package/dist/chunks/websocket.js +16 -2
  204. package/dist/host-bridge.js +2 -1
  205. package/dist/http.d.ts +2 -2
  206. package/dist/index.d.ts +182 -4
  207. package/dist/index.js +1393 -129
  208. package/dist/media.d.ts +1 -10
  209. package/dist/media.js +16 -27
  210. package/dist/protocol/dist/board-assets.d.ts +1 -0
  211. package/dist/protocol/dist/board-constants.js +2 -2
  212. package/dist/protocol/dist/board-model.d.ts +201 -11
  213. package/dist/protocol/dist/board-model.js +33 -14
  214. package/dist/protocol/dist/board-patch.js +3 -2
  215. package/dist/protocol/dist/board-schema.d.ts +4 -2
  216. package/dist/protocol/dist/board-schema.js +18 -4
  217. package/dist/protocol/dist/board-script-modules.d.ts +25 -0
  218. package/dist/protocol/dist/board-script-modules.js +127 -0
  219. package/dist/protocol/dist/board.d.ts +28 -2
  220. package/dist/protocol/dist/board.js +3 -1
  221. package/dist/protocol/dist/core/media.d.ts +5 -0
  222. package/dist/protocol/dist/core/media.js +39 -0
  223. package/dist/protocol/dist/display/index.d.ts +1 -0
  224. package/dist/protocol/dist/index.d.ts +7 -5
  225. package/dist/protocol/dist/realtime/types.d.ts +1 -0
  226. package/dist/protocol/dist/rtc/index.d.ts +1 -0
  227. package/dist/types.d.ts +1 -1
  228. package/package.json +21 -8
  229. package/dist/board/core/shape-definition.d.ts +0 -23
  230. package/dist/board/core/shape-definition.js +0 -45
  231. package/dist/board/core/shape-types.d.ts +0 -38
  232. package/dist/board/core/shape-types.js +0 -22
  233. package/dist/board/headless/index.d.ts +0 -55
  234. package/dist/board/headless/sketch.d.ts +0 -19
  235. package/dist/board/headless/sketch.js +0 -146
  236. package/dist/board/index.d.ts +0 -30
  237. package/dist/board/index.js +0 -29
  238. package/dist/board/render/renderers/sketch-card-renderer.d.ts +0 -5
  239. package/dist/board/render/sketch-host.d.ts +0 -14
  240. package/dist/board/render/sketch-host.js +0 -216
@@ -0,0 +1,137 @@
1
+ import { cameraForRect, normalizeViewport, panBy, pointToWorld, screenPoint, zoomAround } from "../model/geometry.js";
2
+ import { normalizeWheelDelta, wheelZoomFactor } from "./wheel.js";
3
+ //#region src/board/editor/camera.ts
4
+ const ZOOM_STEP = 1.28;
5
+ const CAMERA_ANIMATION_MS = 240;
6
+ function easeOutCubic(t) {
7
+ return 1 - (1 - t) * (1 - t) * (1 - t);
8
+ }
9
+ function createCameraModule(ctx) {
10
+ const { state } = ctx;
11
+ let animation = 0;
12
+ function setCamera(viewport) {
13
+ state.camera = normalizeViewport(viewport, state.camera);
14
+ state.hoverPoint = null;
15
+ state.hoverId = null;
16
+ }
17
+ function setCameraPolicy(policy) {
18
+ if (state.cameraPolicy === policy) return;
19
+ state.cameraPolicy = policy;
20
+ ctx.preferencesChanged();
21
+ }
22
+ function takeCameraControl() {
23
+ setCameraPolicy("free");
24
+ }
25
+ function cancelAnimation() {
26
+ if (animation) cancelAnimationFrame(animation);
27
+ animation = 0;
28
+ }
29
+ function animateCamera(target) {
30
+ cancelAnimation();
31
+ const from = normalizeViewport(state.camera);
32
+ const to = normalizeViewport(target, from);
33
+ const started = performance.now();
34
+ const step = (now) => {
35
+ const t = Math.min(1, (now - started) / CAMERA_ANIMATION_MS);
36
+ const eased = easeOutCubic(t);
37
+ setCamera({
38
+ x: from.x + (to.x - from.x) * eased,
39
+ y: from.y + (to.y - from.y) * eased,
40
+ zoom: from.zoom + (to.zoom - from.zoom) * eased
41
+ });
42
+ animation = t < 1 ? requestAnimationFrame(step) : 0;
43
+ };
44
+ animation = requestAnimationFrame(step);
45
+ }
46
+ function surfaceCenter() {
47
+ return screenPoint(state.surfaceSize.width / 2, state.surfaceSize.height / 2);
48
+ }
49
+ function viewCenter() {
50
+ return pointToWorld(surfaceCenter(), state.camera);
51
+ }
52
+ function visibleRect() {
53
+ const { camera, surfaceSize } = state;
54
+ if (surfaceSize.width <= 0 || surfaceSize.height <= 0) return null;
55
+ return {
56
+ x: -camera.x / camera.zoom,
57
+ y: -camera.y / camera.zoom,
58
+ width: surfaceSize.width / camera.zoom,
59
+ height: surfaceSize.height / camera.zoom
60
+ };
61
+ }
62
+ function zoomAt(point, factor, animate = false) {
63
+ const target = zoomAround(state.camera, point, state.camera.zoom * factor);
64
+ if (animate) animateCamera(target);
65
+ else setCamera(target);
66
+ }
67
+ function focusRect(rect, focus = {}) {
68
+ const { surfaceSize } = state;
69
+ if (surfaceSize.width <= 0 || surfaceSize.height <= 0) return;
70
+ const target = cameraForRect(rect, surfaceSize, {
71
+ fit: focus.fit ?? "contain",
72
+ padding: focus.padding ?? 32,
73
+ minZoom: focus.minZoom,
74
+ maxZoom: focus.maxZoom
75
+ });
76
+ if (focus.animate === false) setCamera(target);
77
+ else animateCamera(target);
78
+ }
79
+ function fitView(focus = {}) {
80
+ const content = ctx.query.contentBounds();
81
+ if (content && state.surfaceSize.width > 0) focusRect(content, {
82
+ padding: 64,
83
+ ...focus
84
+ });
85
+ else if (focus.animate === false) setCamera({
86
+ x: 0,
87
+ y: 0,
88
+ zoom: 1
89
+ });
90
+ else animateCamera({
91
+ x: 0,
92
+ y: 0,
93
+ zoom: 1
94
+ });
95
+ }
96
+ function focusItems(ids, focus) {
97
+ const rect = ctx.query.contentBounds(ids);
98
+ if (rect) focusRect(rect, focus);
99
+ }
100
+ function focusSelection(focus) {
101
+ const rect = ctx.query.contentBounds(state.selection);
102
+ if (rect) focusRect(rect, focus);
103
+ else fitView();
104
+ }
105
+ function wheel(point, deltaX, deltaY, zoomKey, deltaMode = 0) {
106
+ cancelAnimation();
107
+ const { camera } = state;
108
+ if (zoomKey) {
109
+ setCamera(zoomAround(camera, point, camera.zoom * wheelZoomFactor(deltaY, deltaMode)));
110
+ return;
111
+ }
112
+ setCamera(panBy(camera, -normalizeWheelDelta(deltaX, deltaMode) * 1.15, -normalizeWheelDelta(deltaY, deltaMode) * 1.15));
113
+ }
114
+ return {
115
+ setCamera,
116
+ setCameraPolicy,
117
+ takeCameraControl,
118
+ cancelAnimation,
119
+ viewCenter,
120
+ visibleRect,
121
+ zoomAt,
122
+ zoomIn: () => zoomAt(surfaceCenter(), ZOOM_STEP, true),
123
+ zoomOut: () => zoomAt(surfaceCenter(), 1 / ZOOM_STEP, true),
124
+ resetZoom: () => animateCamera({
125
+ ...state.camera,
126
+ zoom: 1
127
+ }),
128
+ focusRect,
129
+ fitView,
130
+ focusItems,
131
+ focusNode: (id, focus) => focusItems([id], focus),
132
+ focusSelection,
133
+ wheel
134
+ };
135
+ }
136
+ //#endregion
137
+ export { createCameraModule };
@@ -0,0 +1,41 @@
1
+ import { BoardItem } from "../../protocol/dist/board-model.js";
2
+ import { BOARD_CLIPBOARD_KIND, BOARD_CLIPBOARD_MIME } from "../../protocol/dist/board.js";
3
+ import "../../protocol/dist/index.js";
4
+ import "../model/index.js";
5
+ //#region src/board/editor/clipboard.d.ts
6
+ /** How far a duplicate or repeated paste lands from its source. */
7
+ declare const DUPLICATE_OFFSET = 24;
8
+ declare const BOARD_CLIPBOARD_VERSION: 3;
9
+ declare const MAX_CLIPBOARD_ITEMS = 2000;
10
+ declare const MAX_CLIPBOARD_CHARS: number;
11
+ type BoardClipboardPayload = {
12
+ kind: typeof BOARD_CLIPBOARD_KIND;
13
+ version: typeof BOARD_CLIPBOARD_VERSION;
14
+ items: Record<string, BoardItem>;
15
+ origin: {
16
+ x: number;
17
+ y: number;
18
+ };
19
+ };
20
+ declare function encodeClipboard(items: Record<string, BoardItem>, origin: {
21
+ x: number;
22
+ y: number;
23
+ }): BoardClipboardPayload | null;
24
+ declare function parseClipboard(raw: unknown): BoardClipboardPayload | null;
25
+ declare function materializeClipboard(payload: BoardClipboardPayload, at: {
26
+ x: number;
27
+ y: number;
28
+ }, resolveEnd?: (id: string, which: "start" | "end") => {
29
+ x: number;
30
+ y: number;
31
+ } | null): Record<string, BoardItem>;
32
+ declare function remapItems(items: Record<string, BoardItem>, idMap: ReadonlyMap<string, string>, place?: (item: BoardItem) => BoardItem, resolveEnd?: (id: string, which: "start" | "end") => {
33
+ x: number;
34
+ y: number;
35
+ } | null): Record<string, BoardItem>;
36
+ declare function defaultPasteOffset(count?: number): {
37
+ x: number;
38
+ y: number;
39
+ };
40
+ //#endregion
41
+ export { BOARD_CLIPBOARD_MIME, BOARD_CLIPBOARD_VERSION, BoardClipboardPayload, DUPLICATE_OFFSET, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, defaultPasteOffset, encodeClipboard, materializeClipboard, parseClipboard, remapItems };
@@ -0,0 +1,129 @@
1
+ import { parseBoardItem } from "../../protocol/dist/board-model.js";
2
+ import { arrowBindings, isArrowBinding } from "../../protocol/dist/board-layout.js";
3
+ import { BOARD_CLIPBOARD_KIND, BOARD_CLIPBOARD_MIME } from "../../protocol/dist/board.js";
4
+ import { createBoardItemId } from "../model/items/id.js";
5
+ //#region src/board/editor/clipboard.ts
6
+ /** How far a duplicate or repeated paste lands from its source. */
7
+ const DUPLICATE_OFFSET = 24;
8
+ const BOARD_CLIPBOARD_VERSION = 3;
9
+ const MAX_CLIPBOARD_ITEMS = 2e3;
10
+ const MAX_CLIPBOARD_CHARS = 4194304;
11
+ function encodeClipboard(items, origin) {
12
+ const ids = Object.keys(items);
13
+ if (ids.length === 0 || ids.length > 2e3) return null;
14
+ return {
15
+ kind: BOARD_CLIPBOARD_KIND,
16
+ version: 3,
17
+ items: Object.fromEntries(ids.map((id) => {
18
+ const item = items[id];
19
+ return [id, item.parent && items[item.parent] ? item : shiftItem(item, -origin.x, -origin.y)];
20
+ })),
21
+ origin
22
+ };
23
+ }
24
+ function parseClipboard(raw) {
25
+ if (raw == null) return null;
26
+ if (typeof raw === "string") {
27
+ if (raw.length > 4194304) return null;
28
+ try {
29
+ raw = JSON.parse(raw);
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ if (!raw || typeof raw !== "object") return null;
35
+ const record = raw;
36
+ if (record.kind !== "cohub.board.clipboard" || record.version !== 3) return null;
37
+ const origin = record.origin;
38
+ if (typeof origin?.x !== "number" || typeof origin.y !== "number" || !Number.isFinite(origin.x) || !Number.isFinite(origin.y)) return null;
39
+ if (!record.items || typeof record.items !== "object" || Array.isArray(record.items)) return null;
40
+ const entries = Object.entries(record.items);
41
+ if (entries.length === 0 || entries.length > 2e3) return null;
42
+ const items = {};
43
+ for (const [id, value] of entries) {
44
+ const parsed = parseBoardItem(value);
45
+ if (!parsed.ok) return null;
46
+ items[id] = parsed.item;
47
+ }
48
+ return {
49
+ kind: BOARD_CLIPBOARD_KIND,
50
+ version: 3,
51
+ items,
52
+ origin: {
53
+ x: origin.x,
54
+ y: origin.y
55
+ }
56
+ };
57
+ }
58
+ function materializeClipboard(payload, at, resolveEnd = () => null) {
59
+ const idMap = new Map(Object.keys(payload.items).map((id) => [id, createBoardItemId()]));
60
+ return remapItems(payload.items, idMap, (item) => item.parent && idMap.has(item.parent) ? item : shiftItem(item, at.x, at.y), resolveEnd);
61
+ }
62
+ function remapItems(items, idMap, place = (item) => item, resolveEnd = () => null) {
63
+ const result = {};
64
+ for (const [id, source] of Object.entries(items)) {
65
+ let item = place(source);
66
+ const { locked: _locked, ...unlocked } = item;
67
+ item = unlocked;
68
+ if (item.script) item = {
69
+ ...item,
70
+ script: {
71
+ ...item.script,
72
+ targets: Object.fromEntries(Object.entries(item.script.targets).map(([name, target]) => [name, idMap.get(target) ?? target]))
73
+ }
74
+ };
75
+ if (item.parent) {
76
+ const parent = idMap.get(item.parent);
77
+ if (parent) item = {
78
+ ...item,
79
+ parent
80
+ };
81
+ else {
82
+ const { parent: _parent, ...root } = item;
83
+ item = root;
84
+ }
85
+ }
86
+ if (item.type === "arrow" && arrowBindings(item).length) {
87
+ const end = (which) => {
88
+ const value = item.type === "arrow" ? item.props[which] : null;
89
+ if (!value || !isArrowBinding(value)) return value;
90
+ const mapped = idMap.get(value.item);
91
+ if (mapped) return {
92
+ ...value,
93
+ item: mapped
94
+ };
95
+ return resolveEnd(id, which) ?? {
96
+ x: 0,
97
+ y: 0
98
+ };
99
+ };
100
+ item = {
101
+ ...item,
102
+ props: {
103
+ ...item.props,
104
+ start: end("start"),
105
+ end: end("end")
106
+ }
107
+ };
108
+ }
109
+ result[idMap.get(id) ?? createBoardItemId()] = item;
110
+ }
111
+ return result;
112
+ }
113
+ function defaultPasteOffset(count = 1) {
114
+ return {
115
+ x: 24 * count,
116
+ y: 24 * count
117
+ };
118
+ }
119
+ function shiftItem(item, dx, dy) {
120
+ return {
121
+ ...item,
122
+ position: {
123
+ x: item.position.x + dx,
124
+ y: item.position.y + dy
125
+ }
126
+ };
127
+ }
128
+ //#endregion
129
+ export { BOARD_CLIPBOARD_MIME, BOARD_CLIPBOARD_VERSION, DUPLICATE_OFFSET, MAX_CLIPBOARD_CHARS, MAX_CLIPBOARD_ITEMS, defaultPasteOffset, encodeClipboard, materializeClipboard, parseClipboard, remapItems };
@@ -0,0 +1,19 @@
1
+ import { BoardAnchorSide } from "../../protocol/dist/board-model.js";
2
+ import { BoardFrame } from "../../protocol/dist/board-layout.js";
3
+ import { WorldPoint } from "../model/geometry.js";
4
+ import "../model/index.js";
5
+ //#region src/board/editor/connection-ports.d.ts
6
+ declare const CONNECTION_SIDES: readonly BoardAnchorSide[];
7
+ declare const CONNECTION_PORT_OFFSET = 14;
8
+ declare const CONNECTION_PORT_RADIUS = 4;
9
+ declare const CONNECTION_PORT_HIT_RADIUS = 11;
10
+ declare const CONNECTION_PORT_TOUCH_HIT_RADIUS = 22;
11
+ type ConnectionPort = {
12
+ side: BoardAnchorSide;
13
+ point: WorldPoint;
14
+ };
15
+ declare function portHitRadius(pointerType: string): number;
16
+ declare function connectionPorts(frame: BoardFrame, zoom: number): ConnectionPort[];
17
+ declare function connectionPortAt(frame: BoardFrame, point: WorldPoint, zoom: number, pointerType: string): ConnectionPort | null;
18
+ //#endregion
19
+ export { CONNECTION_PORT_HIT_RADIUS, CONNECTION_PORT_OFFSET, CONNECTION_PORT_RADIUS, CONNECTION_PORT_TOUCH_HIT_RADIUS, CONNECTION_SIDES, ConnectionPort, connectionPortAt, connectionPorts, portHitRadius };
@@ -0,0 +1,85 @@
1
+ import { degToRad, frameRect, rectCenter, rotatePointAround, worldPoint } from "../model/geometry.js";
2
+ //#region src/board/editor/connection-ports.ts
3
+ const CONNECTION_SIDES = [
4
+ "top",
5
+ "right",
6
+ "bottom",
7
+ "left"
8
+ ];
9
+ const CONNECTION_PORT_OFFSET = 14;
10
+ const CONNECTION_PORT_RADIUS = 4;
11
+ const CONNECTION_PORT_HIT_RADIUS = 11;
12
+ const CONNECTION_PORT_TOUCH_HIT_RADIUS = 22;
13
+ function portHitRadius(pointerType) {
14
+ return pointerType === "touch" || pointerType === "pen" ? 22 : 11;
15
+ }
16
+ const SIDE_NORMAL = {
17
+ top: {
18
+ x: 0,
19
+ y: -1
20
+ },
21
+ right: {
22
+ x: 1,
23
+ y: 0
24
+ },
25
+ bottom: {
26
+ x: 0,
27
+ y: 1
28
+ },
29
+ left: {
30
+ x: -1,
31
+ y: 0
32
+ }
33
+ };
34
+ const SIDE_ANCHOR = {
35
+ top: {
36
+ nx: .5,
37
+ ny: 0
38
+ },
39
+ right: {
40
+ nx: 1,
41
+ ny: .5
42
+ },
43
+ bottom: {
44
+ nx: .5,
45
+ ny: 1
46
+ },
47
+ left: {
48
+ nx: 0,
49
+ ny: .5
50
+ }
51
+ };
52
+ function connectionPorts(frame, zoom) {
53
+ const offset = 14 / Math.max(zoom, 1e-4);
54
+ const center = rectCenter(frameRect(frame));
55
+ const rotation = frame.rotation ? degToRad(frame.rotation) : 0;
56
+ return CONNECTION_SIDES.map((side) => {
57
+ const anchor = SIDE_ANCHOR[side];
58
+ const base = worldPoint(frame.x + anchor.nx * frame.width, frame.y + anchor.ny * frame.height);
59
+ const rotated = rotation ? rotatePointAround(base, center, rotation) : base;
60
+ const normal = SIDE_NORMAL[side];
61
+ const direction = rotation ? {
62
+ x: normal.x * Math.cos(rotation) - normal.y * Math.sin(rotation),
63
+ y: normal.x * Math.sin(rotation) + normal.y * Math.cos(rotation)
64
+ } : normal;
65
+ return {
66
+ side,
67
+ point: worldPoint(rotated.x + direction.x * offset, rotated.y + direction.y * offset)
68
+ };
69
+ });
70
+ }
71
+ function connectionPortAt(frame, point, zoom, pointerType) {
72
+ const radius = portHitRadius(pointerType) / Math.max(zoom, 1e-4);
73
+ let closest = null;
74
+ let closestDistance = Number.POSITIVE_INFINITY;
75
+ for (const port of connectionPorts(frame, zoom)) {
76
+ const distance = Math.hypot(port.point.x - point.x, port.point.y - point.y);
77
+ if (distance <= radius && distance < closestDistance) {
78
+ closest = port;
79
+ closestDistance = distance;
80
+ }
81
+ }
82
+ return closest;
83
+ }
84
+ //#endregion
85
+ export { CONNECTION_PORT_HIT_RADIUS, CONNECTION_PORT_OFFSET, CONNECTION_PORT_RADIUS, CONNECTION_PORT_TOUCH_HIT_RADIUS, CONNECTION_SIDES, connectionPortAt, connectionPorts, portHitRadius };
@@ -0,0 +1,111 @@
1
+ import { BoardDocument, BoardPatch, BoardSettings } from "../../protocol/dist/board-model.js";
2
+ import { BoardViewport, WorldPoint } from "../model/geometry.js";
3
+ import { BoardScene } from "../model/scene.js";
4
+ import { BoardToolStyleMap } from "../model/tool-styles.js";
5
+ import { BoardRegistry } from "../model/items/registry.js";
6
+ import "../model/index.js";
7
+ import { DocumentModule } from "./document.js";
8
+ import { QueryModule } from "./query.js";
9
+ import { CameraModule } from "./camera.js";
10
+ import { SelectionModule } from "./selection.js";
11
+ import { BoardInteraction, InteractionModule } from "./interaction.js";
12
+ import { CreationModule } from "./creation.js";
13
+ import { DeletionModule } from "./deletion.js";
14
+ import { CopyModule } from "./copy.js";
15
+ import { SnapshotsModule } from "./snapshots.js";
16
+ import { RemoteModule } from "./remote.js";
17
+ import { SnapGuide } from "./snapping.js";
18
+ import { BoardEditorChannel, BoardEditorChannels } from "./state.js";
19
+ import { BoardToolId } from "./tool.js";
20
+ import { AnimationModule } from "./animation.js";
21
+ //#region src/board/editor/context.d.ts
22
+ type BoardCameraPolicy = "follow" | "free";
23
+ type BoardPlayhead = {
24
+ animationId: string;
25
+ time: number;
26
+ };
27
+ type BoardUndoEntry = {
28
+ undo: BoardPatch;
29
+ redo: BoardPatch;
30
+ };
31
+ type BoardEditorState = {
32
+ base: BoardDocument;
33
+ settingsPreview: BoardSettings | null;
34
+ scene: BoardScene;
35
+ structureVersion: number;
36
+ geometryVersion: number;
37
+ selection: string[];
38
+ camera: BoardViewport;
39
+ surfaceSize: {
40
+ width: number;
41
+ height: number;
42
+ };
43
+ cameraPolicy: BoardCameraPolicy;
44
+ tool: BoardToolId;
45
+ toolStyles: BoardToolStyleMap;
46
+ interaction: BoardInteraction;
47
+ snapGuides: SnapGuide[];
48
+ spaceHeld: boolean;
49
+ hoverId: string | null;
50
+ hoverPoint: WorldPoint | null;
51
+ hoverPointerType: string;
52
+ editingId: string | null;
53
+ draftTextId: string | null;
54
+ playhead: BoardPlayhead | null;
55
+ recording: boolean;
56
+ undoStack: BoardUndoEntry[];
57
+ redoStack: BoardUndoEntry[];
58
+ saveError: string | null;
59
+ pendingCommits: number;
60
+ };
61
+ declare const STATE_CHANNELS: { readonly [K in keyof BoardEditorState]: BoardEditorChannel; };
62
+ type BoardEditorPreferences = {
63
+ toolStyles: BoardToolStyleMap;
64
+ cameraPolicy: BoardCameraPolicy;
65
+ };
66
+ type BoardViewState = {
67
+ visibleRect: {
68
+ x: number;
69
+ y: number;
70
+ width: number;
71
+ height: number;
72
+ } | null;
73
+ selectedNodes: Array<{
74
+ id: string;
75
+ type: string;
76
+ title?: string;
77
+ }>;
78
+ };
79
+ type BoardEditorOptions = {
80
+ document: BoardDocument;
81
+ registry?: BoardRegistry;
82
+ viewport?: BoardViewport;
83
+ initialTool?: BoardToolId;
84
+ key?: string;
85
+ readonly?: boolean;
86
+ preferences?: Partial<BoardEditorPreferences>;
87
+ onPreferencesChange?: (preferences: BoardEditorPreferences) => void;
88
+ onCommit: (patch: BoardPatch) => void | Promise<void>;
89
+ onViewStateChange?: (state: BoardViewState) => void;
90
+ track?: (channel: BoardEditorChannel) => void;
91
+ };
92
+ type EditorContext = {
93
+ readonly options: BoardEditorOptions;
94
+ readonly registry: BoardRegistry;
95
+ readonly state: BoardEditorState;
96
+ readonly channels: BoardEditorChannels;
97
+ preferencesChanged(): void;
98
+ document: DocumentModule;
99
+ query: QueryModule;
100
+ camera: CameraModule;
101
+ selection: SelectionModule;
102
+ creation: CreationModule;
103
+ deletion: DeletionModule;
104
+ copy: CopyModule;
105
+ interaction: InteractionModule;
106
+ animation: AnimationModule;
107
+ snapshots: SnapshotsModule;
108
+ remote: RemoteModule;
109
+ };
110
+ //#endregion
111
+ export { BoardCameraPolicy, BoardEditorOptions, BoardEditorPreferences, BoardEditorState, BoardPlayhead, BoardUndoEntry, BoardViewState, EditorContext, STATE_CHANNELS };
@@ -0,0 +1,30 @@
1
+ //#region src/board/editor/context.ts
2
+ const STATE_CHANNELS = {
3
+ base: "document",
4
+ settingsPreview: "document",
5
+ scene: "scene",
6
+ structureVersion: "scene",
7
+ geometryVersion: "scene",
8
+ selection: "selection",
9
+ camera: "camera",
10
+ surfaceSize: "camera",
11
+ cameraPolicy: "camera",
12
+ tool: "tool",
13
+ toolStyles: "tool",
14
+ interaction: "interaction",
15
+ snapGuides: "interaction",
16
+ spaceHeld: "interaction",
17
+ hoverId: "hover",
18
+ hoverPoint: "hover",
19
+ hoverPointerType: "hover",
20
+ editingId: "editing",
21
+ draftTextId: "editing",
22
+ playhead: "playback",
23
+ recording: "playback",
24
+ undoStack: "history",
25
+ redoStack: "history",
26
+ saveError: "status",
27
+ pendingCommits: "status"
28
+ };
29
+ //#endregion
30
+ export { STATE_CHANNELS };
@@ -0,0 +1,17 @@
1
+ import { BoardItem } from "../../protocol/dist/board-model.js";
2
+ import { WorldPoint } from "../model/geometry.js";
3
+ import "../model/index.js";
4
+ import { BoardClipboardPayload } from "./clipboard.js";
5
+ import { EditorContext } from "./context.js";
6
+ //#region src/board/editor/copy.d.ts
7
+ declare function createCopyModule(ctx: EditorContext): {
8
+ duplicates: (ids: string[], offset: number) => Map<string, BoardItem>;
9
+ rootsOfCopies: (copies: ReadonlyMap<string, BoardItem>) => string[];
10
+ duplicateSelection: () => void;
11
+ copySelection: () => BoardClipboardPayload | null;
12
+ cutSelection: () => BoardClipboardPayload | null;
13
+ pasteClipboard: (raw?: unknown, at?: WorldPoint) => void;
14
+ };
15
+ type CopyModule = ReturnType<typeof createCopyModule>;
16
+ //#endregion
17
+ export { CopyModule, createCopyModule };
@@ -0,0 +1,121 @@
1
+ import { applyMatrix, arrowBindings, invertMatrix } from "../../protocol/dist/board-layout.js";
2
+ import { resolveSceneArrow } from "../model/arrow-geometry.js";
3
+ import { createBoardItemId } from "../model/items/id.js";
4
+ import { defaultPasteOffset, encodeClipboard, materializeClipboard, parseClipboard, remapItems } from "./clipboard.js";
5
+ //#region src/board/editor/copy.ts
6
+ function createCopyModule(ctx) {
7
+ const { state, options } = ctx;
8
+ let internalClipboard = null;
9
+ let pasteCount = 0;
10
+ function detachedSubtrees(ids, scene) {
11
+ const items = {};
12
+ for (const root of ctx.query.rootsOf(ids)) {
13
+ const item = scene.get(root);
14
+ if (!item || !state.base.items[root]) continue;
15
+ const { id: _id, frame, ...stored } = item;
16
+ items[root] = ctx.document.reparent(stored, frame, void 0, scene.layout.box(root));
17
+ for (const child of scene.descendants(root)) {
18
+ if (!state.base.items[child]) continue;
19
+ const shown = scene.get(child);
20
+ if (shown) {
21
+ const { id: _id, frame: _frame, ...value } = shown;
22
+ items[child] = value;
23
+ }
24
+ }
25
+ }
26
+ return items;
27
+ }
28
+ function arrowEndPoint(scene, id, which) {
29
+ const arrow = scene.get(id);
30
+ if (arrow?.type !== "arrow") return null;
31
+ const resolved = resolveSceneArrow(arrow, scene);
32
+ return applyMatrix(invertMatrix(scene.layout.matrix(id)), (which === "start" ? resolved.start : resolved.end).point);
33
+ }
34
+ function duplicates(ids, offset) {
35
+ const scene = ctx.document.copyScene();
36
+ const copied = detachedSubtrees(ids, scene);
37
+ for (const id of Object.keys(copied)) for (const arrowId of scene.binders(id)) {
38
+ if (!state.base.items[arrowId]) continue;
39
+ const shown = scene.get(arrowId);
40
+ if (copied[arrowId] || !shown || !arrowBindings(shown).every((bound) => copied[bound])) continue;
41
+ const { id: _id, frame, ...arrow } = shown;
42
+ copied[arrowId] = ctx.document.reparent(arrow, frame, void 0, scene.layout.box(arrowId));
43
+ }
44
+ const idMap = new Map(Object.keys(copied).map((id) => [id, createBoardItemId()]));
45
+ const moved = remapItems(copied, idMap, (item) => item.parent && copied[item.parent] ? item : {
46
+ ...item,
47
+ position: {
48
+ x: item.position.x + offset,
49
+ y: item.position.y + offset
50
+ }
51
+ }, (id, which) => arrowEndPoint(scene, id, which));
52
+ let z = ctx.document.topZ(void 0, scene);
53
+ return new Map(Object.entries(moved).map(([id, item]) => [id, item.parent ? item : {
54
+ ...item,
55
+ z: z++
56
+ }]));
57
+ }
58
+ function rootsOfCopies(copies) {
59
+ return [...copies].filter(([, item]) => !item.parent || !copies.has(item.parent)).map(([id]) => id);
60
+ }
61
+ function duplicateSelection() {
62
+ if (options.readonly || state.selection.length === 0) return;
63
+ const copies = duplicates(state.selection, 24);
64
+ if (copies.size === 0) return;
65
+ const roots = rootsOfCopies(copies);
66
+ ctx.document.markAdded(roots);
67
+ state.selection = roots;
68
+ ctx.document.commitItems(copies);
69
+ }
70
+ function copySelection() {
71
+ const { selection } = state;
72
+ if (selection.length === 0) return null;
73
+ const scene = ctx.document.copyScene();
74
+ const copied = detachedSubtrees(selection, scene);
75
+ const origin = ctx.query.contentBounds(ctx.query.rootsOf(selection), scene);
76
+ const payload = origin ? encodeClipboard(copied, {
77
+ x: origin.x,
78
+ y: origin.y
79
+ }) : null;
80
+ if (payload) {
81
+ internalClipboard = payload;
82
+ pasteCount = 0;
83
+ }
84
+ return payload;
85
+ }
86
+ function cutSelection() {
87
+ const payload = copySelection();
88
+ if (payload) ctx.deletion.deleteSelection();
89
+ return payload;
90
+ }
91
+ function pasteClipboard(raw, at) {
92
+ if (options.readonly) return;
93
+ const parsed = parseClipboard(raw) ?? (raw == null ? parseClipboard(internalClipboard) : null);
94
+ if (!parsed) return;
95
+ pasteCount += 1;
96
+ const offset = at ?? {
97
+ x: parsed.origin.x + defaultPasteOffset(pasteCount).x,
98
+ y: parsed.origin.y + defaultPasteOffset(pasteCount).y
99
+ };
100
+ const pasted = materializeClipboard(parsed, offset);
101
+ const entries = Object.entries(pasted).map(([id, item]) => ({
102
+ id,
103
+ item
104
+ }));
105
+ const roots = entries.filter(({ item }) => !item.parent).map(({ id }) => id);
106
+ const changes = ctx.creation.placeNew(entries);
107
+ ctx.document.markAdded(roots);
108
+ state.selection = roots;
109
+ ctx.document.commitItems(changes);
110
+ }
111
+ return {
112
+ duplicates,
113
+ rootsOfCopies,
114
+ duplicateSelection,
115
+ copySelection,
116
+ cutSelection,
117
+ pasteClipboard
118
+ };
119
+ }
120
+ //#endregion
121
+ export { createCopyModule };