@oh-just-another/state 0.57.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 (269) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +60 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/actions/actionClipboard.d.ts +6 -0
  6. package/dist/actions/actionClipboard.d.ts.map +1 -0
  7. package/dist/actions/actionClipboard.js +26 -0
  8. package/dist/actions/actionClipboard.js.map +1 -0
  9. package/dist/actions/actionGrouping.d.ts +5 -0
  10. package/dist/actions/actionGrouping.d.ts.map +1 -0
  11. package/dist/actions/actionGrouping.js +26 -0
  12. package/dist/actions/actionGrouping.js.map +1 -0
  13. package/dist/actions/actionHistory.d.ts +5 -0
  14. package/dist/actions/actionHistory.d.ts.map +1 -0
  15. package/dist/actions/actionHistory.js +25 -0
  16. package/dist/actions/actionHistory.js.map +1 -0
  17. package/dist/actions/actionKeyboard.d.ts +3 -0
  18. package/dist/actions/actionKeyboard.d.ts.map +1 -0
  19. package/dist/actions/actionKeyboard.js +130 -0
  20. package/dist/actions/actionKeyboard.js.map +1 -0
  21. package/dist/actions/actionLayout.d.ts +3 -0
  22. package/dist/actions/actionLayout.d.ts.map +1 -0
  23. package/dist/actions/actionLayout.js +65 -0
  24. package/dist/actions/actionLayout.js.map +1 -0
  25. package/dist/actions/actionMode.d.ts +13 -0
  26. package/dist/actions/actionMode.d.ts.map +1 -0
  27. package/dist/actions/actionMode.js +116 -0
  28. package/dist/actions/actionMode.js.map +1 -0
  29. package/dist/actions/actionSelection.d.ts +9 -0
  30. package/dist/actions/actionSelection.d.ts.map +1 -0
  31. package/dist/actions/actionSelection.js +57 -0
  32. package/dist/actions/actionSelection.js.map +1 -0
  33. package/dist/actions/actionView.d.ts +7 -0
  34. package/dist/actions/actionView.d.ts.map +1 -0
  35. package/dist/actions/actionView.js +14 -0
  36. package/dist/actions/actionView.js.map +1 -0
  37. package/dist/actions/actionZOrder.d.ts +7 -0
  38. package/dist/actions/actionZOrder.d.ts.map +1 -0
  39. package/dist/actions/actionZOrder.js +42 -0
  40. package/dist/actions/actionZOrder.js.map +1 -0
  41. package/dist/actions/actionZoom.d.ts +8 -0
  42. package/dist/actions/actionZoom.d.ts.map +1 -0
  43. package/dist/actions/actionZoom.js +61 -0
  44. package/dist/actions/actionZoom.js.map +1 -0
  45. package/dist/actions/index.d.ts +12 -0
  46. package/dist/actions/index.d.ts.map +1 -0
  47. package/dist/actions/index.js +11 -0
  48. package/dist/actions/index.js.map +1 -0
  49. package/dist/actions/registry.d.ts +68 -0
  50. package/dist/actions/registry.d.ts.map +1 -0
  51. package/dist/actions/registry.js +300 -0
  52. package/dist/actions/registry.js.map +1 -0
  53. package/dist/actions/types.d.ts +103 -0
  54. package/dist/actions/types.d.ts.map +1 -0
  55. package/dist/actions/types.js +10 -0
  56. package/dist/actions/types.js.map +1 -0
  57. package/dist/animation-tick.d.ts +28 -0
  58. package/dist/animation-tick.d.ts.map +1 -0
  59. package/dist/animation-tick.js +50 -0
  60. package/dist/animation-tick.js.map +1 -0
  61. package/dist/auto-compact.d.ts +27 -0
  62. package/dist/auto-compact.d.ts.map +1 -0
  63. package/dist/auto-compact.js +46 -0
  64. package/dist/auto-compact.js.map +1 -0
  65. package/dist/auto-layout-scheduler.d.ts +50 -0
  66. package/dist/auto-layout-scheduler.d.ts.map +1 -0
  67. package/dist/auto-layout-scheduler.js +94 -0
  68. package/dist/auto-layout-scheduler.js.map +1 -0
  69. package/dist/built-in-handlers.d.ts +24 -0
  70. package/dist/built-in-handlers.d.ts.map +1 -0
  71. package/dist/built-in-handlers.js +210 -0
  72. package/dist/built-in-handlers.js.map +1 -0
  73. package/dist/clipboard.d.ts +51 -0
  74. package/dist/clipboard.d.ts.map +1 -0
  75. package/dist/clipboard.js +105 -0
  76. package/dist/clipboard.js.map +1 -0
  77. package/dist/constants.d.ts +544 -0
  78. package/dist/constants.d.ts.map +1 -0
  79. package/dist/constants.js +537 -0
  80. package/dist/constants.js.map +1 -0
  81. package/dist/dom-events.d.ts +13 -0
  82. package/dist/dom-events.d.ts.map +1 -0
  83. package/dist/dom-events.js +70 -0
  84. package/dist/dom-events.js.map +1 -0
  85. package/dist/editor/anchor-points.d.ts +22 -0
  86. package/dist/editor/anchor-points.d.ts.map +1 -0
  87. package/dist/editor/anchor-points.js +28 -0
  88. package/dist/editor/anchor-points.js.map +1 -0
  89. package/dist/editor/applies/create.d.ts +60 -0
  90. package/dist/editor/applies/create.d.ts.map +1 -0
  91. package/dist/editor/applies/create.js +107 -0
  92. package/dist/editor/applies/create.js.map +1 -0
  93. package/dist/editor/applies/edge.d.ts +32 -0
  94. package/dist/editor/applies/edge.d.ts.map +1 -0
  95. package/dist/editor/applies/edge.js +44 -0
  96. package/dist/editor/applies/edge.js.map +1 -0
  97. package/dist/editor/applies/link-move.d.ts +53 -0
  98. package/dist/editor/applies/link-move.d.ts.map +1 -0
  99. package/dist/editor/applies/link-move.js +160 -0
  100. package/dist/editor/applies/link-move.js.map +1 -0
  101. package/dist/editor/applies/move.d.ts +38 -0
  102. package/dist/editor/applies/move.d.ts.map +1 -0
  103. package/dist/editor/applies/move.js +83 -0
  104. package/dist/editor/applies/move.js.map +1 -0
  105. package/dist/editor/applies/resize.d.ts +68 -0
  106. package/dist/editor/applies/resize.d.ts.map +1 -0
  107. package/dist/editor/applies/resize.js +199 -0
  108. package/dist/editor/applies/resize.js.map +1 -0
  109. package/dist/editor/applies/selection.d.ts +34 -0
  110. package/dist/editor/applies/selection.d.ts.map +1 -0
  111. package/dist/editor/applies/selection.js +65 -0
  112. package/dist/editor/applies/selection.js.map +1 -0
  113. package/dist/editor/applies/snap-grid.d.ts +45 -0
  114. package/dist/editor/applies/snap-grid.d.ts.map +1 -0
  115. package/dist/editor/applies/snap-grid.js +90 -0
  116. package/dist/editor/applies/snap-grid.js.map +1 -0
  117. package/dist/editor/container-ops.d.ts +74 -0
  118. package/dist/editor/container-ops.d.ts.map +1 -0
  119. package/dist/editor/container-ops.js +262 -0
  120. package/dist/editor/container-ops.js.map +1 -0
  121. package/dist/editor/event-fanout.d.ts +48 -0
  122. package/dist/editor/event-fanout.d.ts.map +1 -0
  123. package/dist/editor/event-fanout.js +53 -0
  124. package/dist/editor/event-fanout.js.map +1 -0
  125. package/dist/editor/gesture-tx.d.ts +77 -0
  126. package/dist/editor/gesture-tx.d.ts.map +1 -0
  127. package/dist/editor/gesture-tx.js +93 -0
  128. package/dist/editor/gesture-tx.js.map +1 -0
  129. package/dist/editor/hit-test.d.ts +178 -0
  130. package/dist/editor/hit-test.d.ts.map +1 -0
  131. package/dist/editor/hit-test.js +460 -0
  132. package/dist/editor/hit-test.js.map +1 -0
  133. package/dist/editor/long-press.d.ts +34 -0
  134. package/dist/editor/long-press.d.ts.map +1 -0
  135. package/dist/editor/long-press.js +58 -0
  136. package/dist/editor/long-press.js.map +1 -0
  137. package/dist/editor/pinch.d.ts +37 -0
  138. package/dist/editor/pinch.d.ts.map +1 -0
  139. package/dist/editor/pinch.js +90 -0
  140. package/dist/editor/pinch.js.map +1 -0
  141. package/dist/editor/pointer-binding.d.ts +18 -0
  142. package/dist/editor/pointer-binding.d.ts.map +1 -0
  143. package/dist/editor/pointer-binding.js +1087 -0
  144. package/dist/editor/pointer-binding.js.map +1 -0
  145. package/dist/editor/public/annotations.d.ts +54 -0
  146. package/dist/editor/public/annotations.d.ts.map +1 -0
  147. package/dist/editor/public/annotations.js +106 -0
  148. package/dist/editor/public/annotations.js.map +1 -0
  149. package/dist/editor/public/arrange-group.d.ts +81 -0
  150. package/dist/editor/public/arrange-group.d.ts.map +1 -0
  151. package/dist/editor/public/arrange-group.js +193 -0
  152. package/dist/editor/public/arrange-group.js.map +1 -0
  153. package/dist/editor/public/brush.d.ts +33 -0
  154. package/dist/editor/public/brush.d.ts.map +1 -0
  155. package/dist/editor/public/brush.js +54 -0
  156. package/dist/editor/public/brush.js.map +1 -0
  157. package/dist/editor/public/clipboard.d.ts +31 -0
  158. package/dist/editor/public/clipboard.d.ts.map +1 -0
  159. package/dist/editor/public/clipboard.js +35 -0
  160. package/dist/editor/public/clipboard.js.map +1 -0
  161. package/dist/editor/public/image-insert.d.ts +30 -0
  162. package/dist/editor/public/image-insert.d.ts.map +1 -0
  163. package/dist/editor/public/image-insert.js +57 -0
  164. package/dist/editor/public/image-insert.js.map +1 -0
  165. package/dist/editor/public/layers.d.ts +56 -0
  166. package/dist/editor/public/layers.d.ts.map +1 -0
  167. package/dist/editor/public/layers.js +104 -0
  168. package/dist/editor/public/layers.js.map +1 -0
  169. package/dist/editor/public/link.d.ts +25 -0
  170. package/dist/editor/public/link.d.ts.map +1 -0
  171. package/dist/editor/public/link.js +74 -0
  172. package/dist/editor/public/link.js.map +1 -0
  173. package/dist/editor/public/placement.d.ts +60 -0
  174. package/dist/editor/public/placement.d.ts.map +1 -0
  175. package/dist/editor/public/placement.js +111 -0
  176. package/dist/editor/public/placement.js.map +1 -0
  177. package/dist/editor/public/selection-ops.d.ts +78 -0
  178. package/dist/editor/public/selection-ops.d.ts.map +1 -0
  179. package/dist/editor/public/selection-ops.js +226 -0
  180. package/dist/editor/public/selection-ops.js.map +1 -0
  181. package/dist/editor/public/text-edit.d.ts +17 -0
  182. package/dist/editor/public/text-edit.d.ts.map +1 -0
  183. package/dist/editor/public/text-edit.js +28 -0
  184. package/dist/editor/public/text-edit.js.map +1 -0
  185. package/dist/editor/public/z-order.d.ts +47 -0
  186. package/dist/editor/public/z-order.d.ts.map +1 -0
  187. package/dist/editor/public/z-order.js +151 -0
  188. package/dist/editor/public/z-order.js.map +1 -0
  189. package/dist/editor/public/zoom-pan.d.ts +44 -0
  190. package/dist/editor/public/zoom-pan.d.ts.map +1 -0
  191. package/dist/editor/public/zoom-pan.js +116 -0
  192. package/dist/editor/public/zoom-pan.js.map +1 -0
  193. package/dist/editor/render-orchestrator.d.ts +11 -0
  194. package/dist/editor/render-orchestrator.d.ts.map +1 -0
  195. package/dist/editor/render-orchestrator.js +473 -0
  196. package/dist/editor/render-orchestrator.js.map +1 -0
  197. package/dist/editor/resize-helpers.d.ts +30 -0
  198. package/dist/editor/resize-helpers.d.ts.map +1 -0
  199. package/dist/editor/resize-helpers.js +121 -0
  200. package/dist/editor/resize-helpers.js.map +1 -0
  201. package/dist/editor/shape-filters.d.ts +11 -0
  202. package/dist/editor/shape-filters.d.ts.map +1 -0
  203. package/dist/editor/shape-filters.js +19 -0
  204. package/dist/editor/shape-filters.js.map +1 -0
  205. package/dist/editor/shape-traits.d.ts +16 -0
  206. package/dist/editor/shape-traits.d.ts.map +1 -0
  207. package/dist/editor/shape-traits.js +29 -0
  208. package/dist/editor/shape-traits.js.map +1 -0
  209. package/dist/editor/viewport-helpers.d.ts +24 -0
  210. package/dist/editor/viewport-helpers.d.ts.map +1 -0
  211. package/dist/editor/viewport-helpers.js +63 -0
  212. package/dist/editor/viewport-helpers.js.map +1 -0
  213. package/dist/editor-events.d.ts +33 -0
  214. package/dist/editor-events.d.ts.map +1 -0
  215. package/dist/editor-events.js +2 -0
  216. package/dist/editor-events.js.map +1 -0
  217. package/dist/editor.d.ts +1841 -0
  218. package/dist/editor.d.ts.map +1 -0
  219. package/dist/editor.js +4803 -0
  220. package/dist/editor.js.map +1 -0
  221. package/dist/file-drop.d.ts +99 -0
  222. package/dist/file-drop.d.ts.map +1 -0
  223. package/dist/file-drop.js +198 -0
  224. package/dist/file-drop.js.map +1 -0
  225. package/dist/frame-helpers.d.ts +30 -0
  226. package/dist/frame-helpers.d.ts.map +1 -0
  227. package/dist/frame-helpers.js +105 -0
  228. package/dist/frame-helpers.js.map +1 -0
  229. package/dist/group-helpers.d.ts +39 -0
  230. package/dist/group-helpers.d.ts.map +1 -0
  231. package/dist/group-helpers.js +116 -0
  232. package/dist/group-helpers.js.map +1 -0
  233. package/dist/handle.d.ts +73 -0
  234. package/dist/handle.d.ts.map +1 -0
  235. package/dist/handle.js +184 -0
  236. package/dist/handle.js.map +1 -0
  237. package/dist/index.d.ts +26 -0
  238. package/dist/index.d.ts.map +1 -0
  239. package/dist/index.js +16 -0
  240. package/dist/index.js.map +1 -0
  241. package/dist/interactive.d.ts +21 -0
  242. package/dist/interactive.d.ts.map +1 -0
  243. package/dist/interactive.js +11 -0
  244. package/dist/interactive.js.map +1 -0
  245. package/dist/link-selection.d.ts +21 -0
  246. package/dist/link-selection.d.ts.map +1 -0
  247. package/dist/link-selection.js +37 -0
  248. package/dist/link-selection.js.map +1 -0
  249. package/dist/machine.d.ts +322 -0
  250. package/dist/machine.d.ts.map +1 -0
  251. package/dist/machine.js +568 -0
  252. package/dist/machine.js.map +1 -0
  253. package/dist/modes.d.ts +24 -0
  254. package/dist/modes.d.ts.map +1 -0
  255. package/dist/modes.js +2 -0
  256. package/dist/modes.js.map +1 -0
  257. package/dist/overlay.d.ts +262 -0
  258. package/dist/overlay.d.ts.map +1 -0
  259. package/dist/overlay.js +673 -0
  260. package/dist/overlay.js.map +1 -0
  261. package/dist/platform.d.ts +70 -0
  262. package/dist/platform.d.ts.map +1 -0
  263. package/dist/platform.js +115 -0
  264. package/dist/platform.js.map +1 -0
  265. package/dist/selection.d.ts +18 -0
  266. package/dist/selection.d.ts.map +1 -0
  267. package/dist/selection.js +31 -0
  268. package/dist/selection.js.map +1 -0
  269. package/package.json +59 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brush.js","sourceRoot":"","sources":["../../../src/editor/public/brush.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,WAAW,GAKZ,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAU,EAAE;IAC1D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,mBAAmB,CAAC;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,eAAe,CAAC,CAAC;AACnD,CAAC,CAAC;AAYF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAW,EAAE,QAAgB,EAAoB,EAAE,CAAC,CAAC;IACpF,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC1D,MAAM,EAAE,KAAK;CACd,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,MAAwB,EACxB,KAAW,EACX,QAAgB,EACV,EAAE;IACR,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QACjB,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChB,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChB,KAAK,EAAE,eAAe,CAAC,QAAQ,CAAC;KACjC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,KAAY,EACZ,MAA+B,EAC/B,aAAsB,EACtB,YAAuB,EACiE,EAAE;IAC1F,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,KAAK,GAAG,WAAW,CACvB,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;SACzB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,aAAa,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CACvB,CAAC;IACF,MAAM,KAAK,GAAY;QACrB,EAAE,EAAE,YAAY;QAChB,OAAO,EAAE,aAAa;QACtB,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE,MAAM,CAAC,MAAM;QACvB,QAAQ,EAAE,CAAC;QACX,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;QACrB,KAAK;QACL,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;KAC9B,CAAC;IACF,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACnC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACrE,CAAC,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAa,EAAE,CACpD,aAAa,CAAC,SAAS,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { Scene, Element } from "@oh-just-another/scene";
