@draftably/editor 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +5 -3
  2. package/dist/assets/engine.worker-C6o2x40z.js +7609 -0
  3. package/dist/element.js +171 -17
  4. package/dist/index.js +9837 -8468
  5. package/dist/types/src/cli/raster-fonts.d.ts +3 -0
  6. package/dist/types/src/components/canvas/Canvas.d.ts +4 -0
  7. package/dist/types/src/components/scene/NodeView.d.ts +7 -0
  8. package/dist/types/src/components/scene/parts.d.ts +7 -11
  9. package/dist/types/src/components/session-context.d.ts +4 -1
  10. package/dist/types/src/dsl/cells.d.ts +1 -0
  11. package/dist/types/src/dsl/compile.d.ts +15 -1
  12. package/dist/types/src/dsl/corridors.d.ts +38 -0
  13. package/dist/types/src/dsl/legend-notes.d.ts +14 -0
  14. package/dist/types/src/dsl/syntax.d.ts +3 -1
  15. package/dist/types/src/editor/css.d.ts +3 -0
  16. package/dist/types/src/editor/element.d.ts +8 -6
  17. package/dist/types/src/editor/internal.d.ts +483 -0
  18. package/dist/types/src/editor/mount.d.ts +70 -5
  19. package/dist/types/src/graph-api/core.d.ts +125 -0
  20. package/dist/types/src/model/appearance.d.ts +19 -0
  21. package/dist/types/src/model/colors.d.ts +10 -2
  22. package/dist/types/src/model/labels.d.ts +22 -6
  23. package/dist/types/src/model/types.d.ts +9 -0
  24. package/dist/types/src/runtime/anchors.d.ts +46 -0
  25. package/dist/types/src/runtime/camera.d.ts +41 -0
  26. package/dist/types/src/runtime/element-base.d.ts +40 -0
  27. package/dist/types/src/runtime/geometry.d.ts +72 -0
  28. package/dist/types/src/runtime/host.d.ts +46 -0
  29. package/dist/types/src/runtime/page-shared.d.ts +24 -0
  30. package/dist/types/src/runtime/policy.d.ts +7 -0
  31. package/dist/types/src/runtime/render.d.ts +10 -0
  32. package/dist/types/src/runtime/svg-renderer.d.ts +8 -0
  33. package/dist/types/src/runtime/types.d.ts +115 -0
  34. package/dist/types/src/runtime/version.d.ts +2 -0
  35. package/dist/types/src/runtime/viewer.d.ts +123 -0
  36. package/dist/types/src/state/active-session.d.ts +7 -1
  37. package/dist/types/src/state/dom-root.d.ts +8 -0
  38. package/dist/types/src/state/session/document.d.ts +3 -1
  39. package/dist/types/src/state/session/editor.d.ts +3 -2
  40. package/dist/types/src/state/session/index.d.ts +5 -1
  41. package/package.json +1 -1
  42. package/dist/assets/engine.worker-DwN9io_m.js +0 -6856
  43. package/dist/types/src/cli/png.d.ts +0 -23
  44. package/dist/types/src/model/embed.d.ts +0 -17
