@draftably/runtime 0.0.0-stage → 0.1.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 (62) hide show
  1. package/README.md +56 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/THIRD_PARTY_NOTICES +286 -0
  4. package/dist/assets/render.worker-BpUp6NY_.js +7618 -0
  5. package/dist/element.js +122 -0
  6. package/dist/font-data-DbrpaYkG.js +30 -0
  7. package/dist/fonts/inter-latin-500-normal.woff2 +0 -0
  8. package/dist/fonts/inter-latin-600-normal.woff2 +0 -0
  9. package/dist/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
  10. package/dist/fonts/source-serif-4-latin-600-normal.woff2 +0 -0
  11. package/dist/fonts.css +4 -0
  12. package/dist/index.js +9 -0
  13. package/dist/render-BOAZBzVa.js +7633 -0
  14. package/dist/types/src/dsl/cells.d.ts +91 -0
  15. package/dist/types/src/dsl/channels.d.ts +53 -0
  16. package/dist/types/src/dsl/compile.d.ts +162 -0
  17. package/dist/types/src/dsl/frame.d.ts +38 -0
  18. package/dist/types/src/dsl/lanes.d.ts +23 -0
  19. package/dist/types/src/dsl/legend-notes.d.ts +3 -0
  20. package/dist/types/src/dsl/project.d.ts +39 -0
  21. package/dist/types/src/dsl/syntax.d.ts +317 -0
  22. package/dist/types/src/engines/explorer-engine.d.ts +67 -0
  23. package/dist/types/src/engines/layout-engine.d.ts +120 -0
  24. package/dist/types/src/engines/routing-engine.d.ts +108 -0
  25. package/dist/types/src/engines/sculpt-engine.d.ts +71 -0
  26. package/dist/types/src/lib/font-data.d.ts +9 -0
  27. package/dist/types/src/model/appearance.d.ts +209 -0
  28. package/dist/types/src/model/bounds.d.ts +19 -0
  29. package/dist/types/src/model/colors.d.ts +22 -0
  30. package/dist/types/src/model/document.d.ts +24 -0
  31. package/dist/types/src/model/font-faces.d.ts +24 -0
  32. package/dist/types/src/model/fonts.d.ts +47 -0
  33. package/dist/types/src/model/footnote.d.ts +34 -0
  34. package/dist/types/src/model/frame.d.ts +71 -0
  35. package/dist/types/src/model/geometry.d.ts +29 -0
  36. package/dist/types/src/model/graph.d.ts +53 -0
  37. package/dist/types/src/model/heading.d.ts +38 -0
  38. package/dist/types/src/model/icons.d.ts +2 -0
  39. package/dist/types/src/model/labels.d.ts +88 -0
  40. package/dist/types/src/model/legend.d.ts +25 -0
  41. package/dist/types/src/model/link.d.ts +63 -0
  42. package/dist/types/src/model/markers.d.ts +47 -0
  43. package/dist/types/src/model/note.d.ts +75 -0
  44. package/dist/types/src/model/panels.d.ts +11 -0
  45. package/dist/types/src/model/rich-text.d.ts +24 -0
  46. package/dist/types/src/model/routes.d.ts +170 -0
  47. package/dist/types/src/model/stack.d.ts +8 -0
  48. package/dist/types/src/model/status.d.ts +84 -0
  49. package/dist/types/src/model/styles.d.ts +13 -0
  50. package/dist/types/src/model/surface.d.ts +31 -0
  51. package/dist/types/src/model/types.d.ts +277 -0
  52. package/dist/types/src/runtime/element.d.ts +12 -0
  53. package/dist/types/src/runtime/index.d.ts +5 -0
  54. package/dist/types/src/runtime/policy.d.ts +7 -0
  55. package/dist/types/src/runtime/render.d.ts +10 -0
  56. package/dist/types/src/runtime/svg-renderer.d.ts +38 -0
  57. package/dist/types/src/runtime/types.d.ts +109 -0
  58. package/dist/types/src/runtime/version.d.ts +2 -0
  59. package/dist/types/src/runtime/viewer.d.ts +84 -0
  60. package/dist/viewer.css +90 -0
  61. package/dist/viewer.js +438 -0
  62. package/package.json +50 -4
