@embedpdf/core-annotation 3.0.0-next.14 → 3.0.0-next.16

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/dist/index.d.cts CHANGED
@@ -1,157 +1,391 @@
1
- import { AnnotationDTO, AnnotationFlags, AnnotationFlags as AnnotationFlags$1, AnnotationRef, BlendMode, BlendMode as BlendMode$1, CaretIntent, InkIntent, LineDimensionCaption, LineEnding, LineEndings, LineLeader, NO_ANNOTATION_FLAGS, PdfLinkTarget, PdfMeasurement, PdfPoint, PdfRect, RichTextDocumentInput, RichTextParagraph, RichTextRunStyle, ShapeDimensionCaption, StrikeoutIntent } from "@embedpdf/engine-core/runtime";
2
- import { PageObjectNumber } from "@embedpdf/core";
3
- import { PageRotation, Point, Rect as Rect$1, Size, TextQuad, TextQuad as TextQuad$1 } from "@embedpdf/core-geometry";
4
- //#region src/flags.d.ts
5
- /** The `/F` every freshly DRAWN annotation starts with: `print` set (Acrobat
6
- * parity — without it the annotation silently disappears when printed). */
7
- declare const DRAWN_FLAGS: AnnotationFlags;
8
- /** All ten flags, for iteration/equality — kept in the primitives' order. */
9
- declare const FLAG_KEYS: ReadonlyArray<keyof AnnotationFlags>;
10
- declare const flagsEqual: (a: AnnotationFlags, b: AnnotationFlags) => boolean;
11
- /** Merge a partial write over the current flags. */
12
- declare const mergeFlags: (base: AnnotationFlags, patch: Partial<AnnotationFlags>) => AnnotationFlags;
1
+ import { PageRotation, Point, Point as Point$1, Quad, Quad as Quad$1, QuadRing, Rect as Rect$1, Size } from "@embedpdf/core-geometry";
2
+ import { Annotation, AnnotationBorderStyle, AnnotationDraft, AnnotationFlags, AnnotationFlags as AnnotationFlags$1, AnnotationPatch, AnnotationRef, AnnotationSubtype, BlendMode, BlendMode as BlendMode$1, Color, FaceRequest, FileAttachmentIcon, FontHandle, InkIntent, LineEnding, LineEnding as LineEnding$1, LineEndings, LineEndings as LineEndings$1, NO_ANNOTATION_FLAGS, NoteIcon, PageRef, PdfLinkTarget, PdfLinkTargetWritable, RichTextBody, RichTextDocument, RichTextDocumentInput, RichTextParagraph, RichTextRunStyle, STANDARD_FACES, WidgetAppearance } from "@embedpdf/engine-core/runtime";
3
+ //#region src/editable.d.ts
4
+ /** The intents a measurement goes by in {@link EditableKinds}. */
5
+ type MeasurementIntent = 'line-dimension' | 'polyline-dimension' | 'polygon-dimension';
6
+ /** What {@link EditableKinds} names an annotation by: {@link editableKindOf}. */
7
+ type EditableKind = AnnotationSubtype | MeasurementIntent;
8
+ /**
9
+ * Which annotations people may change by hand: `true`, every one; `false`,
10
+ * none; a list, the annotations of the kinds it names, each by its subtype,
11
+ * or a measurement by its intent (so measurements and the shapes they're
12
+ * drawn as are told apart).
13
+ */
14
+ type EditableKinds = boolean | readonly EditableKind[];
15
+ /** The name {@link EditableKinds} knows an annotation by: a measurement's intent, else its subtype. */
16
+ declare function editableKindOf(annotation: Annotation): EditableKind;
17
+ /** Whether the session's rule lets people change this annotation by hand. */
18
+ declare function isEditableByHand(model: Pick<Session, 'editable'>, annotation: Annotation): boolean;
19
+ /** Whether people may move, resize, turn, reshape or restyle the record by hand now. */
20
+ declare const isTransformableByHand: (model: Pick<Session, "editable">, record: ModelAnnotation) => boolean;
21
+ /** Whether people may delete the record by hand now. */
22
+ declare const isDeletableByHand: (model: Pick<Session, "editable">, record: ModelAnnotation) => boolean;
23
+ /** Whether people may edit the record's text in place now. */
24
+ declare const isContentsEditableByHand: (model: Pick<Session, "editable">, record: ModelAnnotation) => boolean;
25
+ //#endregion
26
+ //#region src/painted.d.ts
27
+ /** One piece of what an annotation paints, in page space. */
28
+ type PaintedPiece =
29
+ /** Ink along a path, `halfWidth` either side of it; `closed` runs it back to the first point. */
30
+ {
31
+ readonly kind: 'stroke';
32
+ readonly points: readonly Point[];
33
+ readonly closed: boolean;
34
+ readonly halfWidth: number;
35
+ } |
36
+ /** A filled polygon (even-odd). A margin doesn't grow it: ink around it does. */
37
+ {
38
+ readonly kind: 'area';
39
+ readonly ring: readonly Point[];
40
+ } |
13
41
  /**
14
- * On-screen visibility. `hidden` beats everything; `noView` hides unless
15
- * `toggleNoView` and the annotation is ENGAGED — the spec says hover/selection,
16
- * and v1 uses selection (the model tracks no hover).
42
+ * An ellipse turned `rotation` degrees clockwise about `center`: its ink,
43
+ * `halfWidth` either side of it, and its inside when `filled`. The ink's
44
+ * width is measured in radii along the smaller radius, so a long ellipse's
45
+ * ink is a little wider at its ends.
17
46
  */
18
- declare const viewable: (f: AnnotationFlags, engaged?: boolean) => boolean;
19
- /** Any pointer interaction (click-to-select, hover). ReadOnly kills it. */
20
- declare const interactive: (f: AnnotationFlags) => boolean;
21
- /** The subset of an Annot these predicates read — keeps them testable bare. */
22
- interface FlagBearer {
23
- subtype: string;
24
- flags: AnnotationFlags;
25
- /** Session authority projected at ingest (permissions.md). Absent =
26
- * unstamped (drafts, wildcard local engines, tests) = allowed. */
27
- authority?: {
28
- update: boolean;
29
- delete: boolean;
30
- };
31
- }
32
- /** Interaction gate for a concrete annotation: widget kinds ignore `readOnly`
33
- * (ISO 32000 — a ReadOnly form field must still be movable by a form designer;
34
- * the form-filling layer enforces field ReadOnly itself). */
35
- declare const annotInteractive: (a: FlagBearer) => boolean;
47
+ {
48
+ readonly kind: 'oval';
49
+ readonly center: Point;
50
+ readonly rx: number;
51
+ readonly ry: number;
52
+ readonly rotation: number;
53
+ readonly halfWidth: number;
54
+ readonly filled: boolean;
55
+ };
36
56
  /**
37
- * Geometry/style mutations — `locked` freezes the OBJECT, not its contents
38
- * (that's `lockedContents`), AND the session must hold update authority over
39
- * the record. One predicate for hit-test, chrome, and props alike, so a
40
- * record you may not edit renders and behaves exactly like a locked one.
57
+ * A solid body: anything inside the ring, and anything within a margin of
58
+ * its edge (a rim of ink with no width). A text box, a caret and a
59
+ * measurement's caption are hit this way.
41
60
  */
42
- declare const annotTransformable: (a: FlagBearer) => boolean;
43
- /** Deletion — the delete half of the authority split (a narrowed grant can
44
- * allow update but not delete, or vice versa). Flags gate like transforms. */
45
- declare const annotDeletable: (a: FlagBearer) => boolean;
46
- /** `/Contents` text edits — the contents counterpart of `locked`. */
47
- declare const annotContentsEditable: (a: FlagBearer) => boolean;
61
+ declare const bodyPieces: (ring: readonly Point[]) => PaintedPiece[];
62
+ /** Is `point` within `margin` of what the pieces paint? */
63
+ declare const paintedNear: (pieces: readonly PaintedPiece[], point: Point, margin: number) => boolean;
64
+ /** Does the rect touch what the pieces paint: cross its ink, or lie on its fill? */
65
+ declare const paintedTouches: (pieces: readonly PaintedPiece[], rect: Rect) => boolean;
48
66
  //#endregion
49
67
  //#region src/measurement-shape.d.ts
50
- interface ShapeMeasurementAppearance {
51
- intent: 'PolyLineDimension' | 'PolygonDimension';
52
- measure: PdfMeasurement | null;
53
- caption: ShapeDimensionCaption;
54
- crop: PdfRect;
55
- text: string;
56
- }
68
+ type ShapeAnnotation = Extract<Annotation, {
69
+ subtype: 'polygon' | 'polyline';
70
+ }>;
71
+ /**
72
+ * A perimeter's or area's measurement: the engine's own fields for its scale,
73
+ * whether its caption shows, and its label (`contents`, which the engine
74
+ * works out). Where the caption sits is the shape's `captionCenter`.
75
+ */
76
+ type ShapeMeasurementAppearance = {
77
+ intent: 'polyline-dimension' | 'polygon-dimension';
78
+ } & Pick<ShapeAnnotation, 'measure' | 'captionEnabled' | 'contents'>;
57
79
  interface ShapeMeasurementLayout {
58
80
  caption: DistanceCaptionLayout | null;
59
81
  visualBounds: Rect;
60
- selectionPoints: Vec[];
82
+ selectionPoints: Point[];
61
83
  }
62
- /** The public caption center stays in PDF space; the editor draws in content space. */
63
- declare function shapeCaptionPoint(appearance: ShapeMeasurementAppearance): Vec | undefined;
64
- declare function withShapeCaptionPoint(appearance: ShapeMeasurementAppearance, center: Vec): ShapeMeasurementAppearance;
65
- declare function transformMeasurementCaption(appearance: MeasurementAppearance | undefined, transform: (point: Vec) => Vec): MeasurementAppearance | undefined;
66
- declare function shapeMeasurementReadout(geom: Geom, appearance: ShapeMeasurementAppearance): import("@embedpdf/engine-core/runtime").MeasurementReadout | import("@embedpdf/engine-core/runtime").MeasurementUnavailable;
67
- declare function shapeMeasurementLabel(geom: Geom, appearance: ShapeMeasurementAppearance): string;
84
+ declare function shapeMeasurementReadout(geometry: Shape, appearance: ShapeMeasurementAppearance): import("@embedpdf/engine-core/runtime").MeasurementReadout | import("@embedpdf/engine-core/runtime").MeasurementUnavailable;
85
+ declare function shapeMeasurementLabel(geometry: Shape, appearance: ShapeMeasurementAppearance): string;
68
86
  /** Match the native shape-caption anchor, including an interior fallback for concave areas. */
69
- declare function automaticShapeCaptionCenter(points: readonly Vec[], closed: boolean): Vec;
70
- declare function shapeMeasurementLayout(geom: Geom, appearance: ShapeMeasurementAppearance, style: Style): ShapeMeasurementLayout | null;
71
- declare function measurementLayout(geom: Geom, appearance: MeasurementAppearance, style: Style): ShapeMeasurementLayout | null;
72
- declare function measurementSelectionQuad(geom: Geom, appearance: MeasurementAppearance, style: Style): Quad;
73
- declare function moveMeasurementCaption(geom: Geom, appearance: MeasurementAppearance, delta: Vec, style: Style): MeasurementAppearance;
87
+ declare function automaticShapeCaptionCenter(points: readonly Point[], closed: boolean): Point;
88
+ declare function shapeMeasurementLayout(geometry: Shape, appearance: ShapeMeasurementAppearance, style: Style): ShapeMeasurementLayout | null;
89
+ declare function measurementLayout(geometry: Shape, appearance: MeasurementAppearance, style: Style): ShapeMeasurementLayout | null;
90
+ declare function measurementSelectionQuad(geometry: Shape, appearance: MeasurementAppearance, style: Style): QuadRing;
91
+ /**
92
+ * The caption dragged by `delta`: a distance's caption offset changes (its
93
+ * measurement), a perimeter's or area's caption center (its shape, kept
94
+ * upright with the vertices).
95
+ */
96
+ declare function moveMeasurementCaption(geometry: Shape, appearance: MeasurementAppearance, delta: Point, style: Style): {
97
+ geometry: Shape;
98
+ measure: MeasurementAppearance;
99
+ };
74
100
  //#endregion
75
101
  //#region src/measurement.d.ts
76
102
  type MeasurementAppearance = DistanceAppearance | ShapeMeasurementAppearance;
103
+ type LineAnnotation = Extract<Annotation, {
104
+ subtype: 'line';
105
+ }>;
77
106
  /**
78
- * A distance annotation's render projection. Offsets stay in directed PDF line
79
- * axes. The crop keeps numeric rounding in the original PDF coordinate frame.
107
+ * A distance's measurement: the engine's own fields for its scale, its
108
+ * caption (on or off, its position, and its offset in the line's own axes),
109
+ * its leader and its label (`contents`, which the engine works out).
80
110
  */
81
- interface DistanceAppearance {
82
- intent: 'LineDimension';
83
- measure: PdfMeasurement | null;
84
- caption: LineDimensionCaption;
85
- leader?: LineLeader;
86
- crop: PdfRect;
87
- text: string;
88
- }
111
+ type DistanceAppearance = {
112
+ intent: 'line-dimension';
113
+ } & Pick<LineAnnotation, 'measure' | 'captionEnabled' | 'captionPosition' | 'captionOffset' | 'leader' | 'contents'>;
114
+ /**
115
+ * The annotation's measurement, when it is one: a line measuring a
116
+ * distance, a polyline a perimeter, a polygon an area, as its intent says.
117
+ * Its measurement fields, read off it for drawing and hit-testing.
118
+ */
119
+ declare function measurementOf(annotation: Annotation): MeasurementAppearance | undefined;
120
+ /**
121
+ * What a new measurement states beside its points: its intent, its scale
122
+ * (one the engine can write), its caption and leader as it has them, and its
123
+ * subject. Its label is the engine's: it works it out from the points and
124
+ * the scale.
125
+ */
126
+ declare function measurementDraftFields(appearance: MeasurementAppearance): FieldValues;
89
127
  interface DistanceSegment {
90
- from: Vec;
91
- to: Vec;
128
+ from: Point;
129
+ to: Point;
92
130
  }
93
131
  interface DistanceCaptionLayout {
94
132
  text: string;
95
- center: Vec;
96
- along: Vec;
97
- normal: Vec;
133
+ center: Point;
134
+ along: Point;
135
+ normal: Point;
98
136
  width: number;
99
137
  height: number;
100
- bounds: Quad;
138
+ bounds: QuadRing;
101
139
  }
102
140
  /** One layout drives the preview, selection, handles, and hit testing. */
103
141
  interface DistanceLayout {
104
- along: Vec;
105
- normal: Vec;
142
+ along: Point;
143
+ normal: Point;
106
144
  length: number;
107
- measuredStart: Vec;
108
- measuredEnd: Vec;
109
- dimensionStart: Vec;
110
- dimensionEnd: Vec;
145
+ measuredStart: Point;
146
+ measuredEnd: Point;
147
+ dimensionStart: Point;
148
+ dimensionEnd: Point;
111
149
  dimensionSegments: DistanceSegment[];
112
150
  leaderSegments: DistanceSegment[];
113
151
  captionConnector: DistanceSegment[];
114
152
  caption: DistanceCaptionLayout | null;
115
153
  arrowPlacement: 'inside' | 'outside';
116
154
  endings: RenderNode[];
117
- selectionPoints: Vec[];
155
+ selectionPoints: Point[];
118
156
  visualBounds: Rect;
119
157
  }
120
- declare function distanceLabel(geom: Geom, appearance: DistanceAppearance): string;
121
- declare function distanceLayout(geom: Geom, appearance: DistanceAppearance, strokeWidth: number): DistanceLayout | null;
158
+ declare function distanceLabel(geometry: Shape, appearance: DistanceAppearance): string;
159
+ declare function distanceLayout(geometry: Shape, appearance: DistanceAppearance, strokeWidth: number): DistanceLayout | null;
122
160
  declare function expandDistanceBounds(bounds: Rect, padding: number): Rect;
123
- declare function distanceSelectionQuad(geom: Geom, appearance: DistanceAppearance, strokeWidth: number): Quad;
161
+ declare function distanceSelectionQuad(geometry: Shape, appearance: DistanceAppearance, strokeWidth: number): QuadRing;
124
162
  declare function distanceHandles(layout: DistanceLayout): Handle[];
125
163
  declare function distanceCaptionHit(layout: {
126
164
  caption: DistanceCaptionLayout | null;
127
- }, point: Vec, margin?: number): boolean;
128
- declare function distanceHit(layout: DistanceLayout, point: Vec, strokeWidth: number, margin: number): boolean;
129
- declare function distanceCaptionAt(geom: Geom, appearance: DistanceAppearance, width: number): Vec | null;
130
- declare function distanceLeaderLength(geom: Geom, point: Vec): number;
131
- declare function moveDistanceCaption(geom: Geom, appearance: DistanceAppearance, delta: Vec): DistanceAppearance;
132
- declare function distanceScene(geom: Geom, appearance: DistanceAppearance, style: Style): SceneNode[];
165
+ }, point: Point, margin?: number): boolean;
166
+ /** What a measurement's caption paints: its box, grabbed anywhere in it and near its edge. */
167
+ declare const captionPainted: (caption: DistanceCaptionLayout | null) => PaintedPiece[];
168
+ /**
169
+ * What a distance measurement paints: its caption, the ink of its dimension
170
+ * line, leaders and caption connector, and its endings.
171
+ */
172
+ declare function distancePainted(layout: DistanceLayout, strokeWidth: number): PaintedPiece[];
173
+ declare function distanceCaptionAt(geometry: Shape, appearance: DistanceAppearance, width: number): Point | null;
174
+ declare function distanceLeaderLength(geometry: Shape, point: Point): number;
175
+ declare function moveDistanceCaption(geometry: Shape, appearance: DistanceAppearance, delta: Point): DistanceAppearance;
176
+ /**
177
+ * The upright box around all a distance paints: its dimension line, leader
178
+ * and connector lines and its endings stroked at `strokeWidth` (what
179
+ * `distanceScene` draws), and its caption's box.
180
+ */
181
+ declare function distanceDrawnBounds(layout: DistanceLayout, strokeWidth: number): Rect;
182
+ declare function distanceScene(geometry: Shape, appearance: DistanceAppearance, style: Style): SceneNode[];
133
183
  declare function measurementCaptionScene(caption: DistanceCaptionLayout | null, style: Style): SceneNode[];
134
184
  //#endregion
135
- //#region src/types.d.ts
185
+ //#region src/shapes/family.d.ts
186
+ /** A box's four corners as the page shows them: nw, ne, se, sw. */
187
+ type Corners = [Point, Point, Point, Point];
188
+ interface ShapeFamily<S extends Shape = Shape> {
189
+ /** The shape, read off an annotation of a kind in this family. */
190
+ read(annotation: Annotation): S;
191
+ /** The engine fields that state `shape` on an annotation of `subtype`. */
192
+ write(shape: S, subtype: string): FieldValues;
193
+ /**
194
+ * The shape a create gesture makes at `placement` (a clicked or dragged box
195
+ * or segment, `placement.ts`) for `annotation`, what the tool creates before
196
+ * it has a shape; `null` when this family's kinds aren't made that way.
197
+ */
198
+ placed(placement: Placement, annotation: Annotation): S | null;
199
+ /** The box around its box or points, the stroke left out. */
200
+ bounds(shape: S): Rect;
201
+ /**
202
+ * The box the live drawing is painted into (the stroke, endings, a cloud's
203
+ * bumps): a box's or caret's own, before its turn (the renderer turns it),
204
+ * the page's for the rest.
205
+ */
206
+ drawnBounds(shape: S, stroke: Stroke): Rect;
207
+ /** The upright page box around all it paints: the engine's `rect`, as it measures its appearance. */
208
+ rect(shape: S, stroke: Stroke): Rect;
209
+ /** The box a selection wraps: where its handles sit, and where a selected shape is grabbed. */
210
+ selectionBounds(shape: S, stroke: Stroke): Rect;
211
+ /** Does the shape have a turned box of its own, which a selection outlines turned? */
212
+ oriented(shape: S): boolean;
213
+ /** That turned box's corners, or `null` when the shape has none. */
214
+ turnedCorners(shape: S, stroke: Stroke): Corners | null;
215
+ /** Where a turn of the shape pivots, as the engine turns it. */
216
+ pivot(shape: S): Point;
217
+ /** The shape moved by `delta`. */
218
+ translate(shape: S, delta: Point): S;
219
+ /** The shape turned `degrees` clockwise about `pivot`; unchanged when it doesn't turn. */
220
+ rotateAbout(shape: S, pivot: Point, degrees: number): S;
221
+ /**
222
+ * The shape scaled about `anchor` by `(sx, sy)`, exactly. A resize passes
223
+ * its limit, `minSize`: a box then ends up no smaller than that.
224
+ */
225
+ scaleAbout(shape: S, anchor: Point, sx: number, sy: number, minSize?: number): S;
226
+ /** The shape with its turn cleared, turned back about `pivot` where that matters. */
227
+ upright(shape: S, pivot?: Point): S;
228
+ /** Its handles: resize corners and sides, or vertices. */
229
+ handles(shape: S): Handle[];
230
+ /** The shape with `handle` dragged to `to`. */
231
+ drag(shape: S, handle: string, to: Point): S;
232
+ /**
233
+ * What it paints (`painted.ts`): its ink, and its inside when `filled`. A
234
+ * click and the marquee both read these pieces.
235
+ */
236
+ painted(shape: S, stroke: Stroke, filled: boolean): PaintedPiece[];
237
+ /** What it draws, for the live (vector) view. */
238
+ scene(shape: S, stroke: Stroke): RenderNode[];
239
+ }
240
+ //#endregion
241
+ //#region src/shapes/box.d.ts
242
+ /** A box and its turn, as the engine keeps them. */
243
+ interface TurnedBox {
244
+ /** The box before any turn: the engine's `box`, or its `rect` for a kind that never turns. */
245
+ box: Rect;
246
+ /** Degrees clockwise about the middle of `box`; 0 when upright. */
247
+ rotation: number;
248
+ }
249
+ /** A box family record's shape: the engine's box and its turn. */
250
+ interface BoxShape extends TurnedBox {
251
+ kind: 'box';
252
+ /** Drawn as the ellipse in `box`: a circle. */
253
+ ellipse: boolean;
254
+ }
255
+ /** The box family: a box and its turn, drawn as a rectangle, an ellipse or a cloud. */
256
+ declare const boxFamily: ShapeFamily<BoxShape>;
257
+ //#endregion
258
+ //#region src/shapes/caret.d.ts
259
+ /** A caret record's shape: the engine's box and its turn. */
260
+ interface CaretShape extends TurnedBox {
261
+ kind: 'caret';
262
+ }
263
+ /**
264
+ * The caret box for a text-edit anchor, upright: half the glyph's ink height,
265
+ * centred on the trailing baseline corner in reading direction (`advance`
266
+ * decides which end, so a right-to-left caret lands on the visual left),
267
+ * sitting on the baseline. {@link caretFromAnchor} gives the turned caret.
268
+ */
269
+ declare function caretRectFromAnchor(anchor: TextEndAnchor): Rect;
270
+ /**
271
+ * The caret for a text-edit anchor: a box whose middle sits half a caret size
272
+ * toward the ascent from the trailing baseline corner, turned by the text's
273
+ * baseline tilt (degrees clockwise), so the mark hugs the turned baseline and
274
+ * points at its text. For upright text this is exactly
275
+ * {@link caretRectFromAnchor} with no turn.
276
+ */
277
+ declare function caretFromAnchor(anchor: TextEndAnchor): CaretShape;
136
278
  /**
137
- * Content-space point/rect (y-down, PDF points, crop-relative) — the viewer space.
138
- * These are the shared `@embedpdf/core-geometry` primitives: a `Vec` IS a `Point`
139
- * and `Rect` IS geometry's `Rect`, so the whole stack speaks one vocabulary and
140
- * the coordinate math lives in ONE package.
279
+ * The caret family: a mark in a turned box that follows its text. It moves
280
+ * and scales with a selection, but no gesture turns, resizes or drags it.
141
281
  */
