@draftably/runtime 0.3.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 (35) hide show
  1. package/README.md +1 -0
  2. package/dist/assets/render.worker-BXjS76Gy.js +8485 -0
  3. package/dist/compile-C2lvlbqE.js +7369 -0
  4. package/dist/element.js +275 -125
  5. package/dist/{font-data-DLXJur0j.js → font-data-DME3E71S.js} +1 -1
  6. package/dist/geometry-DfUVE8b2.js +79 -0
  7. package/dist/graph.js +336 -373
  8. package/dist/host.css +37 -0
  9. package/dist/host.js +16 -0
  10. package/dist/index.js +3 -3
  11. package/dist/{render-Dgo42PR4.js → render-CFWEEKPT.js} +339 -325
  12. package/dist/types/src/dsl/cells.d.ts +1 -0
  13. package/dist/types/src/dsl/compile.d.ts +2 -1
  14. package/dist/types/src/dsl/legend-notes.d.ts +14 -0
  15. package/dist/types/src/dsl/syntax.d.ts +2 -0
  16. package/dist/types/src/model/appearance.d.ts +19 -0
  17. package/dist/types/src/model/colors.d.ts +10 -2
  18. package/dist/types/src/model/labels.d.ts +22 -6
  19. package/dist/types/src/runtime/anchors.d.ts +46 -0
  20. package/dist/types/src/runtime/camera.d.ts +41 -0
  21. package/dist/types/src/runtime/element-base.d.ts +40 -0
  22. package/dist/types/src/runtime/element.d.ts +7 -0
  23. package/dist/types/src/runtime/geometry.d.ts +72 -0
  24. package/dist/types/src/runtime/host.d.ts +46 -0
  25. package/dist/types/src/runtime/index.d.ts +1 -0
  26. package/dist/types/src/runtime/page-shared.d.ts +24 -0
  27. package/dist/types/src/runtime/svg-renderer.d.ts +8 -0
  28. package/dist/types/src/runtime/types.d.ts +6 -0
  29. package/dist/types/src/runtime/viewer.d.ts +41 -7
  30. package/dist/version-CKs2Vl8m.js +4 -0
  31. package/dist/viewer.css +66 -9
  32. package/dist/viewer.js +689 -343
  33. package/package.json +6 -1
  34. package/dist/assets/render.worker-dvseyGmC.js +0 -8310
  35. package/dist/compile-Csfz4rk4.js +0 -7267
@@ -15,6 +15,7 @@ export interface CellInput {
15
15
  size?: {
16
16
  width: number;
17
17
  height?: number;
18
+ minHeight?: number;
18
19
  };
19
20
  }
