@neta-art/cohub 9.0.0 → 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 +25 -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 +26 -14
  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,224 @@
1
+ import { IDENTITY_MATRIX, applyMatrix, invertMatrix } from "../../protocol/dist/board-layout.js";
2
+ import { rectCenter } from "../model/geometry.js";
3
+ import { buildBoardScene } from "../model/scene.js";
4
+ import { isShapeKind } from "../model/shape-types.js";
5
+ import { createBoardItemId } from "../model/items/id.js";
6
+ import { createAppBoardItem, createArrowBoardItem, createDrawBoardItem, createFileNodeForPath, createFrameBoardItem, createShapeBoardItem, createTaskBoardItem, createTextBoardItem } from "../model/items/create.js";
7
+ import { sameJson } from "./document-edits.js";
8
+ import { isContinuousBoardTool } from "./tool.js";
9
+ //#region src/board/editor/creation.ts
10
+ function createCreationModule(ctx) {
11
+ const { state, options } = ctx;
12
+ function placeNew(entries) {
13
+ const created = new Set(entries.map((entry) => entry.id));
14
+ const probe = buildBoardScene({
15
+ ...state.base,
16
+ items: Object.fromEntries(entries.map((entry) => [entry.id, entry.item]))
17
+ });
18
+ const nextZ = /* @__PURE__ */ new Map();
19
+ const changes = /* @__PURE__ */ new Map();
20
+ for (const { id, item } of entries) {
21
+ if (item.parent && created.has(item.parent)) {
22
+ changes.set(id, item);
23
+ continue;
24
+ }
25
+ const shown = probe.get(id);
26
+ const container = shown && item.type !== "frame" && item.type !== "arrow" ? ctx.query.frameAt(rectCenter(shown.frame), created) : null;
27
+ const value = container && shown ? ctx.document.reparent(item, shown.frame, container.id, probe.layout.box(id)) : item;
28
+ const z = nextZ.get(container?.id) ?? ctx.document.topZ(container?.id);
29
+ nextZ.set(container?.id, z + 1);
30
+ changes.set(id, {
31
+ ...value,
32
+ z
33
+ });
34
+ }
35
+ return changes;
36
+ }
37
+ function returnToSelect() {
38
+ if (!isContinuousBoardTool(state.tool)) state.tool = "select";
39
+ }
40
+ function add(entries, { select = !isContinuousBoardTool(state.tool), enter = true } = {}) {
41
+ if (options.readonly || entries.length === 0) return;
42
+ const changes = placeNew(entries);
43
+ if (enter) ctx.document.markAdded([...changes.keys()]);
44
+ state.selection = select ? [...changes.keys()] : [];
45
+ ctx.document.commitItems(changes);
46
+ returnToSelect();
47
+ }
48
+ function addOne(entry) {
49
+ add([entry]);
50
+ return entry.id;
51
+ }
52
+ function addItem(type, input) {
53
+ if (options.readonly) return null;
54
+ return addOne(ctx.registry.create(type, input));
55
+ }
56
+ function addTaskWithSources(taskRunId, snapshot, at, sources, metadata) {
57
+ if (options.readonly) return null;
58
+ const { arrow } = state.toolStyles;
59
+ const task = createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata);
60
+ add([task, ...sources.filter((source) => source.itemId !== task.id && state.scene.get(source.itemId)).map((source) => {
61
+ const entry = createArrowBoardItem({
62
+ x: 0,
63
+ y: 0
64
+ }, { item: task.id }, arrow.color, createBoardItemId(), arrow.size, { item: source.itemId });
65
+ const props = entry.item.props;
66
+ return {
67
+ id: entry.id,
68
+ item: {
69
+ ...entry.item,
70
+ metadata: { boardFlow: {
71
+ version: 1,
72
+ kind: "content",
73
+ sourcePortId: source.sourcePortId,
74
+ targetPortId: source.targetPortId
75
+ } },
76
+ props: {
77
+ ...props,
78
+ start: {
79
+ item: source.itemId,
80
+ anchor: "auto",
81
+ port: source.sourcePortId
82
+ },
83
+ end: {
84
+ item: task.id,
85
+ anchor: "auto",
86
+ port: source.targetPortId
87
+ },
88
+ relation: "input"
89
+ }
90
+ }
91
+ };
92
+ })], { select: false });
93
+ state.selection = [task.id];
94
+ return task.id;
95
+ }
96
+ function commitBox(gesture) {
97
+ const dx = gesture.current.x - gesture.start.x;
98
+ const dy = gesture.current.y - gesture.start.y;
99
+ const click = Math.hypot(dx, dy) <= 6 / Math.max(state.camera.zoom, 1e-4);
100
+ const minimum = gesture.kind === "frame" ? 48 : 24;
101
+ const box = click ? void 0 : {
102
+ x: Math.min(gesture.start.x, gesture.current.x),
103
+ y: Math.min(gesture.start.y, gesture.current.y),
104
+ width: Math.max(minimum, Math.abs(dx)),
105
+ height: Math.max(minimum, Math.abs(dy))
106
+ };
107
+ const geometry = isShapeKind(gesture.geometry) ? gesture.geometry : "rectangle";
108
+ const { x, y } = gesture.start;
109
+ add([gesture.kind === "shape" ? createShapeBoardItem(geometry, x, y, gesture.color, gesture.id, box) : createFrameBoardItem(x, y, gesture.color, "Frame", gesture.id, box)], { enter: false });
110
+ }
111
+ function commitDraw(id, points, color, size) {
112
+ if (points.length === 0) return;
113
+ add([createDrawBoardItem(points, color, size, id)], { enter: false });
114
+ }
115
+ function arrowEnd(point, targetItemId, arrowMatrix = IDENTITY_MATRIX) {
116
+ if (targetItemId) return {
117
+ item: targetItemId,
118
+ anchor: "auto"
119
+ };
120
+ const local = applyMatrix(invertMatrix(arrowMatrix), point);
121
+ return {
122
+ x: local.x,
123
+ y: local.y
124
+ };
125
+ }
126
+ function commitArrow(gesture, cancelled) {
127
+ const { start, current, startItemId, targetItemId } = gesture;
128
+ const bound = !cancelled && targetItemId && targetItemId !== startItemId ? targetItemId : null;
129
+ if (!bound && Math.hypot(current.x - start.x, current.y - start.y) < 2 / state.camera.zoom) return;
130
+ add([createArrowBoardItem(start, bound ? { item: bound } : current, gesture.color, gesture.id, gesture.size, startItemId ? {
131
+ item: startItemId,
132
+ ...gesture.startSide ? { anchor: {
133
+ side: gesture.startSide,
134
+ offset: .5
135
+ } } : {}
136
+ } : void 0)], { enter: false });
137
+ }
138
+ function beginTextDraft(at) {
139
+ const entry = createTextBoardItem("", at.x, at.y, state.toolStyles.text.color);
140
+ ctx.document.writeDraft(placeNew([entry]), true);
141
+ state.draftTextId = entry.id;
142
+ state.selection = [entry.id];
143
+ state.editingId = entry.id;
144
+ }
145
+ function previewTextLayout(id, text) {
146
+ const item = ctx.document.displayItem(id);
147
+ if (item?.type !== "text" || item.props.text === text) return;
148
+ ctx.document.writeDraft(/* @__PURE__ */ new Map([[id, {
149
+ ...item,
150
+ props: {
151
+ ...item.props,
152
+ text
153
+ }
154
+ }]]));
155
+ }
156
+ function updateText(id, text, created = false) {
157
+ const item = ctx.document.displayItem(id);
158
+ if (!item || item.type !== "text" && item.type !== "shape" && item.type !== "arrow" && item.type !== "frame") return;
159
+ const next = item.type === "arrow" || item.type === "frame" ? {
160
+ ...item,
161
+ props: {
162
+ ...item.props,
163
+ label: text
164
+ }
165
+ } : {
166
+ ...item,
167
+ props: {
168
+ ...item.props,
169
+ text
170
+ }
171
+ };
172
+ if (!created && sameJson(next, state.base.items[id])) {
173
+ ctx.document.dropDraftItem(id);
174
+ ctx.document.rescene([id]);
175
+ return;
176
+ }
177
+ ctx.document.commitItems(/* @__PURE__ */ new Map([[id, next]]));
178
+ }
179
+ function commitTextEdit(id, text) {
180
+ const isDraft = id === state.draftTextId;
181
+ const target = ctx.document.displayItem(id);
182
+ const empty = text.trim() === "" && (isDraft || target?.type === "text");
183
+ state.editingId = null;
184
+ state.draftTextId = null;
185
+ if (empty) {
186
+ if (isDraft) {
187
+ ctx.document.dropDraftItem(id);
188
+ ctx.document.rescene();
189
+ } else ctx.deletion.deleteItem(id);
190
+ } else updateText(id, text, isDraft);
191
+ if (state.tool === "text") state.tool = "select";
192
+ ctx.remote.flushPending();
193
+ }
194
+ const styles = () => state.toolStyles;
195
+ return {
196
+ placeNew,
197
+ add,
198
+ addItem,
199
+ addApp: (app, at) => addOne(createAppBoardItem(app, at.x, at.y)),
200
+ addFile: (path, at, snapshot) => addOne(createFileNodeForPath(path, at.x, at.y, snapshot)),
201
+ addTask: (taskRunId, snapshot, at, metadata) => addOne(createTaskBoardItem(taskRunId, snapshot, at.x, at.y, metadata)),
202
+ addTaskWithSources,
203
+ addText: (text, at) => {
204
+ add([createTextBoardItem(text, at.x, at.y, styles().text.color)]);
205
+ },
206
+ addShape: (at, geometry) => {
207
+ const { shape } = styles();
208
+ add([createShapeBoardItem(geometry ?? shape.geometry, at.x, at.y, shape.color)]);
209
+ },
210
+ addFrame: (at) => {
211
+ add([createFrameBoardItem(at.x, at.y, styles().frame.color)]);
212
+ },
213
+ commitBox,
214
+ commitDraw,
215
+ commitArrow,
216
+ arrowEnd,
217
+ beginTextDraft,
218
+ previewTextLayout,
219
+ updateText,
220
+ commitTextEdit
221
+ };
222
+ }
223
+ //#endregion
224
+ export { createCreationModule };
@@ -0,0 +1,9 @@
1
+ import { EditorContext } from "./context.js";
2
+ //#region src/board/editor/deletion.d.ts
3
+ declare function createDeletionModule(ctx: EditorContext): {
4
+ deleteSelection: () => void;
5
+ deleteItem: (id: string) => void;
6
+ };
7
+ type DeletionModule = ReturnType<typeof createDeletionModule>;
8
+ //#endregion
9
+ export { DeletionModule, createDeletionModule };
@@ -0,0 +1,90 @@
1
+ import { applyMatrix, invertMatrix, isArrowBinding } from "../../protocol/dist/board-layout.js";
2
+ import { pruneBoardTrack } from "../../protocol/dist/board-patch.js";
3
+ import { resolveSceneArrow } from "../model/arrow-geometry.js";
4
+ //#region src/board/editor/deletion.ts
5
+ function createDeletionModule(ctx) {
6
+ const { state } = ctx;
7
+ function changesFor(ids) {
8
+ const { scene, base } = state;
9
+ const doomed = /* @__PURE__ */ new Set();
10
+ for (const id of ids) {
11
+ if (!scene.get(id)) continue;
12
+ doomed.add(id);
13
+ for (const child of scene.descendants(id)) doomed.add(child);
14
+ }
15
+ const changes = /* @__PURE__ */ new Map();
16
+ for (const id of doomed) changes.set(id, null);
17
+ for (const id of doomed) for (const arrowId of scene.binders(id)) {
18
+ if (doomed.has(arrowId) || changes.has(arrowId)) continue;
19
+ const arrow = scene.get(arrowId);
20
+ if (!arrow) continue;
21
+ const resolved = resolveSceneArrow(arrow, scene);
22
+ const toLocal = invertMatrix(scene.layout.matrix(arrowId));
23
+ const free = (end, point) => {
24
+ if (!isArrowBinding(end) || !doomed.has(end.item)) return end;
25
+ const local = applyMatrix(toLocal, point);
26
+ return {
27
+ x: local.x,
28
+ y: local.y
29
+ };
30
+ };
31
+ const { id: _id, frame: _frame, ...stored } = arrow;
32
+ changes.set(arrowId, {
33
+ ...stored,
34
+ props: {
35
+ ...arrow.props,
36
+ start: free(arrow.props.start, resolved.start.point),
37
+ end: free(arrow.props.end, resolved.end.point)
38
+ }
39
+ });
40
+ }
41
+ let animations;
42
+ for (const [animationId, animation] of Object.entries(base.animations)) {
43
+ const tracks = { ...animation.tracks };
44
+ let changed = false;
45
+ for (const [id, track] of Object.entries(tracks)) {
46
+ const next = pruneBoardTrack(track, doomed);
47
+ if (next === track) continue;
48
+ changed = true;
49
+ if (next) tracks[id] = next;
50
+ else delete tracks[id];
51
+ }
52
+ if (!changed) continue;
53
+ animations ??= { ...base.animations };
54
+ animations[animationId] = {
55
+ ...animation,
56
+ tracks
57
+ };
58
+ }
59
+ return {
60
+ items: changes,
61
+ ...animations ? { animations } : {}
62
+ };
63
+ }
64
+ function deleteIds(ids) {
65
+ const { items: changes, animations } = changesFor(ids);
66
+ if (changes.size === 0) return;
67
+ for (const [id, item] of changes) ctx.document.setDraftItem(id, item);
68
+ ctx.document.commit(animations ? { animations } : {});
69
+ state.selection = state.selection.filter((id) => !changes.has(id) || changes.get(id) !== null);
70
+ if (state.editingId && changes.get(state.editingId) === null) state.editingId = null;
71
+ }
72
+ function deleteSelection() {
73
+ const ids = ctx.query.unlockedIds(state.selection);
74
+ if (ids.length === 0) return;
75
+ deleteIds(ids);
76
+ state.selection = [];
77
+ state.editingId = null;
78
+ }
79
+ function deleteItem(id) {
80
+ const target = state.scene.get(id);
81
+ if (!target || ctx.query.isLocked(target)) return;
82
+ deleteIds([id]);
83
+ }
84
+ return {
85
+ deleteSelection,
86
+ deleteItem
87
+ };
88
+ }
89
+ //#endregion
90
+ export { createDeletionModule };
@@ -0,0 +1,22 @@
1
+ import { BoardDocument, BoardItem, BoardPatch } from "../../protocol/dist/board-model.js";
2
+ import "../model/index.js";
3
+ //#region src/board/editor/document-edits.d.ts
4
+ declare function sameJson(a: unknown, b: unknown): boolean;
5
+ declare function readPath(value: unknown, path: readonly string[]): unknown;
6
+ declare function writePath<T>(value: T, path: readonly string[], next: unknown): T;
7
+ type BoardEditPlayhead = {
8
+ animationId: string;
9
+ time: number;
10
+ };
11
+ declare function routeBoardEdits(input: {
12
+ base: BoardDocument;
13
+ evaluated: ReadonlyMap<string, BoardItem>;
14
+ draft: ReadonlyMap<string, BoardItem | null>;
15
+ displayed?: ReadonlyMap<string, BoardItem>;
16
+ playhead: BoardEditPlayhead | null;
17
+ recording: boolean;
18
+ }): BoardDocument;
19
+ declare function isEmptyBoardPatch(patch: BoardPatch): boolean;
20
+ declare function diffBoardEdits(from: BoardDocument, to: BoardDocument, ids: Iterable<string>): BoardPatch;
21
+ //#endregion
22
+ export { BoardEditPlayhead, diffBoardEdits, isEmptyBoardPatch, readPath, routeBoardEdits, sameJson, writePath };
@@ -0,0 +1,193 @@
1
+ import { scaleVector } from "../../protocol/dist/board-layout.js";
2
+ import { diffMergePatch, listBoardProperties } from "../../protocol/dist/board-patch.js";
3
+ import { createBoardEntityId } from "../model/items/id.js";
4
+ //#region src/board/editor/document-edits.ts
5
+ function sameJson(a, b) {
6
+ if (a === b) return true;
7
+ if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
8
+ if (Array.isArray(a) || Array.isArray(b)) {
9
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
10
+ for (let index = 0; index < a.length; index += 1) if (!sameJson(a[index], b[index])) return false;
11
+ return true;
12
+ }
13
+ const left = a;
14
+ const right = b;
15
+ let members = 0;
16
+ for (const key of Object.keys(left)) {
17
+ if (left[key] === void 0) continue;
18
+ members += 1;
19
+ if (!sameJson(left[key], right[key])) return false;
20
+ }
21
+ for (const key of Object.keys(right)) if (right[key] !== void 0) members -= 1;
22
+ return members === 0;
23
+ }
24
+ function readPath(value, path) {
25
+ let current = value;
26
+ for (const segment of path) {
27
+ if (!current || typeof current !== "object") return void 0;
28
+ current = current[segment];
29
+ }
30
+ return current;
31
+ }
32
+ function writePath(value, path, next) {
33
+ const [head, ...rest] = path;
34
+ if (head === void 0) return next;
35
+ const record = value && typeof value === "object" ? value : {};
36
+ return {
37
+ ...record,
38
+ [head]: writePath(record[head], rest, next)
39
+ };
40
+ }
41
+ const KEYFRAMABLE_KINDS = /* @__PURE__ */ new Set([
42
+ "number",
43
+ "vec2",
44
+ "scale",
45
+ "color"
46
+ ]);
47
+ const STRUCTURAL_KEYS = [
48
+ "parent",
49
+ "z",
50
+ "locked",
51
+ "metadata"
52
+ ];
53
+ function keyframe(animations, playhead, recording, id, property, value, storedValue) {
54
+ const animation = animations[playhead.animationId];
55
+ if (!animation) return false;
56
+ const time = Math.round(playhead.time);
57
+ const existing = Object.entries(animation.tracks).find(([, track]) => track.target === id && track.property === property && track.composite === "replace");
58
+ if (!existing && !recording) return false;
59
+ const candidateId = `${id}-${property.replaceAll(".", "-")}`;
60
+ const newId = candidateId.length <= 160 && !Object.hasOwn(animation.tracks, candidateId) ? candidateId : createBoardEntityId();
61
+ const [trackId, track] = existing ?? [newId, {
62
+ target: id,
63
+ property,
64
+ keyframes: time > 0 ? [{
65
+ at: 0,
66
+ value: storedValue
67
+ }] : [],
68
+ composite: "replace",
69
+ interpolation: "auto"
70
+ }];
71
+ const keyframes = [...track.keyframes.filter((entry) => entry.at !== time), {
72
+ at: time,
73
+ value
74
+ }].sort((a, b) => a.at - b.at);
75
+ animations[playhead.animationId] = {
76
+ ...animation,
77
+ duration: Math.max(animation.duration, time),
78
+ tracks: {
79
+ ...animation.tracks,
80
+ [trackId]: {
81
+ ...track,
82
+ keyframes
83
+ }
84
+ }
85
+ };
86
+ return true;
87
+ }
88
+ function authoredValue(stored, shown, next, property = "") {
89
+ if (sameJson(shown, next)) return stored;
90
+ if (property === "scale" && stored !== void 0 && shown !== void 0 && next !== void 0 && [
91
+ stored,
92
+ shown,
93
+ next
94
+ ].some((value) => typeof value === "object")) return authoredValue(scaleVector(stored), scaleVector(shown), scaleVector(next), "scale.vector");
95
+ if (typeof stored === "number" && typeof shown === "number" && typeof next === "number") {
96
+ if (property === "opacity") return Math.max(0, Math.min(1, shown ? stored * next / shown : next));
97
+ if (property === "scale" || property.startsWith("scale.")) return shown ? stored * next / shown : next;
98
+ return stored + next - shown;
99
+ }
100
+ const record = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
101
+ if (record(stored) && record(shown) && record(next)) {
102
+ const result = { ...stored };
103
+ for (const key of /* @__PURE__ */ new Set([...Object.keys(shown), ...Object.keys(next)])) {
104
+ if (sameJson(shown[key], next[key])) continue;
105
+ if (!(key in next)) delete result[key];
106
+ else result[key] = authoredValue(stored[key], shown[key], next[key], property ? `${property}.${key}` : key);
107
+ }
108
+ return result;
109
+ }
110
+ return next;
111
+ }
112
+ function routeBoardEdits(input) {
113
+ const { base, evaluated, draft, playhead, recording } = input;
114
+ if (draft.size === 0) return base;
115
+ const items = { ...base.items };
116
+ const animations = { ...base.animations };
117
+ let keyed = false;
118
+ for (const [id, next] of draft) {
119
+ const stored = base.items[id];
120
+ if (!next) {
121
+ delete items[id];
122
+ continue;
123
+ }
124
+ if (!stored || next.type !== stored.type) {
125
+ items[id] = next;
126
+ continue;
127
+ }
128
+ const shown = input.displayed?.get(id) ?? evaluated.get(id) ?? stored;
129
+ if (!playhead) {
130
+ items[id] = authoredValue(stored, shown, next);
131
+ continue;
132
+ }
133
+ const properties = listBoardProperties(next.type);
134
+ let item = stored;
135
+ for (const { property, kind } of properties) {
136
+ const path = property.split(".");
137
+ const value = readPath(next, path);
138
+ if (sameJson(value, readPath(shown, path))) continue;
139
+ if (KEYFRAMABLE_KINDS.has(kind) && keyframe(animations, playhead, recording, id, property, value, readPath(stored, path))) keyed = true;
140
+ else item = writePath(item, path, authoredValue(readPath(stored, path), readPath(shown, path), value, property));
141
+ }
142
+ for (const key of STRUCTURAL_KEYS) if (!sameJson(next[key], shown[key])) {
143
+ const copy = { ...item };
144
+ if (next[key] === void 0) delete copy[key];
145
+ else copy[key] = next[key];
146
+ item = copy;
147
+ }
148
+ items[id] = item;
149
+ }
150
+ return {
151
+ ...base,
152
+ items,
153
+ animations: keyed ? animations : base.animations
154
+ };
155
+ }
156
+ function isEmptyBoardPatch(patch) {
157
+ return !patch.board && !patch.items && !patch.animations;
158
+ }
159
+ function diffBoardEdits(from, to, ids) {
160
+ const patch = {};
161
+ if (from.board !== to.board && !sameJson(from.board, to.board)) patch.board = diffMergePatch(from.board, to.board);
162
+ const itemPatch = {};
163
+ for (const id of new Set(ids)) {
164
+ const before = from.items[id];
165
+ const after = to.items[id];
166
+ if (before === after) continue;
167
+ if (!after) {
168
+ if (before) itemPatch[id] = null;
169
+ } else if (!before || before.type !== after.type) itemPatch[id] = after;
170
+ else {
171
+ const change = diffMergePatch(before, after);
172
+ if (change && typeof change === "object" && Object.keys(change).length) itemPatch[id] = change;
173
+ }
174
+ }
175
+ if (Object.keys(itemPatch).length) patch.items = itemPatch;
176
+ if (from.animations !== to.animations) {
177
+ const animationPatch = {};
178
+ for (const id of /* @__PURE__ */ new Set([...Object.keys(from.animations), ...Object.keys(to.animations)])) {
179
+ const before = from.animations[id];
180
+ const after = to.animations[id];
181
+ if (before === after) continue;
182
+ if (!after) animationPatch[id] = null;
183
+ else {
184
+ const change = diffMergePatch(before ?? {}, after);
185
+ if (change && typeof change === "object" && Object.keys(change).length) animationPatch[id] = change;
186
+ }
187
+ }
188
+ if (Object.keys(animationPatch).length) patch.animations = animationPatch;
189
+ }
190
+ return patch;
191
+ }
192
+ //#endregion
193
+ export { diffBoardEdits, isEmptyBoardPatch, readPath, routeBoardEdits, sameJson, writePath };
@@ -0,0 +1,35 @@
1
+ import { BoardAnimation, BoardDocument, BoardItem, BoardSettings } from "../../protocol/dist/board-model.js";
2
+ import { BoardFrame } from "../../protocol/dist/board-layout.js";
3
+ import { Rect } from "../model/geometry.js";
4
+ import "../model/index.js";
5
+ import { EditorContext } from "./context.js";
6
+ //#region src/board/editor/document.d.ts
7
+ declare function createDocumentModule(ctx: EditorContext): {
8
+ markAdded: (ids: readonly string[]) => void;
9
+ consumeRecentlyAdded: () => string[];
10
+ displayItem: (id: string) => BoardItem | undefined;
11
+ reevaluate: () => void;
12
+ setPlayedItems: (items: ReadonlyMap<string, BoardItem>) => void;
13
+ rescene: (ids?: Iterable<string>) => void;
14
+ reset: (next: BoardDocument) => void;
15
+ setDraftItem: (id: string, item: BoardItem | null) => void;
16
+ draftItem: (id: string) => BoardItem | null | undefined;
17
+ dropDraftItem: (id: string) => void;
18
+ writeDraft: (changes: ReadonlyMap<string, BoardItem | null>, structural?: boolean) => void;
19
+ discardDraft: () => void;
20
+ adopt: (next: BoardDocument, changedIds?: Iterable<string>) => void;
21
+ commit: (extra?: {
22
+ board?: BoardSettings;
23
+ animations?: Record<string, BoardAnimation>;
24
+ }, record?: boolean) => void;
25
+ commitItems: (changes: ReadonlyMap<string, BoardItem | null>, record?: boolean) => void;
26
+ undo: () => void;
27
+ redo: () => void;
28
+ previewSettings: (settings: BoardSettings) => void;
29
+ setSettings: (settings: BoardSettings) => void;
30
+ reparent: (item: BoardItem, frame: BoardFrame, parent: string | undefined, box: Rect) => BoardItem;
31
+ topZ: (parent: string | undefined) => number;
32
+ };
33
+ type DocumentModule = ReturnType<typeof createDocumentModule>;
34
+ //#endregion
35
+ export { DocumentModule, createDocumentModule };