142
- type Vec = Point;
282
+ declare const caretFamily: ShapeFamily<CaretShape>;
283
+ //#endregion
284
+ //#region src/shapes/points.d.ts
285
+ /** A line: its two ends upright, and their endings. */
286
+ interface LineShape {
287
+ kind: 'line';
288
+ linePoints: {
289
+ start: Point;
290
+ end: Point;
291
+ };
292
+ lineEndings?: LineEndings$1;
293
+ /** Degrees clockwise about the middle of the box around the upright points; 0 when upright. */
294
+ rotation: number;
295
+ }
296
+ /** A polygon (`closed`) or polyline: its vertices upright, and a polyline's endings. */
297
+ interface PolyShape {
298
+ kind: 'poly';
299
+ vertices: Point[];
300
+ closed: boolean;
301
+ lineEndings?: LineEndings$1;
302
+ /** A measurement's caption center, upright with the vertices; `null` places it automatically. */
303
+ captionCenter?: Point | null;
304
+ /** Degrees clockwise about the middle of the box around the upright points; 0 when upright. */
305
+ rotation: number;
306
+ }
307
+ /** Ink: its strokes upright. */
308
+ interface InkShape {
309
+ kind: 'ink';
310
+ inkList: Point[][];
311
+ /** Degrees clockwise about the middle of the box around the upright points; 0 when upright. */
312
+ rotation: number;
313
+ }
314
+ /** A points family record's shape. */
315
+ type PointsShape = LineShape | PolyShape | InkShape;
316
+ /** A line's or polyline's endings; none for an annotation that has none. */
317
+ declare const lineEndingsOf: (annotation: Annotation) => LineEndings$1;
318
+ /** Where the points are drawn: the upright ones turned by `rotation`. The same lists for the same shape. */
319
+ declare function drawnStrokesOf(shape: PointsShape): Point[][];
320
+ /** Miter limit shared by the bounds math and the SVG renderer (`stroke-miterlimit`),
321
+ * so the computed box and the drawn stroke always agree on where a sharp join
322
+ * bevels instead of spiking. 10 = the PDF default (also what the baked /AP uses). */
323
+ declare const MITER_LIMIT = 10;
324
+ /** The points family: points kept upright and the turn that draws them. */
325
+ declare const pointsFamily: ShapeFamily<PointsShape>;
326
+ //#endregion
327
+ //#region src/shapes/quads.d.ts
328
+ /** A quads family record's shape: its text's quads. */
329
+ interface QuadsShape {
330
+ kind: 'quads';
331
+ quadPoints: Quad$1[];
332
+ }
333
+ /**
334
+ * The quads family: marks bound to their text. They move with a selection,
335
+ * but no gesture turns, scales or drags them, and they have no turned box.
336
+ */
337
+ declare const quadsFamily: ShapeFamily<QuadsShape>;
338
+ //#endregion
339
+ //#region src/shapes/text-box.d.ts
340
+ type FreeTextAnnotation = Extract<Annotation, {
341
+ subtype: 'free-text';
342
+ }>;
343
+ /** A callout's line: its tip, its knee when it bends, and where it meets the box. */
344
+ type CalloutLine = NonNullable<FreeTextAnnotation['calloutLine']>;
345
+ /** A free text's shape: the engine's box, turn, and a callout's line. */
346
+ interface TextBoxShape extends TurnedBox {
347
+ kind: 'text-box';
348
+ /** A callout's line; `null` for a plain text box. */
349
+ calloutLine: CalloutLine | null;
350
+ /** The ending at a callout's tip. */
351
+ lineEnding: LineEnding$1 | null;
352
+ }
353
+ /**
354
+ * Where a callout's line meets its box: the middle of the side `toward` (the
355
+ * knee, else the tip) lies beyond, chosen by which way it is farther from the
356
+ * box's middle, across or up and down. A turned box decides in its own frame,
357
+ * and the point lands on the turned side, the one the page shows. The
358
+ * engine's rule, in page space.
359
+ */
360
+ declare function calloutEnd(box: Rect, toward: Point, rotation?: number): Point;
361
+ /**
362
+ * The text box family: a free text's box and turn, and a callout's line.
363
+ * Only a plain box turns; a callout's box keeps the turn it was made with,
364
+ * and a selection outlines everything it paints, upright.
365
+ */
366
+ declare const textBoxFamily: ShapeFamily<TextBoxShape>;
367
+ /**
368
+ * The text plate inset of a free-text box: twice the border width. The plate
369
+ * — where text lays out, clips and scrolls — is the box deflated by this on
370
+ * every side: the ink band and an equal breathing band, so the text never
371
+ * touches the stroke. Acrobat's rule, measured 1–12 pt on plain boxes and
372
+ * 1–7 pt on callouts; the engine's `FreeTextPlate` is the same formula, so
373
+ * the live editor sits exactly where the baked text lands. Acrobat's thinnest
374
+ * border is 1 pt; a width of 0 is ours alone and gives no inset (the plate is
375
+ * the box).
376
+ */
377
+ declare function textPlateInset(strokeWidth: number): number;
378
+ //#endregion
379
+ //#region src/types.d.ts
143
380
  type Rect = Rect$1;
144
- /** Four POSITIONAL content-space points — selection chrome / OBB corners.
145
- * Text-markup geometry uses the corner-NAMED {@link TextQuad} instead. */
146
- type Quad = [Vec, Vec, Vec, Vec];
147
381
  /**
148
382
  * Where a text-edit annotation (caret / replace-text) anchors: the boundary
149
- * glyph's oriented cell plus the READING direction along its baseline
150
- * (+1 = toward `end`, −1 = toward `start` — sequence-derived, never inferred
151
- * from geometry).
383
+ * glyph's oriented cell plus the reading direction along its baseline
384
+ * (+1 = toward the right end, −1 = toward the left end — sequence-derived,
385
+ * never inferred from geometry).
152
386
  */
153
387
  interface TextEndAnchor {
154
- glyphQuad: TextQuad$1;
388
+ glyphQuad: Quad$1;
155
389
  advance: 1 | -1;
156
390
  }
157
391
  type Id = string;
@@ -159,128 +393,80 @@ type Cursor = string;
159
393
  type PolySubtype = 'polygon' | 'polyline';
160
394
  /**
161
395
  * The per-page view environment the screen-anchor projection consumes:
162
- * `zoom` — the page's zoom RELATIVE to its 100% baseline (dimensionless;
396
+ * `zoom` — the page's zoom relative to its 100% baseline (dimensionless;
163
397
  * 1 = Acrobat's 100% = `viewUnitsPerPoint × userUnit` view px per point) —
164
- * and the TOTAL display rotation (document /Rotate + view rotation).
165
- * Deliberately NOT a px-per-point scale: `noZoom` means "hold the body at its
166
- * 100%-zoom size", a statement about zoom, so feeding it a units conversion
167
- * (the old mistake) shrank bodies at 100%. Per-event / per-call context (the
398
+ * and the total display rotation (document /Rotate + view rotation).
399
+ * Deliberately not a px-per-point scale: `noZoom` means "hold the body at its
400
+ * 100%-zoom size", a statement about zoom; a units conversion here would
401
+ * shrink bodies at 100%. Per-event / per-call context (the
168
402
  * `pageBox` pattern) — never stored on the model; the pointer drafts capture
169
- * it at DOWN so anchored gestures project and commit with the view they
403
+ * it at down so anchored gestures project and commit with the view they
170
404
  * started under. See anchor.ts.
171
405
  */
172
406
  interface ViewEnv {
173
407
  zoom: number;
174
408
  rotation: PageRotation;
175
409
  }
176
- type Subtype = 'highlight' | 'underline' | 'squiggly' | 'strikeout' | 'square' | 'circle' | 'line' | 'polygon' | 'polyline' | 'caret' | 'ink' | 'stamp' | 'text' | 'file-attachment' | 'redact' | 'link' | (string & {});
177
410
  /**
178
- * Content-space geometry — the ONE thing hit-testing, editing, and rendering work
179
- * on. A small closed union covers every kind: shapes (rect/ellipse), line,
180
- * polygon/polyline (poly), text markup (quads), and caret.
411
+ * An annotation kind's name (`kinds/`): its PDF subtype, a widget's field
412
+ * family (`widget-text`…), or a free text's callout (`free-text-callout`).
181
413
  */
414
+ type KindName = 'highlight' | 'underline' | 'squiggly' | 'strikeout' | 'square' | 'circle' | 'line' | 'polygon' | 'polyline' | 'caret' | 'ink' | 'stamp' | 'text' | 'file-attachment' | 'redact' | 'link' | (string & {});
182
415
  /**
183
- * A free-text callout's leader: the `/CL` line + `/LE` arrow. `tip` is the
184
- * called-out point (the arrow is drawn here); `knee` is the optional elbow. The
185
- * point where the leader meets the text box (the third `/CL` point) is NEVER
186
- * stored — it is DERIVED from the box + the knee (see `calloutConnection`), so it
187
- * can't drift when the box or knee moves. Content space (y-down).
188
- */
189
- interface Callout {
190
- tip: Vec;
191
- knee?: Vec;
192
- ending: LineEnding;
193
- }
194
- /**
195
- * Rotation (degrees, clockwise in content space, normalized `[0,360)`) carried by
196
- * the rotatable `Geom` variants. The semantics differ by family, which is exactly
197
- * the box-vs-vertex split:
416
+ * An annotation's shape: what hit-testing, gestures and drawing work on, read
417
+ * off the annotation by its kind's family (`shapeOf`). One arm per family,
418
+ * each the engine's own fields
419
+ * ({@link BoxShape}, {@link TextBoxShape}, {@link PointsShape},
420
+ * {@link QuadsShape}, {@link CaretShape}).
198
421
  *
199
- * - **Box** (`rect`, `text`): `rect` is the UNROTATED local box and `rot` is the
200
- * applied tilt — together they reconstruct the visual. The repository emits
201
- * `rect` as `unrotatedRect`, `rot` as the rendered `/EMBD_Metadata/Rotation`,
202
- * and the rotated visual AABB as `/Rect`, so PDFium bakes a portable `/AP`.
203
- * - **Vertex** (`line`, `poly`, `ink`): the points are ALREADY rotated (they are
204
- * the portable visual), so `rot` is an ADVISORY scalar — the cumulative tilt the
205
- * user applied since authoring. It lets EmbedPDF reconstruct an oriented
206
- * selection box (`obbFromTheta`) and offer reset-to-0; it is inert for
207
- * rendering (PDFium ignores a lone `Rotation` with no `UnrotatedRect`).
208
- */
209
- type Geom = {
210
- t: 'rect';
211
- rect: Rect;
212
- ellipse: boolean;
213
- rot?: number;
214
- } | {
215
- t: 'line';
216
- a: Vec;
217
- b: Vec;
218
- ends?: LineEndings;
219
- rot?: number;
220
- } | {
221
- t: 'poly';
222
- points: Vec[];
223
- closed: boolean;
224
- ends?: LineEndings;
225
- rot?: number;
226
- } | {
227
- t: 'quads';
228
- quads: TextQuad$1[];
229
- } | {
230
- t: 'caret';
231
- rect: Rect;
232
- rot?: number;
233
- } | {
234
- t: 'ink';
235
- strokes: Vec[][];
236
- rot?: number;
237
- } | {
238
- t: 'text';
239
- rect: Rect;
240
- callout?: Callout;
241
- rot?: number;
242
- };
422
+ * Rotation is degrees clockwise in page space, normalized `[0,360)`, and
423
+ * works the same way for every family that turns: the shape is kept upright
424
+ * (a box, or points), and `rotation` turns it about its middle. The engine
425
+ * works out `/Rect` around the turned drawing, so PDFium bakes a portable `/AP`.
426
+ */
427
+ type Shape = BoxShape | PointsShape | QuadsShape | CaretShape | TextBoxShape;
243
428
  /**
244
- * How a shape's outline is stroked. A discriminated union so illegal combinations
245
- * — a dash array on a cloudy border, an intensity on a dashed one — are simply
246
- * unrepresentable. Maps onto the engine's `/BS /S` (`borderStyle`), `/BS /D`
247
- * (`dashArray`), and `/BE /I` (`cloudyIntensity`) wire fields. Cloudy is only
248
- * honoured for shapes (square/circle); other kinds treat it as solid.
429
+ * How an annotation is drawn, by the engine's field names: its colours, its
430
+ * stroke and its border, with its kind's fill-ins (a highlight strokes
431
+ * nothing, a caret draws 1 pt). A view its kind works out from the
432
+ * annotation (`styleOf`, `kinds/styles.ts`) for drawing, bounding and
433
+ * hit-testing: nothing is written back through it.
249
434
  */
250
- type Border = {
251
- kind: 'solid';
252
- } | {
253
- kind: 'dashed';
254
- dash: number[];
255
- } | {
256
- kind: 'cloudy';
257
- intensity: number;
258
- };
259
435
  interface Style {
260
- /** `/C` colour — stroke for geometric kinds, highlight colour for markup. */
261
- color: string;
262
- /** `/IC` interior (fill) colour. `null` when the annotation has no fill. */
263
- interiorColor: string | null;
436
+ /** The stroke for a drawn kind, a markup's colour, a free text's border. */
437
+ color: Color;
438
+ /** The fill; `null` when the annotation has none. */
439
+ interiorColor: Color | null;
264
440
  strokeWidth: number;
265
441
  opacity: number;
266
- /** Effective blend mode of the annotation's normal appearance. */
267
442
  blendMode: BlendMode$1;
268
- /** Outline style — defaults to `{ kind: 'solid' }`. */
269
- border: Border;
443
+ borderStyle: AnnotationBorderStyle;
444
+ /** A dashed border's pattern; `null` draws the engine's default dash. */
445
+ dashArray: number[] | null;
446
+ /** A cloudy border's intensity; `null` without a cloud. A cloud wins over a dash. */
447
+ cloudyIntensity: number | null;
448
+ }
449
+ /**
450
+ * What a shape's bounds, hit test and drawing depend on besides the shape:
451
+ * how wide its stroke is, and a cloud's intensity. A {@link Style} is one.
452
+ */
453
+ interface Stroke {
454
+ strokeWidth: number;
455
+ cloudyIntensity?: number | null;
270
456
  }
271
457
  type TextAlign = 'left' | 'center' | 'right';
272
458
  /**
273
- * Content-space text styling for a text-editable kind (free text) — the text
274
- * counterpart of {@link Style}, projected from the DTO's `/DA` fields the same
275
- * way `style` is projected from `/C`/`/CA`/`/BS`. CSS colour string; the engine
276
- * `Color` seam is crossed only in the plugin repository.
459
+ * How an annotation's text is set, by the engine's field names: its font,
460
+ * size, colour and alignment, and a free text's bold, italic and underline.
461
+ * A view its kind works out from the annotation (`textOf`,
462
+ * `kinds/texts.ts`): nothing is written back through it.
277
463
  */