20
21
  export interface CellBox {
@@ -99,11 +99,12 @@ export declare function layoutFor(m: Model, scope: string): ModelLayout;
99
99
  /**
100
100
  * Size of a node: its requested size, or the default width with a height that fits the title in
101
101
  * the theme's type and padding. A requested size is kept exactly; `overflow` reports a title that
102
- * no longer fits in it.
102
+ * no longer fits in it. With `height=auto`, `min-height` is a floor under the title's height.
103
103
  */
104
104
  export declare function leafSize(o: ModelObject, size: {
105
105
  width: number;
106
106
  height?: number;
107
+ minHeight?: number;
107
108
  } | undefined, look: ResolvedAppearance): {
108
109
  w: number;
109
110
  h: number;
@@ -1,3 +1,17 @@
1
+ /**
2
+ * `check` advice on the key and the prose round a drawing: a key that leaves a color or a line
3
+ * style unexplained, a `legend` swatch drawn in a shade that no node of its family uses, and
4
+ * heading prose that the drawing should say instead.
5
+ */
6
+ import { type Shade } from '../model/appearance.js';
1
7
  import type { Diagnostic } from './compile.js';
2
8
  import type { Model } from './syntax.js';
3
9
  export declare function legendNotes(m: Model): Diagnostic[];
10
+ /**
11
+ * The shade a `legend family` swatch draws when its line sets none: what the family's nodes draw.
12
+ * When no node names the family and its nodes are all member cards (they inherit it from a group
13
+ * on a quiet surface shade), that is the member card's shade; otherwise it is a colored node's.
14
+ */
15
+ export declare function legendShade(m: Model, family: string): Shade;
16
+ /** Give each `legend family` line with no `shade=` the shade its family's nodes draw. */
17
+ export declare function settleLegendShades(m: Model): void;
@@ -210,11 +210,13 @@ export type Hint = {
210
210
  line: number;
211
211
  }
212
212
  /** `height` is unset for `height=auto`: the title's height at that width, as without a size. */
213
+ /** `minHeight` is a floor under `height=auto`, for a host that draws more than the title. */
213
214
  | {
214
215
  type: 'size';
215
216
  id: string;
216
217
  width: number;
217
218
  height?: number;
219
+ minHeight?: number;
218
220
  line: number;
219
221
  };
220
222
  export type Strength = 'prefer' | 'required';
@@ -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. */
@@ -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.js';
9
+ import { type Geometry } from './geometry.js';
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.js';
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.js';
3
+ import type { Anchor } from './anchors.js';
4
+ import type { UpdateResult } from './viewer.js';
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
+ };
@@ -1,8 +1,15 @@
1
1
  import type { UpdateResult, ViewerHandle, ViewerOptions } from './viewer.js';
2
+ export type { DraftEventMap, DraftStatus, HostEventName, HostEvents } from './host.js';
3
+ export { HOST_EVENTS } from './host.js';
2
4
  export interface DraftDiagramElement extends HTMLElement {
3
5
  source: string;
4
6
  /** Reflects the `outline` attribute. */
5
7
  outline: 'visible' | 'hidden' | undefined;
8
+ /**
9
+ * Every `mountViewer` option. `label`, `outline`, `toolbar` and `render` (except `fonts`) apply
10
+ * live; `execution` and `render.fonts` mount a new viewer. A new object with the same values
11
+ * does nothing.
12
+ */
6
13
  options: ViewerOptions;
7
14
  readonly viewer?: ViewerHandle;
8
15
  readonly updateComplete: Promise<UpdateResult | undefined>;
@@ -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.js';
7
+ import type { Diagram, Point, Rect, Side } from '../model/types.js';
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.js';
7
+ export type { Camera, CameraReason, FrameOptions, MoveResult } from './camera.js';
8
+ export type { Box, EdgeOut, Geometry, PlacedBox } from './geometry.js';
9
+ export type { Anchor, DraftSlot } from './anchors.js';
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
+ }
@@ -2,4 +2,5 @@ import type { RenderOptions, RenderResult, ValidateOptions, ValidationResult } f
2
2
  export declare const renderSvg: (source: string, options?: RenderOptions) => Promise<RenderResult>;
3
3
  export declare const validate: (source: string, options?: ValidateOptions) => Promise<ValidationResult>;
4
4
  export { runtimeVersion, fontMetricsVersion } from './version.js';
5
+ export type { Box, EdgeOut, Geometry, PlacedBox } from './geometry.js';
5
6
  export type { CompileLimits, CommonRenderOptions, Diagnostic, DiagramSummary, ItemSummary, LinkPolicy, RenderFailure, RenderLimits, RenderOptions, RenderResult, RenderedDiagram, Severity, SourceLocation, ValidateOptions, ValidationResult, } from './types.js';
@@ -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.js';
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;
@@ -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.js';
6
+ import { computeRoutes } from '../model/routes.js';
6
7
  import type { Diagram, Rect } from '../model/types.js';
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 {};
@@ -1,3 +1,4 @@
1
+ import type { Geometry } from './geometry.js';
1
2
  export type Severity = 'error' | 'warning' | 'info';
2
3
  export interface SourceLocation {
3
4
  sourceId?: string;
@@ -86,6 +87,11 @@ export interface DiagramSummary {
86
87
  }
87
88
  export interface RenderedDiagram extends DiagramSummary {
88
89
  svg: string;
90
+ /**
91
+ * Where each node, group and edge is drawn, in the SVG's own units (its `viewBox`), in the graph
92
+ * API's shapes. `null` for `compare` and `states` drawings, which repeat items across panels.
93
+ */
94
+ geometry: Geometry | null;
89
95
  provenance: {
90
96
  runtimeVersion: string;
91
97
  dslVersion: 1;
@@ -1,11 +1,13 @@
1
+ import { type Camera, type CameraReason, type FrameOptions, type MoveResult } from './camera.js';
2
+ import { type Anchor, type DraftSlot } from './anchors.js';
3
+ import { type Geometry } from './geometry.js';
1
4
  import { fontMetricsVersion, runtimeVersion } from './version.js';
2
5
  import type { CommonRenderOptions, Diagnostic, LinkPolicy, RenderFailure, RenderResult } from './types.js';
3
6
  export { fontMetricsVersion, runtimeVersion };
4
- export interface Camera {
5
- centerX: number;
6
- centerY: number;
7
- zoom: number;
8
- }
7
+ export type { Camera, CameraReason, FrameOptions, MoveResult } from './camera.js';
8
+ export type { Box, EdgeOut, Geometry, PlacedBox } from './geometry.js';
9
+ export type { Anchor, DraftSlot } from './anchors.js';
10
+ export { ZOOM_MAX, ZOOM_MIN } from './camera.js';
9
11
  export type ViewerExecution = {
10
12
  mode: 'worker';
11
13
  createWorker?: () => Worker;
@@ -25,6 +27,8 @@ export interface ViewerOptions {
25
27
  */
26
28
  outline?: 'visible' | 'hidden';
27
29
  execution?: ViewerExecution;
30
+ /** `auto` (default) hides slot bodies and shows titles again below a zoom of 0.4. */
31
+ detail?: 'auto' | 'full';
28
32
  render?: ViewerRenderOptions;
29
33
  }
30
34
  export interface UpdateOptions {
@@ -48,20 +52,27 @@ export type UpdateResult = {
48
52
  revision: number;
49
53
  };
50
54
  export interface ViewerEvents {
55
+ /** After each commit, after `anchor()`, and with `[]` before `destroy` detaches the targets. */
56
+ anchors: {
57
+ anchors: readonly Anchor[];
58
+ };
59
+ /** After each commit: the drawing, and where each item is (`null` for compare and states). */
51
60
  render: {
52
61
  revision: number;
53
62
  value: Extract<RenderResult, {
54
63
  ok: true;
55
64
  }>['value'];
65
+ geometry: Geometry | null;
56
66
  };
57
67
  diagnostics: {
58
68
  revision: number;
59
69
  diagnostics: readonly Diagnostic[];
60
70
  stale: boolean;
61
71
  };
72
+ /** At most once per animation frame, with the latest reason. */
62
73
  camera: {
63
74
  camera: Camera;
64
- reason: 'user' | 'resize' | 'fit' | 'api';
75
+ reason: CameraReason;
65
76
  };
66
77
  itemactivate: {
67
78
  id: string;
@@ -69,20 +80,43 @@ export interface ViewerEvents {
69
80
  input: 'pointer' | 'keyboard';
70
81
  revision: number;
71
82
  };
83
+ /** `preventDefault()` stops the viewer opening the link. */
72
84
  link: {
73
85
  itemId: string;
74
86
  href: string;
75
87
  target: '_self' | '_blank';
76
88
  preventDefault(): void;
77
89
  };
90
+ /** A right-click or the menu key; `id` is null on the background. `preventDefault()` stops the browser's menu. */
91
+ contextmenu: {
92
+ id: string | null;
93
+ kind?: ItemKind;
94
+ clientX: number;
95
+ clientY: number;
96
+ preventDefault(): void;
97
+ };
78
98
  }
99
+ export type ItemKind = 'node' | 'group' | 'edge';
79
100
  export interface ViewerHandle {
101
+ /** Resolves after the first commit or failure; then after each later one. */
102
+ readonly ready: Promise<UpdateResult>;
80
103
  update(source: string, options?: UpdateOptions): Promise<UpdateResult>;
104
+ /** Where each item is drawn, in world units. `null` before the first commit, and for compare and states. The same object for one revision. */
105
+ geometry(): Geometry | null;
81
106
  on<K extends keyof ViewerEvents>(type: K, listener: (event: ViewerEvents[K]) => void): () => void;
82
107
  getCamera(): Camera;
108
+ /** Zoom is clamped to `ZOOM_MIN`–`ZOOM_MAX`. */
83
109
  setCamera(camera: Camera): void;
84
- fit(): void;
110
+ /** Frame these items, or the whole drawing without ids. At most actual size by default. */
111
+ fit(ids?: readonly string[], options?: FrameOptions): MoveResult;
112
+ /** The least move that shows the item; zooms out only if it does not fit. */
113
+ reveal(id: string, options?: FrameOptions): MoveResult;
85
114
  focusItem(id: string): boolean;
115
+ /**
116
+ * Plain-DOM hosts only: keep a target for each slot (`body:<id>`, `overlay:<id>`). Targets come
117
+ * in the `anchors` event; put content in them. The elements set this from their marked children.
118
+ */
119
+ anchor(slots: readonly DraftSlot[]): void;
86
120
  destroy(): void;
87
121
  }
88
122
  export declare function mountViewer(host: HTMLElement, options?: ViewerOptions): ViewerHandle;
@@ -0,0 +1,4 @@
1
+ const n = "0.4.0", o = n;
2
+ export {
3
+ o as r
4
+ };