@draftably/runtime 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.
- package/README.md +14 -0
- package/dist/assets/render.worker-BXjS76Gy.js +8485 -0
- package/dist/compile-C2lvlbqE.js +7369 -0
- package/dist/element.js +275 -125
- package/dist/{font-data-tnqJ-kTK.js → font-data-DME3E71S.js} +1 -1
- package/dist/geometry-DfUVE8b2.js +79 -0
- package/dist/graph.js +632 -0
- package/dist/host.css +37 -0
- package/dist/host.js +16 -0
- package/dist/index.js +5 -4
- package/dist/render-CFWEEKPT.js +1160 -0
- package/dist/types/src/dsl/cells.d.ts +1 -0
- package/dist/types/src/dsl/compile.d.ts +15 -1
- package/dist/types/src/dsl/corridors.d.ts +38 -0
- package/dist/types/src/dsl/legend-notes.d.ts +14 -0
- package/dist/types/src/dsl/syntax.d.ts +2 -0
- package/dist/types/src/graph-api/core.d.ts +125 -0
- package/dist/types/src/graph-api/cytoscape.d.ts +48 -0
- package/dist/types/src/graph-api/dagre.d.ts +33 -0
- package/dist/types/src/graph-api/elk.d.ts +91 -0
- package/dist/types/src/graph-api/index.d.ts +60 -0
- package/dist/types/src/graph-api/react-flow.d.ts +100 -0
- package/dist/types/src/graph-api/transform.d.ts +6 -0
- package/dist/types/src/model/appearance.d.ts +19 -0
- package/dist/types/src/model/colors.d.ts +10 -2
- package/dist/types/src/model/labels.d.ts +22 -6
- package/dist/types/src/model/types.d.ts +9 -0
- package/dist/types/src/runtime/anchors.d.ts +46 -0
- package/dist/types/src/runtime/camera.d.ts +41 -0
- package/dist/types/src/runtime/element-base.d.ts +40 -0
- package/dist/types/src/runtime/element.d.ts +7 -0
- package/dist/types/src/runtime/geometry.d.ts +72 -0
- package/dist/types/src/runtime/host.d.ts +46 -0
- package/dist/types/src/runtime/index.d.ts +1 -0
- package/dist/types/src/runtime/page-shared.d.ts +24 -0
- package/dist/types/src/runtime/svg-renderer.d.ts +8 -0
- package/dist/types/src/runtime/types.d.ts +6 -0
- package/dist/types/src/runtime/viewer.d.ts +41 -7
- package/dist/version-CKs2Vl8m.js +4 -0
- package/dist/viewer.css +66 -9
- package/dist/viewer.js +689 -341
- package/package.json +10 -1
- package/dist/assets/render.worker-fWW85Tf9.js +0 -7661
- package/dist/render-ZzhO-2H6.js +0 -7676
|
@@ -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 `
|
|
16
|
-
export declare function titleHeight(shape: ShapeKind,
|
|
17
|
-
/**
|
|
18
|
-
export
|
|
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:
|
|
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.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
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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:
|
|
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
|
-
|
|
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;
|
package/dist/viewer.css
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Public tokens: set any --draft-* property on the element or an ancestor, and it wins.
|
|
3
|
+
* They inherit through the shadow root; the defaults below apply only when unset.
|
|
4
|
+
*/
|
|
1
5
|
.draft-viewer {
|
|
2
|
-
--dv-border: #cbd5e1;
|
|
3
|
-
--dv-ink: #1e293b;
|
|
4
|
-
--dv-bg: #fff;
|
|
6
|
+
--dv-border: var(--draft-border, #cbd5e1);
|
|
7
|
+
--dv-ink: var(--draft-ink, #1e293b);
|
|
8
|
+
--dv-bg: var(--draft-bg, #fff);
|
|
9
|
+
--dv-focus: var(--draft-focus, #2563eb);
|
|
10
|
+
--dv-shadow: var(--draft-shadow, rgb(15 23 42 / 0.12));
|
|
11
|
+
--dv-warning-ink: var(--draft-warning-ink, #9a3412);
|
|
12
|
+
--dv-warning-bg: var(--draft-warning-bg, #fff7ed);
|
|
5
13
|
position: relative;
|
|
6
14
|
display: grid;
|
|
7
15
|
grid-template-rows: auto minmax(8rem, 1fr) auto auto;
|
|
@@ -15,9 +23,13 @@
|
|
|
15
23
|
font:
|
|
16
24
|
14px/1.45 system-ui,
|
|
17
25
|
sans-serif;
|
|
18
|
-
overflow:
|
|
26
|
+
overflow: clip;
|
|
27
|
+
}
|
|
28
|
+
.draft-viewer__toolbar[hidden] {
|
|
29
|
+
display: none;
|
|
19
30
|
}
|
|
20
31
|
.draft-viewer__toolbar {
|
|
32
|
+
grid-row: 1;
|
|
21
33
|
display: flex;
|
|
22
34
|
flex-wrap: wrap;
|
|
23
35
|
align-items: center;
|
|
@@ -35,12 +47,14 @@
|
|
|
35
47
|
}
|
|
36
48
|
.draft-viewer button:focus-visible,
|
|
37
49
|
.draft-viewer__viewport:focus-visible {
|
|
38
|
-
outline: 3px solid
|
|
50
|
+
outline: 3px solid var(--dv-focus);
|
|
39
51
|
outline-offset: 2px;
|
|
40
52
|
}
|
|
41
53
|
.draft-viewer__viewport {
|
|
54
|
+
grid-row: 2;
|
|
42
55
|
position: relative;
|
|
43
|
-
|
|
56
|
+
/* clip, not hidden: focus on off-screen slot content must not scroll the drawing away. */
|
|
57
|
+
overflow: clip;
|
|
44
58
|
min-height: 8rem;
|
|
45
59
|
touch-action: none;
|
|
46
60
|
cursor: grab;
|
|
@@ -55,10 +69,12 @@
|
|
|
55
69
|
overflow: visible;
|
|
56
70
|
}
|
|
57
71
|
.draft-viewer__status {
|
|
72
|
+
grid-row: 3;
|
|
58
73
|
margin: 0;
|
|
59
74
|
padding: 0.35rem 0.65rem;
|
|
60
75
|
}
|
|
61
76
|
.draft-viewer__outline {
|
|
77
|
+
grid-row: 4;
|
|
62
78
|
margin: 0;
|
|
63
79
|
padding: 0.4rem 0.8rem 0.6rem 2rem;
|
|
64
80
|
max-height: 7rem;
|
|
@@ -85,11 +101,11 @@
|
|
|
85
101
|
position: absolute;
|
|
86
102
|
inset: auto 0 0;
|
|
87
103
|
background: var(--dv-bg);
|
|
88
|
-
box-shadow: 0 -4px 12px
|
|
104
|
+
box-shadow: 0 -4px 12px var(--dv-shadow);
|
|
89
105
|
}
|
|
90
106
|
.draft-viewer--stale .draft-viewer__status {
|
|
91
|
-
color:
|
|
92
|
-
background:
|
|
107
|
+
color: var(--dv-warning-ink);
|
|
108
|
+
background: var(--dv-warning-bg);
|
|
93
109
|
}
|
|
94
110
|
.draft-viewer__status:empty {
|
|
95
111
|
display: none;
|
|
@@ -108,3 +124,44 @@
|
|
|
108
124
|
padding-inline: 0.45rem;
|
|
109
125
|
}
|
|
110
126
|
}
|
|
127
|
+
|
|
128
|
+
/* The anchor layer: slot targets above the drawing. Slot content takes no pointer input unless
|
|
129
|
+
it is marked data-draft-input (host.css). */
|
|
130
|
+
.draft-anchors {
|
|
131
|
+
position: absolute;
|
|
132
|
+
inset: 0;
|
|
133
|
+
pointer-events: none;
|
|
134
|
+
overflow: visible;
|
|
135
|
+
}
|
|
136
|
+
.draft-anchors__bodies {
|
|
137
|
+
position: absolute;
|
|
138
|
+
left: 0;
|
|
139
|
+
top: 0;
|
|
140
|
+
width: 0;
|
|
141
|
+
height: 0;
|
|
142
|
+
transform-origin: 0 0;
|
|
143
|
+
}
|
|
144
|
+
.draft-anchors__overlays {
|
|
145
|
+
position: absolute;
|
|
146
|
+
inset: 0;
|
|
147
|
+
}
|
|
148
|
+
.draft-anchor {
|
|
149
|
+
position: absolute;
|
|
150
|
+
box-sizing: border-box;
|
|
151
|
+
pointer-events: none;
|
|
152
|
+
}
|
|
153
|
+
.draft-anchor--body {
|
|
154
|
+
overflow: clip;
|
|
155
|
+
}
|
|
156
|
+
.draft-anchor--overlay {
|
|
157
|
+
left: 0;
|
|
158
|
+
top: 0;
|
|
159
|
+
overflow: visible;
|
|
160
|
+
}
|
|
161
|
+
.draft-anchors[data-draft-detail='low'] .draft-anchors__bodies {
|
|
162
|
+
visibility: hidden;
|
|
163
|
+
}
|
|
164
|
+
/* A body replaces its node's drawn title; the title stays for screen readers. */
|
|
165
|
+
.draft-viewer__viewport[data-draft-detail='full'] [data-draft-body] text {
|
|
166
|
+
opacity: 0;
|
|
167
|
+
}
|