2
+ import type { HistoryProvider } from "@oh-just-another/history";
3
+ import type { ElementId, Vec2 } from "@oh-just-another/types";
4
+ import * as Selection from "../../selection.js";
5
+ /**
6
+ * Internal clipboard storage. Editor owns the array; copy / paste
7
+ * delegators below read & write through it. Stored as deep-cloned
8
+ * snapshots so subsequent mutations don't affect the buffer. Cross-tab
9
+ * paste uses host-level `navigator.clipboard`.
10
+ */
11
+ /** Copy: returns the shape array the editor stores as `clipboard`. */
12
+ export declare const copySelected: (scene: Scene, selection: Selection.Selection) => readonly Element[];
13
+ /**
14
+ * Paste: produce the new scene + freshly-generated shape ids for the
15
+ * newly-pasted cluster. Editor owns side effects (history push happens
16
+ * inside the pasteElements helper, then `_selection` is updated).
17
+ *
18
+ * `targetWorld` lands the cluster's centroid; caller defaults to the
19
+ * last tracked cursor world position, with a +10 px nudge fallback in
20
+ * `paste()` when even that is unavailable.
21
+ *
22
+ * `nextId` is the editor's monotonic counter; passed in so the editor
23
+ * stays the single source of truth for ids.
24
+ */
25
+ export declare const pasteFromClipboard: (scene: Scene, history: HistoryProvider, clipboard: readonly Element[], target: Vec2 | null, nextIdSeed: () => number) => {
26
+ readonly scene: Scene;
27
+ readonly newIds: readonly ElementId[];
28
+ };
29
+ /** Compose the next selection from a freshly-pasted id list. */
30
+ export declare const selectionFromPasted: (newIds: readonly ElementId[]) => Selection.Selection;
31
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../../src/editor/public/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAK7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAEhD;;;;;GAKG;AAEH,sEAAsE;AACtE,eAAO,MAAM,YAAY,GACvB,OAAO,KAAK,EACZ,WAAW,SAAS,CAAC,SAAS,KAC7B,SAAS,OAAO,EAA0C,CAAC;AAE9D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,KAAK,EACZ,SAAS,eAAe,EACxB,WAAW,SAAS,OAAO,EAAE,EAC7B,QAAQ,IAAI,GAAG,IAAI,EACnB,YAAY,MAAM,MAAM,KACvB;IACD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;CAMvC,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,GAAI,QAAQ,SAAS,SAAS,EAAE,KAAG,SAAS,CAAC,SAI5E,CAAC"}
@@ -0,0 +1,35 @@
1
+ import { copyElements as copyElementsHelper, pasteElements as pasteElementsHelper, } from "../../clipboard.js";
2
+ import { elementId as castElementId } from "@oh-just-another/types";
3
+ import * as Selection from "../../selection.js";
4
+ /**
5
+ * Internal clipboard storage. Editor owns the array; copy / paste
6
+ * delegators below read & write through it. Stored as deep-cloned
7
+ * snapshots so subsequent mutations don't affect the buffer. Cross-tab
8
+ * paste uses host-level `navigator.clipboard`.
9
+ */
10
+ /** Copy: returns the shape array the editor stores as `clipboard`. */
11
+ export const copySelected = (scene, selection) => copyElementsHelper(scene, selection);
12
+ /**
13
+ * Paste: produce the new scene + freshly-generated shape ids for the
14
+ * newly-pasted cluster. Editor owns side effects (history push happens
15
+ * inside the pasteElements helper, then `_selection` is updated).
16
+ *
17
+ * `targetWorld` lands the cluster's centroid; caller defaults to the
18
+ * last tracked cursor world position, with a +10 px nudge fallback in
19
+ * `paste()` when even that is unavailable.
20
+ *
21
+ * `nextId` is the editor's monotonic counter; passed in so the editor
22
+ * stays the single source of truth for ids.
23
+ */
24
+ export const pasteFromClipboard = (scene, history, clipboard, target, nextIdSeed) => {
25
+ const result = pasteElementsHelper(scene, history, clipboard, target, () => castElementId(`shape-${nextIdSeed()}-${Date.now().toString(36)}`));
26
+ return { scene: result.scene, newIds: result.newIds };
27
+ };
28
+ /** Compose the next selection from a freshly-pasted id list. */
29
+ export const selectionFromPasted = (newIds) => {
30
+ let next = Selection.EMPTY;
31
+ for (const id of newIds)
32
+ next = Selection.add(next, id);
33
+ return next;
34
+ };
35
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../../src/editor/public/clipboard.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,IAAI,kBAAkB,EAClC,aAAa,IAAI,mBAAmB,GACrC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAEhD;;;;;GAKG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,KAAY,EACZ,SAA8B,EACV,EAAE,CAAC,kBAAkB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AAE9D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAY,EACZ,OAAwB,EACxB,SAA6B,EAC7B,MAAmB,EACnB,UAAwB,EAIxB,EAAE;IACF,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,CACzE,aAAa,CAAC,SAAS,UAAU,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAClE,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;AACxD,CAAC,CAAC;AAEF,gEAAgE;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA4B,EAAuB,EAAE;IACvF,IAAI,IAAI,GAAwB,SAAS,CAAC,KAAK,CAAC;IAChD,KAAK,MAAM,EAAE,IAAI,MAAM;QAAE,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type Scene, type Element, type Patch } from "@oh-just-another/scene";
2
+ import type { FileId, LayerId, ElementId, Vec2 } from "@oh-just-another/types";
3
+ /**
4
+ * Build the image-shape object for `insertImage`. Caller threads it
5
+ * through `addElement` to pick up selection + history wiring.
6
+ */
7
+ export declare const buildImageElement: (scene: Scene, input: {
8
+ src: string;
9
+ width: number;
10
+ height: number;
11
+ position: Vec2;
12
+ image?: HTMLImageElement;
13
+ animated?: boolean;
14
+ fileId?: FileId;
15
+ animationKind?: string;
16
+ animationData?: unknown;
17
+ }, id: ElementId, layerId: LayerId) => Element;
18
+ /**
19
+ * Read the blob, register it as a `BinaryFile`, and return the next
20
+ * scene + patch (caller pushes the patch). `nextIdSeed` is the
21
+ * editor's monotonic counter, bumped to produce a fresh `FileId`.
22
+ */
23
+ export declare const computeAddBinaryFile: (scene: Scene, blob: Blob, name: string | undefined, nextIdSeed: () => number) => Promise<{
24
+ readonly scene: Scene;
25
+ readonly patch: Patch;
26
+ readonly id: FileId;
27
+ }>;
28
+ /** True if any shape in the scene carries `metadata.animated`. */
29
+ export declare const hasAnimatedElement: (scene: Scene) => boolean;
30
+ //# sourceMappingURL=image-insert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-insert.d.ts","sourceRoot":"","sources":["../../../src/editor/public/image-insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,KAAK,EAEX,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAG/E;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,KAAK,EACZ,OAAO;IACL,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,EACD,IAAI,SAAS,EACb,SAAS,OAAO,KACf,OA0BF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,KAAK,EACZ,MAAM,IAAI,EACV,MAAM,MAAM,GAAG,SAAS,EACxB,YAAY,MAAM,MAAM,KACvB,OAAO,CAAC;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAS/E,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,KAAG,OAKjD,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { apply, createBinaryFile, orderForTop, } from "@oh-just-another/scene";
2
+ import { fileId as castFileId } from "@oh-just-another/types";
3
+ /**
4
+ * Build the image-shape object for `insertImage`. Caller threads it
5
+ * through `addElement` to pick up selection + history wiring.
6
+ */
7
+ export const buildImageElement = (scene, input, id, layerId) => {
8
+ const order = orderForTop(Array.from(scene.elements.values())
9
+ .filter((s) => s.layerId === layerId)
10
+ .map((s) => s.order));
11
+ const metadata = {};
12
+ if (input.image)
13
+ metadata.image = input.image;
14
+ if (input.animated)
15
+ metadata.animated = true;
16
+ return {
17
+ id,
18
+ layerId,
19
+ type: "image",
20
+ position: input.position,
21
+ rotation: 0,
22
+ scale: { x: 1, y: 1 },
23
+ order,
24
+ style: {},
25
+ width: input.width,
26
+ height: input.height,
27
+ src: input.src,
28
+ ...(input.fileId ? { fileId: input.fileId } : {}),
29
+ ...(input.animationKind ? { animationKind: input.animationKind } : {}),
30
+ ...(input.animationData !== undefined ? { animationData: input.animationData } : {}),
31
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
32
+ };
33
+ };
34
+ /**
35
+ * Read the blob, register it as a `BinaryFile`, and return the next
36
+ * scene + patch (caller pushes the patch). `nextIdSeed` is the
37
+ * editor's monotonic counter, bumped to produce a fresh `FileId`.
38
+ */
39
+ export const computeAddBinaryFile = async (scene, blob, name, nextIdSeed) => {
40
+ const data = await blob.arrayBuffer();
41
+ const id = castFileId(`file-${nextIdSeed()}-${Date.now().toString(36)}`);
42
+ const file = createBinaryFile(id, data, {
43
+ mime: blob.type || "application/octet-stream",
44
+ ...(name !== undefined ? { name } : {}),
45
+ });
46
+ const patch = { kind: "file", id, before: null, after: file };
47
+ return { scene: apply(scene, patch), patch, id };
48
+ };
49
+ /** True if any shape in the scene carries `metadata.animated`. */
50
+ export const hasAnimatedElement = (scene) => {
51
+ for (const s of scene.elements.values()) {
52
+ if (s.metadata?.animated === true)
53
+ return true;
54
+ }
55
+ return false;
56
+ };
57
+ //# sourceMappingURL=image-insert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-insert.js","sourceRoot":"","sources":["../../../src/editor/public/image-insert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { type Scene, type Patch } from "@oh-just-another/scene";
2
+ import type { LayerId } from "@oh-just-another/types";
3
+ import type * as Selection from "../../selection.js";
4
+ /**
5
+ * Build a fresh top-of-stack layer + the patch that adds it. Caller
6
+ * assigns activeLayerId and pushes the patch.
7
+ */
8
+ export declare const computeCreateLayer: (scene: Scene, name: string, newLayerId: LayerId) => {
9
+ readonly scene: Scene;
10
+ readonly patch: Patch;
11
+ readonly layerId: LayerId;
12
+ };
13
+ /**
14
+ * Drop a layer + every shape and edge that lived on it. Returns the
15
+ * next scene, all patches, the next active layer id (topmost remaining
16
+ * if the dropped one was active), and a flag for whether the selection
17
+ * should be cleared.
18
+ *
19
+ * Returns `null` when the layer doesn't exist. Throws when asked to
20
+ * remove the only layer; callers should guard against that beforehand.
21
+ */
22
+ export declare const computeRemoveLayer: (scene: Scene, id: LayerId, currentActive: LayerId) => {
23
+ scene: Scene;
24
+ patches: Patch[];
25
+ nextActiveLayerId: LayerId;
26
+ } | null;
27
+ /** Rename a layer. Returns `null` when nothing actually changes. */
28
+ export declare const computeRenameLayer: (scene: Scene, id: LayerId, name: string) => {
29
+ readonly scene: Scene;
30
+ readonly patch: Patch;
31
+ } | null;
32
+ /** Flip the `visible` flag. */
33
+ export declare const computeToggleLayerVisibility: (scene: Scene, id: LayerId) => {
34
+ readonly scene: Scene;
35
+ readonly patch: Patch;
36
+ } | null;
37
+ /** Flip the `locked` flag. */
38
+ export declare const computeToggleLayerLock: (scene: Scene, id: LayerId) => {
39
+ readonly scene: Scene;
40
+ readonly patch: Patch;
41
+ } | null;
42
+ /**
43
+ * Move every shape in the selection to `targetLayer`. Links are left
44
+ * alone — they stay on whichever layer they were already on;
45
+ * cross-layer edges are valid.
46
+ *
47
+ * Returns `null` when the layer doesn't exist, the selection is empty,
48
+ * or all selected shapes are already on the target layer.
49
+ */
50
+ export declare const computeMoveSelectionToLayer: (scene: Scene, selection: Selection.Selection, targetLayer: LayerId) => {
51
+ readonly scene: Scene;
52
+ readonly patches: Patch[];
53
+ } | null;
54
+ /** Generate a fresh layer id with the editor's nextId counter. */
55
+ export declare const newLayerId: (next: number) => LayerId;
56
+ //# sourceMappingURL=layers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../../src/editor/public/layers.ts"],"names":[],"mappings":"AAAA,OAAO,EAUL,KAAK,KAAK,EACV,KAAK,KAAK,EACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEtD,OAAO,KAAK,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAErD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,KAAK,EACZ,MAAM,MAAM,EACZ,YAAY,OAAO,KAClB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAK3E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,KAAK,EACZ,IAAI,OAAO,EACX,eAAe,OAAO,KACrB;IACD,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,GAAG,IA+BH,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,GAC7B,OAAO,KAAK,EACZ,IAAI,OAAO,EACX,MAAM,MAAM,KACX;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAKrD,CAAC;AAEF,+BAA+B;AAC/B,eAAO,MAAM,4BAA4B,GACvC,OAAO,KAAK,EACZ,IAAI,OAAO,KACV;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAIrD,CAAC;AAEF,8BAA8B;AAC9B,eAAO,MAAM,sBAAsB,GACjC,OAAO,KAAK,EACZ,IAAI,OAAO,KACV;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IAIrD,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GACtC,OAAO,KAAK,EACZ,WAAW,SAAS,CAAC,SAAS,EAC9B,aAAa,OAAO,KACnB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAA;CAAE,GAAG,IAazD,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,KAAG,OACe,CAAC"}
@@ -0,0 +1,104 @@
1
+ import { addLayer, getElement, orderForTop, removeLink, removeLayer, removeElement, updateLayer, updateElement, } from "@oh-just-another/scene";
2
+ import { layerId as castLayerId } from "@oh-just-another/types";
3
+ /**
4
+ * Build a fresh top-of-stack layer + the patch that adds it. Caller
5
+ * assigns activeLayerId and pushes the patch.
6
+ */
7
+ export const computeCreateLayer = (scene, name, newLayerId) => {
8
+ const topOrder = orderForTop([...scene.layers.values()].map((l) => l.order));
9
+ const layer = { id: newLayerId, name, visible: true, locked: false, order: topOrder };
10
+ const result = addLayer(scene, layer);
11
+ return { scene: result.scene, patch: result.patch, layerId: newLayerId };
12
+ };
13
+ /**
14
+ * Drop a layer + every shape and edge that lived on it. Returns the
15
+ * next scene, all patches, the next active layer id (topmost remaining
16
+ * if the dropped one was active), and a flag for whether the selection
17
+ * should be cleared.
18
+ *
19
+ * Returns `null` when the layer doesn't exist. Throws when asked to
20
+ * remove the only layer; callers should guard against that beforehand.
21
+ */
22
+ export const computeRemoveLayer = (scene, id, currentActive) => {
23
+ if (!scene.layers.has(id))
24
+ return null;
25
+ if (scene.layers.size <= 1) {
26
+ throw new Error("Cannot remove the only remaining layer.");
27
+ }
28
+ const patches = [];
29
+ let s = scene;
30
+ for (const shape of [...s.elements.values()]) {
31
+ if (shape.layerId !== id)
32
+ continue;
33
+ const r = removeElement(s, shape.id);
34
+ s = r.scene;
35
+ patches.push(r.patch);
36
+ }
37
+ for (const edge of [...s.links.values()]) {
38
+ if (edge.layerId !== id)
39
+ continue;
40
+ const r = removeLink(s, edge.id);
41
+ s = r.scene;
42
+ patches.push(r.patch);
43
+ }
44
+ const r = removeLayer(s, id);
45
+ s = r.scene;
46
+ patches.push(r.patch);
47
+ let nextActive = currentActive;
48
+ if (currentActive === id) {
49
+ const top = [...s.layers.values()].sort((a, b) => a.order > b.order ? -1 : a.order < b.order ? 1 : 0)[0];
50
+ if (top)
51
+ nextActive = top.id;
52
+ }
53
+ return { scene: s, patches, nextActiveLayerId: nextActive };
54
+ };
55
+ /** Rename a layer. Returns `null` when nothing actually changes. */
56
+ export const computeRenameLayer = (scene, id, name) => {
57
+ const layer = scene.layers.get(id);
58
+ if (!layer || layer.name === name)
59
+ return null;
60
+ const r = updateLayer(scene, id, (l) => ({ ...l, name }));
61
+ return { scene: r.scene, patch: r.patch };
62
+ };
63
+ /** Flip the `visible` flag. */
64
+ export const computeToggleLayerVisibility = (scene, id) => {
65
+ if (!scene.layers.has(id))
66
+ return null;
67
+ const r = updateLayer(scene, id, (l) => ({ ...l, visible: !l.visible }));
68
+ return { scene: r.scene, patch: r.patch };
69
+ };
70
+ /** Flip the `locked` flag. */
71
+ export const computeToggleLayerLock = (scene, id) => {
72
+ if (!scene.layers.has(id))
73
+ return null;
74
+ const r = updateLayer(scene, id, (l) => ({ ...l, locked: !l.locked }));
75
+ return { scene: r.scene, patch: r.patch };
76
+ };
77
+ /**
78
+ * Move every shape in the selection to `targetLayer`. Links are left
79
+ * alone — they stay on whichever layer they were already on;
80
+ * cross-layer edges are valid.
81
+ *
82
+ * Returns `null` when the layer doesn't exist, the selection is empty,
83
+ * or all selected shapes are already on the target layer.
84
+ */
85
+ export const computeMoveSelectionToLayer = (scene, selection, targetLayer) => {
86
+ if (!scene.layers.has(targetLayer) || selection.size === 0)
87
+ return null;
88
+ let s = scene;
89
+ const patches = [];
90
+ for (const id of selection) {
91
+ const shape = getElement(s, id);
92
+ if (!shape || shape.layerId === targetLayer)
93
+ continue;
94
+ const r = updateElement(s, id, (sh) => ({ ...sh, layerId: targetLayer }));
95
+ s = r.scene;
96
+ patches.push(r.patch);
97
+ }
98
+ if (patches.length === 0)
99
+ return null;
100
+ return { scene: s, patches };
101
+ };
102
+ /** Generate a fresh layer id with the editor's nextId counter. */
103
+ export const newLayerId = (next) => castLayerId(`layer-${next}-${Date.now().toString(36)}`);
104
+ //# sourceMappingURL=layers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layers.js","sourceRoot":"","sources":["../../../src/editor/public/layers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type Scene, type Patch } from "@oh-just-another/scene";
2
+ import type { ElementId } from "@oh-just-another/types";
3
+ /**
4
+ * Normalise user-entered link text into a safe, storable href, or
5
+ * `null` to clear / reject. Adds `https://` to a scheme-less URL,
6
+ * `mailto:` to a bare email, and refuses unsafe schemes.
7
+ */
8
+ export declare const normalizeHref: (input: string) => string | null;
9
+ /**
10
+ * Final gate before navigation. Returns the href only if it's a safe
11
+ * scheme; otherwise `null`. Defensive even though `normalizeHref`
12
+ * already filtered — stored scenes from other sources may carry
13
+ * anything.
14
+ */
15
+ export declare const safeHref: (href: string | undefined | null) => string | null;
16
+ /**
17
+ * Set (or clear, with `null`) the `href` on every shape in `ids`.
18
+ * Returns the next scene + a single / batched patch, or `null` when no
19
+ * shape applied or nothing changed.
20
+ */
21
+ export declare const computeSetLink: (scene: Scene, ids: Iterable<ElementId>, href: string | null) => {
22
+ readonly scene: Scene;
23
+ readonly patch: Patch;
24
+ } | null;
25
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/editor/public/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAUxD;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,IAQtD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,GAAG,SAAS,GAAG,IAAI,KAAG,MAAM,GAAG,IAGnE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,KAAK,EACZ,KAAK,QAAQ,CAAC,SAAS,CAAC,EACxB,MAAM,MAAM,GAAG,IAAI,KAClB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IA2BrD,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { getElement, updateElement } from "@oh-just-another/scene";
2
+ /**
3
+ * Schemes allowed to be stored / opened. Everything else (notably
4
+ * `javascript:`, `data:`, `vbscript:`, `file:`) is rejected so a scene
5
+ * can't smuggle an XSS / local-file payload through an element link.
6
+ */
7
+ const SAFE_SCHEME = /^(https?:|mailto:)/i;
8
+ const BARE_EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
+ /**
10
+ * Normalise user-entered link text into a safe, storable href, or
11
+ * `null` to clear / reject. Adds `https://` to a scheme-less URL,
12
+ * `mailto:` to a bare email, and refuses unsafe schemes.
13
+ */
14
+ export const normalizeHref = (input) => {
15
+ const s = input.trim();
16
+ if (s === "")
17
+ return null;
18
+ if (SAFE_SCHEME.test(s))
19
+ return s;
20
+ // Has a scheme but not a safe one (javascript:, data:, …) → reject.
21
+ if (/^[a-z][a-z0-9+.-]*:/i.test(s))
22
+ return null;
23
+ if (BARE_EMAIL.test(s))
24
+ return `mailto:${s}`;
25
+ return `https://${s}`;
26
+ };
27
+ /**
28
+ * Final gate before navigation. Returns the href only if it's a safe
29
+ * scheme; otherwise `null`. Defensive even though `normalizeHref`
30
+ * already filtered — stored scenes from other sources may carry
31
+ * anything.
32
+ */
33
+ export const safeHref = (href) => {
34
+ if (!href)
35
+ return null;
36
+ return SAFE_SCHEME.test(href.trim()) ? href.trim() : null;
37
+ };
38
+ /**
39
+ * Set (or clear, with `null`) the `href` on every shape in `ids`.
40
+ * Returns the next scene + a single / batched patch, or `null` when no
41
+ * shape applied or nothing changed.
42
+ */
43
+ export const computeSetLink = (scene, ids, href) => {
44
+ const targetIds = [];
45
+ for (const id of ids) {
46
+ const s = getElement(scene, id);
47
+ if (s && (s.href ?? null) !== href)
48
+ targetIds.push(id);
49
+ }
50
+ if (targetIds.length === 0)
51
+ return null;
52
+ let s = scene;
53
+ const patches = [];
54
+ for (const id of targetIds) {
55
+ const r = updateElement(s, id, (sh) => {
56
+ const next = { ...sh };
57
+ if (href === null)
58
+ delete next.href;
59
+ else
60
+ next.href = href;
61
+ return next;
62
+ });
63
+ s = r.scene;
64
+ patches.push(r.patch);
65
+ }
66
+ const firstPatch = patches[0];
67
+ return {
68
+ scene: s,
69
+ patch: patches.length === 1 && firstPatch !== undefined
70
+ ? firstPatch
71
+ : { kind: "batch", patches },
72
+ };
73
+ };
74
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../../src/editor/public/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAA0B,MAAM,wBAAwB,CAAC;AAG3F;;;;GAIG;AACH,MAAM,WAAW,GAAG,qBAAqB,CAAC;AAC1C,MAAM,UAAU,GAAG,4BAA4B,CAAC;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAa,EAAiB,EAAE;IAC5D,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACvB,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC1B,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,oEAAoE;IACpE,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC,EAAE,CAAC;IAC7C,OAAO,WAAW,CAAC,EAAE,CAAC;AACxB,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAA+B,EAAiB,EAAE;IACzE,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5D,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,KAAY,EACZ,GAAwB,EACxB,IAAmB,EACsC,EAAE;IAC3D,MAAM,SAAS,GAAgB,EAAE,CAAC;IAClC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,IAAI;YAAE,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,CAAC,GAAG,KAAK,CAAC;IACd,MAAM,OAAO,GAAY,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE;YACpC,MAAM,IAAI,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;YACvB,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC;;gBAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QACH,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9B,OAAO;QACL,KAAK,EAAE,CAAC;QACR,KAAK,EACH,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,KAAK,SAAS;YAC9C,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE;KACjC,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { type Scene, type Element, type Patch } from "@oh-just-another/scene";
2
+ import type { LayerId, ElementId, Vec2 } from "@oh-just-another/types";
3
+ import type { Mode } from "../../modes.js";
4
+ /**
5
+ * Build a shape for keyboard-friendly creation centred on
6
+ * `worldCenter`. Picks `rectangle` / `ellipse` based on `mode` (falls
7
+ * back to rectangle for non-draw modes), with a 120×80 default size.
8
+ */
9
+ export declare const buildElementAtCursor: (scene: Scene, mode: Mode, worldCenter: Vec2, layerId: LayerId, id: ElementId) => Element;
10
+ /**
11
+ * Build an empty text shape anchored at `worldPoint` (top-left). The
12
+ * `draw-text` tool drops this and opens the inline editor straight
13
+ * away, so the shape starts with no text — the renderer skips empty
14
+ * strings, and an untouched shape is cleaned up on commit.
15
+ */
16
+ export declare const buildTextElementAt: (scene: Scene, worldPoint: Vec2, layerId: LayerId, id: ElementId) => Element;
17
+ /** Generate a fresh shape id with the editor's nextId counter. */
18
+ export declare const newElementIdAtCursor: (next: number) => ElementId;
19
+ /**
20
+ * Mutable state for an in-progress palette / drag-to-place gesture.
21
+ * Owned by the `beginPlacement` closure.
22
+ */
23
+ export interface PlacementState {
24
+ current: Element;
25
+ readonly halfWidth: number;
26
+ readonly halfHeight: number;
27
+ }
28
+ /** Compute the patch that moves the placed shape to `worldCenter`. */
29
+ export declare const computePlacementUpdate: (scene: Scene, state: PlacementState, worldCenter: Vec2) => {
30
+ readonly scene: Scene;
31
+ readonly patch: Patch;
32
+ readonly next: Element;
33
+ };
34
+ /**
35
+ * Post-process the placed shape on commit — if it lands inside an
36
+ * auto-layout container's drop zone, reparent it. The caller passes a
37
+ * callback that excludes the placed shape itself from the container
38
+ * hit-test (otherwise a container template could parent itself).
39
+ *
40
+ * Returns the reparented shape + the corresponding patch, or `null`
41
+ * when no container hit applies.
42
+ */
43
+ export declare const computePlacementContainerDrop: (scene: Scene, state: PlacementState) => {
44
+ readonly scene: Scene;
45
+ readonly patch: Patch;
46
+ readonly next: Element;
47
+ } | null;
48
+ /** Build the initial placement state for `beginPlacement`. */
49
+ export declare const beginPlacementState: (shape: Element) => {
50
+ readonly scene: (s: Scene) => {
51
+ readonly scene: Scene;
52
+ readonly patch: Patch;
53
+ };
54
+ readonly state: PlacementState;
55
+ };
56
+ /** Undo of `beginPlacement` — remove the placed shape. */
57
+ export declare const computePlacementCancel: (scene: Scene, elementId: ElementId) => {
58
+ readonly scene: Scene;
59
+ };
60
+ //# sourceMappingURL=placement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../../../src/editor/public/placement.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,KAAK,EACX,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEvE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAO3C;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,KAAK,EACZ,MAAM,IAAI,EACV,aAAa,IAAI,EACjB,SAAS,OAAO,EAChB,IAAI,SAAS,KACZ,OAmBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,KAAK,EACZ,YAAY,IAAI,EAChB,SAAS,OAAO,EAChB,IAAI,SAAS,KACZ,OAiBF,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,SACO,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,GACjC,OAAO,KAAK,EACZ,OAAO,cAAc,EACrB,aAAa,IAAI,KAChB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAexE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,GACxC,OAAO,KAAK,EACZ,OAAO,cAAc,KACpB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,IAe7E,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG;IACnD,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK;QAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;KAAE,CAAC;IAC/E,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAchC,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,GACjC,OAAO,KAAK,EACZ,WAAW,SAAS,KACnB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAwD,CAAC"}
@@ -0,0 +1,111 @@
1
+ import { addElement, apply, findContainerAt, getElementWorldBounds, orderForTop, removeElement, } from "@oh-just-another/scene";
2
+ import { elementId as castElementId } from "@oh-just-another/types";
3
+ import { TEXT_DEFAULT_FILL, TEXT_DEFAULT_FONT_FAMILY, TEXT_DEFAULT_FONT_SIZE, } from "../../constants.js";
4
+ /**
5
+ * Build a shape for keyboard-friendly creation centred on
6
+ * `worldCenter`. Picks `rectangle` / `ellipse` based on `mode` (falls
7
+ * back to rectangle for non-draw modes), with a 120×80 default size.
8
+ */
9
+ export const buildElementAtCursor = (scene, mode, worldCenter, layerId, id) => {
10
+ const order = orderForTop([...scene.elements.values()].filter((s) => s.layerId === layerId).map((s) => s.order));
11
+ const type = mode === "draw-ellipse" ? "ellipse" : "rectangle";
12
+ const width = 120;
13
+ const height = 80;
14
+ return {
15
+ id,
16
+ layerId,
17
+ type,
18
+ position: { x: worldCenter.x - width / 2, y: worldCenter.y - height / 2 },
19
+ rotation: 0,
20
+ scale: { x: 1, y: 1 },
21
+ order,
22
+ style: { fill: "#bbb", stroke: "#000", strokeWidth: 1 },
23
+ width,
24
+ height,
25
+ };
26
+ };
27
+ /**
28
+ * Build an empty text shape anchored at `worldPoint` (top-left). The
29
+ * `draw-text` tool drops this and opens the inline editor straight
30
+ * away, so the shape starts with no text — the renderer skips empty
31
+ * strings, and an untouched shape is cleaned up on commit.
32
+ */
33
+ export const buildTextElementAt = (scene, worldPoint, layerId, id) => {
34
+ const order = orderForTop([...scene.elements.values()].filter((s) => s.layerId === layerId).map((s) => s.order));
35
+ return {
36
+ id,
37
+ layerId,
38
+ type: "text",
39
+ position: { x: worldPoint.x, y: worldPoint.y },
40
+ rotation: 0,
41
+ scale: { x: 1, y: 1 },
42
+ order,
43
+ text: "",
44
+ fontFamily: TEXT_DEFAULT_FONT_FAMILY,
45
+ fontSize: TEXT_DEFAULT_FONT_SIZE,
46
+ style: { fill: TEXT_DEFAULT_FILL, textAlign: "left", textBaseline: "top" },
47
+ };
48
+ };
49
+ /** Generate a fresh shape id with the editor's nextId counter. */
50
+ export const newElementIdAtCursor = (next) => castElementId(`shape-${next}-${Date.now().toString(36)}`);
51
+ /** Compute the patch that moves the placed shape to `worldCenter`. */
52
+ export const computePlacementUpdate = (scene, state, worldCenter) => {
53
+ const next = {
54
+ ...state.current,
55
+ position: {
56
+ x: worldCenter.x - state.halfWidth,
57
+ y: worldCenter.y - state.halfHeight,
58
+ },
59
+ };
60
+ const patch = {
61
+ kind: "element",
62
+ id: state.current.id,
63
+ before: state.current,
64
+ after: next,
65
+ };
66
+ return { scene: apply(scene, patch), patch, next };
67
+ };
68
+ /**
69
+ * Post-process the placed shape on commit — if it lands inside an
70
+ * auto-layout container's drop zone, reparent it. The caller passes a
71
+ * callback that excludes the placed shape itself from the container
72
+ * hit-test (otherwise a container template could parent itself).
73
+ *
74
+ * Returns the reparented shape + the corresponding patch, or `null`
75
+ * when no container hit applies.
76
+ */
77
+ export const computePlacementContainerDrop = (scene, state) => {
78
+ const center = {
79
+ x: state.current.position.x + state.halfWidth,
80
+ y: state.current.position.y + state.halfHeight,
81
+ };
82
+ const container = findContainerAt(scene, center, new Set([state.current.id]));
83
+ if (!container)
84
+ return null;
85
+ const withParent = { ...state.current, parentId: container.id };
86
+ const patch = {
87
+ kind: "element",
88
+ id: state.current.id,
89
+ before: state.current,
90
+ after: withParent,
91
+ };
92
+ return { scene: apply(scene, patch), patch, next: withParent };
93
+ };
94
+ /** Build the initial placement state for `beginPlacement`. */
95
+ export const beginPlacementState = (shape) => {
96
+ const half = getElementWorldBounds(shape);
97
+ return {
98
+ scene: (s) => {
99
+ const r = addElement(s, shape);
100
+ return { scene: r.scene, patch: r.patch };
101
+ },
102
+ state: {
103
+ current: shape,
104
+ halfWidth: half.width / 2,
105
+ halfHeight: half.height / 2,
106
+ },
107
+ };
108
+ };
109
+ /** Undo of `beginPlacement` — remove the placed shape. */
110
+ export const computePlacementCancel = (scene, elementId) => ({ scene: removeElement(scene, elementId).scene });
111
+ //# sourceMappingURL=placement.js.map