@@ -0,0 +1,125 @@
1
+ import type { Point, RoutingSettings, Side } from '../model/types';
2
+ export type Shape = 'box' | 'pill' | 'circle' | 'diamond' | 'text';
3
+ /** How a scope arranges its children: a recipe, or a layout engine as `engine:<name>`. */
4
+ export type Arrange = 'flow' | 'row' | 'column' | 'lanes' | 'sequence' | `engine:${string}`;
5
+ /**
6
+ * How hard the router works. `full` repairs crossings and evens out lanes; `quick` routes each
7
+ * line once. `auto` (the default) routes quickly first, and fully when that will stay fast.
8
+ */
9
+ export type Quality = 'auto' | 'full' | 'quick';
10
+ export interface RoutingOptions extends Partial<RoutingSettings> {
11
+ quality?: Quality;
12
+ }
13
+ export interface NodeIn {
14
+ id: string;
15
+ label?: string;
16
+ parent?: string;
17
+ shape?: Shape;
18
+ /** Fixed size, kept within 40–1200 by 24–1200. Without one, the box fits its label. */
19
+ width?: number;
20
+ height?: number;
21
+ }
22
+ export interface GroupIn {
23
+ id: string;
24
+ label?: string;
25
+ parent?: string;
26
+ /** How the group arranges its own children; `column` by default. */
27
+ arrange?: Arrange;
28
+ direction?: 'right' | 'down';
29
+ gap?: number;
30
+ }
31
+ export interface EdgeIn {
32
+ /** Unique among edges. Without one, the edge is `e1`, `e2`, … by position. */
33
+ id?: string;
34
+ source: string;
35
+ target: string;
36
+ label?: string;
37
+ dashed?: boolean;
38
+ sourceSide?: Side;
39
+ targetSide?: Side;
40
+ }
41
+ export interface GraphIn {
42
+ nodes: NodeIn[];
43
+ groups?: GroupIn[];
44
+ edges?: EdgeIn[];
45
+ /** How the top level is arranged; `flow` by default. */
46
+ arrange?: Arrange;
47
+ direction?: 'right' | 'down';
48
+ gap?: number;
49
+ fit?: 'wide' | 'slide' | 'square' | 'page' | 'tall';
50
+ /**
51
+ * `auto` (the default) folds a long `flow` into rows, like lines of text, when it is much
52
+ * wider than a 1100×720 page; `never` keeps one row.
53
+ */
54
+ fold?: 'auto' | 'never';
55
+ routing?: RoutingOptions;
56
+ }
57
+ export interface Box {
58
+ x: number;
59
+ y: number;
60
+ width: number;
61
+ height: number;
62
+ }
63
+ export interface PlacedBox extends Box {
64
+ parent?: string;
65
+ }
66
+ export interface EdgeOut {
67
+ source: string;
68
+ target: string;
69
+ sourceSide: Side;
70
+ targetSide: Side;
71
+ /** From the source's outline to the target's. The host draws the arrowhead at the last point. */
72
+ points: Point[];
73
+ /** SVG path data with the rounded corners draftably draws. */
74
+ path: string;
75
+ /** The label's box. Draw a backing behind it: other lines may pass under it. */
76
+ label: Box | null;
77
+ /** True when the router found no clear way and drew a fallback line. */
78
+ blocked: boolean;
79
+ }
80
+ export interface Routed {
81
+ edges: Record<string, EdgeOut>;
82
+ /** The router mode that drew the lines. */
83
+ quality: 'full' | 'quick';
84
+ warnings: string[];
85
+ }
86
+ export interface LayoutResult extends Routed {
87
+ nodes: Record<string, PlacedBox>;
88
+ groups: Record<string, PlacedBox>;
89
+ /** Everything drawn: boxes, lines and labels. */
90
+ bounds: Box;
91
+ /** The `.draft` file this graph is. Open it in the viewer, or pass it to `renderSvg`. */
92
+ draft: string;
93
+ }
94
+ /**
95
+ * `auto` routes fully when the quick routes predict a full pass within the budget. The full
96
+ * pass reroutes each line with a crossing or crowded lane against all the others, so its cost
97
+ * grows with lines times conflicts; this limit keeps it near the 1 s load budget.
98
+ */
99
+ export declare const AUTO_FULL_LIMIT = 1200;
100
+ /** Place and route a graph the way a `.draft` file with the same content draws. */
101
+ export declare function layout(graph: GraphIn): Promise<LayoutResult>;
102
+ /**
103
+ * `layout` without a promise, for callers that cannot wait, such as a stand-in for a synchronous
104
+ * library. It cannot run a layout engine (`engine:` arrangements) or `fit`, which need `layout`.
105
+ */
106
+ export declare function layoutSync(graph: GraphIn): LayoutResult;
107
+ export interface PlacedNodeIn extends Box {
108
+ id: string;
109
+ parent?: string;
110
+ shape?: Shape;
111
+ }
112
+ export interface PlacedGroupIn extends Box {
113
+ id: string;
114
+ parent?: string;
115
+ /** The title, so lines keep out of its band at the top of the group. */
116
+ label?: string;
117
+ }
118
+ export interface RouteIn {
119
+ nodes: PlacedNodeIn[];
120
+ groups?: PlacedGroupIn[];
121
+ edges: EdgeIn[];
122
+ routing?: RoutingOptions;
123
+ }
124
+ /** Route lines between boxes the caller placed. Nothing moves. */
125
+ export declare function route(graph: RouteIn): Routed;
@@ -18,6 +18,12 @@ export type Shade = (typeof SHADES)[number];
18
18
  export declare const isShade: (n: unknown) => n is Shade;