@@ -0,0 +1,277 @@
1
+ export type Point = {
2
+ x: number;
3
+ y: number;
4
+ };
5
+ export type Rect = {
6
+ x: number;
7
+ y: number;
8
+ w: number;
9
+ h: number;
10
+ };
11
+ export type Side = 'left' | 'right' | 'top' | 'bottom';
12
+ export type PortSide = Side | 'auto';
13
+ export type Direction = 'RIGHT' | 'DOWN';
14
+ export type Theme = 'light' | 'dark';
15
+ import type { DiagramAppearance, Shade } from './appearance.js';
16
+ import type { EndMarker } from './markers.js';
17
+ import type { Spot, Status } from './status.js';
18
+ export type ShapeKind = 'box' | 'pill' | 'circle' | 'diamond' | 'text' | 'icon' | 'image'
19
+ /** A seam between two cells of a `cells` group. */
20
+ | 'marker';
21
+ /** A color family: a built-in one, one the file defines, or `auto` for the theme's surface. */
22
+ export type ColorKey = string;
23
+ export type LabelMode = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'detached';
24
+ export interface DiagramNode {
25
+ id: string;
26
+ title: string;
27
+ /** Title before a change, from `was=` on a `~` line. */
28
+ was?: string;
29
+ /** Shape before a change, from `was-shape=` on a `~` line. */
30
+ wasShape?: ShapeKind;
31
+ /** Count before a change, from `was-count=` on a `~` line; 1 means a single box. */
32
+ wasCount?: number | string;
33
+ /** A link to open from exported drawings. */
34
+ href?: string;
35
+ /** How many things this box stands for, or a symbol such as `N`; drawn as a stack with a badge. */
36
+ count?: number | string;
37
+ /** A cell's columns (or units), or `var` for a field of variable length. */
38
+ span?: number | 'var';
39
+ parentId?: string;
40
+ image?: string;
41
+ /** A sequence participant: length of the dashed lifeline below the box. */
42
+ lifeline?: number;
43
+ }
44
+ export interface DiagramGroup {
45
+ id: string;
46
+ title: string;
47
+ /** Title before a change, from `was=` on a `~` line. */
48
+ was?: string;
49
+ /** A link to open from exported drawings. */
50
+ href?: string;
51
+ parentId?: string;
52
+ /** `soft` draws a tint behind the members instead of a frame; `none` draws nothing. */
53
+ frame?: 'soft' | 'none';
54
+ /** Lines the title wraps to, when more than one; the header grows by a line for each. */
55
+ titleLines?: number;
56
+ /** A soft group's tint: rectangles relative to the group's top left; the first holds the label. */
57
+ hull?: Rect[];
58
+ }
59
+ export interface DiagramEdge {
60
+ id: string;
61
+ from: string;
62
+ to: string;
63
+ label: string;
64
+ /** Label before a change, from `was=` on a `~` line. */
65
+ was?: string;
66
+ directed: boolean;
67
+ /** The mark at the target end, when it isn't the plain arrow; see `headOf`. */
68
+ head?: EndMarker;
69
+ /** The mark at the source end, when it has one; see `tailOf`. */
70
+ tail?: EndMarker;
71
+ emphasis: boolean;
72
+ dashed: boolean;
73
+ /** How it differs between two versions of the diagram. */
74
+ status?: Status;
75
+ /** How the drawn emphasis state treats it; see `withState`. */
76
+ spot?: Spot;
77
+ }
78
+ export interface NodeLayout extends Rect {
79
+ manual?: boolean;
80
+ pinned?: boolean;
81
+ }
82
+ export interface EdgeLayout {
83
+ fromSide?: PortSide;
84
+ toSide?: PortSide;
85
+ labelDx?: number;
86
+ labelDy?: number;
87
+ waypoints?: Point[];
88
+ /** The reading direction of the layout that places both ends; the edge runs along it. */
89
+ flow?: 'right' | 'down';
90
+ /** Set by the compiler for a fold's wrap edge: the line it returns along, between two bands. */
91
+ lane?: Lane;
92
+ /** From a lane, at route time: where along its target's side the edge arrives, off centre. */
93
+ toOffset?: number;
94
+ /** A sequence message: drawn straight between the two lifelines at this height, not routed. */
95
+ row?: number;
96
+ }
97
+ /**
98
+ * A channel between two bands of a fold: a level line at `y`, or an upright one at `x`. With
99
+ * `leave`, the edge leaves its source's side and turns onto the lane at that coordinate; with
100
+ * `enter`, it runs on along the lane to that coordinate and turns into its target's side.
101
+ * With `out`, it is a return or skip lane beyond both ends.
102
+ */
103
+ export type Lane = ({
104
+ y: number;
105
+ } | {
106
+ x: number;
107
+ }) & {
108
+ leave?: number;
109
+ enter?: number;
110
+ /**
111
+ * A lane beyond both ends, outside their band: the edge leaves by the side facing it, runs
112
+ * along it, and at `enter` turns into the target's side that faces back toward the source.
113
+ */
114
+ out?: boolean;
115
+ /**
116
+ * With `out`: the gap beside the target holds another edge's label, so the edge arrives square
117
+ * on the target's face toward the lane, at `enter` (its middle), instead of turning into that gap.
118
+ */
119
+ face?: boolean;
120
+ };
121
+ export interface LabelLayout {
122
+ mode?: LabelMode;
123
+ dx?: number;
124
+ dy?: number;
125
+ width?: number;
126
+ leader?: boolean;
127
+ }
128
+ export interface ContainerLayout {
129
+ direction: 'inherit' | Direction;
130
+ padding: number;
131
+ manualSize: boolean;
132
+ /** Set for a `cells` group: its members touch, under a title band this tall. */
133
+ cellsBand?: number;
134
+ /** A byte or bit layout's extra marks, relative to the group's top-left corner. */
135
+ cellsChrome?: CellsChrome;
136
+ }
137
+ /** What a byte or bit layout draws besides its cells. */
138
+ export interface CellsChrome {
139
+ /** Unit numbers in a strip under the title band. */
140
+ ruler?: {
141
+ y: number;
142
+ h: number;
143
+ ticks: Array<{
144
+ x: number;
145
+ label: string;
146
+ }>;
147
+ };
148
+ /** Further pieces of fields that run on past a row's end, by field. */
149
+ pieces?: Record<string, Rect[]>;
150
+ /** Fields of variable length, drawn with broken ends. */
151
+ open?: string[];
152
+ }
153
+ export type IntentRole = 'start' | 'end';
154
+ export type IntentPlacement = 'top-left' | 'left-center' | 'center' | 'top-center' | 'top-right' | 'right-center' | 'bottom-left' | 'bottom-center' | 'bottom-right';
155
+ export type LayoutIntent = Partial<Record<IntentRole, {
156
+ id: string;
157
+ placement: IntentPlacement;
158
+ }>>;
159
+ export interface NodeStyle {
160
+ shape: ShapeKind;
161
+ color: ColorKey;
162
+ /** Numeric shade; omitted means the default for the object (500 for nodes, 100 for groups). */
163
+ shade?: Shade;
164
+ icon: string;
165
+ /** How it differs between two versions of the diagram. */
166
+ status?: Status;
167
+ /** How the drawn emphasis state treats it; see `withState`. */
168
+ spot?: Spot;
169
+ }
170
+ export type LayoutAlgorithmId = 'compound-flow' | 'boundary-align' | 'compound-spring' | 'tidy' | 'stress' | 'force' | 'flow' | 'layered' | 'tree' | 'grid' | 'separate';
171
+ export type AspectSetting = 'canvas' | '1' | '1.333' | '1.6' | '2' | '0.75';
172
+ export interface LayoutSettings {
173
+ algorithm: LayoutAlgorithmId;
174
+ direction: Direction;
175
+ mode: 'pass' | 'rebuild';
176
+ maxMove: number;
177
+ stability: number;
178
+ iterations: number;
179
+ nodeGap: number;
180
+ rankGap: number;
181
+ edgeLength: number;
182
+ repulsion: number;
183
+ flowStrength: number;
184
+ alignment: number;
185
+ compactness: number;
186
+ gridStrength: number;
187
+ aspect: AspectSetting;
188
+ fold: 'auto' | 'never';
189
+ respectDirection: boolean;
190
+ ordering: 'stable' | 'crossings';
191
+ rootId: string;
192
+ respectManual: boolean;
193
+ growContainers: boolean;
194
+ boundaryPull: number;
195
+ hierarchySweeps: number;
196
+ groupGap: number;
197
+ }
198
+ export interface RoutingSettings {
199
+ clearance: number;
200
+ portGap: number;
201
+ edgeGap: number;
202
+ bendPenalty: number;
203
+ crossingPenalty: number;
204
+ sharedPenalty: number;
205
+ parallelPenalty: number;
206
+ portStability: number;
207
+ fanoutPenalty: number;
208
+ passes: number;
209
+ cornerRadius: number;
210
+ standoff: number;
211
+ }
212
+ export interface DiagramSettings extends LayoutSettings {
213
+ snap: boolean;
214
+ showManual: boolean;
215
+ routing: RoutingSettings;
216
+ }
217
+ export interface Camera {
218
+ x: number;
219
+ y: number;
220
+ k: number;
221
+ }
222
+ export interface Diagram {
223
+ schemaVersion: 2;
224
+ id: string;
225
+ title: string;
226
+ graph: {
227
+ nodes: DiagramNode[];
228
+ edges: DiagramEdge[];
229
+ groups: DiagramGroup[];
230
+ };
231
+ layout: {
232
+ nodes: Record<string, NodeLayout>;
233
+ edges: Record<string, EdgeLayout>;
234
+ labels: Record<string, LabelLayout>;
235
+ containers: Record<string, ContainerLayout>;
236
+ intent: LayoutIntent;
237
+ /** A sequence diagram's frame and the row bands of its `phase` statements. */
238
+ sequence?: SequenceLayout;
239
+ };
240
+ styles: Record<string, NodeStyle>;
241
+ collapsed: string[];
242
+ settings: DiagramSettings;
243
+ camera: Camera;
244
+ /** Editor light or dark mode; independent of the diagram's appearance. */
245
+ theme: Theme;
246
+ /** Diagram theme, overrides and color definitions. Saved with the diagram. */
247
+ appearance: DiagramAppearance;
248
+ }
249
+ export type NodeGeometry = Record<string, NodeLayout>;
250
+ /** The panel a sequence diagram is drawn on, and a row band for each `phase`. */
251
+ export interface SequenceLayout {
252
+ panel: Rect;
253
+ phases: SequencePhase[];
254
+ /** Left edge for phase labels, when a note in the left margin widens the panel past them. */
255
+ phaseX?: number;
256
+ /** Spots of the notes that sit in a side margin, by index in `appearance.notes`. */
257
+ notes?: Array<Rect | null>;
258
+ /** With `notes`: the panel without those margins, for drawings with their notes in the frame. */
259
+ inner?: Rect;
260
+ /**
261
+ * From `was-order`: each message's row and the phase bands in the older message order, and how
262
+ * much further that order runs down the panel.
263
+ */
264
+ before?: {
265
+ rows: Record<string, number>;
266
+ phases: SequencePhase[];
267
+ grow: number;
268
+ };
269
+ }
270
+ export type SequencePhase = {
271
+ id: string;
272
+ label: string;
273
+ y: number;
274
+ h: number;
275
+ };
276
+ /** Any object that can be selected: a shape, a container, or a connection. */
277
+ export type Entity = DiagramNode | DiagramGroup | DiagramEdge;
@@ -0,0 +1,12 @@
1
+ import type { UpdateResult, ViewerHandle, ViewerOptions } from './viewer.js';
2
+ export interface DraftDiagramElement extends HTMLElement {
3
+ source: string;
4
+ options: ViewerOptions;
5
+ readonly viewer?: ViewerHandle;
6
+ readonly updateComplete: Promise<UpdateResult | undefined>;
7
+ }
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'draft-diagram': DraftDiagramElement;
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ import type { RenderOptions, RenderResult, ValidateOptions, ValidationResult } from './types.js';
2
+ export declare const renderSvg: (source: string, options?: RenderOptions) => Promise<RenderResult>;
3
+ export declare const validate: (source: string, options?: ValidateOptions) => Promise<ValidationResult>;
4
+ export { runtimeVersion, fontMetricsVersion } from './version.js';
5
+ export type { CompileLimits, CommonRenderOptions, Diagnostic, DiagramSummary, ItemSummary, LinkPolicy, RenderFailure, RenderLimits, RenderOptions, RenderResult, RenderedDiagram, Severity, SourceLocation, ValidateOptions, ValidationResult, } from './types.js';
@@ -0,0 +1,7 @@
1
+ import type { Diagram } from '../model/types.js';
2
+ import type { Diagnostic, RenderOptions } from './types.js';
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.js';
2
+ import type { RenderOptions, RenderResult, ValidateOptions, ValidationResult } from './types.js';
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>;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * A standalone SVG of a projected diagram for terminal and agent inspection. It draws with the
3
+ * same appearance, shapes and title wrapping as the canvas and embeds the fonts it uses.
4
+ */
5
+ import { type ResolvedAppearance } from '../model/appearance.js';
6
+ import type { Diagram, Rect } from '../model/types.js';
7
+ /** How an object's `href` is written into the file; as written unless the caller says. */
8
+ export interface SvgOptions {
9
+ href?: (href: string) => string;
10
+ /** Writes a link in note or footnote text; null draws the text without a link. */
11
+ textHref?: (href: string) => string | null;
12
+ /** Draw every emphasis state as its own panel. */
13
+ states?: boolean;
14
+ /** Host-owned policies used by the portable runtime. */
15
+ idPrefix?: string;
16
+ fontCss?: string;
17
+ title?: string;
18
+ description?: string;
19
+ styleNonce?: string;
20
+ linkTarget?: '_self' | '_blank';
21
+ metadata?: string;
22
+ maxOutputBytes?: number;
23
+ }
24
+ export declare class SvgLimitError extends Error {
25
+ }
26
+ /**
27
+ * A sequence diagram's panel, a light surface with a border that bounds the exchange, and a row
28
+ * band for each phase with its label in a tab at the left edge. Drawn under everything else.
29
+ */
30
+ export declare function sequenceBackdrop(doc: Diagram, look: ResolvedAppearance): {
31
+ panel: string[];
32
+ phases: string[];
33
+ };
34
+ /** Inset of a phase label from its band's top left corner. */
35
+ export declare const PHASE_TAB = 8;
36
+ /** A thin stripe across the top of a box, inside its rounded corners. */
37
+ export declare function accentPath(r: Rect, radius: number): string;
38
+ export declare function renderSvg(doc: Diagram, opts?: SvgOptions): string;
@@ -0,0 +1,109 @@
1
+ export type Severity = 'error' | 'warning' | 'info';
2
+ export interface SourceLocation {
3
+ sourceId?: string;
4
+ line: number;
5
+ column?: number;
6
+ }
7
+ export interface Diagnostic {
8
+ code: string;
9
+ severity: Severity;
10
+ message: string;
11
+ location?: SourceLocation;
12
+ itemIds: readonly string[];
13
+ related?: readonly {
14
+ message: string;
15
+ location: SourceLocation;
16
+ }[];
17
+ nativeCode?: string;
18
+ }
19
+ export interface CompileLimits {
20
+ maxSourceChars: number;
21
+ maxSourceBytes: number;
22
+ maxObjects: number;
23
+ maxEdges: number;
24
+ maxDepth: number;
25
+ }
26
+ export interface RenderLimits extends CompileLimits {
27
+ maxOutputBytes: number;
28
+ }
29
+ export type LinkPolicy = {
30
+ mode: 'none';
31
+ } | {
32
+ mode: 'safe';
33
+ baseUrl?: string;
34
+ target?: '_self' | '_blank';
35
+ resolve?: (request: {
36
+ itemId: string;
37
+ href: string;
38
+ }) => string | null;
39
+ cacheKey?: string;
40
+ };
41
+ export interface CommonRenderOptions {
42
+ title?: string;
43
+ description?: string;
44
+ includeSource?: boolean;
45
+ links?: LinkPolicy;
46
+ styleNonce?: string;
47
+ state?: string | null;
48
+ sourceId?: string;
49
+ signal?: AbortSignal;
50
+ limits?: Partial<RenderLimits>;
51
+ }
52
+ export type RenderOptions = CommonRenderOptions & ({
53
+ target?: 'image';
54
+ idPrefix?: never;
55
+ fonts?: 'embed';
56
+ } | {
57
+ target: 'inline';
58
+ idPrefix: string;
59
+ fonts?: 'embed' | 'host';
60
+ });
61
+ export interface ValidateOptions {
62
+ sourceId?: string;
63
+ signal?: AbortSignal;
64
+ limits?: Partial<CompileLimits>;
65
+ }
66
+ export interface ItemSummary {
67
+ id: string;
68
+ kind: 'node' | 'group' | 'edge';
69
+ label: string;
70
+ line: number;
71
+ from?: string;
72
+ to?: string;
73
+ }
74
+ export interface DiagramSummary {
75
+ title: string;
76
+ description?: string;
77
+ width: number;
78
+ height: number;
79
+ viewBox: readonly [number, number, number, number];
80
+ items: readonly ItemSummary[];
81
+ states: readonly {
82
+ id: string;
83
+ label: string;
84
+ }[];
85
+ activeState: string | null;
86
+ }
87
+ export interface RenderedDiagram extends DiagramSummary {
88
+ svg: string;
89
+ provenance: {
90
+ runtimeVersion: string;
91
+ dslVersion: 1;
92
+ fontMetricsVersion: string;
93
+ renderContextVersion: string;
94
+ };
95
+ }
96
+ export type RenderFailure = {
97
+ ok: false;
98
+ reason: 'input' | 'limit' | 'aborted' | 'environment' | 'internal';
99
+ diagnostics: readonly Diagnostic[];
100
+ };
101
+ export type RenderResult = {
102
+ ok: true;
103
+ value: RenderedDiagram;
104
+ diagnostics: readonly Diagnostic[];
105
+ } | RenderFailure;
106
+ export type ValidationResult = {
107
+ ok: true;
108
+ diagnostics: readonly Diagnostic[];
109
+ } | RenderFailure;
@@ -0,0 +1,2 @@
1
+ export declare const runtimeVersion: string;
2
+ export { fontMetricsVersion } from '../model/font-faces.js';
@@ -0,0 +1,84 @@
1
+ import { fontMetricsVersion, runtimeVersion } from './version.js';
2
+ import type { CommonRenderOptions, Diagnostic, LinkPolicy, RenderFailure, RenderResult } from './types.js';
3
+ export { fontMetricsVersion, runtimeVersion };
4
+ export interface Camera {
5
+ centerX: number;
6
+ centerY: number;
7
+ zoom: number;
8
+ }
9
+ export type ViewerExecution = {
10
+ mode: 'worker';
11
+ createWorker?: () => Worker;
12
+ timeoutMs?: number;
13
+ } | {
14
+ mode: 'main';
15
+ };
16
+ export type ViewerRenderOptions = Omit<CommonRenderOptions, 'signal' | 'state'> & {
17
+ fonts?: 'host' | 'embed';
18
+ };
19
+ export interface ViewerOptions {
20
+ label?: string;
21
+ toolbar?: boolean;
22
+ execution?: ViewerExecution;
23
+ render?: ViewerRenderOptions;
24
+ }
25
+ export interface UpdateOptions {
26
+ camera?: 'preserve' | 'fit';
27
+ state?: string | null;
28
+ signal?: AbortSignal;
29
+ }
30
+ export type UpdateResult = {
31
+ status: 'committed';
32
+ revision: number;
33
+ value: Extract<RenderResult, {
34
+ ok: true;
35
+ }>['value'];
36
+ diagnostics: readonly Diagnostic[];
37
+ } | {
38
+ status: 'error';
39
+ revision: number;
40
+ error: RenderFailure;
41
+ } | {
42
+ status: 'superseded' | 'destroyed' | 'aborted';
43
+ revision: number;
44
+ };
45
+ export interface ViewerEvents {
46
+ render: {
47
+ revision: number;
48
+ value: Extract<RenderResult, {
49
+ ok: true;
50
+ }>['value'];
51
+ };
52
+ diagnostics: {
53
+ revision: number;
54
+ diagnostics: readonly Diagnostic[];
55
+ stale: boolean;
56
+ };
57
+ camera: {
58
+ camera: Camera;
59
+ reason: 'user' | 'resize' | 'fit' | 'api';
60
+ };
61
+ itemactivate: {
62
+ id: string;
63
+ kind: 'node' | 'group' | 'edge';
64
+ input: 'pointer' | 'keyboard';
65
+ revision: number;
66
+ };
67
+ link: {
68
+ itemId: string;
69
+ href: string;
70
+ target: '_self' | '_blank';
71
+ preventDefault(): void;
72
+ };
73
+ }
74
+ export interface ViewerHandle {
75
+ update(source: string, options?: UpdateOptions): Promise<UpdateResult>;
76
+ on<K extends keyof ViewerEvents>(type: K, listener: (event: ViewerEvents[K]) => void): () => void;
77
+ getCamera(): Camera;
78
+ setCamera(camera: Camera): void;
79
+ fit(): void;
80
+ focusItem(id: string): boolean;
81
+ destroy(): void;
82
+ }
83
+ export declare function mountViewer(host: HTMLElement, options?: ViewerOptions): ViewerHandle;
84
+ export type { LinkPolicy };
@@ -0,0 +1,90 @@
1
+ .draft-viewer {
2
+ --dv-border: #cbd5e1;
3
+ --dv-ink: #1e293b;
4
+ --dv-bg: #fff;
5
+ display: grid;
6
+ grid-template-rows: auto minmax(8rem, 1fr) auto auto;
7
+ box-sizing: border-box;
8
+ height: 100%;
9
+ min-height: 16rem;
10
+ border: 1px solid var(--dv-border);
11
+ border-radius: 0.5rem;
12
+ color: var(--dv-ink);
13
+ background: var(--dv-bg);
14
+ font:
15
+ 14px/1.45 system-ui,
16
+ sans-serif;
17
+ overflow: hidden;
18
+ }
19
+ .draft-viewer__toolbar {
20
+ display: flex;
21
+ flex-wrap: wrap;
22
+ align-items: center;
23
+ gap: 0.4rem;
24
+ padding: 0.5rem;
25
+ border-bottom: 1px solid var(--dv-border);
26
+ }
27
+ .draft-viewer__toolbar button {
28
+ font: inherit;
29
+ padding: 0.25rem 0.55rem;
30
+ border: 1px solid var(--dv-border);
31
+ border-radius: 0.3rem;
32
+ background: var(--dv-bg);
33
+ color: inherit;
34
+ }
35
+ .draft-viewer button:focus-visible,
36
+ .draft-viewer__viewport:focus-visible {
37
+ outline: 3px solid #2563eb;
38
+ outline-offset: 2px;
39
+ }
40
+ .draft-viewer__viewport {
41
+ position: relative;
42
+ overflow: hidden;
43
+ min-height: 8rem;
44
+ touch-action: none;
45
+ cursor: grab;
46
+ }
47
+ .draft-viewer__viewport:active {
48
+ cursor: grabbing;
49
+ }
50
+ .draft-viewer__viewport svg {
51
+ position: absolute;
52
+ left: 0;
53
+ top: 0;
54
+ overflow: visible;
55
+ }
56
+ .draft-viewer__status {
57
+ margin: 0;
58
+ padding: 0.35rem 0.65rem;
59
+ }
60
+ .draft-viewer__outline {
61
+ margin: 0;
62
+ padding: 0.4rem 0.8rem 0.6rem 2rem;
63
+ max-height: 7rem;
64
+ overflow: auto;
65
+ border-top: 1px solid var(--dv-border);
66
+ }
67
+ .draft-viewer__outline:empty {
68
+ display: none;
69
+ }
70
+ .draft-viewer--stale .draft-viewer__status {
71
+ color: #9a3412;
72
+ background: #fff7ed;
73
+ }
74
+ .draft-viewer__status:empty {
75
+ display: none;
76
+ }
77
+ @media (prefers-reduced-motion: reduce) {
78
+ .draft-viewer * {
79
+ scroll-behavior: auto !important;
80
+ transition: none !important;
81
+ }
82
+ }
83
+ @media (max-width: 600px) {
84
+ .draft-viewer__toolbar {
85
+ gap: 0.3rem;
86
+ }
87
+ .draft-viewer__toolbar button {
88
+ padding-inline: 0.45rem;
89
+ }
90
+ }