278
464
  interface TextStyle {
279
465
  /** A PDF standard font name or a registered font key. */
280
466
  fontFamily: string;
281
- /** Content units (PDF points). */
467
+ /** Page units (PDF points); 0 fits a form field's or a redaction label's box. */
282
468
  fontSize: number;
283
- fontColor: string;
469
+ fontColor: Color;
284
470
  textAlign: TextAlign;
285
471
  /**
286
472
  * The rich body's formatting (free text only): bold = body weight ≥ 600,
@@ -293,86 +479,40 @@ interface TextStyle {
293
479
  underline?: boolean;
294
480
  }
295
481
  /**
296
- * The ONE flat vocabulary for editable appearance properties — what property
297
- * sidebars/toolbars read and write, regardless of where each property is stored
298
- * internally (`style`, `geom.ends`, or `text`). Which keys apply to a kind, and
299
- * in what UI order, is declared per kind in the KIND table (`propsFor`).
482
+ * Engine fields by name, as a create or an update states them: a tool's
483
+ * defaults (a draft without its shape), or what a selection edit writes to
484
+ * each member. The kind table's {@link AnnotationProperty} lists say which a style panel edits.
485
+ */
486
+ type FieldValues = Readonly<Record<string, unknown>>;
487
+ /**
488
+ * One annotation as the core works on it: the engine's record, and how it is
489
+ * drawn right now. The record is the only data: its ref, page, kind and
490
+ * relationships are read off it (`refOf`, `kindOf`, `irtOf`, `groupOf`), so
491
+ * is its shape (`shapeOf`), and an edit writes engine fields back
492
+ * (`withShape`, `withValues`; record/).
300
493
  */
301
- interface AnnotationProps extends Style, TextStyle {
302
- /** `/LE` endings (line / polyline). */
303
- lineEndings: LineEndings;
494
+ interface ModelAnnotation {
304
495
  /**
305
- * `/Name` icon of an icon kind (text note, file attachment). Optional in
306
- * the bag — each kind falls back to its own spec default ('comment',
307
- * 'paperclip') at creation, so one flat vocabulary needs no per-kind base
308
- * value. Valid values are the `options` of the kind's `icon` PropSpec.
496
+ * The record's key: `annotationKey(annotation.ref)`, for life. A record
497
+ * this session creates has its final ref from the start: the object number
498
+ * its create names.
309
499
  */
310
- icon?: string;
500
+ id: Id;
311
501
  /**
312
- * Where this annotation LINKS to, or `null` for none. Optional in the bag
313
- * like `icon`. One key, two storages (the props.ts routing rule): on the
314
- * link kind it is the annotation's OWN target (`/A`); on every other
315
- * linkable kind it is the target of the ATTACHED link child(ren) —
316
- * grouped `/Link` annotations the model folds into their parent (the
317
- * serialization the PDF spec forces, since only Link annotations are
318
- * clickable). Writing it creates/retargets/deletes those children through
319
- * the ONE `syncLink` seam.
502
+ * A record this session created that the engine hasn't written yet: it
503
+ * has its ref, but no raster of its appearance.
320
504
  */
321
- link?: PdfLinkTarget | null;
322
- }
323
- type PropKey = keyof AnnotationProps;
324
- /**
325
- * What a committed edit CHANGED — carried on the `patch` effect so the shell
326
- * emits exactly that intent (repository `toScopedPatch`) instead of
327
- * reconstructing a full projection. `geometry` covers every gesture commit
328
- * (move/resize/rotate/vertex edit); `props` forwards the user's patch keys
329
- * verbatim. Text content never rides this effect (the debounced text-edit
330
- * write owns `contents`).
331
- */
332
- type PatchScope = {
333
- kind: 'geometry';
334
- } | {
335
- kind: 'caption';
336
- } | {
337
- kind: 'leader';
338
- } | {
339
- kind: 'props';
340
- keys: PropKey[];
341
- };
342
- /** A partial property write. `lineEndings` merges per side (set just `end`
343
- * without knowing `start`); every other key overwrites. */
344
- type AnnotationPropsPatch = { [K in PropKey]?: K extends 'lineEndings' ? Partial<LineEndings> : AnnotationProps[K]; };
345
- interface Annot {
346
- id: Id;
347
- ref: AnnotationRef | null;
348
- pon: PageObjectNumber;
349
- subtype: Subtype;
350
- geom: Geom;
351
- style: Style;
352
- /** Text styling — present only for text-editable kinds (free text). Like
353
- * `style`, a content-space projection of `data`, editable via `setProps`. */
354
- text?: TextStyle;
355
- /** Redaction label (`/OverlayText` + `/Repeat`) — redact kind only. A
356
- * projection of `data` like `text`; the hover preview scene draws it. */
357
- label?: {
358
- text: string;
359
- repeat: boolean;
360
- };
361
- measure?: MeasurementAppearance;
362
- /** `/Name` icon — present only for icon kinds (text note, file attachment).
363
- * Like `style`, a projection of `data`, editable via `setProps`. */
364
- icon?: string;
505
+ unconfirmed?: true;
365
506
  /**
366
- * The `/F` annotation flags, verbatim from the DTO (freshly drawn annotations
367
- * start at {@link DRAWN_FLAGS} — `print` set). NEVER read individual keys to
368
- * gate behavior — the predicates in `flags.ts` (`annotInteractive`,
369
- * `annotTransformable`, `annotContentsEditable`, `viewable`) are the one
370
- * interpretation of the spec, and `anchorModeOf` owns `noZoom`/`noRotate`.
507
+ * How the record renders: `baked` blits the engine's appearance raster,
508
+ * `vector` draws it live from its geometry and style. The core sets
509
+ * `vector` when an edit makes the raster stale (a resize, a restyle,
510
+ * typing); a move keeps the raster. The plugin decides it for confirmed
511
+ * records: this session's edits stay live, another session's are baked.
371
512
  */
372
- flags: AnnotationFlags$1;
373
513
  source: 'baked' | 'vector';
374
514
  /**
375
- * Content-space box of the engine appearance raster (the AP `/Rect`), set when
515
+ * Page-space box of the engine appearance raster (the AP `/Rect`), set when
376
516
  * the annotation is derived from a DTO. While `source === 'baked'` the renderer
377
517
  * blits the engine bitmap into this box; a move translates it (a rigid shift
378
518
  * keeps the raster valid), so the bitmap rides along without re-rendering.
@@ -380,72 +520,58 @@ interface Annot {
380
520
  */
381
521
  apBox?: Rect;
382
522
  /**
383
- * Rotation (deg, CW) that was STRIPPED from the baked raster — present only
384
- * when the engine rendered this appearance rotation-free (a box-family kind
385
- * whose DTO carries BOTH `rotation` and `unrotatedRect`; `apBox` is then the
386
- * unrotated box). The blit re-applies it as a view transform about the box
387
- * centre. Vertex kinds pre-rotate their geometry, so this stays unset there
388
- * and their rasters blit untransformed.
523
+ * Rotation (deg, CW) that was stripped from the baked raster — present only
524
+ * when the engine rendered this appearance rotation-free (`appearanceTurnOf`:
525
+ * a box kind drawn turned, inside its turned box; `apBox` is then its `box`).
526
+ * The blit re-applies it as a view transform about the box centre. Vertex
527
+ * kinds pre-rotate their geometry, so this stays unset there and their
528
+ * rasters blit untransformed.
389
529
  */
390
530
  apRot?: number;
391
531
  /**
392
- * Revision of the engine-baked `/AP` CONTENT (absent ≡ 0). The raster a baked
532
+ * The version of the engine's picture of it (absent ≡ 0). The picture a baked
393
533
  * annotation shows depends on exactly this and the render scale — never on
394
534
  * position (`apBox` translates the blit) or rotation (`apRot` transforms it) —
395
- * so the shell re-fetches appearances precisely when it changes. Bumped by the
396
- * `upsert` that confirms an engine re-bake with new content: a geometry patch
397
- * that changed the authoring frame's SIZE resolving (`Effect.apChanged`), or a
398
- * remote edit folding in. A move or rotate leaves it untouched: the old raster
399
- * is still pixel-exact, so those cost zero re-renders.
535
+ * so the shell re-fetches pictures precisely when it changes. Owned by the
536
+ * plugin's confirmed records, which advance it when the engine reports a
537
+ * re-baked appearance; the core only reads it.
538
+ */
539
+ pictureVersion?: number;
540
+ /**
541
+ * A lent record's live drawing, from its lender (a form widget, drawn by the
542
+ * form's painter): the record drawn upright in `box`, in page space. It
543
+ * gives a kind with no live drawing of its own one (`drawsLive`), so the
544
+ * record draws live while a gesture reshapes it, as a square does.
400
545
  */
401
- apVersion?: number;
546
+ painter?: (box: Rect) => readonly SceneNode[];
402
547
  /**
403
- * This SESSION's authority over this record, projected from the security
404
- * service's collab mirrors at ingest (see permissions.md) — model-owned
405
- * derived state like `apVersion`, never DTO-derived. Fused into
548
+ * This session's authority over this record (see permissions.md), set by the
549
+ * plugin from the security service; the core only reads it. Fused into
406
550
  * `annotTransformable`/`annotDeletable`, so a record the session may not
407
551
  * edit renders and behaves exactly like a `locked` one (bare outline, no
408
- * handles, no drag). Absent = unstamped (a local draft, a wildcard local
409
- * engine, tests) and treated as allowed — the client gate is a COURTESY
410
- * that keeps the UI truthful; the engine independently enforces.
552
+ * handles, no drag). Absent = unstamped (a record created in this session,
553
+ * a wildcard local engine, tests) and treated as allowed — the client gate
554
+ * is a courtesy that keeps the UI truthful; the engine independently enforces.
411
555
  */
412
556
  authority?: {
413
557
  update: boolean;
414
558
  delete: boolean;
415
559
  };
416
560
  /**
417
- * The canonical engine DTO this annotation was derived from (PDF-space, sRGB)
418
- * — the single source of truth for its data. `geom` and `style` are
419
- * content-space RENDER PROJECTIONS of it, recomputed (never edited directly)
420
- * whenever `data` changes, so the two can't drift. Absent only for a vector
421
- * draft that hasn't been committed to the engine yet (no DTO exists).
422
- */
423
- data?: AnnotationDTO;
424
- /** Normalized PDF `/IT` for intent-bearing annotations authored before a DTO exists. */
425
- intent?: CaretIntent | StrikeoutIntent | InkIntent;
426
- /**
427
- * The link KIND's own `/A` target (see {@link AnnotationProps.link}) —
428
- * present only on `subtype: 'link'`. Every OTHER kind's link is an
429
- * attached child annotation in the substrate, read through the `linkOf`
430
- * lens and materialized by the shell's `syncLink` reconciler; parents
431
- * store nothing.
432
- */
433
- link?: PdfLinkTarget | null;
434
- /**
435
- * Relationship to another annotation. `irt` ("in reply to") links a child to a
436
- * parent — a reply in a comment thread, or a caret bound to its strikeout in a
437
- * replace-text pair. `group` ties a set into one composite unit (created and,
438
- * typically, deleted together). Both are unused until comments / replace-text
439
- * land, but the field lives here from the start so select/delete/persistence
440
- * never have to be retrofitted around it.
561
+ * The annotation's data in the engine's shape: what the engine will read
562
+ * back once this session's writes land. A confirmed record's is the
563
+ * engine's own read; a record this session created reads as its create
564
+ * writes it (`newRecord`); an edit merges the fields it changed, and
565
+ * `update` applies them as the engine will, a drawn kind's `rect`
566
+ * included (`record/written.ts`). Its attribution waits for the engine's
567
+ * answer.
441
568
  */
442
- irt?: Id;
443
- group?: string;
569
+ annotation: Annotation;
444
570
  }
445
571
  /** A draggable handle: a resize corner/edge (rect) or a vertex (line/poly). */
446
572
  interface Handle {
447
573
  id: string;
448
- at: Vec;
574
+ at: Point$1;
449
575
  cursor: Cursor;
450
576
  }
451
577
  /** An alignment guide produced by move-snapping: a vertical (`axis: 'x'`) or
@@ -456,221 +582,297 @@ interface Guide {
456
582
  lo: number;
457
583
  hi: number;
458
584
  }
459
- /** Snapping behaviour — seeded from the plugin config, live-adjustable via the
460
- * `setSnap` msg (so an app can wire a UI toggle). */
585
+ /**
586
+ * Snapping: to other annotations and the page while moving, and to angles
587
+ * while turning. The plugin keeps it in its settings and hands it in with
588
+ * `setSnap`.
589
+ */
461
590
  interface SnapSettings {
462
- /** Alignment guides while moving (snap to other annotations + the page). */
463
- guides: boolean;
464
- /** Guide snap tolerance, content units (PDF pt) — the `hitMargin` convention. */
465
- guideThreshold: number;
466
- /** Snap the rotate gesture onto `rotationAngles`. */
467
- rotation: boolean;
468
- rotationAngles: number[];
469
- /** Rotation snap tolerance, degrees. */
470
- rotationThreshold: number;
591
+ /** Snap to the edges and centres of other annotations and of the page while moving. */
592
+ readonly alignment: boolean;
593
+ /**
594
+ * How close a move comes before it snaps, in screen pixels: each pointer
595
+ * sample converts it by its `scale`. Without one it is in page units.
596
+ */
597
+ readonly alignmentThreshold: number;
598
+ /** Snap a turn onto `rotationAngles`. */
599
+ readonly rotation: boolean;
600
+ /** The angles a turn snaps to, in degrees clockwise. */
601
+ readonly rotationAngles: readonly number[];
602
+ /** How close a turn comes before it snaps, in degrees. */
603
+ readonly rotationThreshold: number;
471
604
  }
472
605
  /**
473
606
  * The `defaults` key a creation draft resolves its props from — the authoring
474
- * TOOL that started it, which may differ from the PDF `subtype`. Two tools can
607
+ * tool that started it, which may differ from the PDF `subtype`. Two tools can
475
608
  * share a subtype but carry distinct defaults (an "arrow" is a `line` with an
476
609
  * arrowhead default); the preset keeps them apart. Absent → fall back to
477
610
  * `subtype` (a headless / programmatic caller that isn't tool-driven), so the
478
- * built-in tools where preset === subtype behave exactly as before.
611
+ * built-in tools where preset === subtype resolve from their subtype.
479
612
  */
480
613
  type Draft = {
481
- g: 'create-rect';
482
- subtype: Subtype;
614
+ kind: 'create-rect';
615
+ subtype: KindName;
483
616
  preset?: string;
484
- pon: PageObjectNumber;
485
- from: Vec;
486
- to: Vec;
487
- ellipse: boolean;
488
- /** Display rotation + upright policy captured at DOWN (the gesture's home
489
- * page), so the commit counter-rotates against what the author SAW even
617
+ page: PageRef;
618
+ from: Point$1;
619
+ to: Point$1;
620
+ /** Display rotation + upright policy captured at down (the gesture's home
621
+ * page), so the commit counter-rotates against what the author saw even
490
622
  * if the up sample resolves elsewhere. See {@link PointerInput}. */
491
623
  displayRotation?: PageRotation;
492
624
  upright?: boolean;
493
- /** The tool's click-create policy, captured at DOWN like `upright`. */
625
+ /** The tool's click-create policy, captured at down like `upright`. */
494
626
  clickCreate?: ClickCreate | false;
495
- /** The tool's `/F` seed, captured at DOWN like `upright` — merged over
627
+ /** The tool's `/F` seed, captured at down like `upright` — merged over
496
628
  * {@link DRAWN_FLAGS} at commit (a note tool sets noZoom/noRotate). */
497
629
  flags?: Partial<AnnotationFlags$1>;
498
630
  } | {
499
- g: 'create-distance';
631
+ kind: 'create-distance';
500
632
  step: 'endpoints' | 'offset';
501
633
  subtype: 'line';
502
634
  preset: string;
503
- pon: PageObjectNumber;
504
- from: Vec;
505
- to: Vec;
635
+ page: PageRef;
636
+ from: Point$1;
637
+ to: Point$1;
506
638
  measure: DistanceAppearance;
507
639
  flags?: Partial<AnnotationFlags$1>;
508
640
  } | {
509
- g: 'create-line';
641
+ kind: 'create-line';
510
642
  measure?: MeasurementAppearance;
511
643
  capture?: string;
512
- subtype: Subtype;
644
+ subtype: KindName;
513
645
  preset?: string;
514
- pon: PageObjectNumber;
515
- from: Vec;
516
- to: Vec;
517
- /** The tool's click-create policy, captured at DOWN like `upright`. */
646
+ page: PageRef;
647
+ from: Point$1;
648
+ to: Point$1;
649
+ /** Display rotation + upright policy captured at down (see the rect
650
+ * draft): an upright tool's click lays its line as the author saw the page. */
651
+ displayRotation?: PageRotation;
652
+ upright?: boolean;
653
+ /** The tool's click-create policy, captured at down like `upright`. */
518
654
  clickCreate?: ClickCreate | false;
519
- /** The tool's `/F` seed, captured at DOWN (see the rect draft). */
655
+ /** The tool's `/F` seed, captured at down (see the rect draft). */
520
656
  flags?: Partial<AnnotationFlags$1>;
521
657
  } | {
522
- g: 'create-poly';
658
+ kind: 'create-poly';
523
659
  measure?: ShapeMeasurementAppearance;
524
- subtype: Subtype;
660
+ subtype: KindName;
525
661
  preset?: string;
526
- pon: PageObjectNumber;
527
- points: Vec[];
528
- cur: Vec;
662
+ page: PageRef;
663
+ points: Point$1[];
664
+ current: Point$1;
529
665
  closed: boolean;
530
- /** The tool's `/F` seed, captured at DOWN (see the rect draft). */
666
+ /** The tool's `/F` seed, captured at down (see the rect draft). */
531
667
  flags?: Partial<AnnotationFlags$1>;
532
668
  } | {
533
- g: 'create-ink';
534
- subtype: Subtype;
669
+ kind: 'create-ink';
670
+ subtype: KindName;
535
671
  preset?: string;
536
- pon: PageObjectNumber;
537
- strokes: Vec[][];
672
+ page: PageRef;
673
+ strokes: Point$1[][];
538
674
  intent?: InkIntent;
539
- /** The tool's `/F` seed, captured at DOWN (see the rect draft). */
675
+ /** The tool's `/F` seed, captured at down (see the rect draft). */
540
676
  flags?: Partial<AnnotationFlags$1>;
541
677
  } | {
542
- g: 'create-callout';
543
- subtype: Subtype;
678
+ kind: 'create-callout';
679
+ subtype: KindName;
544
680
  preset?: string;
545
- pon: PageObjectNumber;
681
+ page: PageRef;
546
682
  step: 'knee' | 'box';
547
- tip: Vec;
548
- knee?: Vec;
549
- cur: Vec;
550
- boxFrom?: Vec;
551
- boxTo?: Vec;
552
- /** Display rotation + upright policy captured at the TIP click (the
553
- * gesture's home page) — the text BOX commits counter-rotated so it
683
+ tip: Point$1;
684
+ knee?: Point$1;
685
+ current: Point$1;
686
+ boxFrom?: Point$1;
687
+ boxTo?: Point$1;
688
+ /** Home-page box captured at the tip click. The default text box slides
689
+ * fully inside it; a drag is already point-clamped and ignores this. */
690
+ pageBox?: Rect;
691
+ /** Display rotation + upright policy captured at the tip click (the
692
+ * gesture's home page) — the text box commits counter-rotated so it
554
693
  * reads upright; the leader (tip/knee) is page-space and never turns.
555
694
  * Same capture rule as the rect draft. */
556
695
  displayRotation?: PageRotation;
557
696
  upright?: boolean;
558
- /** The tool's `/F` seed, captured at DOWN (see the rect draft). */
697
+ /** The tool's `/F` seed, captured at down (see the rect draft). */
559
698
  flags?: Partial<AnnotationFlags$1>;
560
699
  } | {
561
- g: 'move';
700
+ kind: 'move';
562
701
  ids: Id[];
563
- start: Vec;
564
- delta: Vec;
702
+ start: Point$1;
703
+ delta: Point$1;
565
704
  guides: Guide[];
566
705
  } | {
567
- g: 'handle';
706
+ kind: 'handle';
568
707
  id: Id;
569
708
  handle: string;
570
- base: Geom;
571
- cur: Geom;
709
+ base: Shape;
710
+ current: Shape;
572
711
  view?: ViewEnv;
573
712
  } | {
574
- g: 'rotate';
713
+ kind: 'rotate';
575
714
  ids: Id[];
576
- pivot: Vec;
577
- start: Vec;
578
- cur: Vec;
715
+ pivot: Point$1;
716
+ start: Point$1;
717
+ current: Point$1;
579
718
  free?: boolean;
580
719
  view?: ViewEnv;
581
720
  } | {
582
- g: 'group';
721
+ kind: 'group';
583
722
  op: 'resize';
584
723
  ids: Id[];
585
724
  handle: string;
586
- anchor: Vec;
725
+ anchor: Point$1;
587
726
  base: Rect;
588
- cur: Rect;
727
+ current: Rect;
589
728
  view?: ViewEnv;
590
729
  } | {
591
- g: 'caption';
730
+ kind: 'caption';
592
731
  id: Id;
593
- start: Vec;
594
- delta: Vec;
732
+ start: Point$1;
733
+ delta: Point$1;
595
734
  } | {
596
- g: 'leader';
735
+ kind: 'leader';
597
736
  id: Id;
598
- start: Vec;
737
+ start: Point$1;
599
738
  delta: number;
600
739
  } | {
601
- g: 'marquee';
602
- pon: PageObjectNumber;
603
- from: Vec;
604
- to: Vec;
740
+ kind: 'marquee';
741
+ page: PageRef;
742
+ from: Point$1;
743
+ to: Point$1;
605
744
  };
606
745
  /** A live text-markup preview (the in-progress selection rendered as the markup it
607
746
  * will become). Per page, since a selection can span pages. */
608
747
  interface MarkupPreview {
609
- subtype: Subtype;
748
+ subtype: KindName;
610
749
  /** Defaults key, distinct from subtype for presets such as replace-text. */
611
750
  preset: string;
612
- byPage: Record<number, TextQuad$1[]>;
751
+ /** Keyed by `page.pageObjectNumber` (an internal lookup, not an address). */
752
+ byPage: Record<number, Quad$1[]>;
613
753
  }
614
754
  /** Anchor + affordance state for UI that controls an in-progress creation draft. */
755
+ /**
756
+ * A rotation in progress, for the badge that follows the pointer: the page
757
+ * it runs on, the pointer there (page space) and the selection's angle as the
758
+ * commit will apply it (degrees clockwise, whole degrees).
759
+ */
760
+ interface RotationAnchor {
761
+ page: PageRef;
762
+ at: Point$1;
763
+ angle: number;
764
+ }
615
765
  interface CreationDraftAnchor {
616
766
  kind: 'poly';
617
767
  subtype: PolySubtype;
618
- pon: PageObjectNumber;
768
+ page: PageRef;
619
769
  bounds: Rect;
620
770
  pointCount: number;
621
771
  minPoints: number;
622
772
  canFinish: boolean;
623
773
  }
624
- interface Model {
625
- byId: Record<Id, Annot>;
626
- order: Id[];
774
+ /**
775
+ * What the core owns: everything about the user's session that is not a
776
+ * record. `update` returns the next session; the plugin stores it.
777
+ */
778
+ interface Session {
627
779
  selected: Id[];
628
780
  /** The annotation under the pointer (topmost hit), or null. View-model
629
781
  * state like `selected` — drives hover affordances (a redaction mark's
630
- * applied-look preview) purely from the scene. Updated on CHANGE only
782
+ * applied-look preview) purely from the scene. Updated on change only
631
783
  * (enter/leave cadence, never per-move). */
632
784
  hovered: Id | null;
785
+ /** The gesture in progress (a move, a resize, a shape being drawn), or null. */
633
786
  draft: Draft | null;
634
787
  /** Transient ghost of an in-progress markup selection (null when idle). */
635
788
  preview: MarkupPreview | null;
636
- seq: number;
637
- /** The base style new annotations inherit (per-tool `defaults` layer on top). */
638
- style: Style;
639
- /** Per-tool (keyed by subtype / tool id) property overrides for newly drawn
640
- * annotations — the SAME flat vocabulary `setProps` uses. `lineEndings` is
641
- * stored fully resolved (merged at `setDefaults` time). */
642
- defaults: Record<string, AnnotationPropsPatch>;
643
- /** Extra clickable margin (content units) around a stroke — bump it for touch. */
789
+ /**
790
+ * The object numbers this session holds for the records it creates next: a
791
+ * message's first new record takes the first, and so on. The plugin hands
792
+ * the session numbers from the document's pool before a message that may
793
+ * create (`newRecordsAtMost`).
794
+ */
795
+ objectNumbers: readonly number[];
796
+ /**
797
+ * Each tool's defaults for the annotations it draws, keyed by the tool's
798
+ * preset: the engine fields its creates state. The engine's own defaults
799
+ * fill in what they leave out.
800
+ */
801
+ defaults: Record<string, FieldValues>;
802
+ /**
803
+ * Extra clickable margin around what an annotation paints, in screen
804
+ * pixels: converted by the page's view scale, so a click reaches as far at
805
+ * every zoom. Bump it for touch.
806
+ */
644
807
  hitMargin: number;
645
- /** The free-text annotation currently in TEXT-EDIT mode (its `contentEditable`
808
+ /** The free-text annotation currently in text-edit mode (its `contentEditable`
646
809
  * is focused), or null. Distinct from `selected`: you select to move/resize,
647
810
  * you edit to type. */
648
811
  editing: Id | null;
649
812
  /** Snapping behaviour (alignment guides + rotation). */
650
813
  snap: SnapSettings;
814
+ /** Which annotations people may change by hand (editable.ts): the plugin sets it from its settings. */
815
+ editable: EditableKinds;
651
816
  }
652
817
  /**
653
- * Selection-chrome geometry in CONTENT units — the grab zones and the knob
818
+ * The records a gesture works on, read-only: what the engine confirmed with
819
+ * this session's pending changes on top. The plugin builds it; the core never
820
+ * stores a record.
821
+ */
822
+ interface AnnotationView {
823
+ byId: Record<Id, ModelAnnotation>;
824
+ /** Paint order: the document's order, then records created in this session. */
825
+ order: Id[];
826
+ }
827
+ /** What every transition and read takes: the session composed with the records it works on. */
828
+ type Model = Session & AnnotationView;
829
+ /**
830
+ * What one message changed in the records. `put` holds new or changed records
831
+ * as the user produced them; `drop` holds the ids the user deleted. The plugin
832
+ * shows them until the engine writes that carry them settle.
833
+ */
834
+ interface ChangeSet {
835
+ readonly put: readonly ModelAnnotation[];
836
+ readonly drop: readonly Id[];
837
+ /**
838
+ * What each changed record's change means to the engine, by id: the fields
839
+ * it changed, as `update` takes them. None for a new record (its create
840
+ * writes it) or for a change the engine keeps nothing of (a record handed
841
+ * to live rendering, a value set to what it was).
842
+ */
843
+ readonly patches: Readonly<Record<Id, AnnotationPatch>>;
844
+ }
845
+ /** The result of one message: the next session, the records it changed, and the engine work to do. */
846
+ interface UpdateResult {
847
+ readonly session: Session;
848
+ readonly change: ChangeSet;
849
+ readonly effects: readonly Effect[];
850
+ }
851
+ /**
852
+ * Selection-chrome geometry in content units — the grab zones and the knob
654
853
  * stalk. The core is unit-agnostic and zoom-free: callers own the px→content
655
854
  * conversion (settings are CSS px; divide by the page's view scale), so grab
656
855
  * zones stay screen-constant across zoom.
657
856
  */
658
- interface ChromeGeom {
857
+ interface ChromeGeometry {
659
858
  /** Half-side of a resize/vertex handle's square grab zone. */
660
859
  handleTol: number;
661
860
  /** Half-side of the rotate knob's square grab zone. */
662
861
  knobTol: number;
663
862
  /** How far the rotate knob hangs off the selection edge. */
664
863
  knobOffset: number;
864
+ /** `false`: the selection has no rotation handle to grab (it isn't drawn either). */
865
+ rotationHandle?: boolean;
665
866
  }
666
867
  interface PointerInput {
667
- pon: PageObjectNumber;
668
- point: Vec;
868
+ /** The page the sample resolved against (its own page-space frame). */
869
+ page: PageRef;
870
+ point: Point$1;
669
871
  shift: boolean;
670
872
  finish?: boolean;
671
873
  /**
672
874
  * The page's content box (`{0, 0, crop.width, crop.height}`) — when present,
673
- * gestures are CLAMPED to it: a move keeps the selection's bounds inside the
875
+ * gestures are clamped to it: a move keeps the selection's bounds inside the
674
876
  * page (sliding along the edge when the pointer overshoots), resize/create
675
877
  * points pin to the edge. Annotations are page-bound; the pointer isn't.
676
878
  */
@@ -680,34 +882,41 @@ interface PointerInput {
680
882
  * exactly like `pageBox` (the caller converts its CSS-px settings by the
681
883
  * page's view scale at dispatch). Absent → `DEFAULT_CHROME_GEOM`.
682
884
  */
683
- chrome?: ChromeGeom;
885
+ chrome?: ChromeGeometry;
684
886
  /**
685
- * The page's TOTAL display rotation (document /Rotate + view rotation) at the
686
- * gesture — per-event environmental context like `pageBox`. A creation DOWN
887
+ * The page's total display rotation (document /Rotate + view rotation) at the
888
+ * gesture — per-event environmental context like `pageBox`. A creation down
687
889
  * captures it on the draft (an `upright` commit counter-rotates against how
688
- * the page was DISPLAYED); edit gestures read it per sample, paired with
890
+ * the page was displayed); edit gestures read it per sample, paired with
689
891
  * `scale`, so screen-anchored (`noZoom`/`noRotate`) annotations hit-test and
690
- * clamp at their EFFECTIVE geometry. Content space itself never rotates.
892
+ * clamp at their effective geometry. Page space itself never rotates.
691
893
  */
692
894
  displayRotation?: PageRotation;
693
895
  /**
694
- * The page's zoom RELATIVE to its 100% baseline at the event — the other
896
+ * Screen pixels per page unit at the event: the page's view scale. A
897
+ * setting in screen pixels (`snap.alignmentThreshold`) is converted by it,
898
+ * so snapping feels the same at every zoom. Absent → 1.
899
+ */
900
+ scale?: number;
901
+ /**
902
+ * The page's zoom relative to its 100% baseline at the event — the other
695
903
  * half of the {@link ViewEnv} pair with `displayRotation` (dimensionless,
696
- * `transform.zoom`; NOT the px-per-point scale the caller uses for its
904
+ * `transform.zoom`; not the px-per-point scale the caller uses for its
697
905
  * CSS-px chrome conversion). Absent → 1 (headless callers; screen-anchored
698
906
  * bodies then hit-test at rect size).
699
907
  */
700
908
  zoom?: number;
701
909
  /**
702
- * Counter-rotate the created annotation so it reads upright at
703
- * `displayRotation` — the authoring TOOL's `upright` policy, resolved by the
704
- * caller (the core knows subtypes, not tools). Box kinds only (free-text /
705
- * stamp are the ones with a natural reading orientation); vertex kinds and
706
- * callouts ignore it.
910
+ * Lay the created annotation out as the author sees the page at
911
+ * `displayRotation` — the authoring tool's `upright` policy, resolved by the
912
+ * caller (the core knows subtypes, not tools). A box counter-rotates so it
913
+ * reads upright (free text, stamps); a clicked line points the way its
914
+ * `ClickCreate` says on screen. A dragged line and a callout's leader
915
+ * follow the pointer and ignore it.
707
916
  */
708
917
  upright?: boolean;
709
918
  /**
710
- * Ids invisible to hit-testing and marquee for THIS event — per-event
919
+ * Ids invisible to hit-testing and marquee for this event — per-event
711
920
  * environmental context like `pageBox`. The caller (plugin shell) resolves
712
921
  * its engaged Behaviors (form widgets under a fill tool render their own
713
922
  * DOM and must not select/move), so the core stays behavior-agnostic.
@@ -715,17 +924,19 @@ interface PointerInput {
715
924
  inert?: ReadonlySet<Id>;
716
925
  }
717
926
  /**
718
- * What a bare CLICK (a press-release under the drag threshold) creates for a
719
- * tool: a default-size box with an EXPLICIT anchor (`center` unless stated —
720
- * free text declares `top-left` so the box hangs where you'll type,
721
- * display-frame-aware under `upright`), or a default-length line from the
722
- * point (`angleDeg` 0 = rightward, CW-positive in y-down space). Resolved
723
- * from the TOOL by the caller and passed on the message — the core knows
724
- * subtypes, not tools (the `upright` pattern). `false` suppresses a kind's
725
- * own click fallback (free text always click-creates by default).
726
- * Anchoring is policy DATA, never inferred from the kind — the same policy
727
- * drives annotation commits, footprint ghosts, and form-field placement
728
- * (see `resolveClickPlacement`).
927
+ * What a bare click (a press-release under the drag threshold) creates for a
928
+ * tool, in PDF points:
929
+ * - a box, `width` × `height`, whose `anchor` sits on the click: its middle
930
+ * (`center`, the default) or its top-left corner (`top-left`: free text
931
+ * hangs where you'll type);
932
+ * - a line, `length` long, pointing `rotation` degrees clockwise from
933
+ * rightward, whose `anchor` sits on the click: its middle (`center`, the
934
+ * default), its start, or its end (an arrow's tip).
935
+ * Under an `upright` tool the anchor, the box's sides and the line's
936
+ * direction are as the person sees the turned page. Resolved from the tool
937
+ * by the caller and passed on the message: the core knows kinds, not tools.
938
+ * The same policy drives the commit, the tool's ghost and a form field's
939
+ * placement (see `resolveClickPlacement`).
729
940
  */
730
941
  type ClickCreate = {
731
942
  width: number;
@@ -733,22 +944,40 @@ type ClickCreate = {
733
944
  anchor?: 'center' | 'top-left';
734
945
  } | {
735
946
  length: number;
736
- angleDeg?: number;
947
+ rotation?: number;
948
+ anchor?: 'center' | 'start' | 'end';
737
949
  };
738
- type Msg = {
739
- t: 'editPointer';
950
+ /**
951
+ * Where a create gesture puts what it makes, on the page and slid inside it:
952
+ * a box and the turn an `upright` tool gives it (degrees clockwise, 0 for
953
+ * none), or a segment from `a` to `b`. A click's comes from the tool's
954
+ * {@link ClickCreate}, a drag's from the dragged points. It says nothing of
955
+ * what is drawn there: each shape family turns it into its shape
956
+ * (`ShapeFamily.placed`), and a form field takes the box as its bounds.
957
+ */
958
+ type Placement = {
959
+ kind: 'box';
960
+ rect: Rect;
961
+ rot: number;
962
+ } | {
963
+ kind: 'segment';
964
+ a: Point$1;
965
+ b: Point$1;
966
+ };
967
+ type Message = {
968
+ type: 'editPointer';
740
969
  phase: 'down' | 'move' | 'up';
741
970
  in: PointerInput;
742
971
  } | {
743
- t: 'marqueePointer';
972
+ type: 'marqueePointer';
744
973
  phase: 'down' | 'move' | 'up';
745
974
  in: PointerInput;
746
975
  } | {
747
- t: 'createPointer';
976
+ type: 'createPointer';
748
977
  measure?: MeasurementAppearance;
749
978
  capture?: string;
750
979
  phase: 'down' | 'move' | 'up';
751
- subtype: Subtype;
980
+ subtype: KindName;
752
981
  /** The authoring tool's `defaults` key (see {@link Draft}). Defaults to `subtype`. */
753
982
  preset?: string;
754
983
  /** PDF intent carried by an ink authoring preset. */
@@ -764,195 +993,171 @@ type Msg = {
764
993
  straightenInk?: InkStraightenOptions;
765
994
  in: PointerInput;
766
995
  } | {
767
- t: 'finishInkDraft';
996
+ type: 'finishInkDraft';
768
997
  } | {
769
- t: 'finishCreationDraft';
998
+ type: 'finishCreationDraft';
770
999
  } | {
771
- t: 'createCaret';
772
- pon: PageObjectNumber;
1000
+ type: 'createCaret';
1001
+ page: PageRef;
773
1002
  anchor: TextEndAnchor;
774
1003
  flags?: Partial<AnnotationFlags$1>;
775
1004
  } | {
776
- t: 'createReplaceText';
777
- pon: PageObjectNumber;
778
- quads: TextQuad$1[];
1005
+ type: 'createReplaceText';
1006
+ page: PageRef;
1007
+ quads: Quad$1[];
779
1008
  anchor: TextEndAnchor;
780
1009
  preset?: string;
781
1010
  } | {
782
- t: 'createMarkup';
783
- subtype: Subtype;
784
- pon: PageObjectNumber;
785
- quads: TextQuad$1[];
1011
+ type: 'createMarkup';
1012
+ subtype: KindName;
1013
+ page: PageRef;
1014
+ quads: Quad$1[];
786
1015
  preset?: string;
787
1016
  /** The tool's `/F` seed — merged over {@link DRAWN_FLAGS} at commit. */
788
1017
  flags?: Partial<AnnotationFlags$1>;
789
1018
  } | {
790
- t: 'setMarkupPreview';
791
- subtype: Subtype;
792
- quadsByPage: Record<number, TextQuad$1[]>;
1019
+ type: 'setMarkupPreview';
1020
+ subtype: KindName;
1021
+ /** Per-page quads keyed by `page.pageObjectNumber` (a lookup, not an address). */
1022
+ quadsByPage: Record<number, Quad$1[]>;
793
1023
  preset?: string;
794
1024
  } | {
795
- t: 'clearMarkupPreview';
1025
+ type: 'clearMarkupPreview';
796
1026
  } | {
797
- t: 'deselect';
1027
+ type: 'deselect';
798
1028
  ids?: Id[];
799
1029
  } |
800
1030
  /** Pointer entered/left an annotation (topmost hit id, or null). Pure state. */
801
1031
  {
802
- t: 'hover';
1032
+ type: 'hover';
803
1033
  id: Id | null;
804
- } |
805
- /** Force/clear session visibility for specific annotations (the actions
806
- * plane's Hide sink). Hiding also clears transient engagement (selection,
807
- * editing, hover) for the hidden ids. Unknown ids no-op. Zero effects. */
808
- /** Drop overrides for truly DELETED annotations (never for reloads). */
809
- {
810
- t: 'select';
1034
+ } | {
1035
+ type: 'select';
811
1036
  ids: Id[];
812
1037
  add?: boolean;
813
1038
  } | {
814
- t: 'setProps';
815
- patch: AnnotationPropsPatch;
1039
+ type: 'setFields';
1040
+ patches: Readonly<Record<Id, FieldValues>>;
1041
+ } | {
1042
+ type: 'setTextFormat';
1043
+ format: 'bold' | 'italic' | 'underline';
1044
+ on: boolean;
1045
+ } | {
1046
+ type: 'setLink';
1047
+ target: PdfLinkTarget | null;
816
1048
  } | {
817
- t: 'setFlags';
1049
+ type: 'setFlags';
818
1050
  patch: Partial<AnnotationFlags$1>;
819
1051
  ids?: Id[];
820
1052
  } | {
821
- t: 'setDefaults';
822
- subtype: Subtype;
823
- patch: AnnotationPropsPatch;
1053
+ type: 'setDefaults';
1054
+ preset: string;
1055
+ patch: FieldValues;
824
1056
  } | {
825
- t: 'setSnap';
1057
+ type: 'setSnap';
826
1058
  patch: Partial<SnapSettings>;
827
1059
  } | {
828
- t: 'rotate90';
829
- } | {
830
- t: 'resetRotation';
1060
+ type: 'rotateSelection';
1061
+ degrees: 90 | -90;
831
1062
  } | {
832
- t: 'delete';
1063
+ type: 'resetRotation';
833
1064
  } | {
834
- t: 'cancel';
1065
+ type: 'delete';
835
1066
  } | {
836
- t: 'loaded';
837
- annots: Annot[];
1067
+ type: 'cancel';
838
1068
  } |
839
- /**
840
- * Whole-document hydration ingest (and desync re-ingest): the snapshot is
841
- * the committed TRUTH. Incoming annots overwrite by id (gesture-locked ids
842
- * excepted, as in `upsert`); committed model entries ABSENT from the
843
- * snapshot are reaped — they were deleted while we could not watch.
844
- * Uncommitted `tmp:` drafts and gesture-locked ids are never reaped, and
845
- * an in-progress draft survives (unlike `remove`). `bumpAp` marks a
846
- * desync re-ingest: rasters may have changed invisibly during the gap,
847
- * so every replaced annotation re-fetches once.
848
- */
1069
+ /** These records left the view (deleted elsewhere, a refused create): drop every reference to them. */
849
1070
  {
850
- t: 'hydrated';
851
- annots: Annot[];
852
- bumpAp?: boolean;
853
- } | {
854
- t: 'created';
855
- tempId: Id;
856
- id: Id;
857
- ref: AnnotationRef;
858
- } | {
859
- t: 'createFailed';
860
- tempId: Id;
861
- } | {
862
- t: 'upsert';
863
- annots: Annot[];
864
- bumpAp?: boolean;
865
- } | {
866
- t: 'bumpAp';
867
- ids: Id[];
868
- } | {
869
- t: 'remove';
1071
+ type: 'forget';
870
1072
  ids: Id[];
871
1073
  } | {
872
- t: 'beginTextEdit';
1074
+ type: 'beginTextEdit';
873
1075
  id: Id;
874
1076
  } | {
875
- t: 'setText';
1077
+ type: 'setText';
876
1078
  id: Id;
877
1079
  text: string;
878
1080
  } | {
879
- t: 'setRichText';
1081
+ type: 'setRichText';
880
1082
  id: Id;
881
1083
  doc: RichTextDocumentInput;
882
1084
  } | {
883
- t: 'endTextEdit';
1085
+ type: 'endTextEdit';
884
1086
  };
885
1087
  type Effect = {
886
- fx: 'captured';
1088
+ type: 'captured';
887
1089
  tool: string;
888
- pon: number;
889
- geom: Geom;
890
- } | {
891
- fx: 'create';
1090
+ page: PageRef;
1091
+ geometry: Shape;
1092
+ } |
1093
+ /** Write a new record: the draft its annotation was read from. Its ref names the object number it takes. */
1094
+ {
1095
+ type: 'create';
892
1096
  id: Id;
893
- } | {
894
- fx: 'createGroup';
895
- primary: Id;
896
- members: Id[];
1097
+ draft: AnnotationDraft;
897
1098
  } |
898
- /** `apChanged` is set (to `true`) ONLY when this patch INVALIDATED a baked
899
- * raster — the annotation stayed `baked` (an opaque-body kind) and the edit
900
- * resized its `/AP` frame, so the engine's re-bake produces new content (in
901
- * practice: a stamp resize). The shell's resolve handler then turns it into
902
- * an `upsert` with `bumpAp`. Absent for everything else — moves/rotations
903
- * (the blit repositions the same pixels) and any kind that flipped to
904
- * `vector` (it renders live; the raster stops mattering) — so those keep the
905
- * bare `{ fx, id }` shape and trigger no appearance re-fetch. */
1099
+ /** Write one record's change: its entry in the change set's `patches`. `update`
1100
+ * asks for it for every changed record that has one, except typed text, which
1101
+ * its `text` effect writes. Whether the engine's re-baked appearance differs
1102
+ * is the engine's answer, not the core's guess. */
906
1103
  {
907
- fx: 'patch';
1104
+ type: 'patch';
908
1105
  id: Id;
909
- scope: PatchScope;
910
- apChanged?: true;
1106
+ patch: AnnotationPatch;
911
1107
  } |
912
- /** A `/F`-only engine write for one committed annotation: the shell emits a
913
- * flags-only patch (the model already holds the merged flags) and re-syncs
914
- * PRESERVING the render source — flags never re-bake an appearance. */
1108
+ /** Write the edited text of one free-text record. While the user types, the
1109
+ * plugin keeps amending one change with the latest text, and sends it after
1110
+ * a pause. */
915
1111
  {
916
- fx: 'flags';
1112
+ type: 'text';
917
1113
  id: Id;
918
1114
  } |
919
- /** The parent's `link` prop changed on a NON-link kind: reconcile its
920
- * attached link children (create / retarget / delete) against the desired
921
- * state derived from `link` + the parent's geometry. Declarative — the
922
- * shell's reconciler is the only code that spells out child operations.
923
- * (Geometry commits don't emit this; the shell re-runs the reconciler on
924
- * any `patch` of an annotation that has `linkRefs`.) */
1115
+ /** The parent's `link` prop changed on a non-link kind: reconcile its
1116
+ * attached link children (create / retarget / delete) toward `target`
1117
+ * (null = remove them). Declarative — the plugin's reconciler is the only
1118
+ * code that spells out child operations; parents store no link value, the
1119
+ * committed children are the truth (`linkOf` reads them back). Geometry
1120
+ * commits don't emit this: the plugin's write of a `patch` brings an
1121
+ * annotation's attached children along. */
925
1122
  {
926
- fx: 'syncLink';
1123
+ type: 'syncLink';
927
1124
  id: Id;
928
1125
  target: PdfLinkTarget | null;
929
- } | {
930
- fx: 'delete';
931
- ref: AnnotationRef;
1126
+ } |
1127
+ /** Delete one record from the document. */
1128
+ {
1129
+ type: 'delete';
1130
+ id: Id;
932
1131
  };
933
1132
  /** Per-annotation render data — its content geometry + style + live state. */
934
1133
  interface RenderItem {
935
1134
  id: Id;
936
1135
  ref: AnnotationRef | null;
937
- subtype: Subtype;
938
- geom: Geom;
939
1136
  /**
940
- * The VISUAL box (geometry + stroke + line endings) in content space — the SAME
941
- * `geomVisualBounds` that feeds the engine `/Rect`. The renderer paints into THIS
942
- * box and does no bounds math of its own, so the on-screen box and the baked
943
- * appearance can never drift (the v2 "patch computes the rect" rule).
1137
+ * The annotation it draws, as the user sees it (the record's, a gesture
1138
+ * in progress aside: that is in `geometry` and `box`). Absent for what isn't
1139
+ * made yet: a drawing in progress or a tool's ghost.
1140
+ */
1141
+ annotation?: Annotation;
1142
+ subtype: KindName;
1143
+ geometry: Shape;
1144
+ /**
1145
+ * The box the live drawing is painted into (its family's `drawnBounds`:
1146
+ * geometry, stroke, endings, cloud): a box's or caret's own, before its
1147
+ * turn (`rot`), the page's for the rest. The renderer paints into this box
1148
+ * and does no bounds math of its own.
944
1149
  */
945
1150
  box: Rect;
946
1151
  /**
947
- * Content-space box the engine appearance raster occupies (the AP `/Rect`),
1152
+ * Page-space box the engine appearance raster occupies (the AP `/Rect`),
948
1153
  * with the live move gesture applied — so a baked annotation's bitmap follows
949
1154
  * a drag. Only meaningful when `source === 'baked'`; absent otherwise.
950
1155
  */
951
1156
  apBox?: Rect;
952
1157
  /**
953
1158
  * Rotation (deg, CW) to apply to the baked raster as a view transform —
954
- * exactly the rotation the engine STRIPPED from it (see `Annot.apRot`).
955
- * Live for `opaqueBody` kinds (a stamp spins with the rotate gesture);
1159
+ * exactly the rotation the engine stripped from it (see `ModelAnnotation.apRot`).
1160
+ * Live for `rasterOnly` kinds (a stamp spins with the rotate gesture);
956
1161
  * absent when the raster already contains its rotation (vertex kinds).
957
1162
  */
958
1163
  apRot?: number;
@@ -961,22 +1166,67 @@ interface RenderItem {
961
1166
  * text, text/choice widgets). Lets a behavior renderer's focused editor
962
1167
  * match the baked appearance's font. */
963
1168
  text?: TextStyle;
964
- source: 'baked' | 'vector' | 'ghost';
1169
+ /**
1170
+ * How it paints: the engine's raster (`baked`), live from its description
1171
+ * (`vector`), or an annotation not made yet, drawn as it will be: a drawing
1172
+ * in progress (`draft`, solid) or the tool's ghost, what a click would
1173
+ * place (`ghost`, see-through by `ghostOpacity`).
1174
+ */
1175
+ source: 'baked' | 'vector' | 'draft' | 'ghost';
1176
+ /**
1177
+ * The version of the record's engine picture (`ModelAnnotation.pictureVersion`):
1178
+ * a picture drawn at another version shows another look of it. Absent for
1179
+ * what isn't made yet.
1180
+ */
1181
+ pictureVersion?: number;
1182
+ /**
1183
+ * A lent record's live drawing, by its lender (`ModelAnnotation.painter`),
1184
+ * upright in `box` (where a gesture has it): `scene(item)` draws it in place
1185
+ * of the kind's own. Made when it's first asked for, not before.
1186
+ */
1187
+ lentScene?: () => readonly SceneNode[];
1188
+ /** How opaque a ghost paints (0–1); a painter lets `--epdf-ghost-opacity` win over it. */
1189
+ ghostOpacity?: number;
965
1190
  selected: boolean;
966
1191
  /** The pointer is over this annotation — scene-level hover affordances
967
1192
  * (e.g. a redaction mark previews its applied look). */
968
1193
  hovered?: boolean;
969
- /** Redaction label projection (redact kind only) — see {@link Annot.label}. */
1194
+ /** Redaction label projection (redact kind only) — see {@link ModelAnnotation.label}. */
970
1195
  label?: {
971
1196
  text: string;
972
1197
  repeat: boolean;
973
1198
  };
1199
+ /**
1200
+ * The box a painter draws into: `box` before its turn, and that turn,
1201
+ * degrees clockwise about the box's middle. Everything the item draws sits
1202
+ * upright inside it: its scene, a custom look, or its raster (`raster`).
1203
+ * `scale` is how large the page draws the item relative to its own size:
1204
+ * 1, except for a body that keeps its size on screen (`noZoom`), drawn
1205
+ * smaller on the page as people zoom in. Made from `box` and `rot` by the
1206
+ * core; painters read this, not `rot`.
1207
+ */
1208
+ frame: {
1209
+ box: Rect;
1210
+ rotation: number;
1211
+ scale: number;
1212
+ };
1213
+ /**
1214
+ * The engine's raster inside the frame, for an item that has one: its box
1215
+ * relative to the frame's top-left before the frame's turn, and its own turn
1216
+ * about its middle. Made from `apBox` and `apRot`; null without a raster box.
1217
+ */
1218
+ raster: {
1219
+ box: Rect;
1220
+ rotation: number;
1221
+ } | null;
1222
+ /** A note's or file attachment's icon (`/Name`), which its live drawing draws. */
1223
+ icon?: NoteIcon | FileAttachmentIcon;
974
1224
  measure?: MeasurementAppearance;
975
1225
  /**
976
- * Applied rotation (deg, CW), or 0/undefined. For BOX kinds (`rect`/`text`)
977
- * `box` is the UNROTATED visual box and the renderer applies this rotation
978
- * about its centre (CSS/SVG transform). For VERTEX kinds the geometry is
979
- * already rotated, so this is advisory only — the renderer must NOT re-apply it.
1226
+ * Applied rotation (deg, CW), or 0/undefined. For box kinds (`box`/`text-box`)
1227
+ * `box` is the unrotated visual box and the renderer applies this rotation
1228
+ * about its centre (CSS/SVG transform). For vertex kinds the geometry is
1229
+ * already rotated, so this is advisory only — the renderer must not re-apply it.
980
1230
  */
981
1231
  rot?: number;
982
1232
  /**
@@ -986,8 +1236,8 @@ interface RenderItem {
986
1236
  */
987
1237
  blend?: Exclude<BlendMode$1, 'normal'>;
988
1238
  }
989
- /** The dumb draw vocabulary the framework renderer maps to SVG (content space).
990
- * A CLOSED node (rect, ellipse, closed poly) takes the annotation's FILL colour;
1239
+ /** The dumb draw vocabulary the framework renderer maps to SVG (page space).
1240
+ * A closed node (rect, ellipse, closed poly) takes the annotation's fill colour;
991
1241
  * an open node (line, open poly — open arrows, butt, slash) is stroke-only. The
992
1242
  * stroke colour applies to every node. Closed-ness is the only fill signal. */
993
1243
  type RenderNode = {
@@ -998,11 +1248,11 @@ type RenderNode = {
998
1248
  rect: Rect;
999
1249
  } | {
1000
1250
  kind: 'line';
1001
- a: Vec;
1002
- b: Vec;
1251
+ a: Point$1;
1252
+ b: Point$1;
1003
1253
  } | {
1004
1254
  kind: 'poly';
1005
- points: Vec[];
1255
+ points: Point$1[];
1006
1256
  closed: boolean;
1007
1257
  } | {
1008
1258
  kind: 'path';
@@ -1010,7 +1260,7 @@ type RenderNode = {
1010
1260
  };
1011
1261
  /**
1012
1262
  * How to paint one node. The pure core fills this in (per kind/subtype), and a
1013
- * framework renderer applies it verbatim — so ALL appearance logic (markup fill vs
1263
+ * framework renderer applies it verbatim — so all appearance logic (markup fill vs
1014
1264
  * stroke, blend, dash, derived widths) lives once, in the portable core, not in
1015
1265
  * every framework. Omitted `fill`/`stroke` mean none.
1016
1266
  */
@@ -1021,16 +1271,17 @@ interface Paint {
1021
1271
  opacity?: number;
1022
1272
  dash?: number[];
1023
1273
  blend?: Exclude<BlendMode$1, 'normal'>;
1024
- cap?: 'round';
1274
+ lineCap?: 'round';
1025
1275
  join?: 'round';
1276
+ fillRule?: 'evenodd';
1026
1277
  }
1027
1278
  /**
1028
1279
  * A fully-painted draw node: geometry + paint. `scene(item)` returns these and a
1029
- * per-framework painter maps each to ONE element, applying `paint` — the entire
1280
+ * per-framework painter maps each to one element, applying `paint` — the entire
1030
1281
  * surface a new framework renderer must implement. Supersedes the geometry-only
1031
1282
  * `RenderNode` for rendering; `geomScene` stays the internal geometry helper.
1032
1283
  */
1033
- type SceneNode = {
1284
+ type SceneNode = ({
1034
1285
  kind: 'rect';
1035
1286
  rect: Rect;
1036
1287
  paint: Paint;
@@ -1040,12 +1291,12 @@ type SceneNode = {
1040
1291
  paint: Paint;
1041
1292
  } | {
1042
1293
  kind: 'line';
1043
- a: Vec;
1044
- b: Vec;
1294
+ a: Point$1;
1295
+ b: Point$1;
1045
1296
  paint: Paint;
1046
1297
  } | {
1047
1298
  kind: 'poly';
1048
- points: Vec[];
1299
+ points: Point$1[];
1049
1300
  closed: boolean;
1050
1301
  paint: Paint;
1051
1302
  } | {
@@ -1053,18 +1304,49 @@ type SceneNode = {
1053
1304
  d: string;
1054
1305
  paint: Paint;
1055
1306
  } |
1056
- /** Painted (non-interactive) text — `at` is the BASELINE start point in
1307
+ /** Painted (non-interactive) text — `at` is the baseline start point in
1057
1308
  * content units. Editable text (free text) stays a framework element;
1058
1309
  * this is for pure pixels, e.g. a redaction label preview. */
1059
1310
  {
1060
1311
  kind: 'text';
1061
- at: Vec;
1312
+ at: Point$1;
1062
1313
  text: string;
1063
1314
  fontSize: number;
1064
1315
  fontFamily?: string;
1065
1316
  rotation?: number;
1066
1317
  paint: Paint;
1318
+ } |
1319
+ /** Painted text placed character by character: `offsets[i]` is how far
1320
+ * along the baseline from `at` character `i` starts, so each glyph lands
1321
+ * where the PDF puts it (a form widget's painted text). `rotation` turns
1322
+ * the run about `at`, degrees clockwise. `program` is the face it draws
1323
+ * in, for whatever draws it to mount (`fontFamily` names it first). */
1324
+ {
1325
+ kind: 'glyphs';
1326
+ at: Point$1;
1327
+ text: string;
1328
+ offsets: number[];
1329
+ fontSize: number;
1330
+ fontFamily: string;
1331
+ fontWeight?: number;
1332
+ fontStyle?: 'normal' | 'italic';
1333
+ rotation?: number;
1334
+ program?: SceneFontProgram;
1335
+ paint: Paint;
1336
+ }) & {
1337
+ /** Drawn only inside this upright rectangle (a widget's text box). */
1338
+ clip?: Rect;
1067
1339
  };
1340
+ /**
1341
+ * A font program a scene's text draws in: mounted under its own family, in
1342
+ * its own weight and style, so the browser takes it as it is.
1343
+ */
1344
+ interface SceneFontProgram {
1345
+ readonly family: string;
1346
+ readonly weight: number;
1347
+ readonly style: 'normal' | 'italic';
1348
+ load(): Promise<Uint8Array>;
1349
+ }
1068
1350
  /** Pure geometry settings for recognising and axis-snapping a freehand stroke. */
1069
1351
  interface InkStraightenOptions {
1070
1352
  /** Maximum `max perpendicular deviation / endpoint distance`. */
@@ -1072,39 +1354,39 @@ interface InkStraightenOptions {
1072
1354
  /** Degrees from horizontal/vertical within which the line snaps to that axis. */
1073
1355
  axisSnapDegrees: number;
1074
1356
  }
1357
+ /** What a selection handle reshapes: a box's corner or side, or one point of a line or a polygon. */
1358
+ type HandleRole = 'corner' | 'side' | 'point';
1075
1359
  type ChromeNode = {
1076
1360
  kind: 'outline';
1077
1361
  rect: Rect;
1078
1362
  } | {
1079
1363
  kind: 'obb';
1080
- corners: [Vec, Vec, Vec, Vec];
1364
+ corners: [Point$1, Point$1, Point$1, Point$1];
1081
1365
  angle: number;
1082
1366
  } | {
1083
1367
  kind: 'handle';
1084
- at: Vec;
1368
+ at: Point$1;
1085
1369
  cursor: Cursor;
1086
1370
  rot?: number;
1371
+ role: HandleRole;
1372
+ active: boolean;
1087
1373
  } | {
1088
1374
  kind: 'rotate-knob';
1089
- at: Vec;
1090
- from: Vec;
1375
+ at: Point$1;
1376
+ from: Point$1;
1091
1377
  } | {
1092
1378
  kind: 'guide';
1093
1379
  axis: 'x' | 'y';
1094
1380
  at: number;
1095
1381
  lo: number;
1096
1382
  hi: number;
1097
- } | {
1098
- kind: 'angle-chip';
1099
- at: Vec;
1100
- angle: number;
1101
1383
  } | {
1102
1384
  kind: 'rotate-guides';
1103
- center: Vec;
1385
+ center: Point$1;
1104
1386
  angle: number;
1105
1387
  lines: Array<{
1106
- a: Vec;
1107
- b: Vec;
1388
+ a: Point$1;
1389
+ b: Point$1;
1108
1390
  role: 'axis' | 'indicator';
1109
1391
  }>;
1110
1392
  } | {
@@ -1112,6 +1394,80 @@ type ChromeNode = {
1112
1394
  rect: Rect;
1113
1395
  };
1114
1396
  //#endregion
1397
+ //#region src/update/session.d.ts
1398
+ declare const initialSession: Session;
1399
+ /** A tool's defaults, by its preset: the engine fields its creates state. */
1400
+ declare const defaultsFor: (model: Model, preset: string) => FieldValues;
1401
+ /**
1402
+ * What a create from a tool starts from, before it has a shape: the tool's
1403
+ * defaults (by its preset) over the engine's for its `kind`, read as an
1404
+ * annotation. A ghost is drawn with its style (`styleOf`).
1405
+ */
1406
+ declare const toolAnnotation: (model: Model, kind: string, preset?: string) => Annotation;
1407
+ //#endregion
1408
+ //#region src/update/edit.d.ts
1409
+ /**
1410
+ * The live rotation of a rotate draft, snapping applied — the one angle rule
1411
+ * shared by the preview (`effGeom`), the commit (`editUp`) and the angle chip,
1412
+ * so they can never disagree. The selection's absolute angle (a single member's
1413
+ * `rot` + the raw pointer delta; a group's raw delta from 0) locks onto the
1414
+ * configured angles within the threshold; `free` (shift held) bypasses.
1415
+ * `delta` is what `geomRotateAbout` applies; `angle` is what the chip shows.
1416
+ */
1417
+ declare function rotateDraftDelta(model: Model, draft: Extract<Draft, {
1418
+ kind: 'rotate';
1419
+ }>): {
1420
+ delta: number;
1421
+ angle: number;
1422
+ snapped: boolean;
1423
+ };
1424
+ //#endregion
1425
+ //#region src/flags.d.ts
1426
+ /** The `/F` every freshly drawn annotation starts with: `print` set (Acrobat
1427
+ * parity — without it the annotation silently disappears when printed). */
1428
+ declare const DRAWN_FLAGS: AnnotationFlags;
1429
+ /** All ten flags, for iteration/equality — kept in the primitives' order. */
1430
+ declare const FLAG_KEYS: ReadonlyArray<keyof AnnotationFlags>;
1431
+ declare const flagsEqual: (left: AnnotationFlags, right: AnnotationFlags) => boolean;
1432
+ /** Merge a partial write over the current flags. */
1433
+ declare const mergeFlags: (base: AnnotationFlags, patch: Partial<AnnotationFlags>) => AnnotationFlags;
1434
+ /**
1435
+ * On-screen visibility. `hidden` beats everything, and `invisible` hides a type
1436
+ * that isn't standard; `noView` hides unless `toggleNoView` and the annotation
1437
+ * is engaged — the spec says hover/selection, and v1 uses selection (the model
1438
+ * tracks no hover).
1439
+ */
1440
+ declare const viewable: (annotation: Annotation, engaged?: boolean) => boolean;
1441
+ /** Any pointer interaction (click-to-select, hover). ReadOnly kills it. */
1442
+ declare const interactive: (flags: AnnotationFlags) => boolean;
1443
+ /** The part of a ModelAnnotation these predicates read. */
1444
+ interface FlagBearer {
1445
+ /** The record's annotation: its kind and `/F` flags are read off it. */
1446
+ annotation: Annotation;
1447
+ /** Session authority projected at ingest (permissions.md). Absent =
1448
+ * unstamped (drafts, wildcard local engines, tests) = allowed. */
1449
+ authority?: {
1450
+ update: boolean;
1451
+ delete: boolean;
1452
+ };
1453
+ }
1454
+ /** Interaction gate for a concrete annotation: widget kinds ignore `readOnly`
1455
+ * (ISO 32000 — a ReadOnly form field must still be movable by a form designer;
1456
+ * the form-filling layer enforces field ReadOnly itself). */
1457
+ declare const annotInteractive: (record: FlagBearer) => boolean;
1458
+ /**
1459
+ * Geometry/style mutations — `locked` freezes the object, not its contents
1460
+ * (that's `lockedContents`), and the session must hold update authority over
1461
+ * the record. One predicate for hit-test, chrome, and props alike, so a
1462
+ * record you may not edit renders and behaves exactly like a locked one.
1463
+ */
1464
+ declare const annotTransformable: (record: FlagBearer) => boolean;
1465
+ /** Deletion — the delete half of the authority split (a narrowed grant can
1466
+ * allow update but not delete, or vice versa). Flags gate like transforms. */
1467
+ declare const annotDeletable: (record: FlagBearer) => boolean;
1468
+ /** `/Contents` text edits — the contents counterpart of `locked`. */
1469
+ declare const annotContentsEditable: (record: FlagBearer) => boolean;
1470
+ //#endregion
1115
1471
  //#region src/anchor.d.ts
1116
1472
  /** Which display factors this annotation is exempt from: `zoom` =
1117
1473
  * screen-constant size, `upright` = screen-constant orientation. */
@@ -1120,46 +1476,46 @@ interface AnchorMode {
1120
1476
  upright: boolean;
1121
1477
  }
1122
1478
  /**
1123
- * The anchor behaviors of one annotation: its `/F` flags OR'd with the kind's
1479
+ * The anchor behaviors of one annotation: its `/F` flags or'd with the kind's
1124
1480
  * statics — the spec's "Text (note) annotations behave as if NoZoom and
1125
- * NoRotate are ALWAYS set", expressed as kind caps. Null when not anchored.
1481
+ * NoRotate are always set", expressed as kind caps. Null when not anchored.
1126
1482
  */
1127
- declare function anchorModeOf(a: FlagBearer): AnchorMode | null;
1128
- /** The fixed page point: the geometry's bounds top-left in content space
1129
- * (y-down) — which IS the spec's "upper-left corner of the annotation
1483
+ declare function anchorModeOf(record: FlagBearer): AnchorMode | null;
1484
+ /** The fixed page point: the geometry's bounds top-left in page space
1485
+ * (y-down) — which is the spec's "upper-left corner of the annotation
1130
1486
  * rectangle", since `/Rect` is emitted from these bounds. */
1131
- declare const anchorOf: (g: Geom) => Vec;
1487
+ declare const anchorOf: (geometry: Shape) => Point;
1132
1488
  /**
1133
- * THE effective content-space geometry of a screen-anchored body at `view`:
1489
+ * The effective page-space geometry of a screen-anchored body at `view`:
1134
1490
  * the stored geometry scaled by `1/max(zoom, 1)` about the anchor (`zoom`
1135
1491
  * exemption, Adobe-clamped) and counter-rotated by `-rotation` about it
1136
1492
  * (`upright`), so that after the page's own display transform the body reads
1137
1493
  * at its 100%-zoom size and its authored tilt, hanging from the fixed anchor.
1138
- * Returns `g` unchanged when there is nothing to do — callers apply it
1494
+ * Returns `geometry` unchanged when there is nothing to do — callers apply it
1139
1495
  * unconditionally.
1140
1496
  */
1141
- declare function anchoredGeom(g: Geom, mode: AnchorMode | null, view: ViewEnv | undefined): Geom;
1497
+ declare function anchoredGeom(geometry: Shape, mode: AnchorMode | null, view: ViewEnv | undefined): Shape;
1142
1498
  /**
1143
- * The exact inverse of {@link anchoredGeom} for a VIEW-space geometry a
1144
- * gesture produced: the stored geometry whose OWN projection is `target`.
1499
+ * The exact inverse of {@link anchoredGeom} for a view-space geometry a
1500
+ * gesture produced: the stored geometry whose own projection is `target`.
1145
1501
  *
1146
1502
  * Solving `anchoredGeom(stored) = target` where the projection anchors at
1147
1503
  * `stored`'s (unknown) bounds top-left `a` gives a closed form: map `target`
1148
1504
  * through the pure linear part (scale `s` + rotate `r` about the origin),
1149
1505
  * then translate so the result's bounds top-left `b` lands where the
1150
1506
  * projection needs it — at `a = R(-r)·b / s`, the unique fixed point. This is
1151
- * what makes a released gesture commit EXACTLY what its preview showed.
1507
+ * what makes a released gesture commit exactly what its preview showed.
1152
1508
  */
1153
- declare function unanchoredGeom(target: Geom, mode: AnchorMode | null, view: ViewEnv | undefined): Geom;
1509
+ declare function unanchoredGeom(target: Shape, mode: AnchorMode | null, view: ViewEnv | undefined): Shape;
1154
1510
  /** Stroke width in the projected (view) space: a `noZoom` body's line weight
1155
- * scales with its geometry so it stays screen-constant — through the SAME
1511
+ * scales with its geometry so it stays screen-constant — through the same
1156
1512
  * factors (incl. the ≤100% clamp) the geometry projects with. */
1157
1513
  declare const anchoredStrokeWidth: (width: number, mode: AnchorMode | null, view: ViewEnv | undefined) => number;
1158
1514
  /**
1159
1515
  * The similarity image of an axis-aligned box (a baked raster's AP `/Rect`)
1160
1516
  * under the anchor projection, as the blit vocabulary expects it: an
1161
- * UNROTATED box (the projected centre + scaled size) plus the rotation to
1162
- * re-apply about its centre. `anchor` is the OWNING GEOMETRY's anchor — the
1517
+ * unrotated box (the projected centre + scaled size) plus the rotation to
1518
+ * re-apply about its centre. `anchor` is the owning geometry's anchor — the
1163
1519
  * raster rides its annotation's projection, it doesn't anchor itself. Null
1164
1520
  * when the projection is the identity.
1165
1521
  */
@@ -1168,7 +1524,7 @@ declare function anchoredBox(box: {
1168
1524
  y: number;
1169
1525
  width: number;
1170
1526
  height: number;
1171
- }, anchor: Vec, mode: AnchorMode | null, view: ViewEnv | undefined): {
1527
+ }, anchor: Point, mode: AnchorMode | null, view: ViewEnv | undefined): {
1172
1528
  box: {
1173
1529
  x: number;
1174
1530
  y: number;
@@ -1178,75 +1534,84 @@ declare function anchoredBox(box: {
1178
1534
  rot: number;
1179
1535
  } | null;
1180
1536
  //#endregion
1181
- //#region src/update.d.ts
1182
- /** The click ↔ drag threshold (content units): a press-release whose width AND
1183
- * height both stay under it is a CLICK. Exported so every gesture owner (the
1184
- * draw handler, the form plugin's place handler) shares ONE definition. */
1185
- declare const MIN_DRAG = 3;
1186
- declare const initialStyle: Style;
1537
+ //#region src/update/marquee.d.ts
1538
+ /**
1539
+ * What a box on `page` selects: every annotation it touches
1540
+ * ({@link annotsInBox}), and the rest of the group of each. The marquee and
1541
+ * the capability's `selectInRect` both select this.
1542
+ */
1543
+ declare function selectionInBox(model: Model, page: PageRef, box: Rect, inert?: ReadonlySet<Id>, view?: ViewEnv): Id[];
1544
+ //#endregion
1545
+ //#region src/update/index.d.ts
1546
+ /** The model before any record or gesture: an empty view over the initial session. */
1187
1547
  declare const initialModel: Model;
1188
1548
  /**
1189
- * Resolve a tool's effective defaults as a FULL flat props bag: the base `style`
1190
- * + the font/endings base, with the per-tool override layered on top. This is
1191
- * what a defaults-editing UI reads, and what creation projects `style`/`text`
1192
- * from (`styleFromProps` / `textStyleFromProps`).
1549
+ * Run one message. The session is returned whole; the records are returned
1550
+ * as the difference the message made, so the caller decides where they live.
1193
1551
  */
1194
- declare function defaultsFor(m: Model, subtype: Subtype): AnnotationProps;
1552
+ declare function update(model: Model, message: Message): UpdateResult;
1195
1553
  /**
1196
- * The live rotation of a rotate draft, snapping applied — the ONE angle rule
1197
- * shared by the preview (`effGeom`), the commit (`editUp`) and the angle chip,
1198
- * so they can never disagree. The selection's ABSOLUTE angle (a single member's
1199
- * `rot` + the raw pointer delta; a group's raw delta from 0) locks onto the
1200
- * configured angles within the threshold; `free` (shift held) bypasses.
1201
- * `delta` is what `geomRotateAbout` applies; `angle` is what the chip shows.
1554
+ * The most records `message` can create: how many object numbers the session
1555
+ * must hold before it runs (`Session.objectNumbers`). A pointer press or
1556
+ * release of a creating tool can finish a drawing; a move never does.
1202
1557
  */
1203
- declare function rotateDraftDelta(m: Model, d: Extract<Draft, {
1204
- g: 'rotate';
1205
- }>): {
1206
- delta: number;
1207
- angle: number;
1208
- snapped: boolean;
1209
- };
1210
- declare function update(m: Model, msg: Msg): [Model, Effect[]];
1558
+ declare function newRecordsAtMost(message: Message): number;
1559
+ /** True when every session field is the same value: a message that changed nothing. */
1560
+ declare const sameSession: (left: Session, right: Session) => boolean;
1561
+ /** The change set of a message that changed no record. */
1562
+ declare const EMPTY_CHANGE: ChangeSet;
1211
1563
  //#endregion
1212
1564
  //#region src/placement.d.ts
1213
- /** A resolved click placement: what the click will occupy, page-clamped. */
1214
- type ClickPlacement = {
1215
- kind: 'box';
1216
- rect: Rect;
1217
- /** Upright counter-rotation captured for the commit (deg CW; 0 = none). */
1218
- rot: number;
1219
- } | {
1220
- kind: 'segment';
1221
- a: Vec;
1222
- b: Vec;
1223
- };
1224
- /** Slide a rect (as a unit) to sit inside `box`; pins at the origin edge when
1225
- * it doesn't fit. Placements are page-bound; the pointer isn't. */
1226
- declare const clampRectToBox: (r: Rect, box: Rect | undefined) => Rect;
1227
- /**
1228
- * Resolve a click-create POLICY at a content point. `anchor` defaults to
1229
- * `center`; under `upright` a box counter-rotates against the page's display
1230
- * rotation exactly as the drag commit would (centre-anchored boxes transpose
1231
- * about their centre, top-left boxes anchor in the DISPLAY frame).
1232
- */
1233
- declare function resolveClickPlacement(point: Vec, policy: ClickCreate, opts?: {
1565
+ /** The click ↔ drag threshold (content units): a press-release that stays
1566
+ * under it is a click. Every gesture owner (the draw handler, the form
1567
+ * plugin's place handler) shares this one definition. */
1568
+ declare const MIN_DRAG = 3;
1569
+ /** What a create gesture lays: a box (shapes, text boxes, form fields) or a segment (lines). */
1570
+ type GestureForm = 'box' | 'segment';
1571
+ /** The page a placement is made on, and how the person sees it. */
1572
+ interface PlacementFrame {
1573
+ /** The page's content box: a placement slides inside it. */
1234
1574
  pageBox?: Rect;
1575
+ /** Lay it out as the person sees the page: the tool's `upright`. */
1235
1576
  upright?: boolean;
1577
+ /** How far the page shows turned (document /Rotate + view rotation, degrees clockwise). */
1236
1578
  displayRotation?: PageRotation;
1237
- }): ClickPlacement;
1238
- /**
1239
- * ANNOTATION-ONLY: convert a placement into the `Geom` the commit stores and
1240
- * the ghost paints, for a routing kind. This is where annotation VISUAL
1241
- * semantics live — ellipse for circles, the cloudy outer-box via
1242
- * `shapeRectFor`. Forms never call this; a field box is the placement rect
1243
- * itself. Null for kinds a click cannot author.
1244
- */
1245
- declare function clickCreateGeom(subtype: Subtype, placement: ClickPlacement, def: AnnotationProps): Geom | null;
1579
+ }
1580
+ /** Slide a rect (as a unit) to sit inside `box`; pins at the origin edge when
1581
+ * it doesn't fit. Placements are page-bound; the pointer isn't. */
1582
+ declare const clampRectToBox: (rect: Rect, box: Rect | undefined) => Rect;
1583
+ /** Whether a gesture from `from` to `to` is a drag: a segment once it is
1584
+ * `MIN_DRAG` long, a box once either side is. */
1585
+ declare function isDrag(form: GestureForm, from: Point, to: Point): boolean;
1586
+ /**
1587
+ * Where a create gesture from `from` to `to` puts what it makes: the dragged
1588
+ * box or segment once it is a drag, else the tool's click default at `from`
1589
+ * (`null` when a click makes nothing). A drag stays on the page: its points
1590
+ * pin to the edge. An upright drag keeps the region the author dragged: under
1591
+ * a quarter turn the box before its turn is the dragged one with its sides
1592
+ * swapped about its middle, so the turn lands it back on that region.
1593
+ */
1594
+ declare function gesturePlacement(form: GestureForm, from: Point, to: Point, clickCreate: ClickCreate | false | undefined, frame?: PlacementFrame): Placement | null;
1595
+ /**
1596
+ * Where a bare click at `point` puts what the tool makes (see
1597
+ * {@link ClickCreate}), slid inside the page. Under `upright` it is laid out
1598
+ * as the person sees the page: a line's direction and a box's top-left corner
1599
+ * are the screen's, and a box turns to read upright, showing `width` ×
1600
+ * `height` as configured.
1601
+ */
1602
+ declare function resolveClickPlacement(point: Point, policy: ClickCreate, frame?: PlacementFrame): Placement;
1603
+ /**
1604
+ * What a create from a tool makes at `placement`: its kind's shape there, as
1605
+ * the kind's shape family lays it (`ShapeFamily.placed`), or `null` when the
1606
+ * kind can't be made that way (a line from a box). `annotation` is what the
1607
+ * tool creates before it has a shape (`toolAnnotation`): its kind, and what
1608
+ * the shape takes from it (a line's endings, a circle's roundness).
1609
+ */
1610
+ declare function placedShape(annotation: Annotation, placement: Placement): Shape | null;
1246
1611
  //#endregion
1247
1612
  //#region src/snap.d.ts
1248
1613
  interface SnapResult {
1249
- delta: Vec;
1614
+ delta: Point;
1250
1615
  guides: Guide[];
1251
1616
  }
1252
1617
  /**
@@ -1255,10 +1620,20 @@ interface SnapResult {
1255
1620
  * closest in-threshold pair per axis as an adjustment. Targets are the page box
1256
1621
  * (edges + center) and every non-moving annotation on the page.
1257
1622
  */
1258
- declare function computeMoveSnap(m: Model, ids: Id[], pon: number, raw: Vec, threshold: number, pageBox: Rect | undefined): SnapResult;
1623
+ declare function computeMoveSnap(model: Model, ids: Id[], page: PageRef, raw: Point, threshold: number, pageBox: Rect | undefined): SnapResult;
1259
1624
  //#endregion
1260
1625
  //#region src/view.d.ts
1261
- declare function pageItems(m: Model, pon: number, view?: ViewEnv): RenderItem[];
1626
+ /** A note's or file attachment's icon: what its live drawing draws. */
1627
+ declare const iconOf: (annotation: Annotation) => Pick<RenderItem, "icon">;
1628
+ /**
1629
+ * How an annotation not made yet paints, drawn as it will be once made: a
1630
+ * drawing in progress (`draft`) or the tool's ghost (`ghost`, what a click
1631
+ * would place). `annotation` is what the tool creates, its defaults and
1632
+ * flags; `shape` is where it goes, as the made one will store it, so a
1633
+ * screen-anchored icon shows at its size on screen, as a made one does.
1634
+ */
1635
+ declare function unmadeItem(id: Id, annotation: Annotation, shape: Shape, source: 'draft' | 'ghost', view?: ViewEnv, measure?: MeasurementAppearance): RenderItem;
1636
+ declare function pageItems(model: Model, page: PageRef, view?: ViewEnv): RenderItem[];
1262
1637
  /** One free-text box, geometry only (the live move/resize gesture applied), with
1263
1638
  * its edit flag. The framework renders an editable element here; the plugin
1264
1639
  * layers the DTO-derived text style on top. */
@@ -1266,95 +1641,129 @@ interface TextBox {
1266
1641
  id: Id;
1267
1642
  box: Rect;
1268
1643
  editing: boolean;
1269
- /** Applied rotation (deg, CW). `box` is the UNROTATED text box; the framework
1644
+ /** Applied rotation (deg, CW). `box` is the unrotated text box; the framework
1270
1645
  * rotates the editable element about its centre by this. 0/undefined = none. */
1271
1646
  rot?: number;
1272
1647
  }
1273
1648
  /** The free-text boxes on a page — the text counterpart of `pageItems`. */
1274
- declare function textBoxes(m: Model, pon: number, view?: ViewEnv): TextBox[];
1649
+ declare function textBoxes(model: Model, page: PageRef, view?: ViewEnv): TextBox[];
1650
+ /** One free text's box, live or baked: a renderer that draws the box itself
1651
+ * shows its text in every state, as no /AP image is drawn under it. `null`
1652
+ * for a record that isn't a text box. */
1653
+ declare function textBoxOf(model: Model, id: Id, view?: ViewEnv): TextBox | null;
1275
1654
  /** The currently selected annotations as render items (live gesture applied) —
1276
1655
  * cross-page, for selection-aware toolbars (style + line-ending editing). */
1277
- declare function selectedItems(m: Model, view?: ViewEnv): RenderItem[];
1656
+ declare function selectedItems(model: Model, view?: ViewEnv): RenderItem[];
1278
1657
  /**
1279
- * The rotate knob for the current selection on `pon` — the SAME knob `chrome`
1658
+ * The rotate knob for the current selection on `page` — the same knob `chrome`
1280
1659
  * draws — or null when the selection has none (non-rotatable single, or a group
1281
1660
  * whose caps aren't rotatable). A single shape's knob hangs off its OBB top edge;
1282
- * a group's off the union box. With `pageBox` the knob is placed PAGE-BOUND
1661
+ * a group's off the union box. With `pageBox` the knob is placed page-bound
1283
1662
  * (`placeRotateKnob`: flip below / clamp inside) — off-page it would be visible
1284
1663
  * but unreachable, since pointer dispatch resolves pages by containment. Shared
1285
1664
  * by `chrome` (to draw) and `selectionAnchor` (to push the menu clear of it), so
1286
1665
  * the two can never disagree on where it is.
1287
1666
  *
1288
- * The placement DECISION is made at rest; a gesture never re-decides — it RIDES.
1667
+ * The placement decision is made at rest; a gesture never re-decides — it rides.
1289
1668
  * During a live rotate the knob starts exactly where it was grabbed (the rest
1290
- * placement on the COMMITTED geometry) and turns rigidly about the pivot by the
1669
+ * placement on the committed geometry) and turns rigidly about the pivot by the
1291
1670
  * same snapped delta the shape uses; the policy re-evaluates only on release.
1292
1671
  * Re-deciding per frame would jump at grab and side-switch mid-spin.
1293
1672
  */
1294
- declare function selectionKnob(m: Model, pon: number, pageBox?: Rect, knobOffset?: number, view?: ViewEnv): {
1295
- at: Vec;
1296
- from: Vec;
1673
+ declare function selectionKnob(model: Model, page: PageRef, pageBox?: Rect, knobOffset?: number, view?: ViewEnv): {
1674
+ at: Point;
1675
+ from: Point;
1297
1676
  } | null;
1298
- declare function chrome(m: Model, pon: number, pageBox?: Rect, knobOffset?: number, view?: ViewEnv): ChromeNode[];
1299
- /** The content-space union of the selectable selected items on `pon`, or null if
1300
- * the page holds none. This is the SAME box the chrome outline draws, so a
1677
+ declare function chrome(model: Model, page: PageRef, pageBox?: Rect, knobOffset?: number, view?: ViewEnv): ChromeNode[];
1678
+ /** The page-space union of the selectable selected items on `page`, or null if
1679
+ * the page holds none. This is the same box the chrome outline draws, so a
1301
1680
  * floating menu sits exactly on the selection. */
1302
- declare function selectionBoundsOnPage(m: Model, pon: number, view?: ViewEnv): Rect | null;
1303
- /** The anchor for a selection-aware floating menu: the PRIMARY page (the first
1681
+ declare function selectionBoundsOnPage(model: Model, page: PageRef, view?: ViewEnv): Rect | null;
1682
+ /** The anchor for a selection-aware floating menu: the primary page (the first
1304
1683
  * selectable selected id) + the union box of the selection on that page (content
1305
1684
  * space). Null when nothing selectable is selected. A cross-page selection
1306
1685
  * anchors to its primary page, so there is exactly one menu. `pageBoxOf` is a
1307
1686
  * lookup (the anchor resolves its own page) feeding the page-bound knob
1308
- * placement — the menu then dodges the knob where it ACTUALLY sits. */
1309
- declare function selectionAnchor(m: Model, pageBoxOf?: (pon: number) => Rect | undefined, knobOffsetOf?: (pon: number) => number | undefined, viewOf?: (pon: number) => ViewEnv | undefined): {
1310
- pon: number;
1687
+ * placement — the menu then dodges the knob where it actually sits. */
1688
+ declare function selectionAnchor(model: Model, pageBoxOf?: (page: PageRef) => Rect | undefined, knobOffsetOf?: (page: PageRef) => number | undefined, viewOf?: (page: PageRef) => ViewEnv | undefined): {
1689
+ page: PageRef;
1311
1690
  bounds: Rect;
1312
- knob?: Vec;
1691
+ knob?: Point;
1313
1692
  } | null;
1314
1693
  /** Anchor for controls that finish/cancel an active multi-click creation draft.
1315
1694
  * It is rect-based like selectionAnchor, using the committed vertices only so
1316
1695
  * the menu remains stable while the hover preview follows the pointer. */
1317
- declare function creationDraftAnchor(m: Model): CreationDraftAnchor | null;
1696
+ /**
1697
+ * The rotation in progress, for the badge that follows the pointer: `null`
1698
+ * when nothing is being turned. The badge is a screen overlay, so it stays
1699
+ * upright however the page is shown.
1700
+ */
1701
+ declare function rotationAnchor(model: Model): RotationAnchor | null;
1702
+ declare function creationDraftAnchor(model: Model): CreationDraftAnchor | null;
1703
+ /**
1704
+ * Where UI attaches to one annotation: its page and the box around what it
1705
+ * shows, a gesture in progress included, so an overlay follows a drag. `null`
1706
+ * for a record the model doesn't have.
1707
+ */
1708
+ declare function annotationAnchor(model: Model, id: Id, view?: ViewEnv): {
1709
+ page: PageRef;
1710
+ bounds: Rect;
1711
+ } | null;
1318
1712
  //#endregion
1319
1713
  //#region src/hit.d.ts
1320
1714
  type Target = {
1321
- t: 'handle';
1715
+ kind: 'handle';
1322
1716
  id: Id;
1323
1717
  handle: string;
1324
1718
  cursor: Cursor;
1325
1719
  } | {
1326
- t: 'rotate';
1720
+ kind: 'rotate';
1327
1721
  ids: Id[];
1328
- pivot: Vec;
1722
+ pivot: Point;
1329
1723
  } | {
1330
- t: 'group-handle';
1724
+ kind: 'group-handle';
1331
1725
  ids: Id[];
1332
1726
  handle: string;
1333
1727
  cursor: Cursor;
1334
1728
  box: Rect;
1335
1729
  } | {
1336
- t: 'annot';
1730
+ kind: 'annot';
1337
1731
  id: Id;
1338
1732
  } | {
1339
- t: 'empty';
1733
+ kind: 'empty';
1340
1734
  };
1341
1735
  /** Can this annotation be clicked to select? (`/F` interaction flags override
1342
- * all caps — hidden/noView/readOnly are inert; LOCKED stays selectable, it
1736
+ * all caps — hidden/noView/readOnly are inert; locked stays selectable, it
1343
1737
  * just won't transform.) */
1344
- declare const isSelectable: (m: Model, id: Id) => boolean;
1345
- /** Can this annotation be dragged by its body to move? (`locked` freezes it.) */
1346
- declare const canMove: (m: Model, id: Id) => boolean;
1738
+ declare const isSelectable: (model: Model, id: Id) => boolean;
1739
+ /** Can this annotation be dragged by its body to move? (`locked` and the
1740
+ * session's `editable` rule freeze it.) */
1741
+ declare const canMove: (model: Model, id: Id) => boolean;
1742
+ /**
1743
+ * What an annotation paints where the pointer meets it (`painted.ts`): its
1744
+ * shape's pieces, projected as a screen-anchored body shows at `view`, and a
1745
+ * measurement's caption, dimension line and leaders. A click and the marquee
1746
+ * both read this.
1747
+ */
1748
+ declare function paintedOf(record: ModelAnnotation, view?: ViewEnv): readonly PaintedPiece[];
1347
1749
  /**
1348
- * What's under the content point.
1750
+ * Is `point` on a text box's own box, where its text is? Not on a callout's
1751
+ * line, and not in the empty rest of its frame. `margin` reaches the resize
1752
+ * handles on its border. The box is tested where the page shows it, as a
1753
+ * click tests it.
1754
+ */
1755
+ declare function isOnTextBox(record: ModelAnnotation, point: Point, margin: number, view?: ViewEnv): boolean;
1756
+ /**
1757
+ * What's under the page point.
1349
1758
  * 1. a resize/vertex handle of the single selection,
1350
- * 2. an editable annotation body — a SELECTED one anywhere in its bounds (so you
1351
- * can drag to move it), an UNSELECTED one only on its stroke/fill (margin-aware,
1759
+ * 2. an editable annotation body — a selected one anywhere in its bounds (so you
1760
+ * can drag to move it), an unselected one only on its stroke/fill (margin-aware,
1352
1761
  * so an unfilled circle is grabbed only on its outline),
1353
1762
  * 3. else empty.
1354
1763
  */
1355
- declare function hitTest(m: Model, pon: number, p: Vec, geom: ChromeGeom, strokeMargin: number, pageBox?: Rect, inert?: ReadonlySet<Id>, view?: ViewEnv): Target;
1764
+ declare function hitTest(model: Model, page: PageRef, point: Point, chromeGeometry: ChromeGeometry, strokeMargin: number, pageBox?: Rect, inert?: ReadonlySet<Id>, view?: ViewEnv): Target;
1356
1765
  /** The cursor to show on hover: a resize cursor over a handle, move/pointer over a body. */
1357
- declare function cursorAt(m: Model, pon: number, p: Vec, geom: ChromeGeom, strokeMargin: number, pageBox?: Rect, inert?: ReadonlySet<Id>, view?: ViewEnv): Cursor | null;
1766
+ declare function cursorAt(model: Model, page: PageRef, point: Point, geometry: ChromeGeometry, strokeMargin: number, pageBox?: Rect, inert?: ReadonlySet<Id>, view?: ViewEnv): Cursor | null;
1358
1767
  //#endregion
1359
1768
  //#region src/richtext.d.ts
1360
1769
  type RichTextStyleDelta = Partial<RichTextRunStyle>;
@@ -1368,7 +1777,7 @@ declare function richTextLength(doc: {
1368
1777
  paragraphs: readonly RichTextParagraph[];
1369
1778
  }): number;
1370
1779
  /** Two deltas override the same properties with the same values. */
1371
- declare function sameStyleDelta(a: RichTextStyleDelta | undefined, b: RichTextStyleDelta | undefined): boolean;
1780
+ declare function sameStyleDelta(left: RichTextStyleDelta | undefined, right: RichTextStyleDelta | undefined): boolean;
1372
1781
  /**
1373
1782
  * Merge adjacent runs whose deltas agree, drop empty runs, strip empty
1374
1783
  * deltas. A paragraph with no text keeps a single empty run so the editor
@@ -1419,13 +1828,38 @@ declare function plainTextOf(doc: {
1419
1828
  /** True when nothing in the document overrides the body: no run delta, no
1420
1829
  * paragraph property. Such a document commits as plain `contents`. */
1421
1830
  declare function isPlainRichText(doc: RichTextDocumentInput): boolean;
1831
+ /** The registered fonts an engine knows (the local engine's `fonts.list()`;
1832
+ * none on the cloud engine). */
1833
+ type FontLookup = () => readonly FontHandle[];
1834
+ /** The face a DTO font names: a standard font's family, weight and italic, a
1835
+ * registered key's identity, else the string itself as a family. */
1836
+ declare const faceForFont: (font: string, fonts?: FontLookup) => FaceRequest;
1837
+ /** The rich body the `/DA` text style describes (a draft's body before its
1838
+ * DTO exists; also the fallback for a DTO without `richText`). */
1839
+ declare function bodyFromTextStyle(style: TextStyle, fonts?: FontLookup): RichTextBody;
1840
+ /** The annotation's rich document: a free text's own, else one made from
1841
+ * its plain text and how its text is set. */
1842
+ declare function richDocOf(annotation: Annotation, fonts?: FontLookup): RichTextDocument;
1843
+ //#endregion
1844
+ //#region src/font-css.d.ts
1845
+ type StandardFamily = (typeof STANDARD_FACES)[keyof typeof STANDARD_FACES]['family'];
1846
+ /** Web stacks with the standard families' metrics. */
1847
+ declare const STANDARD_FONT_STACKS: Readonly<Record<StandardFamily, string>>;
1848
+ /**
1849
+ * The family `mountWebFont` (`@embedpdf/web`) mounts a registered font's key
1850
+ * under: namespaced, so a page's own family of the same name never stands in
1851
+ * for it.
1852
+ */
1853
+ declare const mountedFontFamily: (key: string) => string;
1854
+ /** The CSS family list for a DTO font: a standard kebab name or a registered key. */
1855
+ declare function cssFontFamilyForFont(font: string): string;
1422
1856
  //#endregion
1423
1857
  //#region src/group.d.ts
1424
1858
  /**
1425
1859
  * The transform capabilities of a multi-target (group) selection: a group can
1426
- * move/resize/rotate only if EVERY member's kind allows that group op AND no
1427
- * member is locked. This is the SINGLE resolver every adapter consumes (v2
1428
- * duplicated the `.every(...)` across three framework components). The iso-vs-
1860
+ * move/resize/rotate only if every member's kind allows that group op and no
1861
+ * member is locked or frozen by the session's `editable` rule. This is the single resolver every adapter consumes, so no
1862
+ * framework component re-derives the rule. The iso-vs-
1429
1863
  * aniso resize choice is computed separately, per-gesture, since it depends on
1430
1864
  * the live `rot` of the members, not the static caps.
1431
1865
  */
@@ -1434,135 +1868,162 @@ interface GroupCaps {
1434
1868
  resizable: boolean;
1435
1869
  rotatable: boolean;
1436
1870
  }
1437
- declare function groupCaps(m: Model, ids: Id[]): GroupCaps;
1871
+ declare function groupCaps(model: Model, ids: Id[]): GroupCaps;
1438
1872
  /**
1439
1873
  * The key of the group `id` belongs to, or `null` when it is ungrouped. A
1440
- * subordinate's key is its `group` field; a primary's key is its own id (it is
1441
- * the target of at least one member's `group`). An annotation that is neither a
1874
+ * subordinate's key is its `groupOf`; a primary's key is its own id (it is the
1875
+ * target of at least one member's `groupOf`). An annotation that is neither a
1442
1876
  * subordinate nor the target of any subordinate is ungrouped.
1443
1877
  */
1444
- declare function groupKeyOf(m: Model, id: Id): Id | null;
1878
+ declare function groupKeyOf(model: Model, id: Id): Id | null;
1445
1879
  /**
1446
1880
  * Every member of the group containing `id` (primary first), or just `[id]`
1447
1881
  * when it is ungrouped. The primary is the annotation whose id is the key; the
1448
- * rest are everything whose `group` equals the key, in `order`.
1882
+ * rest are everything whose `groupOf` equals the key, in `order`.
1449
1883
  */
1450
- declare function groupMembers(m: Model, id: Id): Id[];
1884
+ declare function groupMembers(model: Model, id: Id): Id[];
1451
1885
  /** Union of every id's full group — the selection seen as whole groups. */
1452
- declare function expandGroups(m: Model, ids: Id[]): Id[];
1886
+ declare function expandGroups(model: Model, ids: Id[]): Id[];
1453
1887
  //#endregion
1454
1888
  //#region src/plane.d.ts
1455
1889
  /** True when this annotation belongs to the conversation plane only —
1456
1890
  * never painted, hit, marquee-selected, or counted into a page's
1457
1891
  * appearance epoch. */
1458
- declare function isConversationOnly(a: Pick<Annot, 'irt' | 'group' | 'subtype' | 'data'>): boolean;
1892
+ declare function isConversationOnly(record: Pick<ModelAnnotation, 'annotation'>): boolean;
1459
1893
  /**
1460
- * A link CHILD attached to another annotation — a `/Link` grouped
1894
+ * A link child attached to another annotation — a `/Link` grouped
1461
1895
  * (`/IRT` + `/RT /Group`) under a non-link parent. Pure substrate: it is
1462
- * the parent's `link` PROPERTY while authoring (derived via `linkOf`) and
1896
+ * the parent's `link` property while authoring (derived via `linkOf`) and
1463
1897
  * the navigation plane's anchor while reading — never painted, never hit
1464
- * as itself. Its rect is RECONCILED from the parent's geometry, so direct
1898
+ * as itself. Its rect is reconciled from the parent's geometry, so direct
1465
1899
  * manipulation would be overwritten anyway.
1466
1900
  */
1467
- declare function isAttachedLink(a: Pick<Annot, 'subtype' | 'group'>): boolean;
1901
+ declare function isAttachedLink(record: Pick<ModelAnnotation, 'annotation'>): boolean;
1468
1902
  /**
1469
- * The ONE page-surface cull: everything that lives in the substrate but is
1903
+ * The one page-surface cull: everything that lives in the substrate but is
1470
1904
  * not a page visual of its own — conversation members (replies, review
1471
1905
  * states) and attached link children. Paint order, hit-testing, marquee
1472
- * and the appearance epoch all filter through THIS, never the parts.
1906
+ * and the appearance epoch all filter through this, never the parts.
1473
1907
  */
1474
- declare function isSubstrateOnly(a: Pick<Annot, 'irt' | 'group' | 'subtype' | 'data'>): boolean;
1908
+ declare function isSubstrateOnly(record: Pick<ModelAnnotation, 'annotation'>): boolean;
1475
1909
  //#endregion
1476
1910
  //#region src/links.d.ts
1477
1911
  /** Every attached link child of `parentId`, in z-order (multi-segment
1478
1912
  * markup parents carry several children with one shared target). */
1479
- declare function linkChildrenOf(m: Model, parentId: Id): Annot[];
1913
+ declare function linkChildrenOf(model: Model, parentId: Id): ModelAnnotation[];
1480
1914
  /** The parent's link target, derived from its first attached child — the
1481
1915
  * read side of the `syncLink` reconciler. Null when no child exists. */
1482
- declare function linkOf(m: Model, parentId: Id): PdfLinkTarget | null;
1916
+ declare function linkOf(model: Model, parentId: Id): PdfLinkTarget | null;
1483
1917
  //#endregion
1484
- //#region src/kinds.d.ts
1918
+ //#region src/shapes/index.d.ts
1485
1919
  /**
1486
- * One editable property of a kind, as a UI contract: which {@link AnnotationProps}
1487
- * key, rendered how (the union arm fixes the control + its constraints), labelled
1488
- * what by default. A property sidebar is a `switch (spec.key)` over these — the
1489
- * per-kind lists below are the v2 snippet's hand-rolled `TOOL_PROPERTIES` schema,
1490
- * promoted into the library so every consumer gets it for free.
1491
- *
1492
- * `label` is a default (English) display name — apps with i18n map `key`s to
1493
- * their own strings and ignore it. Array ORDER is display order.
1920
+ * The family that answers for `shape`. Each family works on its own shapes
1921
+ * only; looking it up by the shape's `kind` is what hands it one.
1494
1922
  */
1495
- type PropSpec = {
1496
- key: 'color';
1497
- label: string;
1498
- } | {
1499
- key: 'interiorColor';
1500
- label: string;
1501
- } | {
1502
- key: 'fontColor';
1503
- label: string;
1504
- } | {
1505
- key: 'opacity';
1506
- label: string;
1507
- min: number;
1508
- max: number;
1509
- step: number;
1510
- } | {
1511
- key: 'strokeWidth';
1512
- label: string;
1513
- min: number;
1514
- max: number;
1515
- step: number;
1516
- } | {
1517
- key: 'fontSize';
1518
- label: string;
1519
- min: number;
1520
- max: number;
1521
- step: number;
1923
+ declare const familyOf: (shape: Shape) => ShapeFamily;
1924
+ /**
1925
+ * The family of a kind whose annotation says which shape it has: `pick`
1926
+ * names the family that reads it, and the shape's own family does the rest.
1927
+ */
1928
+ declare function familyChosenBy(pick: (annotation: Annotation) => ShapeFamily): ShapeFamily;
1929
+ //#endregion
1930
+ //#region src/kinds/define.d.ts
1931
+ /** The flags an annotation carries, each a `flag` property of every kind. */
1932
+ type FlagKey = 'invisible' | 'hidden' | 'print' | 'noZoom' | 'noRotate' | 'noView' | 'readOnly' | 'locked' | 'toggleNoView' | 'lockedContents';
1933
+ /**
1934
+ * One choice of a `choice` property, as a picker shows it: the value it
1935
+ * writes, and its name, `label` in English and `labelKey` to translate it
1936
+ * (`annotation.lineEndings.openArrow`).
1937
+ */
1938
+ interface AnnotationOption {
1939
+ readonly value: string;
1940
+ readonly label: string;
1941
+ readonly labelKey: string;
1942
+ }
1943
+ /**
1944
+ * One property a style panel edits, as a UI contract: which field, edited
1945
+ * with which control, labelled what. A panel switches on `control` (or on
1946
+ * `key`, for a richer editor of its own), reads `values[property.key]` and
1947
+ * writes `{ [property.key]: value }`, so each kind's list is its property
1948
+ * schema, kept in the library so every consumer gets it for free.
1949
+ *
1950
+ * `key` is the engine's field name, with three exceptions: the border's
1951
+ * `borderStyle` reads and writes `borderStyle`, `dashArray` and
1952
+ * `cloudyIntensity` together; `bold`, `italic` and `underline` are the rich
1953
+ * body's formatting; and `link` is an attached link, not a field of the
1954
+ * annotation.
1955
+ *
1956
+ * `label` is the English name and `labelKey` its translation key: a panel
1957
+ * shows `t(property.labelKey, { fallback: property.label })`, as a command's
1958
+ * label reads. The key names the wording, not the field, since one field is
1959
+ * called different things by different kinds: `color` is
1960
+ * `annotation.style.stroke` on a shape and `annotation.style.outline` on a
1961
+ * redaction mark. A `choice` property's options carry the same pair. Array
1962
+ * order is display order.
1963
+ */
1964
+ type AnnotationProperty = {
1965
+ readonly key: 'color' | 'interiorColor' | 'fontColor';
1966
+ readonly control: 'color';
1967
+ readonly label: string;
1968
+ readonly labelKey: string;
1969
+ } | {
1970
+ readonly key: 'opacity' | 'strokeWidth' | 'fontSize';
1971
+ readonly control: 'number';
1972
+ readonly label: string;
1973
+ readonly labelKey: string;
1974
+ readonly min: number;
1975
+ readonly max: number;
1976
+ readonly step: number;
1522
1977
  } |
1523
- /** Border style picker; `cloudy` says whether this kind honours a cloudy border. */
1978
+ /** The border over `borderStyle`, `dashArray` and `cloudyIntensity`;
1979
+ * `cloudy` says whether this kind takes a cloudy border. */
1524
1980
  {
1525
- key: 'border';
1526
- label: string;
1527
- cloudy: boolean;
1528
- } | {
1529
- key: 'lineEndings';
1530
- label: string;
1531
- } | {
1532
- key: 'fontFamily';
1533
- label: string;
1534
- } | {
1535
- key: 'textAlign';
1536
- label: string;
1981
+ readonly key: 'borderStyle';
1982
+ readonly control: 'choice';
1983
+ readonly label: string;
1984
+ readonly labelKey: string;
1985
+ readonly options: readonly AnnotationOption[];
1986
+ readonly cloudy: boolean;
1537
1987
  } |
1538
- /** Rich-text formatting toggles (free text): the body's formatting, or —
1539
- * while the text editor has a range — that range's runs. */
1988
+ /** A value from a list: line endings, a font, an alignment, a blend mode, an icon. */
1540
1989
  {
1541
- key: 'bold';
1542
- label: string;
1543
- } | {
1544
- key: 'italic';
1545
- label: string;
1546
- } | {
1547
- key: 'underline';
1548
- label: string;
1549
- } | {
1550
- key: 'blendMode';
1551
- label: string;
1990
+ readonly key: 'lineEndings' | 'fontFamily' | 'textAlign' | 'blendMode' | 'icon';
1991
+ readonly control: 'choice';
1992
+ readonly label: string;
1993
+ readonly labelKey: string;
1994
+ readonly options: readonly AnnotationOption[];
1995
+ } |
1996
+ /** One of the annotation's flags, on or off. */
1997
+ {
1998
+ readonly key: FlagKey;
1999
+ readonly control: 'flag';
2000
+ readonly label: string;
2001
+ readonly labelKey: string;
2002
+ } |
2003
+ /** Free text, such as a redaction's label. */
2004
+ {
2005
+ readonly key: 'overlayText';
2006
+ readonly control: 'text';
2007
+ readonly label: string;
2008
+ readonly labelKey: string;
1552
2009
  } |
1553
- /** `/Name` icon picker for icon kinds; `options` are the legal names. */
2010
+ /** Bold, italic or underline of a text box: the body's formatting, or,
2011
+ * while the text editor has a range, that range's runs. */
1554
2012
  {
1555
- key: 'icon';
1556
- label: string;
1557
- options: readonly string[];
2013
+ readonly key: 'bold' | 'italic' | 'underline';
2014
+ readonly control: 'textFormat';
2015
+ readonly label: string;
2016
+ readonly labelKey: string;
2017
+ readonly format: 'bold' | 'italic' | 'underline';
1558
2018
  } |
1559
- /** Link-target editor (URL / page destination). Declared by the link kind
1560
- * (its own target) and by every kind that may carry an ATTACHED link;
1561
- * kinds that omit it (widgets, caret, redact…) simply cannot be links —
1562
- * `applyProps` drops the key and menus never show the control. */
2019
+ /** Where it links to (a website or a page). Declared by the link kind (its
2020
+ * own target) and by every kind that may carry an attached link; kinds
2021
+ * that omit it (widgets, caret, redaction…) can't be links. */
1563
2022
  {
1564
- key: 'link';
1565
- label: string;
2023
+ readonly key: 'link';
2024
+ readonly control: 'link';
2025
+ readonly label: string;
2026
+ readonly labelKey: string;
1566
2027
  };
1567
2028
  /** Orthogonal capability flags. Static data — the annotation's `/F` flags are
1568
2029
  * the runtime overrides (a locked annotation is never transformable, a hidden
@@ -1578,13 +2039,13 @@ interface KindCaps {
1578
2039
  vertexEditable: boolean;
1579
2040
  /** Can be rotated (shapes, free text, lines/polys/ink). */
1580
2041
  rotatable: boolean;
1581
- /** Can be MOVED as part of a multi-target (group) transform. */
2042
+ /** Can be moved as part of a multi-target (group) transform. */
1582
2043
  groupMovable: boolean;
1583
- /** Can be uniformly SCALED as part of a multi-target (group) transform — ON
2044
+ /** Can be uniformly scaled as part of a multi-target (group) transform — on
1584
2045
  * even for vertex kinds that have no single-shape box resize (their handles
1585
- * ARE the vertices; in a group they scale fine). */
2046
+ * are the vertices; in a group they scale fine). */
1586
2047
  groupResizable: boolean;
1587
- /** Can be ROTATED as part of a multi-target (group) transform. */
2048
+ /** Can be rotated as part of a multi-target (group) transform. */
1588
2049
  groupRotatable: boolean;
1589
2050
  /** Carries editable text content (free text, the comment popup). */
1590
2051
  textEditable: boolean;
@@ -1594,6 +2055,8 @@ interface KindCaps {
1594
2055
  hasPopup: boolean;
1595
2056
  /** Bound to underlying text (markup, caret) — never freely moved/resized. */
1596
2057
  anchored: boolean;
2058
+ /** Paints with the page's text, beneath every other kind (text markup, redaction marks). */
2059
+ paintsBeneath: boolean;
1597
2060
  /** Has an interior fill (`/IC`). */
1598
2061
  hasFill: boolean;
1599
2062
  /** Has line endings (`/LE` — line, polyline). */
@@ -1601,10 +2064,15 @@ interface KindCaps {
1601
2064
  /** Can take a cloudy border effect (`/BE` — shapes). */
1602
2065
  hasCloudy: boolean;
1603
2066
  /** The whole body is visible content, so hit-testing grabs anywhere inside
1604
- * the box (stamp images) — NOT just the stroke/fill like outline shapes. */
2067
+ * the box (stamp images, note icons) — not just the stroke/fill like
2068
+ * outline shapes. */
1605
2069
  opaqueBody: boolean;
1606
- /** The `/F` ReadOnly flag is IGNORED for this kind (ISO 32000: widgets — a
1607
- * ReadOnly form FIELD must still be movable by a form designer; the
2070
+ /** Its raster is its drawing, drawn where its shape is: the kind draws none
2071
+ * of it live (a stamp's image, a form widget, a link's border). A lent
2072
+ * record can still draw live with its lender's painter (`drawsLive`). */
2073
+ rasterOnly: boolean;
2074
+ /** The `/F` ReadOnly flag is ignored for this kind (ISO 32000: widgets — a
2075
+ * ReadOnly form field must still be movable by a form designer; the
1608
2076
  * form-filling layer enforces field ReadOnly itself). */
1609
2077
  ignoresReadOnly: boolean;
1610
2078
  /** Behaves as if `/F` NoZoom is always set (screen-constant size — the
@@ -1614,270 +2082,240 @@ interface KindCaps {
1614
2082
  * rule for Text/note icons). See anchor.ts. */
1615
2083
  noRotate: boolean;
1616
2084
  }
2085
+ /** No capability at all: a kind spreads this and turns on what it can do. */
2086
+ declare const NO_CAPS: KindCaps;
2087
+ /** One annotation kind: what it is, and what a user can do to it. */
1617
2088
  interface AnnotationKind {
1618
- subtype: Subtype;
1619
- /** Future: PDF `/IT` intent (free-text vs callout, caret insert vs replace). */
1620
- intent?: string;
1621
- variant: Geom['t'];
1622
- caps: KindCaps;
1623
- /** The kind's editable properties, in DISPLAY ORDER — the contract a property
1624
- * sidebar renders from (see {@link PropSpec}). Empty = nothing to edit. */
1625
- props: PropSpec[];
2089
+ /** Its name: the PDF subtype, or a widget's field family (`widget-text`…). */
2090
+ readonly name: string;
2091
+ /**
2092
+ * The shape family that reads its shape off the annotation (`shapes/`).
2093
+ * The shape's family then hits, handles, moves and draws it.
2094
+ */
2095
+ readonly family: ShapeFamily;
2096
+ /**
2097
+ * How it is drawn: its colours, stroke and border, read off the annotation
2098
+ * with its kind's fill-ins (`styles.ts`).
2099
+ */
2100
+ readonly style: (annotation: Annotation) => Style;
2101
+ /**
2102
+ * How its text is set, for a kind with text: its font, size, colour and
2103
+ * alignment, read off the annotation (`texts.ts`).
2104
+ */
2105
+ readonly text?: (annotation: Annotation) => TextStyle;
2106
+ /** What a user can do to it. The annotation's `/F` flags override these at runtime (flags.ts). */
2107
+ readonly caps: KindCaps;
2108
+ /**
2109
+ * What a style panel edits, in display order, keyed by the engine's field
2110
+ * names. The flags every kind has are added by `propertiesOf`.
2111
+ */
2112
+ readonly properties: readonly AnnotationProperty[];
1626
2113
  }
1627
- /** The built-in kinds. Shapes resize, lines/polys vertex-edit, markup is anchored
1628
- * (selectable + recolor/delete, never move/resize). */
1629
- declare const KINDS: Record<string, AnnotationKind>;
1630
- /** The capabilities of a subtype, or the read-only default for unknown kinds. */
1631
- declare const capsFor: (subtype: string) => KindCaps;
1632
- /** A kind's editable properties in display order — empty for unknown kinds.
1633
- * Stable references, so selectors can compare by identity. */
1634
- declare const propsFor: (subtype: string) => PropSpec[];
2114
+ /**
2115
+ * Declares a kind. It returns the kind exactly as written: nothing is
2116
+ * registered and nothing is filled in. `index.ts` lists every kind.
2117
+ */
2118
+ declare const defineKind: <const Kind extends AnnotationKind>(kind: Kind) => Kind;
2119
+ //#endregion
2120
+ //#region src/kinds/index.d.ts
2121
+ /** The kinds, by name. */
2122
+ declare const KINDS: Readonly<Record<string, AnnotationKind>>;
2123
+ /** The kind named `name`; a name no kind has reads as one that is only shown. */
2124
+ declare function kindNamed(name: string): AnnotationKind;
1635
2125
  //#endregion
1636
2126
  //#region src/props.d.ts
1637
2127
  /** Base text styling for kinds/tools with no explicit defaults. */
1638
2128
  declare const initialTextStyle: TextStyle;
1639
- /** The `Style` slice of a resolved props bag (the create-time projection). */
1640
- declare const styleFromProps: (p: AnnotationProps) => Style;
1641
- /** The `TextStyle` slice of a resolved props bag (the create-time projection). */
1642
- declare const textStyleFromProps: (p: AnnotationProps) => TextStyle;
2129
+ /** Does this kind's property table declare `link` (attachable link)? The link
2130
+ * kind itself also declares it, but stores its own `/A` instead. */
2131
+ declare const kindTakesLink: (kind: AnnotationKind) => boolean;
2132
+ /**
2133
+ * Everything a style panel edits for one kind: its own properties, then the
2134
+ * flags every kind has. The same array for a kind on every call.
2135
+ */
2136
+ declare function propertiesOf(kind: AnnotationKind): readonly AnnotationProperty[];
1643
2137
  /**
1644
- * Read one property off an annotation — from wherever it lives — or `undefined`
1645
- * when the annotation's kind doesn't carry it (a `fontSize` on a square, endings
1646
- * on a polygon). The read side of `applyProps`.
2138
+ * The ordered properties every given kind has: the schema for a mixed
2139
+ * selection, in the first kind's display order. One kind: its own list
2140
+ * (`propertiesOf`), the same array every time.
1647
2141
  */
1648
- declare function readProp<K extends PropKey>(a: Annot, key: K): AnnotationProps[K] | undefined;
2142
+ declare function sharedProperties(kinds: readonly AnnotationKind[]): readonly AnnotationProperty[];
2143
+ //#endregion
2144
+ //#region src/kinds/fields.d.ts
2145
+ /** Every line ending the engine draws. */
2146
+ declare const LINE_ENDING_NAMES: readonly ["none", "square", "circle", "diamond", "open-arrow", "closed-arrow", "butt", "r-open-arrow", "r-closed-arrow", "slash"];
2147
+ /** The flags every kind has, in the order a panel lists them. */
2148
+ declare const FLAG_PROPERTIES: readonly AnnotationProperty[];
2149
+ //#endregion
2150
+ //#region src/record/defaults.d.ts
2151
+ /** The engine subtype a client kind creates: a callout is a free text, a form tool's kind a widget. */
2152
+ declare const engineSubtypeOf: (kind: string) => AnnotationSubtype;
1649
2153
  /**
1650
- * Apply a property patch to one annotation, honouring its kind's declared keys.
1651
- * Returns the changed annotation, or `null` when nothing applied (locked /
1652
- * read-only per its `/F` flags, or no declared key in the patch) — so the
1653
- * caller emits no spurious engine write. Flags themselves are NOT props: they
1654
- * write through the `setFlags` message, which is deliberately not gated here
1655
- * (unlocking must work on a locked annotation).
2154
+ * A tool's `defaults` over the engine's defaults for `kind`: what a create
2155
+ * from the tool starts from. A form tool's reads as a widget of its field
2156
+ * family, so it reads as the tool's own kind (a radio tool's is round).
1656
2157
  */
1657
- declare function applyProps(a: Annot, patch: AnnotationPropsPatch): Annot | null;
2158
+ declare function readOfDefaults(kind: string, defaults: FieldValues): Annotation;
1658
2159
  /**
1659
- * The ordered property specs EVERY given kind declares — the schema for a mixed
1660
- * selection, in the FIRST kind's display order. One kind → its own list, verbatim.
2160
+ * A form tool's defaults as the engine's widget appearance for `doc.forms`
2161
+ * authoring: the widget's style fields among them, as they are. Absent fields
2162
+ * stay absent (the engine writes nothing for them).
1661
2163
  */
1662
- declare function sharedProps(subtypes: readonly string[]): PropSpec[];
2164
+ declare function widgetAppearanceOf(defaults: FieldValues): WidgetAppearance;
1663
2165
  //#endregion
1664
2166
  //#region src/geometry.d.ts
1665
- type RectHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
1666
- declare const RECT_HANDLES: RectHandle[];
1667
- /** The resize cursor for a handle on a box tilted by `rot` deg: rotate the
1668
- * handle's compass direction with the box and pick the nearest 45° sector.
1669
- * At `rot = 0` this reproduces {@link RECT_CURSOR} exactly. */
1670
- declare const rotatedHandleCursor: (h: RectHandle, rot: number) => Cursor;
1671
- declare const rectFromPoints: (a: Vec, b: Vec) => Rect;
1672
- declare function unionRect(pts: Vec[]): Rect;
1673
2167
  /** How far (content units) the rotate knob hangs off the top edge of the box. */
1674
2168
  declare const ROTATE_KNOB_OFFSET = 24;
1675
2169
  /** Fallback chrome geometry (content units) when the caller supplies none —
1676
2170
  * the pre-settings behavior, so bare-core callers and tests stay stable. */
1677
- declare const DEFAULT_CHROME_GEOM: {
2171
+ declare const DEFAULT_CHROME_GEOMETRY: {
1678
2172
  readonly handleTol: 6;
1679
2173
  readonly knobTol: 6;
1680
2174
  readonly knobOffset: 24;
1681
2175
  };
1682
- /** Normalize degrees into `[0, 360)`. */
1683
- declare const normalizeDeg: (d: number) => number;
1684
- /** A geom's applied rotation (deg), or 0 for the non-rotatable kinds. A
1685
- * caret's `rot` is AUTHORING metadata (its text's baseline tilt): reported
1686
- * here so the renderer and selection chrome follow it, while the caret's
1687
- * caps (not movable/resizable) keep every rotate gesture away from it. */
1688
- declare function geomRotation(g: Geom): number;
1689
- /**
1690
- * The rotation PIVOT for a single shape: a box turns about its own `rect`
1691
- * centre; a vertex shape about the CENTROID of its points (the mean). Rotating a
1692
- * point set about its centroid leaves the centroid fixed, so the advisory `rot`
1693
- * stays cleanly additive across gestures and reset is exact.
1694
- */
1695
- declare function centroidOf(g: Geom): Vec;
2176
+ /** A geom's applied rotation (deg), or 0 for text markup. A caret's turn is
2177
+ * its text's baseline tilt: reported here so the renderer and selection
2178
+ * chrome follow it, while the caret's caps (not movable/resizable) keep
2179
+ * every rotate gesture away from it. */
2180
+ declare function geomRotation(geometry: Shape): number;
2181
+ /**
2182
+ * Where a turn of a single shape pivots, as the engine turns it: a box about
2183
+ * its own `rect` centre; a line, polygon, polyline or ink about the middle of
2184
+ * the box around its points upright. A turn then changes only the angle, never
2185
+ * where the upright points are, and a caption or an arrowhead never moves the
2186
+ * pivot.
2187
+ */
2188
+ declare const turnPivotOf: (geometry: Shape) => Point;
1696
2189
  /** Does the geometry carry a meaningful `rot` (an oriented local box exists)?
1697
- * ORIENTATION is a geometry fact; whether the USER may rotate is the separate
2190
+ * Orientation is a geometry fact; whether the user may rotate is the separate
1698
2191
  * `caps.rotatable` gate — a caret is oriented (it rides its text's tilt) yet
1699
2192
  * offers no rotate gesture. */
1700
- declare function isRotatableGeom(g: Geom): boolean;
1701
- /**
1702
- * Rotate a geom by `deltaDeg` (clockwise) about `pivot`.
1703
- * - BOX (`rect`/plain `text`): orbit the box centre about the pivot (a rigid
1704
- * translation of the unrotated `rect`) and add the angle to `rot`. When the
1705
- * pivot IS the box centre this is a pure `rot += delta`.
1706
- * - VERTEX (`line`/`poly`/`ink`): map every point through the rotation AND bump
1707
- * the advisory `rot` (the points stay the authoritative visual).
1708
- * Kinds without a rotate VERB are returned unchanged: quads and callouts, and
1709
- * also the caret — oriented (`isRotatableGeom`) but text-anchored, so its tilt
1710
- * is authoring metadata that no gesture edits (`geomResetRotation` still
1711
- * clears it).
1712
- */
1713
- declare function geomRotateAbout(g: Geom, pivot: Vec, deltaDeg: number): Geom;
1714
- /** The AABB of `rect` rotated `deg` about its own centre. */
1715
- declare function rotatedAabb(rect: Rect, deg: number): Rect;
2193
+ declare const isRotatableGeom: (geometry: Shape) => boolean;
2194
+ /**
2195
+ * Rotate a geom by `deltaDeg` (clockwise) about `pivot`: a box's middle
2196
+ * orbits the pivot and its turn grows, and so does a points shape's. A shape
2197
+ * without a rotate verb comes back unchanged: quads, a callout, and the
2198
+ * caret (oriented, but it follows its text; `geomResetRotation` still clears
2199
+ * its turn).
2200
+ */
2201
+ declare function geomRotateAbout(geometry: Shape, pivot: Point, deltaDeg: number): Shape;
1716
2202
  /**
1717
2203
  * The counter-rotation (CW content degrees) that makes an annotation read
1718
2204
  * upright at `displayRotation`: on screen the two compose to 0.
1719
2205
  */
1720
2206
  declare const uprightRotation: (displayRotation: number) => number;
1721
- /** `rect` with width↔height swapped about its own centre — the unrotated box
1722
- * whose quarter-turn AABB is exactly `rect` again. */
1723
- declare function transposedAboutCenter(rect: Rect): Rect;
1724
2207
  /**
1725
- * The unrotated content rect for a DEFAULT-SIZE upright box "placed at" a
2208
+ * The unrotated content rect for a default-size upright box "placed at" a
1726
2209
  * point: positioned so that, after the upright counter-rotation, the box shows
1727
- * `width`×`height` with its TOP-LEFT at `anchor` in the ROTATED view — the
2210
+ * `width`×`height` with its top-left at `anchor` in the rotated view — the
1728
2211
  * same on-screen feel as the rotation-0 `{ x: anchor.x, y: anchor.y }` box at
1729
2212
  * every quarter-turn (a click-created free-text box always opens down-right of
1730
2213
  * the cursor as the author sees it). Derived by placing the box in the display
1731
2214
  * frame and pulling its AABB back through the quarter-turn; the page dims
1732
2215
  * cancel, so no page box is needed.
1733
2216
  */
1734
- declare function uprightAnchoredRect(anchor: Vec, width: number, height: number, displayRotation: number): Rect;
2217
+ declare function uprightAnchoredRect(anchor: Point, width: number, height: number, displayRotation: number): Rect;
1735
2218
  /**
1736
- * The LOGICAL (unrotated) content-space box for a stamp placed at `center`,
2219
+ * The logical (unrotated) page-space box for a stamp placed at `center`,
1737
2220
  * sized `desired` (points), fit onto the page and clamped fully within it —
1738
- * the v2 rubber-stamp rule: keep the image's own size unless it would overflow
2221
+ * the rubber-stamp rule: keep the image's own size unless it would overflow
1739
2222
  * the page, then scale down (aspect preserved, never up) so it just fits.
1740
2223
  *
1741
- * Under an upright quarter-turn the on-page FOOTPRINT is the box transposed
1742
- * (w↔h), so BOTH the fit and the edge clamp use that footprint — a landscape
2224
+ * Under an upright quarter-turn the on-page footprint is the box transposed
2225
+ * (w↔h), so both the fit and the edge clamp use that footprint — a landscape
1743
2226
  * stamp uprighted onto a portrait page fits by its rotated silhouette, not its
1744
2227
  * logical box. The box centre and its footprint AABB centre coincide (rotation
1745
2228
  * is about the centre), so clamping one clamps the other.
1746
2229
  */
1747
- declare function fitStampBox(center: Vec, desired: Size, page: Size, rotCW: number): Rect;
1748
- /** Reset a geom to its as-authored orientation (`rot → 0`). Box: drop `rot`.
1749
- * Vertex: spin the points by `-rot` about the supplied selection center.
1750
- * Geometry-only callers retain the centroid default. */
1751
- declare function geomResetRotation(g: Geom, pivot?: Vec): Geom;
2230
+ declare function fitStampBox(center: Point, desired: Size, page: Size, rotCW: number): Rect;
2231
+ /** Reset a geom to its as-authored orientation (turn → 0). Box: drop the turn.
2232
+ * Points: turn back about `pivot` (the middle of the upright points by
2233
+ * default, which only clears the turn). */
2234
+ declare function geomResetRotation(geometry: Shape, pivot?: Point): Shape;
1752
2235
  /**
1753
2236
  * The oriented selection box (OBB) of a rotatable geom: four corners (in order
1754
- * nw, ne, se, sw of the LOCAL box, transformed) + the angle. For a box this is
1755
- * the `rect` rotated about its centre; for a vertex shape it is reconstructed
1756
- * from the advisory `rot` — un-rotate the points to recover the as-authored
1757
- * shape, take that tight local box, then rotate it back — giving the snug tilted
2237
+ * nw, ne, se, sw of the local box, transformed) + the angle. For a box this is
2238
+ * the box rotated about its centre; for a points shape it is the drawn bounds
2239
+ * of its upright points, turned about their middle — the snug tilted
1758
2240
  * rectangle. Returns null for non-rotatable kinds.
1759
2241
  */
1760
- declare function obbFromGeom(g: Geom, strokeWidth: number, border?: Border): {
1761
- corners: [Vec, Vec, Vec, Vec];
2242
+ declare function obbFromGeom(geometry: Shape, stroke: Stroke): {
2243
+ corners: [Point, Point, Point, Point];
1762
2244
  angle: number;
1763
2245
  } | null;
1764
- /** The fixed point of a group resize: the OPPOSITE handle's point on the box. */
1765
- declare function groupResizeAnchor(base: Rect, handle: string): Vec;
2246
+ /** The fixed point of a group resize: the opposite handle's point on the box. */
2247
+ declare function groupResizeAnchor(base: Rect, handle: string): Point;
1766
2248
  /**
1767
2249
  * The live group resize box for a drag. Anisotropic = the plain axis-aligned
1768
2250
  * `resizeRect`; isotropic = a uniform scale of `base` about the anchor by the
1769
2251
  * larger of the two drag ratios (so the preview matches the committed scale and
1770
2252
  * never shears a rotated member).
1771
2253
  */
1772
- declare function groupResizeBox(base: Rect, handle: string, to: Vec, isotropic: boolean): Rect;
2254
+ declare function groupResizeBox(base: Rect, handle: string, to: Point, isotropic: boolean): Rect;
1773
2255
  /** The (sx, sy) factors a `base`→`cur` group resize applied about its anchor. */
1774
- declare function groupResizeFactors(base: Rect, cur: Rect): {
2256
+ declare function groupResizeFactors(base: Rect, current: Rect): {
1775
2257
  sx: number;
1776
2258
  sy: number;
1777
2259
  };
1778
2260
  /**
1779
- * Scale a geom about `anchor` by `(sx, sy)`. For a rotated box (iso scale, so
1780
- * `sx === sy`) the unrotated `rect` is scaled about the anchor and `rot` is
1781
- * preserved (a uniform scale commutes with rotation). For unrotated members
1782
- * (the anisotropic case) every point/extent scales directly.
2261
+ * Scale a geom about `anchor` by `(sx, sy)`, exactly. For a rotated box (iso
2262
+ * scale, so `sx === sy`) the unrotated box is scaled about the anchor and its
2263
+ * turn is preserved (a uniform scale commutes with rotation). For unrotated
2264
+ * members (the anisotropic case) every point/extent scales directly. A resize
2265
+ * passes `minSize`, the smallest a box may end up.
1783
2266
  */
1784
- declare function geomScaleAbout(g: Geom, anchor: Vec, sx: number, sy: number): Geom;
2267
+ declare function geomScaleAbout(geometry: Shape, anchor: Point, sx: number, sy: number, minSize?: number): Shape;
1785
2268
  /** Where the rotate knob sits, given the OBB corners (nw, ne, se, sw) and the
1786
2269
  * outward offset (content units). `from` is the top-edge midpoint the connector
1787
2270
  * stalk anchors to; `at` is the grab dot, offset along the outward normal. */
1788
- declare function rotateKnob(corners: [Vec, Vec, Vec, Vec], offset: number): {
1789
- at: Vec;
1790
- from: Vec;
2271
+ declare function rotateKnob(corners: [Point, Point, Point, Point], offset: number): {
2272
+ at: Point;
2273
+ from: Point;
1791
2274
  };
1792
2275
  /**
1793
- * `rotateKnob` with a TOTAL page-bound placement policy: annotations, gestures
2276
+ * `rotateKnob` with a total page-bound placement policy: annotations, gestures
1794
2277
  * and chrome are all page-bound, so the grab dot must land inside `pageBox` —
1795
2278
  * off-page it is unreachable (the pointer dispatch resolves pages by
1796
- * containment). Policy: top edge (the default) → FLIP to the bottom edge when
1797
- * the stalk exits the page → CLAMP the top candidate inside (degenerate: the
2279
+ * containment). Policy: top edge (the default) → flip to the bottom edge when
2280
+ * the stalk exits the page → clamp the top candidate inside (degenerate: the
1798
2281
  * selection spans ~the whole page; the knob may then overlap the shape, and
1799
2282
  * hit-testing checks it first so it stays grabbable). Both render (`chrome`)
1800
- * and hit-test (`hitTest`) place the knob through THIS function, so what you
2283
+ * and hit-test (`hitTest`) place the knob through this function, so what you
1801
2284
  * see is what you can grab — by construction. No `pageBox` → the raw knob.
1802
2285
  */
1803
- declare function placeRotateKnob(corners: [Vec, Vec, Vec, Vec], offset: number, pageBox?: Rect): {
1804
- at: Vec;
1805
- from: Vec;
2286
+ declare function placeRotateKnob(corners: [Point, Point, Point, Point], offset: number, pageBox?: Rect): {
2287
+ at: Point;
2288
+ from: Point;
1806
2289
  };
1807
- /** The box-edge midpoint the leader connects to: the side `ref` (the knee, else
1808
- * the tip) points toward, by horizontal/vertical dominance vs the box centre.
1809
- * With `rot` (a callout box tilted by the upright policy) the decision runs in
1810
- * the box's LOCAL frame and the midpoint lands back on the ROTATED edge — the
1811
- * edge the user actually sees. Mirror of v2's `computeCalloutConnectionPoint`. */
1812
- declare function calloutConnection(box: Rect, ref: Vec, rot?: number): Vec;
1813
- /** The leader polyline `[tip, knee?, conn]`, with `conn` derived from the box
1814
- * (its ROTATED footprint when the box carries `rot`). */
1815
- declare function calloutLinePoints(g: Extract<Geom, {
1816
- t: 'text';
1817
- }>): Vec[];
1818
- /**
1819
- * The stored `rect` for a freshly-drawn shape, given the box the user dragged. A
1820
- * cloudy border stores the OUTER box (dragged + cloud extent), so the dragged box
1821
- * is its inner edge and the scallops bulge out to the stored box — just like a
1822
- * solid shape, whose /Rect is the dragged box. So `g.rect` is ALWAYS the outer box;
1823
- * the cloud-vs-solid difference lives only here, at creation.
1824
- */
1825
- declare function shapeRectFor(dragged: Rect, ellipse: boolean, style: Style): Rect;
1826
- declare function caretRectFromTextEnd(lineRect: Rect): Rect;
1827
- /**
1828
- * Caret box for a text-edit anchor: half the glyph's ink height, centered on
1829
- * the TRAILING baseline corner in READING direction (`advance` decides which
1830
- * end — RTL carets land on the visual left), sitting on the baseline. The box
1831
- * stays axis-aligned; use {@link caretGeomFromAnchor} for the tilt-carrying
1832
- * geometry.
1833
- */
1834
- declare function caretRectFromAnchor(anchor: TextEndAnchor): Rect;
1835
- /**
1836
- * Caret GEOMETRY for a text-edit anchor: the box-family pair — an UNROTATED
1837
- * box whose centre sits half a caret-size ascent-ward of the trailing
1838
- * baseline corner, plus `rot` = the text's baseline tilt (deg, CW in y-down
1839
- * content space). Rotating the box about its centre by `rot` lands it
1840
- * hugging the rotated baseline, symbol pointing at its text. For upright
1841
- * anchors this degenerates EXACTLY to {@link caretRectFromAnchor} with no
1842
- * `rot` key — the dominant case is byte-identical.
1843
- */
1844
- declare function caretGeomFromAnchor(anchor: TextEndAnchor): Extract<Geom, {
1845
- t: 'caret';
1846
- }>;
1847
- /** Miter limit shared by the bounds math AND the SVG renderer (`stroke-miterlimit`),
1848
- * so the computed box and the drawn stroke always agree on where a sharp join
1849
- * bevels instead of spiking. 10 = the PDF default (also what the baked /AP uses). */
1850
- declare const MITER_LIMIT = 10;
1851
2290
  /**
1852
- * A geom's VISUAL bounds: the rect that encloses the drawn appearance, so the
1853
- * baked /AP is never clipped and the selection outline wraps exactly what's drawn.
2291
+ * A geom's visual bounds: the rect that encloses the drawn appearance, so the
2292
+ * baked /AP is never clipped.
1854
2293
  *
1855
- * A shape's `rect` IS its visual box (the PDF /Rect): solid/dashed strokes draw
1856
- * inside it, and a cloudy border's scallops also inset back into it (the dragged
1857
- * inner edge sits `/RD` in from it — see `shapeRectFor`). So growing the stroke or
1858
- * the cloud thickens inward, never spilling past the handles. Lines / polylines /
1859
- * ink have no box, so they expand by the stroke (+ endings) — the SAME math feeds
1860
- * `geomScene`, so the visual box and what's drawn always agree.
2294
+ * A box's strokes draw inside it; a cloudy border's bumps reach out from it,
2295
+ * so its bounds grow by the cloud's reach (`boxDrawnBounds`), before its
2296
+ * turn. Lines / polylines / ink have no box, so they expand by the stroke
2297
+ * (+ endings) — the same math feeds `geomScene`, so the visual box and what's
2298
+ * drawn always agree.
1861
2299
  *
1862
- * `border` matters for ONE case: a CLOSED poly with a cloudy border, whose curls
1863
- * are centred on the vertex path and reach OUTWARD (there is no outer box to
1864
- * inset into) — the bounds grow by the cloud extent, and the engine `/Rect`
1865
- * (via `geomPdfBounds`) grows with them so the baked scallops are never clipped.
2300
+ * The stroke's cloud matters too: a box's, and a closed poly's, whose curls
2301
+ * are centred on the vertex path and reach outward — the bounds grow by the
2302
+ * cloud extent, and the engine's `rect` grows with them so the baked scallops
2303
+ * are never clipped.
1866
2304
  */
1867
- declare function geomVisualBounds(g: Geom, strokeWidth: number, border?: Border): Rect;
2305
+ declare const geomVisualBounds: (geometry: Shape, stroke: Stroke) => Rect;
1868
2306
  /**
1869
- * The rect the SELECTION wraps — and the region a SELECTED annotation can be grabbed
2307
+ * The rect the selection wraps — and the region a selected annotation can be grabbed
1870
2308
  * from. Centre-line geometries (line / polyline / polygon / ink) straddle their path,
1871
- * so this is their VISUAL bounds (the join-aware stroke outline + endings) — a
2309
+ * so this is their visual bounds (the join-aware stroke outline + endings) — a
1872
2310
  * polygon's outline wraps its stroke exactly like a polyline. Box kinds (square /
1873
- * circle / free-text) sit tight on their `rect` (their stroke draws INSIDE the box,
1874
- * so the 8 resize handles land on the corners). The chrome outline AND the selected
2311
+ * circle / free-text) sit tight on their box (a stroke draws inside it, and a
2312
+ * cloud's bumps reach past it), so the 8 resize handles land on its corners. The chrome outline and the selected
1875
2313
  * hit-test both call this, so what you see highlighted is exactly what you can grab —
1876
2314
  * they can never drift.
1877
2315
  */
1878
- declare function selectionBounds(g: Geom, strokeWidth: number, border?: Border): Rect;
2316
+ declare const selectionBounds: (geometry: Shape, stroke: Stroke) => Rect;
1879
2317
  /**
1880
- * The four corners of the ORIENTED selection box — the SAME quad `chrome` draws.
2318
+ * The four corners of the oriented selection box — the same quad `chrome` draws.
1881
2319
  * For a rotatable kind this is the OBB (the tilted box, from `obbFromGeom`); at
1882
2320
  * `rot == 0` those are just the axis-aligned corners of `selectionBounds`, and
1883
2321
  * for non-rotatable kinds (markup quads, callouts) it falls back to the rect
@@ -1885,85 +2323,227 @@ declare function selectionBounds(g: Geom, strokeWidth: number, border?: Border):
1885
2323
  * all consume this, so what you can grab / where the menu sits never drifts from
1886
2324
  * the outline you see.
1887
2325
  */
1888
- declare function selectionQuad(g: Geom, strokeWidth: number, border?: Border): [Vec, Vec, Vec, Vec];
2326
+ declare function selectionQuad(geometry: Shape, stroke: Stroke): [Point, Point, Point, Point];
1889
2327
  /** Is the point inside the (convex) selection quad? Even-odd ring test. */
1890
- declare const pointInQuad: (p: Vec, quad: [Vec, Vec, Vec, Vec]) => boolean;
2328
+ declare const pointInQuad: (point: Point, quad: [Point, Point, Point, Point]) => boolean;
2329
+ /** The box around the shape's own box or points, the stroke left out. */
2330
+ declare const geomBounds: (geometry: Shape) => Rect;
1891
2331
  /**
1892
- * Does the (convex) selection quad intersect an axis-aligned rect? Separating
1893
- * axis test on the only four candidate axes — the rect's x/y plus the quad's
1894
- * two edge normals; a gap on any axis proves disjoint, otherwise they overlap
1895
- * (touching counts). This is the marquee's predicate: it must catch a tilted
1896
- * shape by the ORIENTED box that is actually drawn — the AABB of that quad has
1897
- * empty corners covering most of the unrotated footprint, so testing it selects
1898
- * shapes the marquee never touched. At `rot 0` the quad is axis-aligned and
1899
- * this degenerates to exactly `rectsIntersect`.
2332
+ * Is the page point on what the shape paints ({@link geomPainted}): within
2333
+ * `margin` of its ink, which widens with the stroke width, or inside it when
2334
+ * `filled`. A cloudy border is hit on its curves.
1900
2335
  */
1901
- declare function quadIntersectsRect(quad: [Vec, Vec, Vec, Vec], r: Rect): boolean;
2336
+ declare function geomHit(geometry: Shape, point: Point, margin: number, filled: boolean, stroke: Stroke): boolean;
1902
2337
  /**
1903
- * The centre of the ORIENTED selection box — the middle of the rect you see.
1904
- * For box kinds this is the rect centre (so squares/circles are unchanged); for
1905
- * vertex kinds it is the OBB centre, so rotation spins the shape in place rather
1906
- * than swinging it about the off-centre vertex mean (`centroidOf`).
2338
+ * What the shape paints (`painted.ts`): its ink for `stroke`, and its inside
2339
+ * when `filled`. Kept per shape object for the stroke and fill last asked
2340
+ * about, since a hover asks again on every pointer move.
1907
2341
  */
1908
- declare function selectionCenter(g: Geom, strokeWidth: number): Vec;
1909
- declare function geomBounds(g: Geom): Rect;
2342
+ declare function geomPainted(geometry: Shape, stroke: Stroke, filled: boolean): readonly PaintedPiece[];
2343
+ /** The shape's handles: resize corners and sides, a callout's tip and knee, or vertices. */
2344
+ declare const geomHandles: (geometry: Shape) => Handle[];
2345
+ declare const geomTranslate: (geometry: Shape, delta: Point) => Shape;
2346
+ declare const geomDragHandle: (geometry: Shape, handle: string, to: Point) => Shape;
1910
2347
  /**
1911
- * Is the content point ON the annotation: within `margin` of the stroke, or
1912
- * inside the fill (when `filled`). The stroke band widens with the stroke width.
2348
+ * What the shape draws for the live view. A text box's box — its fill and
2349
+ * its border — is the scene's, plain box and callout alike, so the live view
2350
+ * paints exactly what the AP generator bakes; the framework's editable
2351
+ * element owns only the text.
2352
+ */
2353
+ declare const geomScene: (geometry: Shape, stroke: Stroke) => RenderNode[];
2354
+ //#endregion
2355
+ //#region src/rect.d.ts
2356
+ type RectHandle = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w';
2357
+ declare const RECT_HANDLES: RectHandle[];
2358
+ /** The resize cursor for a handle on a box tilted by `rot` deg: rotate the
2359
+ * handle's compass direction with the box and pick the nearest 45° sector.
2360
+ * At `rot = 0` this reproduces {@link RECT_CURSOR} exactly. */
2361
+ declare const rotatedHandleCursor: (handle: RectHandle, rot: number) => Cursor;
2362
+ declare const rectFromPoints: (from: Point, to: Point) => Rect;
2363
+ declare function unionRect(points: Point[]): Rect;
2364
+ /** Normalize degrees into `[0, 360)`. */
2365
+ declare const normalizeDeg: (degrees: number) => number;
2366
+ /** `rect` with width↔height swapped about its own centre — the unrotated box
2367
+ * whose quarter-turn AABB is exactly `rect` again. */
2368
+ declare function transposedAboutCenter(rect: Rect): Rect;
2369
+ /** The AABB of `rect` rotated `deg` about `pivot` (its own centre unless given). */
2370
+ declare function rotatedAabb(rect: Rect, deg: number, pivot?: Point): Rect;
2371
+ //#endregion
2372
+ //#region src/record/identity.d.ts
2373
+ /**
2374
+ * An annotation's kind (`kinds/`): the one its subtype names; for a widget,
2375
+ * the one its field family picks (`widget-text`…); for a free text with the
2376
+ * callout intent, the callout.
2377
+ */
2378
+ declare function kindOf(annotation: Annotation): AnnotationKind;
2379
+ /**
2380
+ * A record's engine ref, a new one's included (it names the object number
2381
+ * its create takes); `null` for a record the view doesn't hold.
2382
+ */
2383
+ declare const refOf: (record: ModelAnnotation | undefined) => AnnotationRef | null;
2384
+ /**
2385
+ * The key of the annotation an annotation answers (`/IRT`): a comment reply's
2386
+ * parent, or a group member's primary.
2387
+ */
2388
+ declare const irtOf: (annotation: Annotation) => Id | undefined;
2389
+ /**
2390
+ * The key of the group an annotation belongs to: its primary's, for a
2391
+ * `/RT /Group` member only (a visual group acts as a unit; a comment reply
2392
+ * answers its parent but is not part of it).
2393
+ */
2394
+ declare const groupOf: (annotation: Annotation) => Id | undefined;
2395
+ //#endregion
2396
+ //#region src/record/shape.d.ts
2397
+ /**
2398
+ * The annotation's shape (`shapes/`), read by its kind's family. The same
2399
+ * shape for the same annotation, so what is worked out from a shape once
2400
+ * (where its points are drawn) is kept.
2401
+ */
2402
+ declare function shapeOf(annotation: Annotation): Shape;
2403
+ /**
2404
+ * The record with its shape changed: the engine fields that state `shape`,
2405
+ * written through `withValues`. The same record when the shape is the one
2406
+ * it has, or when the record may not change now.
2407
+ */
2408
+ declare const withShape: (record: ModelAnnotation, shape: Shape) => ModelAnnotation;
2409
+ //#endregion
2410
+ //#region src/record/style.d.ts
2411
+ /**
2412
+ * How the annotation is drawn: its colours, stroke and border, by the
2413
+ * engine's names, with its kind's fill-ins. A view: nothing is written back
2414
+ * through it. The same style for the same annotation.
2415
+ */
2416
+ declare function styleOf(annotation: Annotation): Style;
2417
+ //#endregion
2418
+ //#region src/record/text.d.ts
2419
+ /**
2420
+ * How the annotation's text is set: its font, size, colour and alignment by
2421
+ * the engine's names, and a free text's bold, italic and underline. A view:
2422
+ * nothing is written back through it. `undefined` for a kind without text.
2423
+ * The same value for the same annotation.
2424
+ */
2425
+ declare function textOf(annotation: Annotation): TextStyle | undefined;
2426
+ //#endregion
2427
+ //#region src/record/values.d.ts
2428
+ /**
2429
+ * The record with engine `values` written, as far as it takes them:
2430
+ *
2431
+ * 1. Only the fields its kind has and this session may change now
2432
+ * (`mayWrite`). The rest are ignored, so one set of values restyles a
2433
+ * mixed selection.
2434
+ * 2. A `rect` is a command, resolved as the engine resolves it
2435
+ * (`resolveRectCommand`): it becomes the shape that puts the drawing
2436
+ * there, and one the engine would refuse throws.
2437
+ * 3. Merged into its annotation as given. The engine's follow rules (a
2438
+ * measurement's label, a callout's end) run once, where the change is
2439
+ * applied (`applyChange`).
1913
2440
  *
1914
- * A shape's stroke is drawn INSIDE its box, centred on `insetRect(rect, sw/2)`
1915
- * (see `geomScene`), so the clickable band follows that inset centre-line — not
1916
- * the box edge. The fill still reaches the box. (Cloudy scallops aren't modelled
1917
- * here; with their typically thin stroke the inset is sub-pixel, so this matches.)
2441
+ * A value set to what it was is no change: the same record comes back.
1918
2442
  */
1919
- declare function geomHit(g: Geom, p: Vec, margin: number, filled: boolean, strokeWidth: number): boolean;
1920
- declare function geomHandles(g: Geom): Handle[];
1921
- declare function geomTranslate(g: Geom, d: Vec): Geom;
1922
- declare function geomDragHandle(g: Geom, handle: string, to: Vec): Geom;
2443
+ declare function withValues(record: ModelAnnotation, values: FieldValues): ModelAnnotation;
2444
+ //#endregion
2445
+ //#region src/record/links.d.ts
2446
+ /**
2447
+ * The desired hit rects (page space) of a parent's attached link
2448
+ * children — derived fresh from the parent's committed geometry, never
2449
+ * tracked: markup gets one child per quad (per-line hit areas), a text box
2450
+ * one child over its box, every other kind one child over its visual bounds.
2451
+ * The reconciler and the navigation lens both read this, so the clickable
2452
+ * area and the written `/Rect`s can never disagree.
2453
+ *
2454
+ * A link's `/Rect` is axis-aligned by spec (no rotation exists for links),
2455
+ * so a rotated parent gets the AABB of its rotated footprint — the
2456
+ * `selectionQuad` corners (rotation + stroke included), the same envelope
2457
+ * the selection chrome outlines. A text box's link covers only its turned
2458
+ * box: a callout's frame also takes in its line, and a link that size
2459
+ * would make the empty space around the line clickable. Exact rotated hit
2460
+ * regions need `/QuadPoints` (tier 2).
2461
+ */
2462
+ declare function linkChildRects(shape: Shape, style: Style): Rect[];
2463
+ /** The writable projection of a `link` value: `goto`/`uri` pass through,
2464
+ * read-only arms (`javascript`, `named`, `goto-remote`, `launch`,
2465
+ * `unsupported`) yield `null` — they can be carried, never (re)written. */
2466
+ declare function writableTarget(target: PdfLinkTarget | null | undefined): PdfLinkTargetWritable | null;
2467
+ //#endregion
2468
+ //#region src/record/written.d.ts
1923
2469
  /**
1924
- * The text PLATE inset of a free-text box: twice the border width. The plate
1925
- * — where text lays out, clips and scrolls — is the box deflated by this on
1926
- * every side: the ink band and an equal breathing band, so the text never
1927
- * touches the stroke. Acrobat's rule, measured 1–12 pt on plain boxes and
1928
- * 1–7 pt on callouts (plan `2026-09-15-free-text-plate-inset.md`); the
1929
- * engine's `FreeTextPlate` is the same formula, so the live editor sits
1930
- * exactly where the baked text lands. Acrobat's thinnest border is 1 pt; a
1931
- * width of 0 is ours alone and gives no inset (the plate is the box).
2470
+ * The annotation `draft` creates, as the engine reads it back: the draft's
2471
+ * fields as the engine states them, and a drawn kind's `rect` the box around
2472
+ * its drawing. `at` names the ref it will have.
1932
2473
  */
1933
- declare function textPlateInset(strokeWidth: number): number;
1934
- declare function geomScene(g: Geom, strokeWidth?: number, border?: Border): RenderNode[];
1935
- declare const pdfToContentPoint: (p: PdfPoint, crop: PdfRect) => Vec;
1936
- declare const contentToPdfPoint: (p: Vec, crop: PdfRect) => PdfPoint;
1937
- declare const pdfToContentRect: (pdf: PdfRect, crop: PdfRect) => Rect;
1938
- declare const contentToPdfRect: (r: Rect, crop: PdfRect) => PdfRect;
1939
- /** A geom's VISUAL bounding box (geometry + stroke + line endings) as a PdfRect —
1940
- * the engine requires an explicit `rect` that encloses the baked /AP. */
1941
- declare const geomPdfBounds: (g: Geom, strokeWidth: number, crop: PdfRect, border?: Border) => PdfRect;
2474
+ declare function annotationOfNew(draft: AnnotationDraft, at: {
2475
+ readonly ref: AnnotationRef;
2476
+ }): Annotation;
2477
+ /**
2478
+ * `annotation` after `patch`, as the engine reads it back: the patch applied
2479
+ * by the engine's own rules, and the fields the engine works out as its
2480
+ * verdict on the change says (the table at the top of this file). Throws, as
2481
+ * the engine does, for a patch its rules refuse without its schemas
2482
+ * (`resolveAnnotationPatch`); values only the schemas refuse are applied, and
2483
+ * the engine refuses them when the patch reaches it.
2484
+ */
2485
+ declare function annotationAfter(annotation: Annotation, patch: AnnotationPatch): Annotation;
1942
2486
  //#endregion
1943
- //#region src/cloudy.d.ts
2487
+ //#region src/record/index.d.ts
1944
2488
  /**
1945
- * The per-side inset (content units) from a shape's outer box to the cloud's
1946
- * inner boundary — the scallop radius plus half the stroke. This IS the `/RD`
1947
- * the engine stores, and it's sized so the scallop peaks reach back out to the
1948
- * box edge: the box remains the annotation's outer boundary.
2489
+ * The record an engine annotation reads as: keyed by its ref, drawn from the
2490
+ * engine's appearance raster (`source: 'baked'`, placed by `apBox`). Whether
2491
+ * this session draws it live instead is the appearance rule's
2492
+ * (appearance.ts).
1949
2493
  */
1950
- declare function cloudyBorderExtent(intensity: number, strokeWidth: number, ellipse: boolean): number;
2494
+ declare function fromDTO(dto: Annotation): ModelAnnotation;
2495
+ //#endregion
2496
+ //#region src/cloudy.d.ts
1951
2497
  /**
1952
- * SVG path data for a cloudy square (rect) or circle (ellipse), in ABSOLUTE
1953
- * content coordinates. The scallops are generated on the box inset by
1954
- * `cloudyBorderExtent` and bulge back out to `box`'s edge.
2498
+ * SVG path data for a cloudy square (rect) or circle (ellipse), in absolute
2499
+ * content coordinates. The scallops are generated on `box` and bulge out
2500
+ * from it.
1955
2501
  */
1956
2502
  declare function cloudyPath(box: Rect, ellipse: boolean, intensity: number, strokeWidth: number): string;
1957
2503
  //#endregion
2504
+ //#region src/appearance.d.ts
2505
+ /** The part of a record that says how it is drawn. */
2506
+ type DrawState = Pick<ModelAnnotation, 'source' | 'apBox'>;
2507
+ /** How a record this session creates is drawn at first. */
2508
+ declare const sourceOfNew: (annotation: Annotation) => ModelAnnotation["source"];
2509
+ /**
2510
+ * How a record the engine reports is drawn: live when this session drew it
2511
+ * live (`vector`, the plugin's preference) or when the file holds no
2512
+ * appearance for it; otherwise from the engine's raster.
2513
+ */
2514
+ declare const sourceOfConfirmed: (annotation: Annotation, vector: boolean) => ModelAnnotation["source"];
2515
+ /**
2516
+ * What changes in how `record` is drawn once `patch` is written: the rule at
2517
+ * the top of this file. Only the fields that change. The engine judges the
2518
+ * patch as it will write it: a `rect` command is the shape it moves to by
2519
+ * then.
2520
+ */
2521
+ declare function drawnAfter(record: ModelAnnotation, patch: AnnotationPatch): Partial<DrawState>;
2522
+ //#endregion
1958
2523
  //#region src/selection.d.ts
1959
2524
  interface SelectionFrame {
1960
- corners: Quad;
1961
- center: Vec;
2525
+ corners: QuadRing;
2526
+ center: Point;
1962
2527
  angle: number;
1963
2528
  }
1964
- declare function annotationSelectionFrame(annotation: Annot, view?: ViewEnv): SelectionFrame;
2529
+ /**
2530
+ * The frame of `record` as it is drawn, or with `live` fields laid over its
2531
+ * own (a gesture's geometry, a screen-anchored stroke).
2532
+ */
2533
+ declare function annotationSelectionFrame(record: ModelAnnotation, view?: ViewEnv, live?: {
2534
+ geometry?: Shape;
2535
+ style?: Style;
2536
+ measure?: MeasurementAppearance;
2537
+ }): SelectionFrame;
1965
2538
  //#endregion
1966
2539
  //#region src/scene.d.ts
2540
+ /**
2541
+ * Whether the item can be drawn live now (`scene(item)`): anything not
2542
+ * drawn from the engine's picture, a kind with a live drawing, and a lent
2543
+ * record its lender draws. A stamp or a link can't: its picture is its
2544
+ * drawing.
2545
+ */
2546
+ declare const hasLiveDrawing: (item: RenderItem) => boolean;
1967
2547
  /** The full painted scene for one annotation. */
1968
2548
  declare function scene(item: RenderItem): SceneNode[];
1969
2549
  //#endregion
@@ -1973,7 +2553,7 @@ declare function scene(item: RenderItem): SceneNode[];
1973
2553
  * close to horizontal or vertical. Diagonal strokes remain untouched: this is
1974
2554
  * a highlighter aid, not a general line simplifier.
1975
2555
  */
1976
- declare function straightenInkStroke(points: readonly Vec[], options: InkStraightenOptions): Vec[];
2556
+ declare function straightenInkStroke(points: readonly Point[], options: InkStraightenOptions): Point[];
1977
2557
  //#endregion
1978
- export { type AnchorMode, type Annot, type AnnotationFlags, type AnnotationKind, type AnnotationProps, type AnnotationPropsPatch, type BlendMode, type Border, type Callout, type ChromeGeom, type ChromeNode, type ClickCreate, type ClickPlacement, type CreationDraftAnchor, type Cursor, DEFAULT_CHROME_GEOM, DRAWN_FLAGS, DistanceAppearance, DistanceCaptionLayout, DistanceLayout, DistanceSegment, type Draft, type Effect, FLAG_KEYS, type FlagBearer, type Geom, type GroupCaps, type Guide, type Handle, type Id, type InkStraightenOptions, KINDS, type KindCaps, type LineEnding, type LineEndings, MIN_DRAG, MITER_LIMIT, MeasurementAppearance, type Model, type Msg, NO_ANNOTATION_FLAGS, type Paint, type PatchScope, type PointerInput, type PropKey, type PropSpec, type Quad, RECT_HANDLES, ROTATE_KNOB_OFFSET, type Rect, type RectHandle, type RenderItem, type RenderNode, type RichTextRange, type RichTextStyleDelta, type SceneNode, type SelectionFrame, ShapeMeasurementAppearance, ShapeMeasurementLayout, type SnapResult, type SnapSettings, type Style, type Subtype, type Target, type TextAlign, type TextBox, type TextEndAnchor, type TextQuad, type TextStyle, type Vec, type ViewEnv, anchorModeOf, anchorOf, anchoredBox, anchoredGeom, anchoredStrokeWidth, annotContentsEditable, annotDeletable, annotInteractive, annotTransformable, annotationSelectionFrame, applyProps, applyStyleToRange, automaticShapeCaptionCenter, calloutConnection, calloutLinePoints, canMove, capsFor, caretGeomFromAnchor, caretRectFromAnchor, caretRectFromTextEnd, centroidOf, chrome, clampRectToBox, clickCreateGeom, cloudyBorderExtent, cloudyPath, computeMoveSnap, contentToPdfPoint, contentToPdfRect, creationDraftAnchor, cursorAt, defaultsFor, distanceCaptionAt, distanceCaptionHit, distanceHandles, distanceHit, distanceLabel, distanceLayout, distanceLeaderLength, distanceScene, distanceSelectionQuad, expandDistanceBounds, expandGroups, fitStampBox, flagsEqual, geomBounds, geomDragHandle, geomHandles, geomHit, geomPdfBounds, geomResetRotation, geomRotateAbout, geomRotation, geomScaleAbout, geomScene, geomTranslate, geomVisualBounds, groupCaps, groupKeyOf, groupMembers, groupResizeAnchor, groupResizeBox, groupResizeFactors, hitTest, initialModel, initialStyle, initialTextStyle, interactive, isAttachedLink, isConversationOnly, isPlainRichText, isRotatableGeom, isSelectable, isSubstrateOnly, linkChildrenOf, linkOf, locateOffset, measurementCaptionScene, measurementLayout, measurementSelectionQuad, mergeFlags, moveDistanceCaption, moveMeasurementCaption, normalizeDeg, normalizeRuns, obbFromGeom, pageItems, paragraphsFromPlainText, pdfToContentPoint, pdfToContentRect, placeRotateKnob, plainTextOf, pointInQuad, propsFor, quadIntersectsRect, rangeHasStyle, readProp, rectFromPoints, resolveClickPlacement, richTextLength, rotateDraftDelta, rotateKnob, rotatedAabb, rotatedHandleCursor, sameStyleDelta, scene, selectedItems, selectionAnchor, selectionBounds, selectionBoundsOnPage, selectionCenter, selectionKnob, selectionQuad, shapeCaptionPoint, shapeMeasurementLabel, shapeMeasurementLayout, shapeMeasurementReadout, shapeRectFor, sharedProps, splitRunsAt, straightenInkStroke, styleAt, styleFromProps, textBoxes, textPlateInset, textStyleFromProps, transformMeasurementCaption, transposedAboutCenter, unanchoredGeom, unionRect, update, uprightAnchoredRect, uprightRotation, viewable, withShapeCaptionPoint };
2558
+ export { type AnchorMode, type AnnotationFlags, type AnnotationKind, type AnnotationOption, type AnnotationProperty, type AnnotationView, type BlendMode, type BoxShape, type CalloutLine, type CaretShape, type ChangeSet, type ChromeGeometry, type ChromeNode, type ClickCreate, type Corners, type CreationDraftAnchor, type Cursor, DEFAULT_CHROME_GEOMETRY, DRAWN_FLAGS, DistanceAppearance, DistanceCaptionLayout, DistanceLayout, DistanceSegment, type Draft, type DrawState, EMPTY_CHANGE, type EditableKind, type EditableKinds, type Effect, FLAG_KEYS, FLAG_PROPERTIES, type FieldValues, type FlagBearer, type FlagKey, type FontLookup, type GestureForm, type GroupCaps, type Guide, type Handle, type HandleRole, type Id, type InkShape, type InkStraightenOptions, KINDS, type KindCaps, type KindName, LINE_ENDING_NAMES, type LineEnding, type LineEndings, type LineShape, MIN_DRAG, MITER_LIMIT, MeasurementAppearance, type MeasurementIntent, type Message, type Model, type ModelAnnotation, NO_ANNOTATION_FLAGS, NO_CAPS, type Paint, type PaintedPiece, type Placement, type PlacementFrame, type Point, type PointerInput, type PointsShape, type PolyShape, type Quad, type QuadRing, type QuadsShape, RECT_HANDLES, ROTATE_KNOB_OFFSET, type Rect, type RectHandle, type RenderItem, type RenderNode, type RichTextRange, type RichTextStyleDelta, type RotationAnchor, STANDARD_FONT_STACKS, type SceneFontProgram, type SceneNode, type SelectionFrame, type Session, type Shape, type ShapeFamily, ShapeMeasurementAppearance, ShapeMeasurementLayout, type SnapResult, type SnapSettings, type Stroke, type Style, type Target, type TextAlign, type TextBox, type TextBoxShape, type TextEndAnchor, type TextStyle, type TurnedBox, type UpdateResult, type ViewEnv, anchorModeOf, anchorOf, anchoredBox, anchoredGeom, anchoredStrokeWidth, annotContentsEditable, annotDeletable, annotInteractive, annotTransformable, annotationAfter, annotationAnchor, annotationOfNew, annotationSelectionFrame, applyStyleToRange, automaticShapeCaptionCenter, bodyFromTextStyle, bodyPieces, boxFamily, calloutEnd, canMove, captionPainted, caretFamily, caretFromAnchor, caretRectFromAnchor, chrome, clampRectToBox, cloudyPath, computeMoveSnap, creationDraftAnchor, cssFontFamilyForFont, cursorAt, defaultsFor, defineKind, distanceCaptionAt, distanceCaptionHit, distanceDrawnBounds, distanceHandles, distanceLabel, distanceLayout, distanceLeaderLength, distancePainted, distanceScene, distanceSelectionQuad, drawnAfter, drawnStrokesOf, editableKindOf, engineSubtypeOf, expandDistanceBounds, expandGroups, faceForFont, familyChosenBy, familyOf, fitStampBox, flagsEqual, fromDTO, geomBounds, geomDragHandle, geomHandles, geomHit, geomPainted, geomResetRotation, geomRotateAbout, geomRotation, geomScaleAbout, geomScene, geomTranslate, geomVisualBounds, gesturePlacement, groupCaps, groupKeyOf, groupMembers, groupOf, groupResizeAnchor, groupResizeBox, groupResizeFactors, hasLiveDrawing, hitTest, iconOf, initialModel, initialSession, initialTextStyle, interactive, irtOf, isAttachedLink, isContentsEditableByHand, isConversationOnly, isDeletableByHand, isDrag, isEditableByHand, isOnTextBox, isPlainRichText, isRotatableGeom, isSelectable, isSubstrateOnly, isTransformableByHand, kindNamed, kindOf, kindTakesLink, lineEndingsOf, linkChildRects, linkChildrenOf, linkOf, locateOffset, measurementCaptionScene, measurementDraftFields, measurementLayout, measurementOf, measurementSelectionQuad, mergeFlags, mountedFontFamily, moveDistanceCaption, moveMeasurementCaption, newRecordsAtMost, normalizeDeg, normalizeRuns, obbFromGeom, pageItems, paintedNear, paintedOf, paintedTouches, paragraphsFromPlainText, placeRotateKnob, placedShape, plainTextOf, pointInQuad, pointsFamily, propertiesOf, quadsFamily, rangeHasStyle, readOfDefaults, rectFromPoints, refOf, resolveClickPlacement, richDocOf, richTextLength, rotateDraftDelta, rotateKnob, rotatedAabb, rotatedHandleCursor, rotationAnchor, sameSession, sameStyleDelta, scene, selectedItems, selectionAnchor, selectionBounds, selectionBoundsOnPage, selectionInBox, selectionKnob, selectionQuad, shapeMeasurementLabel, shapeMeasurementLayout, shapeMeasurementReadout, shapeOf, sharedProperties, sourceOfConfirmed, sourceOfNew, splitRunsAt, straightenInkStroke, styleAt, styleOf, textBoxFamily, textBoxOf, textBoxes, textOf, textPlateInset, toolAnnotation, transposedAboutCenter, turnPivotOf, unanchoredGeom, unionRect, unmadeItem, update, uprightAnchoredRect, uprightRotation, viewable, widgetAppearanceOf, withShape, withValues, writableTarget };
1979
2559
  //# sourceMappingURL=index.d.cts.map