19
19
  /** Shade of a node that names a color without a shade. */
20
20
  export declare const NODE_SHADE: Shade;
21
+ /**
22
+ * Shade of a member card: a node drawn in its container's family with no shade of its own. It is
23
+ * the palest stop on the canvas side of the frame (50 in light themes, 950 in dark ones), so the
24
+ * card stands apart from its frame by fill and keeps the family's outline and ink.
25
+ */
26
+ export declare const memberShadeFor: (theme: ThemeId) => Shade;
21
27
  /**
22
28
  * Shade of a group that names a color without a shade. Groups are quiet surfaces behind nodes, so
23
29
  * this is a pale shade in light themes and a deep one in dark themes; the scale itself never flips.
@@ -148,6 +154,8 @@ interface Preset {
148
154
  hues?: Partial<Record<string, [number, number]>>;
149
155
  }
150
156
  export declare const PRESETS: Record<ThemeId, Preset>;
157
+ /** `a` moved toward `b` by `t` (0–1), in sRGB. */
158
+ export declare function mixColors(a: string, b: string, t: number): string;
151
159
  export declare function contrast(a: string, b: string): number;
152
160
  /** Contrast targets (WCAG ratios) every generated color meets. */
153
161
  export declare const CONTRAST: {
@@ -184,6 +192,8 @@ export interface ResolvedAppearance {
184
192
  radius: number;
185
193
  shadow: boolean;
186
194
  title: TypeProfile;
195
+ /** A node title's detail lines (after its first `\n`): a step smaller and lighter. */
196
+ detail: TypeProfile;
187
197
  groupTitle: TypeProfile;
188
198
  annotation: TypeProfile;
189
199
  /** Space above a group's content; the group title sits in it. */
@@ -204,6 +214,15 @@ export declare function resolveAppearance(appearance?: DiagramAppearance): Resol
204
214
  * default surface; the file parser reports them before they get here.
205
215
  */
206
216
  export declare function swatchFor(r: ResolvedAppearance, family: string | undefined, shade: Shade | undefined, kind?: 'node' | 'group'): Swatch;
217
+ /**
218
+ * The quiet panel behind notes and a sequence diagram: slate 50 in light themes, and the theme's
219
+ * own group surface in dark ones, so the theme's text ink reads on it.
220
+ */
221
+ export declare const panelSwatch: (r: ResolvedAppearance) => Swatch;
222
+ /** Bands and dividers drawn over that panel. */
223
+ export declare const bandSwatch: (r: ResolvedAppearance) => Swatch;
224
+ /** Ink for a link in a note or footnote, held to 4.5:1 on `against`. */
225
+ export declare const linkInk: (r: ResolvedAppearance, against: string) => string;
207
226
  /** Shades a family defines, in numeric order. */
208
227
  export declare const shadesOf: (r: ResolvedAppearance, family: string) => Shade[];
209
228
  export {};
@@ -4,8 +4,11 @@
4
4
  *
5
5
  * - A container inside a container of the same family steps one shade toward emphasis per level
6
6
  * (lighter frames underneath, smaller and stronger ones on top), instead of repeating the fill.
7
- * - A shape whose shade is within two steps of its container's, in the same family, moves three
8
- * steps past the container, so a member never melts into its frame.
7
+ * - A member card: a shape in its container's family with no shade of its own draws as a pale
8
+ * card on the frame's tint (`memberShadeFor`), so a group of members reads as calm cards
9
+ * rather than a column of dark slabs. This holds while the frame is a quiet surface shade.
10
+ * - Any other shape whose shade is within two steps of its container's, in the same family, moves
11
+ * three steps past the container, so a member never melts into its frame.
9
12
  *
10
13
  * The canvas, the CLI's SVG and exports all draw through `drawnSwatch`.
11
14
  */
@@ -20,3 +23,8 @@ export declare function drawnSwatch(doc: Diagram, look: ResolvedAppearance, id:
20
23
  * its top. Undefined for anything else, and for a participant with no color of its own.
21
24
  */
22
25
  export declare function participantAccent(doc: Diagram, look: ResolvedAppearance, id: string): string | undefined;
26
+ /**
27
+ * Ink for a title's detail lines on a swatch: the theme's muted ink on the neutral surface, or the
28
+ * swatch's own ink moved toward its fill on a colored one, held to 4.5:1 on the fill.
29
+ */
30
+ export declare function detailInk(look: ResolvedAppearance, swatch: Swatch): string;
@@ -12,10 +12,25 @@ export interface TitleArea {
12
12
  maxHeight: number;
13
13
  }
14
14
  export declare function titleArea(shape: ShapeKind, w: number, h: number, r: ResolvedAppearance, icon?: boolean): TitleArea;
15
- /** Height a shape needs for `lines` title lines; never less than `base`. */
16
- export declare function titleHeight(shape: ShapeKind, lines: number, base: number, r: ResolvedAppearance): number;
17
- /** A shape's title in balanced lines no wider than `width` (see `wrapLines`). */
18
- export declare const wrapTitle: (text: string, width: number, r: ResolvedAppearance) => string[];
15
+ /** Height a shape needs for a title block `text` px tall; never less than `base`. */
16
+ export declare function titleHeight(shape: ShapeKind, text: number, base: number, r: ResolvedAppearance): number;
17
+ /** One line of a shape's title: part of its name, or of its detail. */
18
+ export interface TitleLine {
19
+ text: string;
20
+ /** A detail line, from the text after the title's first `\n`, drawn in `r.detail`. */
21
+ detail: boolean;
22
+ }
23
+ /**
24
+ * A shape's title in balanced lines no wider than `width` (see `wrapLines`). The text before the
25
+ * first `\n` is the name, in the title type; the rest is detail, in the smaller detail type.
26
+ */
27
+ export declare function wrapTitle(text: string, width: number, r: ResolvedAppearance): TitleLine[];
28
+ /** The type a title line is set in. */
29
+ export declare const lineType: (l: TitleLine, r: ResolvedAppearance) => TypeProfile;
30
+ /** Height of a block of title lines. */
31
+ export declare const textHeight: (lines: TitleLine[], r: ResolvedAppearance) => number;
32
+ /** Each line's centre, measured from the middle of the block. */
33
+ export declare function lineOffsets(lines: TitleLine[], r: ResolvedAppearance): number[];
19
34
  /**
20
35
  * Line width for a diamond's title, `s` its side. A text block W wide and H tall fits inside a
21
36
  * square diamond when W + H <= s, so this takes the widest measure whose wrapped lines still fit:
@@ -37,9 +52,10 @@ export interface NodeLabel {
37
52
  y: number;
38
53
  width: number;
39
54
  height: number;
40
- lines: string[];
55
+ lines: TitleLine[];
56
+ /** Each line's centre, relative to `y`. */
57
+ offsets: number[];
41
58
  mode: LabelMode;
42
- lineHeight: number;
43
59
  leader: boolean;
44
60
  }
45
61
  /** Where a shape's title is drawn, relative to the shape. */
@@ -93,6 +93,15 @@ export interface EdgeLayout {
93
93
  toOffset?: number;
94
94
  /** A sequence message: drawn straight between the two lifelines at this height, not routed. */
95
95
  row?: number;
96
+ /**
97
+ * Set by the compiler for a forward line in a `flow` scope: its route planned from the ranks.
98
+ * The quick router takes it as it is when it clears every box.
99
+ */
100
+ plan?: {
101
+ points: Point[];
102
+ fromOffset: number;
103
+ toOffset: number;
104
+ };
96
105
  }
97
106
  /**
98
107
  * A channel between two bands of a fold: a level line at `y`, or an upright one at `x`. With
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The anchor layer both handles share: one stable target element per anchored item, above the
3
+ * drawing. `body:<id>` targets sit in world space in one transformed layer, so they scale and clip
4
+ * with the node; `overlay:<id>` targets sit in screen space at the item's on-screen box. A target
5
+ * keeps its identity while its item exists, across updates and camera moves. The layer itself is
6
+ * never detached while the handle lives.
7
+ */
8
+ import type { Diagnostic } from './types';
9
+ import { type Geometry } from './geometry';
10
+ /** A slot name. IDs come from source text, so the type cannot check them; diagnostics do. */
11
+ export type DraftSlot = `body:${string}` | `overlay:${string}`;
12
+ export type AnchorKind = 'node' | 'group' | 'edge';
13
+ export interface Anchor {
14
+ slot: DraftSlot;
15
+ id: string;
16
+ kind: AnchorKind;
17
+ /** The stable target. */
18
+ el: HTMLElement;
19
+ }
20
+ /** Below this zoom, bodies hide and titles show again (`detail: 'auto'`). */
21
+ export declare const LOW_DETAIL_ZOOM = 0.4;
22
+ export interface AnchorLayer {
23
+ readonly el: HTMLElement;
24
+ /** The slots the host wants. Returns true when the targets changed. */
25
+ setSlots(slots: readonly string[]): boolean;
26
+ /** Whether the host wants any slot. */
27
+ hasSlots(): boolean;
28
+ /** A new committed drawing. Returns true when the targets changed. */
29
+ setGeometry(geometry: Geometry | null): boolean;
30
+ /** The camera: screen = origin + world × zoom. Writes only; reads no layout. */
31
+ place(originX: number, originY: number, zoom: number): void;
32
+ anchors(): Anchor[];
33
+ /** IDs of nodes whose body is anchored, for hiding their titles. */
34
+ bodyIds(): string[];
35
+ /** Slot warnings: unknown IDs and content that overflows its box. */
36
+ warnings(): Diagnostic[];
37
+ /** A pointer press that began in the layer is still down: focus it moves is not a Tab. */
38
+ pressed(): boolean;
39
+ /** The target holding `node`, if any. */
40
+ idOf(node: Node): string | undefined;
41
+ destroy(): void;
42
+ }
43
+ export declare function createAnchorLayer(doc: Document, options?: {
44
+ detail?: 'auto' | 'full';
45
+ onOverflow?: () => void;
46
+ }): AnchorLayer;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * One camera for the viewer and the editor: the world point at the viewport's centre and a zoom.
3
+ * Both handles frame and reveal through these functions, so the same call moves both the same way.
4
+ */
5
+ import type { Box } from '../graph-api/core';
6
+ /** World units at the centre of the viewport. Independent of the viewport size. */
7
+ export interface Camera {
8
+ centerX: number;
9
+ centerY: number;
10
+ zoom: number;
11
+ }
12
+ export type CameraReason = 'user' | 'api' | 'fit' | 'resize' | 'restore';
13
+ /** `pending`: the viewport has no size yet; the latest call runs once it has one. */
14
+ export type MoveResult = 'moved' | 'unchanged' | 'missing' | 'pending';
15
+ export interface FrameOptions {
16
+ /** Screen pixels kept clear, for example under a host panel. Default 32. */
17
+ padding?: number | {
18
+ top: number;
19
+ right: number;
20
+ bottom: number;
21
+ left: number;
22
+ };
23
+ minZoom?: number;
24
+ /** Default 1 for `fit`, so a small diagram is not blown up. */
25
+ maxZoom?: number;
26
+ }
27
+ export declare const ZOOM_MIN = 0.02;
28
+ export declare const ZOOM_MAX = 8;
29
+ type View = {
30
+ width: number;
31
+ height: number;
32
+ };
33
+ export declare const clampZoom: (zoom: number, min?: number, max?: number) => number;
34
+ /** The union of boxes, or undefined for none. */
35
+ export declare function unionBox(boxes: readonly Box[]): Box | undefined;
36
+ /** The camera that shows `box`, centred in the padded viewport, at most `maxZoom` (default 1). */
37
+ export declare function fitCamera(box: Box, view: View, options?: FrameOptions): Camera;
38
+ /** The least move that brings `box` fully into the padded viewport; zooms out only if needed. */
39
+ export declare function revealCamera(camera: Camera, box: Box, view: View, options?: FrameOptions): Camera;
40
+ export declare const sameCamera: (a: Camera, b: Camera) => boolean;
41
+ export {};
@@ -0,0 +1,40 @@
1
+ /** Helpers both custom elements share. No side effects. */
2
+ import { type HostEvents } from './host';
3
+ import type { Anchor } from './anchors';
4
+ import type { UpdateResult } from './viewer';
5
+ /** Structural equality for option values. Functions are read live, so any two are equal. */
6
+ export declare function sameValue(a: unknown, b: unknown): boolean;
7
+ /** Take a property a framework set before the element class was defined. */
8
+ export declare function takeEarly<T>(el: HTMLElement, key: string): T | undefined;
9
+ /** Warn once when another copy of the runtime already defined `name`. */
10
+ export declare function warnOtherCopy(name: string): void;
11
+ /** Re-dispatch a handle event on the element. Returns false when the host prevented it. */
12
+ export declare function redispatch<K extends keyof HostEvents>(el: HTMLElement, type: K, detail: HostEvents[K]): boolean;
13
+ /** The update result for an element whose handle could not start. */
14
+ export declare function mountFailure(revision: number, error: unknown, fallback: string): Extract<UpdateResult, {
15
+ status: 'error';
16
+ }>;
17
+ /**
18
+ * One constructable stylesheet per element type and page, shared by every instance. It needs no
19
+ * nonce under a strict CSP. Undefined where the browser cannot adopt sheets.
20
+ */
21
+ export declare function sharedSheet(doc: Document, key: string, css: string): CSSStyleSheet | undefined;
22
+ /**
23
+ * Re-dispatch a handle event whose payload may carry `preventDefault()`: the DOM event is
24
+ * cancelable instead, and a host that cancels it cancels Draftably's default.
25
+ */
26
+ export declare function relay<K extends keyof HostEvents>(el: HTMLElement, type: K, event: HostEvents[K] | (HostEvents[K] & {
27
+ preventDefault(): void;
28
+ })): undefined;
29
+ /**
30
+ * Manual slot assignment for an element's children. A child marked `data-draft-slot="body:api"`
31
+ * goes into the target for that slot; other children go to `fallback`. The element never writes
32
+ * to the host's DOM: it only calls `slot.assign()`.
33
+ */
34
+ export declare function createSlotting(host: HTMLElement, fallback: HTMLSlotElement | null, onSlots: (names: string[]) => void): {
35
+ start(): void;
36
+ stop(): void;
37
+ /** The handle's targets changed: put each slot's children in its target. */
38
+ setAnchors(next: readonly Anchor[]): void;
39
+ names: () => string[];
40
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Geometry as hosts see it: the graph API's shapes (`Box`, `PlacedBox`, `EdgeOut`) in world units,
3
+ * built from the boxes and routes a drawing uses. `renderSvg`, the viewer, the editor and the graph
4
+ * API all build it here, so their numbers agree.
5
+ */
6
+ import type { Box, EdgeOut, PlacedBox } from '../graph-api/core';
7
+ import type { Diagram, Point, Rect, Side } from '../model/types';
8
+ export type { Box, EdgeOut, PlacedBox };
9
+ /** The graph API's shapes, in world units, for the drawing now committed. */
10
+ export interface Geometry {
11
+ revision: number;
12
+ /** Everything drawn: heading, legend, notes, footnotes. */
13
+ frame: Box;
14
+ /** Shapes, lines and labels only. */
15
+ bounds: Box;
16
+ nodes: Record<string, PlacedBox>;
17
+ groups: Record<string, PlacedBox>;
18
+ /** The routes as drawn. */
19
+ edges: Record<string, EdgeOut>;
20
+ }
21
+ /** The route fields geometry reads; `computeRoutes` returns more. */
22
+ export interface DrawnRoute {
23
+ id: string;
24
+ from: string;
25
+ to: string;
26
+ fromSide: Side;
27
+ toSide: Side;
28
+ points: Point[];
29
+ path: string;
30
+ label?: {
31
+ x: number;
32
+ y: number;
33
+ w: number;
34
+ h: number;
35
+ } | null;
36
+ blocked?: boolean;
37
+ }
38
+ export declare function boundsOf(boxes: readonly Box[]): Box;
39
+ /** An edge's box: its route's points and its label. */
40
+ export declare const edgeBox: (e: EdgeOut) => Box;
41
+ /**
42
+ * Every visible node, group and edge of a projected document, under the caller's IDs. `rects`
43
+ * holds the visible objects' boxes; `routes` the lines as drawn.
44
+ */
45
+ export declare function placedItems(doc: Diagram, rects: ReadonlyMap<string, Rect>, routes: readonly DrawnRoute[], objectId?: (id: string) => string, edgeId?: (id: string) => string): {
46
+ nodes: {
47
+ [k: string]: PlacedBox;
48
+ };
49
+ groups: {
50
+ [k: string]: PlacedBox;
51
+ };
52
+ edges: {
53
+ [k: string]: {
54
+ source: string;
55
+ target: string;
56
+ sourceSide: Side;
57
+ targetSide: Side;
58
+ points: Point[];
59
+ path: string;
60
+ label: {
61
+ x: number;
62
+ y: number;
63
+ width: number;
64
+ height: number;
65
+ } | null;
66
+ blocked: boolean;
67
+ };
68
+ };
69
+ bounds: Box;
70
+ };
71
+ /** The box of any item in a geometry, or undefined. */
72
+ export declare function itemBox(g: Geometry, id: string): Box | undefined;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The host contract both elements share: the events they dispatch, as plain data. Framework
3
+ * packages loop over `HOST_EVENTS`, so a new event adds no lines to any of them.
4
+ * This module has no side effects and no runtime imports.
5
+ */
6
+ import type { UpdateResult, ViewerEvents } from './viewer';
7
+ export type { Camera, CameraReason, FrameOptions, MoveResult } from './camera';
8
+ export type { Box, EdgeOut, Geometry, PlacedBox } from './geometry';
9
+ export type { Anchor, DraftSlot } from './anchors';
10
+ export interface HostEvents {
11
+ /** After each commit, after the marked children change, and with `[]` before destroy. */
12
+ anchors: ViewerEvents['anchors'];
13
+ /** Each update's result: committed, error or a cancellation. */
14
+ update: UpdateResult;
15
+ render: ViewerEvents['render'];
16
+ diagnostics: ViewerEvents['diagnostics'];
17
+ camera: ViewerEvents['camera'];
18
+ itemactivate: ViewerEvents['itemactivate'];
19
+ /** Cancelable: `preventDefault()` on the DOM event stops Draftably opening the link. */
20
+ link: Omit<ViewerEvents['link'], 'preventDefault'>;
21
+ /** Cancelable: `preventDefault()` on the DOM event stops the browser's menu. */
22
+ contextmenu: Omit<ViewerEvents['contextmenu'], 'preventDefault'>;
23
+ /** The editor only: the selection changed, by the user or through `select()`. */
24
+ select: {
25
+ ids: readonly string[];
26
+ reason: 'user' | 'api';
27
+ };
28
+ /** The editor only: the source after a kept edit, undo or redo. */
29
+ change: {
30
+ source: string;
31
+ };
32
+ }
33
+ export type HostEventName = keyof HostEvents;
34
+ /** Every event, in one list. Each element dispatches these as bubbling `draft-<name>` events. */
35
+ export declare const HOST_EVENTS: readonly ["anchors", "update", "render", "diagnostics", "camera", "itemactivate", "link", "contextmenu", "select", "change"];
36
+ /** Events a host can cancel with `preventDefault()`. */
37
+ export declare const CANCELABLE_EVENTS: readonly HostEventName[];
38
+ export type DraftEventMap = {
39
+ [K in HostEventName as `draft-${K}`]: CustomEvent<HostEvents[K]>;
40
+ };
41
+ /** The element's `status` attribute. */
42
+ export type DraftStatus = 'loading' | 'ready' | 'error';
43
+ declare global {
44
+ interface HTMLElementEventMap extends DraftEventMap {
45
+ }
46
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * What every viewer on a page shares: a small pool of render workers and one font registry. A
3
+ * page with 20 diagrams starts at most `POOL_SIZE` workers and loads each font face once.
4
+ */
5
+ import type { RenderOptions, RenderResult } from './types';
6
+ /** Names the worker and the usual fix, so "worker failed" is not a dead end. */
7
+ export declare const WORKER_HELP = "The worker is the module next to @draftably/runtime's viewer. With Vite, add optimizeDeps: { exclude: ['@draftably/runtime', '@draftably/editor'] } to vite.config.";
8
+ /**
9
+ * Render in the page's worker pool. Jobs queue on the main thread; an aborted job leaves the
10
+ * queue before a worker sees it. A job that runs past `timeoutMs` stops its worker only.
11
+ */
12
+ export declare function renderInWorker(source: string, options: RenderOptions, { create, timeoutMs, signal, }: {
13
+ create?: () => Worker;
14
+ timeoutMs?: number;
15
+ signal?: AbortSignal;
16
+ }): Promise<RenderResult>;
17
+ /** The page's registered face for `key`, if any. */
18
+ export declare const sharedFace: (doc: Document, key: string) => FontFace | undefined;
19
+ /** Take a reference to `face` under `key`, adding it to the page on first use. */
20
+ export declare function retainFace(doc: Document, key: string, face: FontFace): undefined;
21
+ /** Drop a reference; the last one removes the face from the page. */
22
+ export declare function releaseFace(doc: Document, key: string): void;
23
+ /** Tests only: stop every worker and forget every face, as a new page would. */
24
+ export declare function resetPageShared(): void;
@@ -0,0 +1,7 @@
1
+ import type { Diagram } from '../model/types';
2
+ import type { Diagnostic, RenderOptions } from './types';
3
+ export declare function assertOptions(options: RenderOptions): void;
4
+ /** A link in note or footnote text: kept only as a safe HTTP(S) URL under a safe policy. */
5
+ export declare const textLink: (options: RenderOptions) => (href: string) => string | null;
6
+ /** The host resolves first; final validation never trusts the callback or authored link-base. */
7
+ export declare function applyLinks(doc: Diagram, options: RenderOptions): Diagnostic[];
@@ -0,0 +1,10 @@
1
+ import { type Scene } from '../dsl/compile';
2
+ import type { RenderOptions, RenderResult, ValidateOptions, ValidationResult } from './types';
3
+ export interface InternalRender {
4
+ result: RenderResult;
5
+ scene?: Scene;
6
+ }
7
+ /** Internal bridge for repository callers that need the editable Scene alongside public output. */
8
+ export declare function renderWithScene(source: string, options?: RenderOptions): Promise<InternalRender>;
9
+ export declare function renderSvg(source: string, options?: RenderOptions): Promise<RenderResult>;
10
+ export declare function validate(source: string, options?: ValidateOptions): Promise<ValidationResult>;
@@ -3,6 +3,7 @@
3
3
  * same appearance, shapes and title wrapping as the canvas and embeds the fonts it uses.
4
4
  */
5
5
  import { type ResolvedAppearance } from '../model/appearance';
6
+ import { computeRoutes } from '../model/routes';
6
7
  import type { Diagram, Rect } from '../model/types';
7
8
  /** How an object's `href` is written into the file; as written unless the caller says. */
8
9
  export interface SvgOptions {
@@ -20,9 +21,15 @@ export interface SvgOptions {
20
21
  linkTarget?: '_self' | '_blank';
21
22
  metadata?: string;
22
23
  maxOutputBytes?: number;
24
+ /** Receives the boxes and routes of a one-panel drawing, as drawn (not for compare or states). */
25
+ onDrawn?: (drawn: {
26
+ rects: Map<string, Rect>;
27
+ routes: Route[];
28
+ }) => void;
23
29
  }
24
30
  export declare class SvgLimitError extends Error {
25
31
  }
32
+ type Route = ReturnType<typeof computeRoutes>['routes'][number];
26
33
  /**
27
34
  * A sequence diagram's panel, a light surface with a border that bounds the exchange, and a row
28
35
  * band for each phase with its label in a tab at the left edge. Drawn under everything else.
@@ -36,3 +43,4 @@ export declare const PHASE_TAB = 8;
36
43
  /** A thin stripe across the top of a box, inside its rounded corners. */
37
44
  export declare function accentPath(r: Rect, radius: number): string;
38
45
  export declare function renderSvg(doc: Diagram, opts?: SvgOptions): string;
46
+ export {};