@rasamjs/tools 0.0.0-stage → 0.2.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.
- package/API.md +308 -0
- package/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +138 -2
- package/RELEASING.md +88 -0
- package/dist/advanced/context.d.ts +15 -0
- package/dist/advanced/context.d.ts.map +1 -0
- package/dist/advanced/context.js +2 -0
- package/dist/advanced/context.js.map +1 -0
- package/dist/advanced/curves.d.ts +5 -0
- package/dist/advanced/curves.d.ts.map +1 -0
- package/dist/advanced/curves.js +65 -0
- package/dist/advanced/curves.js.map +1 -0
- package/dist/advanced/geometry.d.ts +17 -0
- package/dist/advanced/geometry.d.ts.map +1 -0
- package/dist/advanced/geometry.js +61 -0
- package/dist/advanced/geometry.js.map +1 -0
- package/dist/advanced/rotation.d.ts +4 -0
- package/dist/advanced/rotation.d.ts.map +1 -0
- package/dist/advanced/rotation.js +86 -0
- package/dist/advanced/rotation.js.map +1 -0
- package/dist/advanced/session.d.ts +5 -0
- package/dist/advanced/session.d.ts.map +1 -0
- package/dist/advanced/session.js +240 -0
- package/dist/advanced/session.js.map +1 -0
- package/dist/advanced/snapper.d.ts +29 -0
- package/dist/advanced/snapper.d.ts.map +1 -0
- package/dist/advanced/snapper.js +118 -0
- package/dist/advanced/snapper.js.map +1 -0
- package/dist/advanced/types.d.ts +30 -0
- package/dist/advanced/types.d.ts.map +1 -0
- package/dist/advanced/types.js +2 -0
- package/dist/advanced/types.js.map +1 -0
- package/dist/drawing.d.ts +5 -0
- package/dist/drawing.d.ts.map +1 -0
- package/dist/drawing.js +3 -0
- package/dist/drawing.js.map +1 -0
- package/dist/editor/draw-tool.d.ts +35 -0
- package/dist/editor/draw-tool.d.ts.map +1 -0
- package/dist/editor/draw-tool.js +174 -0
- package/dist/editor/draw-tool.js.map +1 -0
- package/dist/editor/path-editor.d.ts +34 -0
- package/dist/editor/path-editor.d.ts.map +1 -0
- package/dist/editor/path-editor.js +208 -0
- package/dist/editor/path-editor.js.map +1 -0
- package/dist/editor/pointer.d.ts +21 -0
- package/dist/editor/pointer.d.ts.map +1 -0
- package/dist/editor/pointer.js +86 -0
- package/dist/editor/pointer.js.map +1 -0
- package/dist/editor/surface.d.ts +31 -0
- package/dist/editor/surface.d.ts.map +1 -0
- package/dist/editor/surface.js +138 -0
- package/dist/editor/surface.js.map +1 -0
- package/dist/grid.d.ts +23 -0
- package/dist/grid.d.ts.map +1 -0
- package/dist/grid.js +84 -0
- package/dist/grid.js.map +1 -0
- package/dist/rulers.d.ts +30 -0
- package/dist/rulers.d.ts.map +1 -0
- package/dist/rulers.js +234 -0
- package/dist/rulers.js.map +1 -0
- package/dist/snapping/advanced.d.ts +3 -0
- package/dist/snapping/advanced.d.ts.map +1 -0
- package/dist/snapping/advanced.js +2 -0
- package/dist/snapping/advanced.js.map +1 -0
- package/dist/snapping/guides.d.ts +14 -0
- package/dist/snapping/guides.d.ts.map +1 -0
- package/dist/snapping/guides.js +109 -0
- package/dist/snapping/guides.js.map +1 -0
- package/dist/snapping/lines.d.ts +43 -0
- package/dist/snapping/lines.d.ts.map +1 -0
- package/dist/snapping/lines.js +74 -0
- package/dist/snapping/lines.js.map +1 -0
- package/dist/snapping/options.d.ts +18 -0
- package/dist/snapping/options.d.ts.map +1 -0
- package/dist/snapping/options.js +44 -0
- package/dist/snapping/options.js.map +1 -0
- package/dist/snapping/overlay.d.ts +22 -0
- package/dist/snapping/overlay.d.ts.map +1 -0
- package/dist/snapping/overlay.js +165 -0
- package/dist/snapping/overlay.js.map +1 -0
- package/dist/snapping/session.d.ts +25 -0
- package/dist/snapping/session.d.ts.map +1 -0
- package/dist/snapping/session.js +89 -0
- package/dist/snapping/session.js.map +1 -0
- package/dist/snapping/snapper.d.ts +22 -0
- package/dist/snapping/snapper.d.ts.map +1 -0
- package/dist/snapping/snapper.js +140 -0
- package/dist/snapping/snapper.js.map +1 -0
- package/dist/snapping/spacing.d.ts +27 -0
- package/dist/snapping/spacing.d.ts.map +1 -0
- package/dist/snapping/spacing.js +127 -0
- package/dist/snapping/spacing.js.map +1 -0
- package/dist/snapping/targets.d.ts +7 -0
- package/dist/snapping/targets.d.ts.map +1 -0
- package/dist/snapping/targets.js +71 -0
- package/dist/snapping/targets.js.map +1 -0
- package/dist/snapping/types.d.ts +35 -0
- package/dist/snapping/types.d.ts.map +1 -0
- package/dist/snapping/types.js +2 -0
- package/dist/snapping/types.js.map +1 -0
- package/dist/snapping.d.ts +4 -0
- package/dist/snapping.d.ts.map +1 -0
- package/dist/snapping.js +3 -0
- package/dist/snapping.js.map +1 -0
- package/package.json +73 -4
- package/src/advanced/context.ts +15 -0
- package/src/advanced/curves.ts +65 -0
- package/src/advanced/geometry.ts +74 -0
- package/src/advanced/rotation.ts +93 -0
- package/src/advanced/session.ts +276 -0
- package/src/advanced/snapper.ts +124 -0
- package/src/advanced/types.ts +31 -0
- package/src/drawing.ts +4 -0
- package/src/editor/draw-tool.ts +190 -0
- package/src/editor/path-editor.ts +224 -0
- package/src/editor/pointer.ts +85 -0
- package/src/editor/surface.ts +141 -0
- package/src/grid.ts +99 -0
- package/src/rulers.ts +245 -0
- package/src/snapping/advanced.ts +8 -0
- package/src/snapping/guides.ts +132 -0
- package/src/snapping/lines.ts +108 -0
- package/src/snapping/options.ts +47 -0
- package/src/snapping/overlay.ts +186 -0
- package/src/snapping/session.ts +116 -0
- package/src/snapping/snapper.ts +147 -0
- package/src/snapping/spacing.ts +157 -0
- package/src/snapping/targets.ts +88 -0
- package/src/snapping/types.ts +27 -0
- package/src/snapping.ts +3 -0
package/API.md
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# Tools API
|
|
2
|
+
|
|
3
|
+
## Snapping entry
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Snapper } from '@rasamjs/tools/snapping';
|
|
7
|
+
import type { SnapperOptions, SnapRequest, SnapSession } from '@rasamjs/tools/snapping';
|
|
8
|
+
|
|
9
|
+
declare class Snapper {
|
|
10
|
+
constructor(drawing: Svg, options?: SnapperOptions);
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
readonly isDestroyed: boolean;
|
|
13
|
+
begin(request: SnapRequest): SnapSession | null;
|
|
14
|
+
update(): this;
|
|
15
|
+
destroy(): void;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`Svg`, `Point` and `AnyNode` come from `@rasamjs/core`. The constructor needs a live,
|
|
20
|
+
mounted rasam SVG drawing with an owning window. Invalid drawings or options fail
|
|
21
|
+
before registering disposal callbacks. Construction and sessions leave managed SVG
|
|
22
|
+
content unchanged; applications use the returned pointer to perform their own edits.
|
|
23
|
+
|
|
24
|
+
## Options
|
|
25
|
+
|
|
26
|
+
| Option | Default | Meaning |
|
|
27
|
+
| ----------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
28
|
+
| `tolerance: number` | `6` | Nonnegative, finite client pixels, converted independently for the current root x/y mapping on each `snap()` call. |
|
|
29
|
+
| `shapes: boolean` | `true` | Enable alignment to target edges and centers. |
|
|
30
|
+
| `spacing: boolean` | `true` | Equalize gaps to two neighbors or match an existing positive gap. Independent of `shapes`. |
|
|
31
|
+
| `grid: number \| { size: number; origin?: Point } \| null` | `null` | Positive, finite size in drawing units. Origin defaults to `{ x: 0, y: 0 }` and is copied. Only moving edges or points snap to grid lines. |
|
|
32
|
+
| `targets: (nodes: readonly AnyNode[]) => Iterable<AnyNode>` | Siblings | Replaces the default target list; called once per `begin()` when shapes or spacing are enabled. |
|
|
33
|
+
| `modifier: 'ctrl' \| 'none'` | `'ctrl'` | Whether the caller's `ignore` flag temporarily disables snapping. |
|
|
34
|
+
| `style: { stroke?: string; gapStroke?: string }` | `#f00` | Nonempty SVG strokes. `stroke` colors alignment; `gapStroke` colors gap markers and defaults to `stroke`. |
|
|
35
|
+
|
|
36
|
+
Default targets combine siblings from every moving node's parent, deduplicated in
|
|
37
|
+
document order. Selected nodes, their descendants and ancestor containers are excluded.
|
|
38
|
+
Detached, destroyed, foreign, CSS-hidden, empty and unavailable targets are skipped.
|
|
39
|
+
Point requests default to drawing children and pass `[]` to `targets`.
|
|
40
|
+
|
|
41
|
+
`begin()` snapshots target boxes and moving features in drawing units. Removing or
|
|
42
|
+
moving a target afterward does not change the active session. Shape candidates win
|
|
43
|
+
equal-distance grid ties. Shape ties use document order even when a custom callback
|
|
44
|
+
returns nodes in another order. Each root axis snaps independently.
|
|
45
|
+
|
|
46
|
+
## Requests and sessions
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
type SnapRequest =
|
|
50
|
+
| { readonly kind: 'point'; readonly client: Point }
|
|
51
|
+
| {
|
|
52
|
+
readonly kind: 'move' | 'resize';
|
|
53
|
+
readonly nodes: readonly AnyNode[];
|
|
54
|
+
readonly client: Point;
|
|
55
|
+
readonly corners: readonly Point[];
|
|
56
|
+
readonly anchor?: Point;
|
|
57
|
+
readonly centered?: boolean;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
interface SnapSession {
|
|
61
|
+
snap(client: Point, modifiers: { readonly ignore: boolean; readonly centered?: boolean }): Point;
|
|
62
|
+
end(): void;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`client` is the initial pointer in client pixels. Move requests snap the union of the
|
|
67
|
+
nodes' root-space axis-aligned boxes. Pointer offsets from shape edges are preserved.
|
|
68
|
+
The `corners` field may be empty for moves. Point requests snap a single root-space
|
|
69
|
+
point without a box.
|
|
70
|
+
|
|
71
|
+
Resize requests need four client-space frame corners in local clockwise order:
|
|
72
|
+
top-left, top-right, bottom-right, bottom-left. `anchor` is an edge or corner of the
|
|
73
|
+
unit frame, using `0`, `0.5` and `1`, with both coordinates at `0.5` disallowed. Only
|
|
74
|
+
dragged axes snap. Quarter turns and reflections are supported; arbitrarily rotated,
|
|
75
|
+
skewed or fully degenerate frames return `null`. An axis-aligned flat frame can still
|
|
76
|
+
snap its nondegenerate axis. The alignment test uses relative floating point tolerance.
|
|
77
|
+
The active-edge correction is the same for centered resizing; the caller applies its
|
|
78
|
+
centered, ratio and minimum-size constraints after receiving the corrected pointer.
|
|
79
|
+
Pass the resolved current policy as `modifiers.centered` if it can change mid-session.
|
|
80
|
+
Omitting it uses the initial request's `centered` value. Transformer 0.3.0 forwards the
|
|
81
|
+
current policy on every sample, including stationary Alt changes.
|
|
82
|
+
|
|
83
|
+
No eligible targets and no grid, empty moving geometry, unavailable mappings, disabled
|
|
84
|
+
snapping or a destroyed owner yield `null`. Invalid request kinds, node membership,
|
|
85
|
+
pointer coordinates, resize corners or anchors throw `TypeError`.
|
|
86
|
+
|
|
87
|
+
Call `snap(client, { ignore: event.ctrlKey || event.metaKey })` for the decided Ctrl/Cmd
|
|
88
|
+
override. `modifier: 'none'` disregards `ignore`. Pan and zoom may change during a
|
|
89
|
+
session: the current client mapping and per-axis tolerance are read on every call.
|
|
90
|
+
Unavailable mappings return the original client point. Inputs are never mutated.
|
|
91
|
+
|
|
92
|
+
## Lifecycle
|
|
93
|
+
|
|
94
|
+
`enabled` starts at `true`. Disabling it makes an active session return the original
|
|
95
|
+
pointer; re-enabling it resumes the session. A new `begin()` ends the previous session.
|
|
96
|
+
`end()` is idempotent, and ended sessions return their input unchanged. Starting no
|
|
97
|
+
replacement session also ends the previous one. Custom target callbacks that destroy
|
|
98
|
+
the owner or begin another session cannot install a stale outer session.
|
|
99
|
+
|
|
100
|
+
`isDestroyed` starts at `false`. `destroy()` ends the active session, marks the owner
|
|
101
|
+
destroyed and removes the drawing-disposal subscription. It can be repeated safely.
|
|
102
|
+
It leaves the drawing and other snappers usable. Drawing disposal destroys its
|
|
103
|
+
snappers automatically. Changing `enabled` cannot revive a destroyed instance.
|
|
104
|
+
|
|
105
|
+
The original `/snapping` entry's only runtime export is `Snapper`; `SnapperOptions`, `SnapRequest` and `SnapSession`
|
|
106
|
+
are type exports. Imports are safe without a document. There is no package root export,
|
|
107
|
+
and internal module subpaths are blocked.
|
|
108
|
+
|
|
109
|
+
## Equal spacing
|
|
110
|
+
|
|
111
|
+
Spacing is enabled independently of shape alignment. Disable both `shapes` and `spacing`
|
|
112
|
+
for a grid-only tool. Target boxes and reference gaps are snapshotted once at `begin()`;
|
|
113
|
+
pointer samples use cached numbers without measuring target DOM geometry again.
|
|
114
|
+
|
|
115
|
+
Neighbors must overlap the proposed moving box on the perpendicular axis (touching
|
|
116
|
+
boundaries and the zero-size point case count). Each axis uses its closest surrounding
|
|
117
|
+
target edges. Targets that contain the moving box on an axis, such as a background or
|
|
118
|
+
panel behind it, are not neighbors on that axis; partially overlapping targets still
|
|
119
|
+
block. A move can center the box between two neighbors or put an edge one
|
|
120
|
+
reference-gap length before/after a neighbor. Reference gaps are positive gaps after
|
|
121
|
+
the furthest preceding target that overlaps the next target perpendicularly; overlapping
|
|
122
|
+
targets do not create a gap. Targets that contain the next target on that axis are
|
|
123
|
+
backgrounds and neither create nor block its gap. Reference gaps may come from another row or column.
|
|
124
|
+
|
|
125
|
+
Resize correction changes only the dragged edge, including after a flip. An ordinary
|
|
126
|
+
resize can equalize its two neighboring gaps or match a reference gap at that edge.
|
|
127
|
+
Centered resizing can match a reference at the dragged edge; already-equal opposing
|
|
128
|
+
gaps are marked with zero correction because changing size cannot fix an off-center
|
|
129
|
+
frame. Other transformer constraints keep their existing priority.
|
|
130
|
+
|
|
131
|
+
The closest correction within the current client-pixel tolerance wins. Exact-distance
|
|
132
|
+
ties prefer shape alignment, then grid, then spacing. Spacing ties prefer the
|
|
133
|
+
two-neighbor result, then reference gaps in snapshot document order. Gap markers are
|
|
134
|
+
shown only when all accepted gaps still match within half a client pixel and the
|
|
135
|
+
moving geometry still overlaps its neighbor perpendicularly.
|
|
136
|
+
|
|
137
|
+
Gap discovery sorts cached far edges once and stops at the nearest eligible preceding
|
|
138
|
+
edge for each target. Its worst case is still quadratic in target
|
|
139
|
+
count. Each pointer query scans cached neighbors and reference gaps (linear), with no
|
|
140
|
+
target DOM reads. Applications can narrow `targets` or disable spacing for large scenes.
|
|
141
|
+
|
|
142
|
+
## Guides
|
|
143
|
+
|
|
144
|
+
Guides live in a separate native SVG beside the outer drawing SVG. They stay out of
|
|
145
|
+
managed drawing content, exports, queries and history. The overlay ignores pointer
|
|
146
|
+
events and accessibility navigation. Its default red lines use a fixed one-client-pixel
|
|
147
|
+
stroke; cross marks remain six client pixels wide. `style.stroke` changes their color.
|
|
148
|
+
|
|
149
|
+
Each snapped axis spans the moving box and its snapshotted target box, with eight
|
|
150
|
+
client pixels of padding and a cross at each box's perpendicular center. Grid and
|
|
151
|
+
point alignment guides use the moving geometry only; this overlay does not draw a grid.
|
|
152
|
+
Applications draw their own grid; the example uses a CSS background that follows the camera.
|
|
153
|
+
Equal gaps use two dimension lines with fixed six-client-pixel perpendicular end caps;
|
|
154
|
+
`gapStroke` defaults to the alignment stroke. Their endpoint positions follow the
|
|
155
|
+
accepted moving box and the snapshotted target edges.
|
|
156
|
+
|
|
157
|
+
Pointer samples coalesce into one animation-frame draw. For shape requests the draw
|
|
158
|
+
reads accepted geometry after the caller applies the corrected pointer. An axis is
|
|
159
|
+
shown only if its accepted feature lies within half a client pixel of the target.
|
|
160
|
+
This hides misleading guides when ratio, minimum-size or other constraints reject
|
|
161
|
+
alignment. Resize guides follow the dragged anchor, including reflected groups.
|
|
162
|
+
|
|
163
|
+
`update()` redraws immediately and returns the snapper, so it fits the viewport's
|
|
164
|
+
`overlays` list alongside the transformer. Call it after changing the camera or layout
|
|
165
|
+
yourself. Page and ancestor scrolls and window resizes schedule redraws automatically.
|
|
166
|
+
It is safe after destruction. Disabling snapping, bypassing it, ending a session or
|
|
167
|
+
destroying the owner clears pending paint and hides guides immediately. Destruction
|
|
168
|
+
also removes the overlay and its window listeners.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const snapper = new Snapper(drawing);
|
|
172
|
+
const transformer = new Transformer(drawing, { snap: snapper });
|
|
173
|
+
const viewport = new Viewport(drawing, { overlays: [transformer, snapper] });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Transformer connection
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { Transformer } from '@rasamjs/transformer';
|
|
180
|
+
const transformer = new Transformer(drawing, { snap: new Snapper(drawing, { grid: 10 }) });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The bridge is structural: transformer imports no tools runtime. Pointer acquisition
|
|
184
|
+
starts one session for a move or eligible resize; finish, Escape, cancellation, detach
|
|
185
|
+
and destruction end it. With the original `Snapper`, keyboard, rotation and pivot
|
|
186
|
+
gestures do not acquire sessions.
|
|
187
|
+
Ctrl/Cmd is forwarded on pointer samples and re-evaluated when held or released while
|
|
188
|
+
stationary. Existing ratio, minimum-size, `boundBox` and commit-precision rules can
|
|
189
|
+
alter the final alignment after pointer correction.
|
|
190
|
+
|
|
191
|
+
## Advanced snapping entry
|
|
192
|
+
|
|
193
|
+
`@rasamjs/tools/snapping/advanced` exports `AdvancedSnapper` and the types
|
|
194
|
+
`AdvancedSnapperOptions`, `AdvancedSnapRequest`, `RotationSnapRequest`,
|
|
195
|
+
`RotationSnapSession` and `RulerGuide`. It implements the transformer's optional
|
|
196
|
+
`rotatedResize`, `keyboard` and `rotate()` capabilities while retaining the original
|
|
197
|
+
constructor, `begin`, `enabled`, `isDestroyed`, `update` and `destroy` lifecycle.
|
|
198
|
+
Existing `Snapper` and custom providers keep their original behavior.
|
|
199
|
+
|
|
200
|
+
Additional options:
|
|
201
|
+
|
|
202
|
+
| Option | Default | Contract |
|
|
203
|
+
| ------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------- |
|
|
204
|
+
| `guides: () => Iterable<RulerGuide>` | None | Snapshot `{ axis: 'x' \| 'y', position: number }` in root drawing units at session start. |
|
|
205
|
+
| `curves: boolean` | `true` | Snap to path, line, polyline, polygon, circle and ellipse contours. Independent of `shapes`. |
|
|
206
|
+
| `curveResolution: number` | `0.25` | Positive finite sampling interval in initial client pixels, capped at 8,192 segments per subpath. |
|
|
207
|
+
| `angleTolerance: number` | `5` | Positive finite degrees for matching another target's root-space x-axis orientation, modulo 180 degrees. |
|
|
208
|
+
| `keyboard: boolean` | `true` | Opt the provider into transformer keyboard move/resize sessions. |
|
|
209
|
+
|
|
210
|
+
Resize correction follows the dragged anchor. An oblique side handle stays on its
|
|
211
|
+
one-dimensional frame axis; a corner can correct both root axes. Skew, reflection
|
|
212
|
+
and nonuniform parent transforms are supported. The transformer's ratio, centered,
|
|
213
|
+
minimum-size and bound-box constraints still take priority; positional guides verify
|
|
214
|
+
the accepted geometry before rendering.
|
|
215
|
+
|
|
216
|
+
`rotate({ nodes, client, corners, pivot })` returns a `RotationSnapSession` or `null`.
|
|
217
|
+
`pivot` and `corners` are client coordinates. Angles and target transforms are
|
|
218
|
+
snapshotted at session start; pointer radius is preserved. `angleSnapped` reports
|
|
219
|
+
whether the most recent pointer sample matched another target. Radius is preserved
|
|
220
|
+
in the gesture's parent coordinate system. A matched target
|
|
221
|
+
wins over the transformer's fixed angle stops; explicit Shift rotation steps retain
|
|
222
|
+
priority. Ctrl/Cmd bypass and `enabled: false` apply to rotation too.
|
|
223
|
+
|
|
224
|
+
Point requests may include `nodes` to exclude the edited path or other moving
|
|
225
|
+
geometry. Requests may include `keyboard: true`; keyboard sessions reject a snap
|
|
226
|
+
that would hold the edit at its origin or push it backward. Raw keyboard motion
|
|
227
|
+
accumulates independently of corrections so held arrows can leave an existing stop.
|
|
228
|
+
|
|
229
|
+
Curve targets are sampled from detached copies once per session and queried as pure
|
|
230
|
+
client-space segments. Separate `M` subpaths are never connected. Sampling is
|
|
231
|
+
approximate: very long contours hit the segment cap, and zooming after the snapshot
|
|
232
|
+
can change the screen-space approximation error. Use a smaller `curveResolution`
|
|
233
|
+
when appropriate or restrict `targets` in large scenes. Target edits do not change
|
|
234
|
+
an active snapshot. Invalid/unavailable native geometry is skipped.
|
|
235
|
+
|
|
236
|
+
## Grid overlay
|
|
237
|
+
|
|
238
|
+
`@rasamjs/tools/grid` exports `GridOverlay` and `GridOptions`.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
const grid = new GridOverlay(drawing, {
|
|
242
|
+
size: 10,
|
|
243
|
+
origin: { x: 0, y: 0 },
|
|
244
|
+
stroke: '#d9dee8',
|
|
245
|
+
});
|
|
246
|
+
grid.enabled = false;
|
|
247
|
+
grid.update();
|
|
248
|
+
grid.destroy();
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
`size` is positive and finite; `origin` is copied and must be finite. Grid lines follow
|
|
252
|
+
the current root mapping, including pan, zoom and CSS transforms. At extreme zoom-out
|
|
253
|
+
the renderer coarsens visible lines to powers-of-two multiples of `size` to keep the
|
|
254
|
+
path bounded; snapping still uses the configured grid size. It is an editor underlay,
|
|
255
|
+
with no managed nodes, exports or undo entries. `isDestroyed` is read-only.
|
|
256
|
+
|
|
257
|
+
## Ruler guides
|
|
258
|
+
|
|
259
|
+
`@rasamjs/tools/rulers` exports `RulerGuides`, `RulerOptions` and `NamedGuide`.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const rulers = new RulerGuides(drawing, { onChange: (guides) => saveGuides(guides) });
|
|
263
|
+
const id = rulers.add('x', 100);
|
|
264
|
+
rulers.move(id, 120);
|
|
265
|
+
rulers.remove(id);
|
|
266
|
+
rulers.clear();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`guides` is a frozen snapshot of `{ id, axis, position }`. Pass
|
|
270
|
+
`guides: () => rulers.guides` to `AdvancedSnapper`. The left ruler creates vertical
|
|
271
|
+
(`x`) guides; the top ruler creates horizontal (`y`) guides. Drag an existing guide
|
|
272
|
+
to move it. Release over a ruler or outside the host to remove it; double-click also
|
|
273
|
+
removes it. Escape, blur, pointer cancellation and capture loss restore the previous
|
|
274
|
+
guide or discard a new one. Focused guide lines support Arrow keys (Shift: 10 units)
|
|
275
|
+
and Delete/Backspace. Enter/Space on a ruler creates a guide at the current view center.
|
|
276
|
+
Guides are application state, independent of model history. `update`, `isDestroyed`
|
|
277
|
+
and `destroy` follow the overlay lifecycle; mutation methods reject a destroyed owner.
|
|
278
|
+
|
|
279
|
+
## Drawing and path editing
|
|
280
|
+
|
|
281
|
+
`@rasamjs/tools/drawing` exports `DrawTool` and `PathEditor`, plus their option,
|
|
282
|
+
handle and adapter types. Their `snapper` option is a structural `PointSnapper`:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
interface PointSnapper {
|
|
286
|
+
begin(request: { kind: 'point'; client: Point; nodes?: readonly AnyNode[] }): SnapSession | null;
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`DrawTool(drawing, { snapper, kind, paint?, onCreate? })` supports `rect`, `ellipse`,
|
|
291
|
+
`line` and `path`. A path drag creates an editable two-anchor path. The enabled tool
|
|
292
|
+
owns a transparent native pointer layer. Drawing previews remain outside the model;
|
|
293
|
+
release creates one managed shape in one neutral change group. Cancellation creates
|
|
294
|
+
no shape and no undo entry. Programmatic `begin(client, ignore?)`, `move(client,
|
|
295
|
+
ignore?)` and `end(cancelled?)` use client coordinates and the same snapping rules.
|
|
296
|
+
`end()` returns the created node or `null`. Set `enabled: false` to return pointer
|
|
297
|
+
ownership to selection/transformer. `onCreate` runs after the committed edit.
|
|
298
|
+
Active previews follow camera changes without adding temporary model nodes.
|
|
299
|
+
|
|
300
|
+
`PathEditor(drawing, { snapper, stroke? }).attach(node)` accepts a live `Path`,
|
|
301
|
+
`Polyline` or `Polygon` in this drawing. `attach(null)` disconnects it. `handles`
|
|
302
|
+
returns anchors and Bezier controls with their command/coordinate indexes; arcs
|
|
303
|
+
expose their endpoints. Native handles use pointer capture, keep geometry in local
|
|
304
|
+
units under parent transforms, and exclude the edited node from snapping targets.
|
|
305
|
+
`begin(handleIndex)`, `move(client, ignore?)`, and `end(cancelled?)` expose the same
|
|
306
|
+
editing lifecycle to integrations. Each gesture is one undo step; cancellation
|
|
307
|
+
restores the original attribute representation exactly. `update`, `isDestroyed`
|
|
308
|
+
and `destroy` release all observers, listeners and ownership.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.2.0 (unreleased)
|
|
4
|
+
|
|
5
|
+
- Add the optional advanced snapping entry: oblique resize anchors, target-relative
|
|
6
|
+
rotation, contour/segment targets, ruler targets and keyboard nudges.
|
|
7
|
+
- Add camera-aware grid and draggable ruler overlays, snapped drawing tools and
|
|
8
|
+
path/Bezier point editing with grouped undo and cancellation.
|
|
9
|
+
- Keep the original snapping entry within its existing 6 KiB budget.
|
|
10
|
+
|
|
11
|
+
## 0.1.0 — 2026-10-31
|
|
12
|
+
|
|
13
|
+
- Add a 1,000-target drag benchmark in all three engines (`bench:drag`, run by the
|
|
14
|
+
release gate) that reports drag-start, snap-call and frame times without a hardware
|
|
15
|
+
threshold, and checks guides, the corrected move and exact undo.
|
|
16
|
+
- Rebuild the example with toggles for snapping, shapes, grid and spacing, and an
|
|
17
|
+
application-drawn grid that follows the viewport camera.
|
|
18
|
+
- Add default equal-spacing snapping for moves, dragged resize edges and points:
|
|
19
|
+
equalize two neighboring gaps or match a positive snapshotted gap.
|
|
20
|
+
- Skip targets that contain the moving box or a gap's next target on that axis, so a
|
|
21
|
+
background or panel sibling no longer disables spacing for the shapes above it.
|
|
22
|
+
- Keep spacing independent of shape alignment, preserve alignment/grid ties and
|
|
23
|
+
recheck accepted gap lengths and perpendicular overlap before drawing markers.
|
|
24
|
+
- Add `spacing` and `style.gapStroke`, with fixed six-client-pixel gap end caps.
|
|
25
|
+
- Accept optional current `centered` snap metadata so live Alt changes retain correct
|
|
26
|
+
spacing; callers that omit it keep the initial request policy.
|
|
27
|
+
- Validate real single/group moves and resizes, exact undo, zoom, target snapshots,
|
|
28
|
+
modifier bypass, opt-out and disposal in all three browser engines.
|
|
29
|
+
- Add a separate red SVG guide overlay, fixed one-pixel strokes, six-pixel cross marks,
|
|
30
|
+
optional `style.stroke` and viewport-compatible `update()`.
|
|
31
|
+
- Draw once per animation frame from accepted geometry, suppress constrained alignment,
|
|
32
|
+
and clear pending guides on bypass, disable, cancellation, replacement and disposal.
|
|
33
|
+
- Cover guide alignment through zoom, scrolling, camera changes, transformed containers
|
|
34
|
+
and the real viewport overlay list in Chromium, Firefox and WebKit.
|
|
35
|
+
- Connect the snapping example to the transformer's structural `snap` option, and
|
|
36
|
+
validate real single/group drags, modifiers, exact undo, cancellation and error cleanup.
|
|
37
|
+
- Extend the packed consumer to typecheck the real tools/transformer bridge after
|
|
38
|
+
validating tools independently with its core-only runtime peer.
|
|
39
|
+
- Add Phase 2 engine sessions for moves, dragged resize axes and points, with shape
|
|
40
|
+
alignment, optional grid snapping and document-order tie rules.
|
|
41
|
+
- Snapshot root-space targets once; convert the six-pixel tolerance through the current
|
|
42
|
+
client mapping per axis, including mid-session pan and zoom.
|
|
43
|
+
- Add custom and multi-parent sibling targets, hidden-target filtering, temporary
|
|
44
|
+
Ctrl/Cmd override and safe session replacement/destruction.
|
|
45
|
+
- Add the private `@rasamjs/tools` package with the `./snapping` entry only.
|
|
46
|
+
- Add `Snapper` construction, the `enabled` flag and idempotent destruction, including
|
|
47
|
+
automatic cleanup when the drawing is destroyed.
|
|
48
|
+
- Add lifecycle tests, the development example, declaration contracts and package gates.
|
|
49
|
+
- Reserve port 4179 for tools browser tests. The owner-approved gzip budget is 6 KiB
|
|
50
|
+
with core external, increased from the initial 4 KiB cap during Phase 4.
|
|
51
|
+
|
|
52
|
+
Equal spacing is a later phase. Publication requires separate
|
|
53
|
+
authorization.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rasam contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,139 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @rasamjs/tools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Opt-in editing tools for rasam drawings. The first entry is `@rasamjs/tools/snapping`;
|
|
4
|
+
the package has no main entry yet. Its only runtime peer is `@rasamjs/core` `^0.1.0`.
|
|
5
|
+
|
|
6
|
+
## Snapping
|
|
7
|
+
|
|
8
|
+
While a shape is moved or resized, its edges and centers line up with other shapes,
|
|
9
|
+
its edges land on an optional grid, and its gaps match its neighbors. Red guides and
|
|
10
|
+
gap markers show what it snapped to. Holding Ctrl (Cmd on macOS) bypasses snapping
|
|
11
|
+
while held.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { SVG } from '@rasamjs/core';
|
|
15
|
+
import { Transformer } from '@rasamjs/transformer';
|
|
16
|
+
import { Snapper } from '@rasamjs/tools/snapping';
|
|
17
|
+
|
|
18
|
+
const drawing = SVG(document.querySelector<HTMLElement>('#drawing')!, {
|
|
19
|
+
width: 600,
|
|
20
|
+
height: 400,
|
|
21
|
+
});
|
|
22
|
+
const snapper = new Snapper(drawing, { grid: 10 });
|
|
23
|
+
const transformer = new Transformer(drawing, { snap: snapper });
|
|
24
|
+
|
|
25
|
+
snapper.enabled = false; // a toolbar toggle
|
|
26
|
+
snapper.enabled = true;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The transformer takes any structural `snap` provider and never imports this package.
|
|
30
|
+
Snapping runs during pointer moves and resizes of frames that are axis-aligned in
|
|
31
|
+
drawing space. Keyboard nudges, rotation and pivot edits keep their existing behavior.
|
|
32
|
+
Ratio, minimum-size, `boundBox` and commit-precision rules keep priority over snapping,
|
|
33
|
+
and guides only show alignments the accepted geometry still has.
|
|
34
|
+
|
|
35
|
+
### Targets
|
|
36
|
+
|
|
37
|
+
Targets default to the moving nodes' siblings, combined across parents in document
|
|
38
|
+
order. Moving subtrees, their ancestor containers, hidden nodes and unavailable bounds
|
|
39
|
+
are skipped. `targets(nodes)` replaces the list, for example to limit snapping to an
|
|
40
|
+
artboard or skip locked layers. Targets are measured once per drag, in drawing units.
|
|
41
|
+
|
|
42
|
+
The tolerance is six client pixels by default and follows the current zoom, so snapping
|
|
43
|
+
feels the same at every zoom level and keeps working when the view changes mid-drag.
|
|
44
|
+
Shape lines win equal-distance ties with the grid, and the grid wins ties with spacing.
|
|
45
|
+
|
|
46
|
+
### Grid
|
|
47
|
+
|
|
48
|
+
The grid is off by default. `grid: 10` or `grid: { size: 10, origin: { x: 5, y: 5 } }`
|
|
49
|
+
snaps edges, not centers, to grid lines in drawing units. The snapper does not draw the
|
|
50
|
+
grid; applications draw their own, as the example does with a CSS background.
|
|
51
|
+
|
|
52
|
+
### Equal spacing
|
|
53
|
+
|
|
54
|
+
Spacing is on by default and independent of `shapes`. A move can center a shape between
|
|
55
|
+
two neighbors, or put it one existing gap away from a neighbor; existing gaps may come
|
|
56
|
+
from another row or column. Neighbors must overlap the shape on the other axis.
|
|
57
|
+
Backgrounds and panels that contain the shape are not neighbors, while partial overlaps
|
|
58
|
+
block a gap. Resizing corrects only the dragged edge. `spacing: false` turns it off;
|
|
59
|
+
turn off both `shapes` and `spacing` for grid-only snapping.
|
|
60
|
+
|
|
61
|
+
### Guides
|
|
62
|
+
|
|
63
|
+
Guides and gap markers live in their own SVG overlay beside the drawing, so they never
|
|
64
|
+
appear in exports, queries or history. Strokes stay one client pixel wide at every zoom.
|
|
65
|
+
`style: { stroke, gapStroke }` sets their colors (default `#f00`). The overlay redraws
|
|
66
|
+
once per animation frame while dragging; `snapper.update()` redraws it immediately.
|
|
67
|
+
With a viewport, list the snapper beside the transformer:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Viewport } from '@rasamjs/viewport';
|
|
71
|
+
const viewport = new Viewport(drawing, { overlays: [transformer, snapper] });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Lifecycle
|
|
75
|
+
|
|
76
|
+
Construct the snapper with a live, mounted rasam SVG drawing. It starts enabled. Options
|
|
77
|
+
are fixed at construction; to change them, destroy the snapper and create another
|
|
78
|
+
behind a small provider object, as the example does. Destroying the drawing destroys its
|
|
79
|
+
snappers. `destroy()` ends an active drag session, removes the overlay and can be
|
|
80
|
+
repeated safely.
|
|
81
|
+
|
|
82
|
+
### Other tools
|
|
83
|
+
|
|
84
|
+
Drawing tools and the path editor can snap single points with the same engine:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const client = drawing.toClient({ x: 8, y: 8 })!;
|
|
88
|
+
const session = snapper.begin({ kind: 'point', client });
|
|
89
|
+
const corrected = session?.snap(client, { ignore: false }) ?? client;
|
|
90
|
+
session?.end();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`session.snap(client, { ignore: ctrlKey || metaKey })` implements the bypass;
|
|
94
|
+
`modifier: 'none'` ignores that flag.
|
|
95
|
+
|
|
96
|
+
## Example
|
|
97
|
+
|
|
98
|
+
Run `npm run dev -w @rasamjs/tools` and open [the snapping example](examples/snapping.html).
|
|
99
|
+
It combines selection, transformer, viewport and snapper, with toggles for snapping,
|
|
100
|
+
shapes, grid and spacing. The server uses port **4179** and refuses an occupied port.
|
|
101
|
+
|
|
102
|
+
See [API.md](API.md), [RELEASING.md](RELEASING.md) and [CHANGELOG.md](CHANGELOG.md).
|
|
103
|
+
|
|
104
|
+
## Complete editor snapping
|
|
105
|
+
|
|
106
|
+
The optional `@rasamjs/tools/snapping/advanced` entry adds oblique resize handles,
|
|
107
|
+
matching other shapes' rotation angles, curve/segment targets, ruler guides and
|
|
108
|
+
keyboard nudges. The original `/snapping` entry retains its 6 KiB budget and behavior.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { AdvancedSnapper } from '@rasamjs/tools/snapping/advanced';
|
|
112
|
+
import { GridOverlay } from '@rasamjs/tools/grid';
|
|
113
|
+
import { RulerGuides } from '@rasamjs/tools/rulers';
|
|
114
|
+
import { DrawTool, PathEditor } from '@rasamjs/tools/drawing';
|
|
115
|
+
|
|
116
|
+
const rulers = new RulerGuides(drawing);
|
|
117
|
+
const grid = new GridOverlay(drawing, { size: 10 });
|
|
118
|
+
const snapper = new AdvancedSnapper(drawing, {
|
|
119
|
+
grid: 10,
|
|
120
|
+
guides: () => rulers.guides,
|
|
121
|
+
curves: true,
|
|
122
|
+
});
|
|
123
|
+
const transformer = new Transformer(drawing, { snap: snapper });
|
|
124
|
+
const draw = new DrawTool(drawing, { kind: 'rect', snapper });
|
|
125
|
+
draw.enabled = false; // Enable while the rectangle tool is selected.
|
|
126
|
+
const pathEditor = new PathEditor(drawing, { snapper });
|
|
127
|
+
pathEditor.attach(drawing.path({ d: 'M 10 10 Q 80 100 160 10' }));
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Use a positioned, isolated host (`position: relative; isolation: isolate`) for these
|
|
131
|
+
native overlays. Include `grid`, `rulers`, `snapper` and `pathEditor` in the viewport's
|
|
132
|
+
`overlays` list, or call their `update()` methods after outside mapping changes.
|
|
133
|
+
All overlays stay outside SVG exports and history. Every tool owns its listeners,
|
|
134
|
+
observers and pointer capture and cleans them up on destruction of the drawing.
|
|
135
|
+
|
|
136
|
+
Run the [complete editor example](examples/advanced.html) with
|
|
137
|
+
`npm run dev -w @rasamjs/tools`, then open
|
|
138
|
+
`http://127.0.0.1:4179/examples/advanced.html`. It demonstrates all seven additions,
|
|
139
|
+
selection, camera controls, undo/redo and editable Bezier controls.
|
package/RELEASING.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Releasing tools
|
|
2
|
+
|
|
3
|
+
The package remains private. Its initial candidate is `@rasamjs/tools@0.1.0`, with
|
|
4
|
+
`@rasamjs/core@^0.1.0` as its only runtime peer. History, selection, transformer and viewport
|
|
5
|
+
are development dependencies for the example and tests.
|
|
6
|
+
|
|
7
|
+
The snapping entry has a **6 KiB (6,144 bytes) gzip** budget with all core entry points
|
|
8
|
+
external. The owner approved increasing Phase 1's 4 KiB cap during Phase 4 on 2026-10-31
|
|
9
|
+
after guide rendering measured 4,544 gzip bytes, leaving room for Phase 5 spacing.
|
|
10
|
+
There is no root entry or root bundle budget. Optional entries have separate
|
|
11
|
+
caps: advanced snapping 9 KiB, grid 2.5 KiB, rulers 4 KiB and drawing/path editing
|
|
12
|
+
5 KiB. All core entry points stay external; no optional entry may acquire runtime
|
|
13
|
+
selection, transformer, viewport or history dependencies.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm run build
|
|
17
|
+
npm run release:check -w @rasamjs/tools
|
|
18
|
+
npm run release:check
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The tools package gate checks formatting, typechecking, lifecycle unit and browser
|
|
22
|
+
tests, build output, exported declarations, documentation links, the packed consumer
|
|
23
|
+
and bundle size, then runs the drag benchmark. Browser tests use port **4179** in
|
|
24
|
+
Chromium, Firefox and WebKit; the benchmark uses **4189**. Both refuse to reuse an
|
|
25
|
+
existing server. Coordinate these ports before running the gate.
|
|
26
|
+
|
|
27
|
+
## Drag benchmark
|
|
28
|
+
|
|
29
|
+
`npm run bench:drag -w @rasamjs/tools` moves one shape with the transformer over 1,000
|
|
30
|
+
targets: a background sibling and 999 small rectangles in rows, with shapes, a 10-unit
|
|
31
|
+
grid and spacing enabled. It times 11 direct `begin()` calls (target measurement, line
|
|
32
|
+
sorting and gap discovery), reporting the first, cold call apart from the 10 warm ones.
|
|
33
|
+
It then sends 600 synthetic pointer moves during native mouse capture in burst and
|
|
34
|
+
paced modes, and writes [drag-benchmark.json](reports/drag-benchmark.json); CI uploads
|
|
35
|
+
it as `tools-drag-benchmark`. Timings are observations with no hardware threshold. The
|
|
36
|
+
run fails unless each drag opens one session, snaps once per applied frame, shows
|
|
37
|
+
guides and hides them on release, follows the corrected pointer exactly, records one
|
|
38
|
+
undo step and undoes to the exact original markup.
|
|
39
|
+
|
|
40
|
+
Run the full workspace gate before merging when its other package ports are available.
|
|
41
|
+
Do not run it alongside another session's gate. Earlier local port-coordination deferrals are resolved. A passing tools gate alone
|
|
42
|
+
does not replace the full workspace check.
|
|
43
|
+
The shared runner registers tools after its development dependencies and rechecks source
|
|
44
|
+
fingerprints after every package gate.
|
|
45
|
+
|
|
46
|
+
Build the package and review its emitted declarations before updating the API contract:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
npm run api:update -w @rasamjs/tools
|
|
50
|
+
npm run api:check -w @rasamjs/tools
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The packed consumer installs only core and tools, typechecks through default `dist`
|
|
54
|
+
resolution, verifies document-free imports and source maps, and rejects the package
|
|
55
|
+
root and internal subpaths. Development packages must not become runtime requirements.
|
|
56
|
+
Reports in `reports/` record the reviewed API, size and candidate readiness. The release
|
|
57
|
+
gate builds a candidate tarball; it does not publish it.
|
|
58
|
+
|
|
59
|
+
Phase 2 adds the engine and options. Its unit suite covers pure box/line/session math
|
|
60
|
+
without a DOM, plus drawing adapters and lifecycle. Browser tests verify engine calls
|
|
61
|
+
through zoomed and panned roots, nested rotations/viewports, multi-parent selections,
|
|
62
|
+
resize eligibility, hidden targets and modifiers. Phase 3 adds the approved transformer
|
|
63
|
+
bridge, real single/group mouse drags, exact undo/cancellation and provider cleanup.
|
|
64
|
+
The packed consumer first verifies tools independently, then installs the transformer
|
|
65
|
+
tarball to typecheck the real structural bridge.
|
|
66
|
+
Phase 4 adds guide rendering and accepted-geometry checks, animation-frame coalescing,
|
|
67
|
+
window listener cleanup, the `update()`/`style.stroke` public API and viewport overlay
|
|
68
|
+
wiring. Browser tests cover fixed pixel dimensions, multiple zoom levels, page scroll,
|
|
69
|
+
mid-drag camera changes, transformed containers and constraint rejection.
|
|
70
|
+
Phase 5 adds the DOM-free spacing engine, accepted gap-marker geometry, independent
|
|
71
|
+
`spacing`/`shapes` flags and `style.gapStroke`. Discovery sorts cached far edges once
|
|
72
|
+
and stops at each target's nearest eligible preceding edge, with quadratic worst-case
|
|
73
|
+
behavior; targets containing a box are backgrounds, not neighbors or obstructions.
|
|
74
|
+
Pointer queries scan cached boxes/gaps. Tests cover both equal-neighbor and existing-gap
|
|
75
|
+
patterns, backgrounds, dragged/centered resize, tie rules, scale-dependent tolerance,
|
|
76
|
+
real undo and fixed-pixel markers. The 6 KiB cap remains unchanged.
|
|
77
|
+
Phase 6 adds the drag benchmark and the example's shape, grid and spacing toggles,
|
|
78
|
+
with an application-drawn grid. It changes no runtime source.
|
|
79
|
+
|
|
80
|
+
## Advanced editor checks
|
|
81
|
+
|
|
82
|
+
The advanced editor example is tested with real ruler, drawing, resize, rotation and
|
|
83
|
+
path-handle gestures in Chromium, Firefox and WebKit. Cases cover contour projection,
|
|
84
|
+
separate path subpaths, self exclusion, keyboard progress, grouped undo, cancellation,
|
|
85
|
+
grid camera updates, export isolation and destruction. Unit tests cover the affine
|
|
86
|
+
resize constraint, guide snapshots, rotation radius, provider opt-ins and raw keyboard
|
|
87
|
+
accumulation. Packed consumers import all optional entries without a document and
|
|
88
|
+
compile a real advanced provider through the transformer's structural protocol.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Svg } from '@rasamjs/core';
|
|
2
|
+
import type { ResolvedOptions } from '../snapping/options.js';
|
|
3
|
+
import type { GuideOverlay } from '../snapping/overlay.js';
|
|
4
|
+
import type { AdvancedSnapperOptions } from './types.js';
|
|
5
|
+
export interface SessionContext<Request> {
|
|
6
|
+
readonly drawing: Svg;
|
|
7
|
+
readonly options: ResolvedOptions;
|
|
8
|
+
readonly advanced: AdvancedSnapperOptions;
|
|
9
|
+
readonly overlay: GuideOverlay;
|
|
10
|
+
readonly request: Request;
|
|
11
|
+
readonly isEnabled: () => boolean;
|
|
12
|
+
readonly isCurrent: () => boolean;
|
|
13
|
+
readonly onEnd: () => void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/advanced/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,MAAM,WAAW,cAAc,CAAC,OAAO;IACrC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/advanced/context.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { AnyNode, Svg } from '@rasamjs/core';
|
|
2
|
+
import type { Segment } from './geometry.js';
|
|
3
|
+
/** Detached geometry is sampled once: target edits never change an active session. */
|
|
4
|
+
export declare function curveSegments(drawing: Svg, nodes: readonly AnyNode[], resolution: number): Segment[];
|
|
5
|
+
//# sourceMappingURL=curves.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curves.d.ts","sourceRoot":"","sources":["../../src/advanced/curves.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAe,MAAM,eAAe,CAAC;AAG/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAgB7C,sFAAsF;AACtF,wBAAgB,aAAa,CAC3B,OAAO,EAAE,GAAG,EACZ,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,UAAU,EAAE,MAAM,GACjB,OAAO,EAAE,CAuCX"}
|