@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,83 @@
1
+ import { getElement, getElementWorldBounds, updateAnnotation, } from "@oh-just-another/scene";
2
+ /**
3
+ * Pure: compute the patch that moves shape `id` by `delta` from
4
+ * the press-down state captured in `originalBounds`. Returns
5
+ * `null` when the shape no longer exists (deleted mid-drag, etc.).
6
+ *
7
+ * `originalBounds` is in world space at press time; `localBounds`
8
+ * (current) compensates for parent-rooted layouts where the shape's
9
+ * own position has already moved relative to its parent. The
10
+ * offset is added so the cursor stays at the press anchor through
11
+ * the drag.
12
+ *
13
+ * Editor's wrapper applies the patch, runs `recordGesturePatch`,
14
+ * and fires notify.
15
+ */
16
+ export const computeElementMovePatch = (scene, id, delta, originalBounds) => {
17
+ const shape = getElement(scene, id);
18
+ if (!shape)
19
+ return null;
20
+ const localBounds = getElementWorldBounds(shape);
21
+ const offsetX = originalBounds.x - localBounds.x;
22
+ const offsetY = originalBounds.y - localBounds.y;
23
+ const next = {
24
+ ...shape,
25
+ position: {
26
+ x: shape.position.x + delta.x + offsetX,
27
+ y: shape.position.y + delta.y + offsetY,
28
+ },
29
+ };
30
+ return { kind: "element", id, before: shape, after: next };
31
+ };
32
+ /**
33
+ * Pure: compute one patch per shape in the active group-drag
34
+ * snapshot. `delta` is the cumulative cursor displacement since
35
+ * press-down, so each shape lands at `origin + delta` every frame
36
+ * — no accumulator state inside the loop. Skips no-op moves
37
+ * (delta after rounding lands the shape back at its current pos).
38
+ */
39
+ export const computeGroupMovePatches = (scene, groupMoveOrigin, delta) => {
40
+ const out = [];
41
+ for (const [id, origin] of groupMoveOrigin) {
42
+ const shape = getElement(scene, id);
43
+ if (!shape)
44
+ continue;
45
+ const next = {
46
+ ...shape,
47
+ position: { x: origin.x + delta.x, y: origin.y + delta.y },
48
+ };
49
+ if (next.position.x === shape.position.x && next.position.y === shape.position.y)
50
+ continue;
51
+ out.push({ kind: "element", id, before: shape, after: next });
52
+ }
53
+ return out;
54
+ };
55
+ /**
56
+ * Pure: compute the scene + patch resulting from an annotation
57
+ * drag. For shape-anchored annotations the stored position is the
58
+ * offset from the host shape's world position, so we translate
59
+ * the requested world target back into local space. Returns
60
+ * `null` when the annotation no longer exists or the move is a
61
+ * pixel-perfect no-op.
62
+ */
63
+ export const computeAnnotationMovePatch = (scene, id, delta, origin) => {
64
+ const ann = scene.annotations.get(id);
65
+ if (!ann)
66
+ return null;
67
+ const targetWorld = { x: origin.x + delta.x, y: origin.y + delta.y };
68
+ let storedPosition = targetWorld;
69
+ if (ann.elementId) {
70
+ const shape = getElement(scene, ann.elementId);
71
+ if (shape) {
72
+ storedPosition = {
73
+ x: targetWorld.x - shape.position.x,
74
+ y: targetWorld.y - shape.position.y,
75
+ };
76
+ }
77
+ }
78
+ if (storedPosition.x === ann.position.x && storedPosition.y === ann.position.y)
79
+ return null;
80
+ const r = updateAnnotation(scene, id, (a) => ({ ...a, position: storedPosition }));
81
+ return { scene: r.scene, patch: r.patch };
82
+ };
83
+ //# sourceMappingURL=move.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move.js","sourceRoot":"","sources":["../../../src/editor/applies/move.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { type Scene, type Element, type Link, type Patch, type TextElement } from "@oh-just-another/scene";
2
+ import type { Bounds, ElementId, LinkId, Vec2 } from "@oh-just-another/types";
3
+ import type { HandleId } from "../../handle.js";
4
+ /**
5
+ * Pure: compute the patch + new scene for a single-shape resize.
6
+ *
7
+ * Only shapes with explicit `width` / `height` fields qualify
8
+ * (`hasWidthHeight`) — others must ride the `scale` multiplier
9
+ * instead (group resize). The container-aware clamp callback is
10
+ * injected by the caller so this module doesn't depend on
11
+ * container-ops.
12
+ *
13
+ * Returns `null` when the shape no longer exists or doesn't
14
+ * qualify for in-place resize.
15
+ */
16
+ export declare const computeElementResize: (scene: Scene, id: ElementId, handle: HandleId, delta: Vec2, originalBounds: Bounds, clampContainer: (shape: Element, raw: Bounds, handle: HandleId) => Bounds) => {
17
+ readonly scene: Scene;
18
+ readonly patch: Patch;
19
+ } | null;
20
+ /**
21
+ * Pure: resize a single text shape.
22
+ *
23
+ * `original` is the pristine shape snapshotted at the start of the
24
+ * gesture (so font scaling is computed from a stable base, never
25
+ * compounding tick-to-tick).
26
+ *
27
+ * - **Corners** (`nw/ne/se/sw`) and **top/bottom edges** (`n/s`) scale
28
+ * `fontSize` (and `maxWidth`, if set) uniformly — the box never
29
+ * distorts. There's no arbitrary height; a vertical drag scales the
30
+ * whole element.
31
+ * - **Left/right edges** (`e/w`) change only the wrap width: the text
32
+ * reflows onto new lines (sets `maxWidth`, no font change).
33
+ */
34
+ export declare const computeTextResize: (scene: Scene, original: TextElement, handle: HandleId, delta: Vec2, originalBounds: Bounds) => {
35
+ readonly scene: Scene;
36
+ readonly patch: Patch;
37
+ } | null;
38
+ /**
39
+ * Pure: compute one patch per shape in the group-resize snapshot.
40
+ *
41
+ * Each member's *position offset* inside the original combined box
42
+ * scales by the same factor; each member's `scale.{x,y}` is
43
+ * multiplied so the visible size tracks the gesture. Mirroring
44
+ * (flip) is allowed when the user drags past the opposite edge.
45
+ *
46
+ * `originSnapshot` is a per-shape press-down snapshot: position,
47
+ * scale, bounds. Caller (Editor) collects it at press time and
48
+ * threads it back every frame.
49
+ */
50
+ export interface GroupResizeOrigin {
51
+ readonly elements: ReadonlyMap<ElementId, {
52
+ readonly position: Vec2;
53
+ readonly scale: Vec2;
54
+ readonly bounds: Bounds;
55
+ }>;
56
+ /**
57
+ * Press-time snapshot of links that scale with the box: selected links
58
+ * (whole, incl. free endpoints) ∪ connectors bound on both ends to resized
59
+ * elements (geometry follows). Optional for back-compat with callers that
60
+ * don't resize links.
61
+ */
62
+ readonly links?: ReadonlyMap<LinkId, Link>;
63
+ }
64
+ export declare const computeGroupResizePatches: (scene: Scene, origin: GroupResizeOrigin, handle: HandleId, delta: Vec2, originalBounds: Bounds, isAspectLocked: boolean) => {
65
+ scene: Scene;
66
+ patches: Patch[];
67
+ };
68
+ //# sourceMappingURL=resize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.d.ts","sourceRoot":"","sources":["../../../src/editor/applies/resize.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,KAAK,EACV,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,KAAK,EACV,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAMhD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,KAAK,EACZ,IAAI,SAAS,EACb,QAAQ,QAAQ,EAChB,OAAO,IAAI,EACX,gBAAgB,MAAM,EACtB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,KAAK,MAAM,KACxE;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IA8BrD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,KAAK,EACZ,UAAU,WAAW,EACrB,QAAQ,QAAQ,EAChB,OAAO,IAAI,EACX,gBAAgB,MAAM,KACrB;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAAG,IA8CrD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAC5B,SAAS,EACT;QACE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;QACxB,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;QACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CACF,CAAC;IACF;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC5C;AAED,eAAO,MAAM,yBAAyB,GACpC,OAAO,KAAK,EACZ,QAAQ,iBAAiB,EACzB,QAAQ,QAAQ,EAChB,OAAO,IAAI,EACX,gBAAgB,MAAM,EACtB,gBAAgB,OAAO,KACtB;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,KAAK,EAAE,CAAA;CA0FlC,CAAC"}
@@ -0,0 +1,199 @@
1
+ import { apply, getAutoLayoutSpec, getElement, getLink, } from "@oh-just-another/scene";
2
+ import { applyResizeConstraints, resizeFromHandle } from "../resize-helpers.js";
3
+ import { hasWidthHeight } from "../shape-traits.js";
4
+ import { scaleLinkAround } from "./link-move.js";
5
+ import { TEXT_RESIZE_MIN_FONT_SIZE } from "../../constants.js";
6
+ /**
7
+ * Pure: compute the patch + new scene for a single-shape resize.
8
+ *
9
+ * Only shapes with explicit `width` / `height` fields qualify
10
+ * (`hasWidthHeight`) — others must ride the `scale` multiplier
11
+ * instead (group resize). The container-aware clamp callback is
12
+ * injected by the caller so this module doesn't depend on
13
+ * container-ops.
14
+ *
15
+ * Returns `null` when the shape no longer exists or doesn't
16
+ * qualify for in-place resize.
17
+ */
18
+ export const computeElementResize = (scene, id, handle, delta, originalBounds, clampContainer) => {
19
+ const shape = getElement(scene, id);
20
+ if (!shape)
21
+ return null;
22
+ if (!hasWidthHeight(shape))
23
+ return null;
24
+ const raw = resizeFromHandle(originalBounds, handle, delta);
25
+ // An auto-layout container (a box holding laid-out children) must NEVER
26
+ // mirror through its own body: dragging an edge inward past the opposite
27
+ // edge would otherwise hand control to that opposite edge ("flip through
28
+ // the face"). Force `noFlip` regardless of the element's stored flag so
29
+ // instances that lack it behave the same.
30
+ const noFlip = shape.noFlip === true || getAutoLayoutSpec(shape) !== null;
31
+ const constraints = noFlip ? ({ ...shape, noFlip: true }) : shape;
32
+ const intermediate = applyResizeConstraints(originalBounds, raw, handle, constraints);
33
+ const constrained = clampContainer(shape, intermediate, handle);
34
+ // `constrained` is in world units (originalBounds was world AABB).
35
+ // For shapes with a width/height field, persist that directly and
36
+ // pin `scale` to 1 — otherwise a non-1 scale carried over from a
37
+ // previous group resize would multiply the new width and the
38
+ // shape would jump out from under the cursor on the next gesture.
39
+ const next = {
40
+ ...shape,
41
+ position: { x: constrained.x, y: constrained.y },
42
+ scale: { x: 1, y: 1 },
43
+ width: constrained.width,
44
+ height: constrained.height,
45
+ };
46
+ const patch = { kind: "element", id, before: shape, after: next };
47
+ return { scene: apply(scene, patch), patch };
48
+ };
49
+ /**
50
+ * Pure: resize a single text shape.
51
+ *
52
+ * `original` is the pristine shape snapshotted at the start of the
53
+ * gesture (so font scaling is computed from a stable base, never
54
+ * compounding tick-to-tick).
55
+ *
56
+ * - **Corners** (`nw/ne/se/sw`) and **top/bottom edges** (`n/s`) scale
57
+ * `fontSize` (and `maxWidth`, if set) uniformly — the box never
58
+ * distorts. There's no arbitrary height; a vertical drag scales the
59
+ * whole element.
60
+ * - **Left/right edges** (`e/w`) change only the wrap width: the text
61
+ * reflows onto new lines (sets `maxWidth`, no font change).
62
+ */
63
+ export const computeTextResize = (scene, original, handle, delta, originalBounds) => {
64
+ const current = getElement(scene, original.id);
65
+ if (!current)
66
+ return null;
67
+ const raw = resizeFromHandle(originalBounds, handle, delta);
68
+ // Left / right edges → wrap-width only (no scale).
69
+ if (handle === "e" || handle === "w") {
70
+ const newMaxWidth = Math.max(original.fontSize, Math.abs(raw.width));
71
+ const anchorX = handle === "w" ? originalBounds.x + originalBounds.width : originalBounds.x;
72
+ const nx = handle === "w" ? anchorX - newMaxWidth : anchorX;
73
+ const next = {
74
+ ...original,
75
+ position: { x: nx, y: originalBounds.y },
76
+ scale: { x: 1, y: 1 },
77
+ maxWidth: newMaxWidth,
78
+ };
79
+ const patch = { kind: "element", id: original.id, before: current, after: next };
80
+ return { scene: apply(scene, patch), patch };
81
+ }
82
+ // Corners + top/bottom edges → uniform font scale. Top/bottom take the
83
+ // vertical ratio (only height changed); corners take the larger axis
84
+ // so the dragged corner tracks the cursor along the diagonal.
85
+ const horizontalEdge = handle === "n" || handle === "s";
86
+ const sx = originalBounds.width > 0 ? Math.abs(raw.width / originalBounds.width) : 1;
87
+ const sy = originalBounds.height > 0 ? Math.abs(raw.height / originalBounds.height) : 1;
88
+ const s = horizontalEdge ? sy : Math.max(sx, sy);
89
+ const newFont = Math.max(TEXT_RESIZE_MIN_FONT_SIZE, original.fontSize * s);
90
+ const factor = newFont / original.fontSize; // applied scale (post-clamp)
91
+ const newW = originalBounds.width * factor;
92
+ const newH = originalBounds.height * factor;
93
+ // Anchor = the edge/corner opposite the dragged handle stays put. For
94
+ // n/s the horizontal position pins to the left edge.
95
+ const ax = handle.includes("w") ? originalBounds.x + originalBounds.width : originalBounds.x;
96
+ const ay = handle.includes("n") ? originalBounds.y + originalBounds.height : originalBounds.y;
97
+ const nx = handle.includes("w") ? ax - newW : ax;
98
+ const ny = handle.includes("n") ? ay - newH : ay;
99
+ const next = {
100
+ ...original,
101
+ position: { x: nx, y: ny },
102
+ scale: { x: 1, y: 1 },
103
+ fontSize: newFont,
104
+ ...(original.maxWidth !== undefined ? { maxWidth: original.maxWidth * factor } : {}),
105
+ };
106
+ const patch = { kind: "element", id: original.id, before: current, after: next };
107
+ return { scene: apply(scene, patch), patch };
108
+ };
109
+ export const computeGroupResizePatches = (scene, origin, handle, delta, originalBounds, isAspectLocked) => {
110
+ const next = resizeFromHandle(originalBounds, handle, delta);
111
+ const minDim = 1;
112
+ let sx = originalBounds.width > 0 ? next.width / originalBounds.width : 1;
113
+ let sy = originalBounds.height > 0 ? next.height / originalBounds.height : 1;
114
+ // Aspect-lock: groups can only scale uniformly. Use the larger
115
+ // magnitude so the dragged corner moves along the diagonal toward
116
+ // the cursor; sign is preserved per-axis so a drag past the anchor
117
+ // still mirrors the group uniformly.
118
+ if (isAspectLocked) {
119
+ const locked = Math.max(Math.abs(sx), Math.abs(sy));
120
+ sx = locked * (sx >= 0 ? 1 : -1);
121
+ sy = locked * (sy >= 0 ? 1 : -1);
122
+ }
123
+ // Uniform factor for aspect-locked members (images) inside a mixed
124
+ // selection: an image must only *scale*, never distort, even while
125
+ // its neighbours follow the box's independent sx/sy (images are
126
+ // SCALE-only). Use the dominant drag axis (larger relative change) so
127
+ // dragging the width edge scales the image by the width ratio, the
128
+ // height edge by the height ratio, and a corner by the dominant of the
129
+ // two. Position still tracks the box via (sx, sy) below, so the image
130
+ // stays put in the group's layout — only its size stays proportional.
131
+ const imgScale = Math.abs(sx - 1) >= Math.abs(sy - 1) ? sx : sy;
132
+ // Anchor for the scale = the unchanging corner / edge midpoint
133
+ // of the original bounds (opposite to the dragged handle).
134
+ const ax = handle.includes("w") ? originalBounds.x + originalBounds.width : originalBounds.x;
135
+ const ay = handle.includes("n") ? originalBounds.y + originalBounds.height : originalBounds.y;
136
+ let runningScene = scene;
137
+ const patches = [];
138
+ for (const [id, snap] of origin.elements) {
139
+ const shape = getElement(runningScene, id);
140
+ if (!shape)
141
+ continue;
142
+ const newPx = ax + (snap.position.x - ax) * sx;
143
+ const newPy = ay + (snap.position.y - ay) * sy;
144
+ if (hasWidthHeight(shape)) {
145
+ // Images scale uniformly (aspect-locked); everything else follows
146
+ // the box's per-axis scale.
147
+ const isImage = shape.type === "image";
148
+ const wScale = isImage ? imgScale : sx;
149
+ const hScale = isImage ? imgScale : sy;
150
+ const newWidth = snap.bounds.width * wScale;
151
+ const newHeight = snap.bounds.height * hScale;
152
+ if (Math.abs(newWidth) < minDim || Math.abs(newHeight) < minDim)
153
+ continue;
154
+ const nextElement = {
155
+ ...shape,
156
+ position: { x: newPx, y: newPy },
157
+ scale: {
158
+ x: newWidth >= 0 ? 1 : -1,
159
+ y: newHeight >= 0 ? 1 : -1,
160
+ },
161
+ width: Math.abs(newWidth),
162
+ height: Math.abs(newHeight),
163
+ };
164
+ const patch = { kind: "element", id, before: shape, after: nextElement };
165
+ runningScene = apply(runningScene, patch);
166
+ patches.push(patch);
167
+ continue;
168
+ }
169
+ const newScaleX = snap.scale.x * sx;
170
+ const newScaleY = snap.scale.y * sy;
171
+ if (Math.abs(newScaleX) < minDim / Math.max(1, snap.bounds.width))
172
+ continue;
173
+ if (Math.abs(newScaleY) < minDim / Math.max(1, snap.bounds.height))
174
+ continue;
175
+ const nextElement = {
176
+ ...shape,
177
+ position: { x: newPx, y: newPy },
178
+ scale: { x: newScaleX, y: newScaleY },
179
+ };
180
+ const patch = { kind: "element", id, before: shape, after: nextElement };
181
+ runningScene = apply(runningScene, patch);
182
+ patches.push(patch);
183
+ }
184
+ // Scale the links that ride with the box (selected ∪ bound-both) about the
185
+ // same anchor by the same (sx, sy) — geometry + free point endpoints.
186
+ for (const [id, linkOrigin] of origin.links ?? []) {
187
+ const current = getLink(runningScene, id);
188
+ if (!current)
189
+ continue;
190
+ const scaled = scaleLinkAround(linkOrigin, ax, ay, sx, sy);
191
+ if (!scaled)
192
+ continue;
193
+ const patch = { kind: "link", id, before: current, after: { ...current, ...scaled } };
194
+ runningScene = apply(runningScene, patch);
195
+ patches.push(patch);
196
+ }
197
+ return { scene: runningScene, patches };
198
+ };
199
+ //# sourceMappingURL=resize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resize.js","sourceRoot":"","sources":["../../../src/editor/applies/resize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { type Scene } from "@oh-just-another/scene";
2
+ import type { Bounds, LayerId } from "@oh-just-another/types";
3
+ import * as Selection from "../../selection.js";
4
+ import * as LinkSelection from "../../link-selection.js";
5
+ /**
6
+ * Compute the selection that results from a lasso rectangle.
7
+ *
8
+ * `mode: "replace"` swaps the selection wholesale; `"add"` extends
9
+ * `current` (Shift / Cmd lasso). Locked-layer shapes are skipped
10
+ * via the host-supplied `isLayerLocked` predicate.
11
+ *
12
+ * Pure — returns the new selection without touching anything. The
13
+ * caller (Editor) writes it back, clears `_selectedLink`, and
14
+ * fires `notify()`.
15
+ */
16
+ export declare const selectByBounds: (scene: Scene, current: Selection.Selection, isLayerLocked: (id: LayerId) => boolean, bounds: Bounds, mode: "replace" | "add") => Selection.Selection;
17
+ /**
18
+ * Live-preview variant — bases the next selection on the captured
19
+ * `lassoBaseSelection` snapshot so `"replace"` mode shrinks back to
20
+ * whatever the box currently covers (instead of accumulating
21
+ * since press-down), and `"add"` mode keeps the user's pre-existing
22
+ * picks intact. Caller still owns the equality short-circuit and
23
+ * the `_selectedLink` clearing.
24
+ */
25
+ export declare const selectByBoundsLive: (scene: Scene, base: Selection.Selection, isLayerLocked: (id: LayerId) => boolean, bounds: Bounds, mode: "replace" | "add") => Selection.Selection;
26
+ /**
27
+ * Link half of the marquee. A connector is captured when its ENTIRE drawn
28
+ * path lies inside `bounds` (fully-enclosed rule), so dragging a box across
29
+ * the canvas grabs only the links that are wholly within it. `"replace"`
30
+ * rebuilds from the box each frame; `"add"` keeps the pre-lasso link picks
31
+ * (`base`).
32
+ */
33
+ export declare const selectLinksByBoundsLive: (scene: Scene, base: LinkSelection.LinkSelection, isLayerLocked: (id: LayerId) => boolean, bounds: Bounds, mode: "replace" | "add") => LinkSelection.LinkSelection;
34
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/editor/applies/selection.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC7F,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAQ,MAAM,wBAAwB,CAAC;AACpE,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,aAAa,MAAM,yBAAyB,CAAC;AAGzD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,KAAK,EACZ,SAAS,SAAS,CAAC,SAAS,EAC5B,eAAe,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,EACvC,QAAQ,MAAM,EACd,MAAM,SAAS,GAAG,KAAK,KACtB,SAAS,CAAC,SAQZ,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,KAAK,EACZ,MAAM,SAAS,CAAC,SAAS,EACzB,eAAe,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,EACvC,QAAQ,MAAM,EACd,MAAM,SAAS,GAAG,KAAK,KACtB,SAAS,CAAC,SAQZ,CAAC;AAKF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GAClC,OAAO,KAAK,EACZ,MAAM,aAAa,CAAC,aAAa,EACjC,eAAe,CAAC,EAAE,EAAE,OAAO,KAAK,OAAO,EACvC,QAAQ,MAAM,EACd,MAAM,SAAS,GAAG,KAAK,KACtB,aAAa,CAAC,aAShB,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { getElementsCoveredByBounds, getLinkPath } from "@oh-just-another/scene";
2
+ import * as Selection from "../../selection.js";
3
+ import * as LinkSelection from "../../link-selection.js";
4
+ import { LASSO_COVERAGE_THRESHOLD } from "../../constants.js";
5
+ /**
6
+ * Compute the selection that results from a lasso rectangle.
7
+ *
8
+ * `mode: "replace"` swaps the selection wholesale; `"add"` extends
9
+ * `current` (Shift / Cmd lasso). Locked-layer shapes are skipped
10
+ * via the host-supplied `isLayerLocked` predicate.
11
+ *
12
+ * Pure — returns the new selection without touching anything. The
13
+ * caller (Editor) writes it back, clears `_selectedLink`, and
14
+ * fires `notify()`.
15
+ */
16
+ export const selectByBounds = (scene, current, isLayerLocked, bounds, mode) => {
17
+ const hits = getElementsCoveredByBounds(scene, bounds, LASSO_COVERAGE_THRESHOLD);
18
+ let next = mode === "replace" ? Selection.EMPTY : current;
19
+ for (const shape of hits) {
20
+ if (isLayerLocked(shape.layerId))
21
+ continue;
22
+ next = Selection.add(next, shape.id);
23
+ }
24
+ return next;
25
+ };
26
+ /**
27
+ * Live-preview variant — bases the next selection on the captured
28
+ * `lassoBaseSelection` snapshot so `"replace"` mode shrinks back to
29
+ * whatever the box currently covers (instead of accumulating
30
+ * since press-down), and `"add"` mode keeps the user's pre-existing
31
+ * picks intact. Caller still owns the equality short-circuit and
32
+ * the `_selectedLink` clearing.
33
+ */
34
+ export const selectByBoundsLive = (scene, base, isLayerLocked, bounds, mode) => {
35
+ let next = mode === "replace" ? Selection.EMPTY : base;
36
+ const hits = getElementsCoveredByBounds(scene, bounds, LASSO_COVERAGE_THRESHOLD);
37
+ for (const shape of hits) {
38
+ if (isLayerLocked(shape.layerId))
39
+ continue;
40
+ next = Selection.add(next, shape.id);
41
+ }
42
+ return next;
43
+ };
44
+ const inside = (p, b) => p.x >= b.x && p.x <= b.x + b.width && p.y >= b.y && p.y <= b.y + b.height;
45
+ /**
46
+ * Link half of the marquee. A connector is captured when its ENTIRE drawn
47
+ * path lies inside `bounds` (fully-enclosed rule), so dragging a box across
48
+ * the canvas grabs only the links that are wholly within it. `"replace"`
49
+ * rebuilds from the box each frame; `"add"` keeps the pre-lasso link picks
50
+ * (`base`).
51
+ */
52
+ export const selectLinksByBoundsLive = (scene, base, isLayerLocked, bounds, mode) => {
53
+ let next = mode === "replace" ? LinkSelection.EMPTY : base;
54
+ for (const edge of scene.links.values()) {
55
+ if (isLayerLocked(edge.layerId))
56
+ continue;
57
+ const path = getLinkPath(scene, edge);
58
+ if (!path || path.length < 2)
59
+ continue;
60
+ if (path.every((p) => inside(p, bounds)))
61
+ next = LinkSelection.add(next, edge.id);
62
+ }
63
+ return next;
64
+ };
65
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selection.js","sourceRoot":"","sources":["../../../src/editor/applies/selection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { Bounds, Vec2 } from "@oh-just-another/types";
2
+ import type { ElementId } from "@oh-just-another/types";
3
+ import type { HandleId } from "../../handle.js";
4
+ /**
5
+ * Snap-to-grid math. Pure functions that round gesture deltas / bounds
6
+ * onto a grid of `spacing` world units. The editor wrappers call these
7
+ * before handing the (adjusted) delta to the move / resize / create
8
+ * compute functions, so the snapping logic lives in one tested place
9
+ * and the compute functions stay grid-agnostic.
10
+ *
11
+ * Spacing resolution and the on/off gate live in scene
12
+ * (`resolveSnapSpacing` / `isSnapToGridEnabled`); these helpers assume
13
+ * snapping is on and `spacing > 0`.
14
+ */
15
+ /** Round a world point to the nearest grid intersection. */
16
+ export declare const snapPointToGrid: (p: Vec2, spacing: number) => Vec2;
17
+ /**
18
+ * Adjust a single-shape move delta so the shape's press-time top-left
19
+ * (`originalBounds` origin) lands on the grid. Keeps the drag anchored
20
+ * to the grid regardless of where the shape started.
21
+ */
22
+ export declare const snapMoveDelta: (originalBounds: Bounds, delta: Vec2, spacing: number) => Vec2;
23
+ /**
24
+ * Adjust a group-move delta so the group's top-most-left reference
25
+ * point snaps to the grid while every member keeps its relative
26
+ * position. The reference is the min corner across the press-time
27
+ * origins (each origin is an element's world position). Returns the
28
+ * delta unchanged when the snapshot is empty.
29
+ */
30
+ export declare const snapGroupDelta: (origins: ReadonlyMap<ElementId, Vec2>, delta: Vec2, spacing: number) => Vec2;
31
+ /**
32
+ * Adjust a resize delta so the edge(s) the handle drags land on the
33
+ * grid. Only the axes the handle actually controls are snapped: edge
34
+ * handles (`n`/`s`/`e`/`w`) snap one axis, corners snap both. The
35
+ * stationary edges are untouched.
36
+ */
37
+ export declare const snapResizeDelta: (originalBounds: Bounds, handle: HandleId, delta: Vec2, spacing: number) => Vec2;
38
+ /**
39
+ * Snap a freshly-drawn shape's bounds so both corners land on the grid.
40
+ * Width / height stay non-negative (the bounds are already normalised
41
+ * by the machine, but rounding can never make them negative since both
42
+ * corners round independently).
43
+ */
44
+ export declare const snapCreateBounds: (bounds: Bounds, spacing: number) => Bounds;
45
+ //# sourceMappingURL=snap-grid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snap-grid.d.ts","sourceRoot":"","sources":["../../../src/editor/applies/snap-grid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;;;;;;;;GAUG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,eAAe,GAAI,GAAG,IAAI,EAAE,SAAS,MAAM,KAAG,IAGzD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,gBAAgB,MAAM,EAAE,OAAO,IAAI,EAAE,SAAS,MAAM,KAAG,IAMpF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GACzB,SAAS,WAAW,CAAC,SAAS,EAAE,IAAI,CAAC,EACrC,OAAO,IAAI,EACX,SAAS,MAAM,KACd,IAUF,CAAC;AAOF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAC1B,gBAAgB,MAAM,EACtB,QAAQ,QAAQ,EAChB,OAAO,IAAI,EACX,SAAS,MAAM,KACd,IAgBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,EAAE,SAAS,MAAM,KAAG,MAYlE,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Snap-to-grid math. Pure functions that round gesture deltas / bounds
3
+ * onto a grid of `spacing` world units. The editor wrappers call these
4
+ * before handing the (adjusted) delta to the move / resize / create
5
+ * compute functions, so the snapping logic lives in one tested place
6
+ * and the compute functions stay grid-agnostic.
7
+ *
8
+ * Spacing resolution and the on/off gate live in scene
9
+ * (`resolveSnapSpacing` / `isSnapToGridEnabled`); these helpers assume
10
+ * snapping is on and `spacing > 0`.
11
+ */
12
+ /** Round a world point to the nearest grid intersection. */
13
+ export const snapPointToGrid = (p, spacing) => ({
14
+ x: Math.round(p.x / spacing) * spacing,
15
+ y: Math.round(p.y / spacing) * spacing,
16
+ });
17
+ /**
18
+ * Adjust a single-shape move delta so the shape's press-time top-left
19
+ * (`originalBounds` origin) lands on the grid. Keeps the drag anchored
20
+ * to the grid regardless of where the shape started.
21
+ */
22
+ export const snapMoveDelta = (originalBounds, delta, spacing) => {
23
+ const target = snapPointToGrid({ x: originalBounds.x + delta.x, y: originalBounds.y + delta.y }, spacing);
24
+ return { x: target.x - originalBounds.x, y: target.y - originalBounds.y };
25
+ };
26
+ /**
27
+ * Adjust a group-move delta so the group's top-most-left reference
28
+ * point snaps to the grid while every member keeps its relative
29
+ * position. The reference is the min corner across the press-time
30
+ * origins (each origin is an element's world position). Returns the
31
+ * delta unchanged when the snapshot is empty.
32
+ */
33
+ export const snapGroupDelta = (origins, delta, spacing) => {
34
+ let refX = Infinity;
35
+ let refY = Infinity;
36
+ for (const o of origins.values()) {
37
+ if (o.x < refX)
38
+ refX = o.x;
39
+ if (o.y < refY)
40
+ refY = o.y;
41
+ }
42
+ if (!Number.isFinite(refX))
43
+ return delta;
44
+ const target = snapPointToGrid({ x: refX + delta.x, y: refY + delta.y }, spacing);
45
+ return { x: target.x - refX, y: target.y - refY };
46
+ };
47
+ const HANDLE_MOVES_WEST = new Set(["nw", "w", "sw"]);
48
+ const HANDLE_MOVES_EAST = new Set(["ne", "e", "se"]);
49
+ const HANDLE_MOVES_NORTH = new Set(["nw", "n", "ne"]);
50
+ const HANDLE_MOVES_SOUTH = new Set(["sw", "s", "se"]);
51
+ /**
52
+ * Adjust a resize delta so the edge(s) the handle drags land on the
53
+ * grid. Only the axes the handle actually controls are snapped: edge
54
+ * handles (`n`/`s`/`e`/`w`) snap one axis, corners snap both. The
55
+ * stationary edges are untouched.
56
+ */
57
+ export const snapResizeDelta = (originalBounds, handle, delta, spacing) => {
58
+ let dx = delta.x;
59
+ let dy = delta.y;
60
+ if (HANDLE_MOVES_WEST.has(handle) || HANDLE_MOVES_EAST.has(handle)) {
61
+ const edgeX = HANDLE_MOVES_WEST.has(handle)
62
+ ? originalBounds.x
63
+ : originalBounds.x + originalBounds.width;
64
+ dx = Math.round((edgeX + delta.x) / spacing) * spacing - edgeX;
65
+ }
66
+ if (HANDLE_MOVES_NORTH.has(handle) || HANDLE_MOVES_SOUTH.has(handle)) {
67
+ const edgeY = HANDLE_MOVES_NORTH.has(handle)
68
+ ? originalBounds.y
69
+ : originalBounds.y + originalBounds.height;
70
+ dy = Math.round((edgeY + delta.y) / spacing) * spacing - edgeY;
71
+ }
72
+ return { x: dx, y: dy };
73
+ };
74
+ /**
75
+ * Snap a freshly-drawn shape's bounds so both corners land on the grid.
76
+ * Width / height stay non-negative (the bounds are already normalised
77
+ * by the machine, but rounding can never make them negative since both
78
+ * corners round independently).
79
+ */
80
+ export const snapCreateBounds = (bounds, spacing) => {
81
+ const tl = snapPointToGrid({ x: bounds.x, y: bounds.y }, spacing);
82
+ const br = snapPointToGrid({ x: bounds.x + bounds.width, y: bounds.y + bounds.height }, spacing);
83
+ return {
84
+ x: tl.x,
85
+ y: tl.y,
86
+ width: Math.max(0, br.x - tl.x),
87
+ height: Math.max(0, br.y - tl.y),
88
+ };
89
+ };
90
+ //# sourceMappingURL=snap-grid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snap-grid.js","sourceRoot":"","sources":["../../../src/editor/applies/snap-grid.ts"],"names":[],"mappings":"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"}