@draftably/editor 0.0.0-stage → 0.2.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 (101) hide show
  1. package/README.md +25 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/assets/engine.worker-DwN9io_m.js +6856 -0
  4. package/dist/element.js +36 -0
  5. package/dist/index.js +19555 -0
  6. package/dist/types/src/cli/links.d.ts +4 -0
  7. package/dist/types/src/cli/pdf.d.ts +29 -0
  8. package/dist/types/src/cli/png.d.ts +23 -0
  9. package/dist/types/src/cli/svg.d.ts +4 -0
  10. package/dist/types/src/components/Icon.d.ts +14 -0
  11. package/dist/types/src/components/LayoutPreviewBar.d.ts +2 -0
  12. package/dist/types/src/components/canvas/Canvas.d.ts +15 -0
  13. package/dist/types/src/components/canvas/Frame.d.ts +22 -0
  14. package/dist/types/src/components/canvas/InlineEditor.d.ts +2 -0
  15. package/dist/types/src/components/canvas/Notes.d.ts +5 -0
  16. package/dist/types/src/components/canvas/Overlay.d.ts +2 -0
  17. package/dist/types/src/components/canvas/StateBar.d.ts +5 -0
  18. package/dist/types/src/components/canvas/TextHits.d.ts +6 -0
  19. package/dist/types/src/components/canvas/ZoomControls.d.ts +1 -0
  20. package/dist/types/src/components/canvas/text-boxes.d.ts +24 -0
  21. package/dist/types/src/components/controls.d.ts +91 -0
  22. package/dist/types/src/components/scene/EdgeView.d.ts +22 -0
  23. package/dist/types/src/components/scene/GroupView.d.ts +22 -0
  24. package/dist/types/src/components/scene/Lifelines.d.ts +15 -0
  25. package/dist/types/src/components/scene/LinkMark.d.ts +11 -0
  26. package/dist/types/src/components/scene/NodeView.d.ts +18 -0
  27. package/dist/types/src/components/scene/parts.d.ts +50 -0
  28. package/dist/types/src/components/sculpt/BrushDock.d.ts +2 -0
  29. package/dist/types/src/components/sculpt/TimelineDeck.d.ts +2 -0
  30. package/dist/types/src/components/session-context.d.ts +486 -0
  31. package/dist/types/src/dsl/cells.d.ts +91 -0
  32. package/dist/types/src/dsl/channels.d.ts +53 -0
  33. package/dist/types/src/dsl/compile.d.ts +162 -0
  34. package/dist/types/src/dsl/frame.d.ts +38 -0
  35. package/dist/types/src/dsl/lanes.d.ts +23 -0
  36. package/dist/types/src/dsl/layout-edits.d.ts +58 -0
  37. package/dist/types/src/dsl/legend-notes.d.ts +3 -0
  38. package/dist/types/src/dsl/project.d.ts +39 -0
  39. package/dist/types/src/dsl/source-edits.d.ts +37 -0
  40. package/dist/types/src/dsl/syntax.d.ts +317 -0
  41. package/dist/types/src/dsl/translate.d.ts +69 -0
  42. package/dist/types/src/editor/Editor.d.ts +6 -0
  43. package/dist/types/src/editor/element.d.ts +15 -0
  44. package/dist/types/src/editor/mount.d.ts +23 -0
  45. package/dist/types/src/lib/engine-tasks.d.ts +88 -0
  46. package/dist/types/src/lib/font-data.d.ts +9 -0
  47. package/dist/types/src/lib/saved-diagram.d.ts +4 -0
  48. package/dist/types/src/lib/scene-cache.d.ts +4 -0
  49. package/dist/types/src/lib/svg-export.d.ts +15 -0
  50. package/dist/types/src/model/appearance.d.ts +209 -0
  51. package/dist/types/src/model/bounds.d.ts +19 -0
  52. package/dist/types/src/model/camera.d.ts +21 -0
  53. package/dist/types/src/model/colors.d.ts +22 -0
  54. package/dist/types/src/model/document.d.ts +24 -0
  55. package/dist/types/src/model/edits.d.ts +133 -0
  56. package/dist/types/src/model/embed.d.ts +17 -0
  57. package/dist/types/src/model/font-faces.d.ts +24 -0
  58. package/dist/types/src/model/fonts.d.ts +47 -0
  59. package/dist/types/src/model/footnote.d.ts +34 -0
  60. package/dist/types/src/model/frame.d.ts +71 -0
  61. package/dist/types/src/model/geometry.d.ts +29 -0
  62. package/dist/types/src/model/graph.d.ts +53 -0
  63. package/dist/types/src/model/heading.d.ts +38 -0
  64. package/dist/types/src/model/icons.d.ts +2 -0
  65. package/dist/types/src/model/labels.d.ts +88 -0
  66. package/dist/types/src/model/legend.d.ts +25 -0
  67. package/dist/types/src/model/link.d.ts +63 -0
  68. package/dist/types/src/model/markers.d.ts +47 -0
  69. package/dist/types/src/model/note.d.ts +75 -0
  70. package/dist/types/src/model/panels.d.ts +11 -0
  71. package/dist/types/src/model/rich-text.d.ts +24 -0
  72. package/dist/types/src/model/routes.d.ts +170 -0
  73. package/dist/types/src/model/samples.d.ts +11 -0
  74. package/dist/types/src/model/stack.d.ts +8 -0
  75. package/dist/types/src/model/status.d.ts +84 -0
  76. package/dist/types/src/model/styles.d.ts +13 -0
  77. package/dist/types/src/model/surface.d.ts +31 -0
  78. package/dist/types/src/model/types.d.ts +277 -0
  79. package/dist/types/src/runtime/svg-renderer.d.ts +38 -0
  80. package/dist/types/src/server/file-session.d.ts +19 -0
  81. package/dist/types/src/server/workspace.d.ts +21 -0
  82. package/dist/types/src/state/active-session.d.ts +479 -0
  83. package/dist/types/src/state/canvas-commands.d.ts +169 -0
  84. package/dist/types/src/state/canvas-keyboard.d.ts +15 -0
  85. package/dist/types/src/state/keys.d.ts +5 -0
  86. package/dist/types/src/state/links.d.ts +14 -0
  87. package/dist/types/src/state/session/actions.d.ts +77 -0
  88. package/dist/types/src/state/session/canvas-text.d.ts +24 -0
  89. package/dist/types/src/state/session/document.d.ts +84 -0
  90. package/dist/types/src/state/session/editor.d.ts +81 -0
  91. package/dist/types/src/state/session/emphasis.d.ts +11 -0
  92. package/dist/types/src/state/session/file-session.d.ts +98 -0
  93. package/dist/types/src/state/session/index.d.ts +496 -0
  94. package/dist/types/src/state/session/layout.d.ts +79 -0
  95. package/dist/types/src/state/session/pointer.d.ts +19 -0
  96. package/dist/types/src/state/session/routing.d.ts +20 -0
  97. package/dist/types/src/state/session/scene.d.ts +144 -0
  98. package/dist/types/src/state/session/sculpt.d.ts +70 -0
  99. package/dist/types/src/state/toast.d.ts +14 -0
  100. package/dist/types/src/state/workspace.d.ts +3 -0
  101. package/package.json +41 -3
@@ -0,0 +1,53 @@
1
+ import type { Diagram, DiagramEdge, DiagramGroup, DiagramNode, Direction, Entity, NodeLayout, NodeStyle, Rect } from './types';
2
+ export declare const findNode: (doc: Diagram, id: string | null | undefined) => DiagramNode | undefined;
3
+ export declare const findGroup: (doc: Diagram, id: string | null | undefined) => DiagramGroup | undefined;
4
+ export declare const findEdge: (doc: Diagram, id: string | null | undefined) => DiagramEdge | undefined;
5
+ export declare const findObject: (doc: Diagram, id: string | null | undefined) => DiagramNode | DiagramGroup | undefined;
6
+ export declare const findEntity: (doc: Diagram, id: string | null | undefined) => Entity | undefined;
7
+ export declare const isNode: (doc: Diagram, id: string) => boolean;
8
+ export declare const isGroup: (doc: Diagram, id: string) => boolean;
9
+ export declare const isEdge: (doc: Diagram, id: string) => boolean;
10
+ export declare const styleOf: (doc: Diagram, id: string) => NodeStyle;
11
+ /** Direct children (shapes and containers) of a container, or of the diagram root. */
12
+ /** The title band of a `cells` group; undefined for any other group. */
13
+ export declare const cellsBandOf: (doc: Diagram, id: string | null | undefined) => number | undefined;
14
+ /** Whether a cell is a field of variable length in a byte or bit layout. */
15
+ export declare const isOpenCell: (doc: Diagram, id: string) => boolean;
16
+ /** Whether a node is a cell of a `cells` group, which holds it in place edge to edge. */
17
+ export declare const isCell: (doc: Diagram, id: string) => boolean;
18
+ export declare function childrenOf(doc: Diagram, parentId: string | null | undefined): (DiagramNode | DiagramGroup)[];
19
+ export declare function descendants(doc: Diagram, id: string): string[];
20
+ /** Containers enclosing an object, nearest first. */
21
+ export declare function ancestors(doc: Diagram, id: string): string[];
22
+ export declare const isCollapsed: (doc: Diagram, id: string) => boolean;
23
+ export declare const isHiddenByCollapse: (doc: Diagram, id: string) => boolean;
24
+ /** The visible object that stands in for `id`: its outermost collapsed container, if any. */
25
+ export declare function visibleProxy(doc: Diagram, id: string): string;
26
+ export interface VisibleEdge extends DiagramEdge {
27
+ /** Visible source, which may be a collapsed container. */
28
+ a: string;
29
+ /** Visible target, which may be a collapsed container. */
30
+ b: string;
31
+ }
32
+ export interface VisibleGraph {
33
+ nodes: DiagramNode[];
34
+ groups: DiagramGroup[];
35
+ edges: VisibleEdge[];
36
+ }
37
+ /** Objects and connections that are not hidden inside a collapsed container. */
38
+ export declare function visibleGraph(doc: Diagram): VisibleGraph;
39
+ /** Selected shapes and containers whose own container is not also selected. */
40
+ export declare function selectionRoots(doc: Diagram, selected: Iterable<string>): string[];
41
+ /** Layout direction for the contents of an object's container. */
42
+ export declare function inheritedDirection(doc: Diagram, id: string): Direction;
43
+ export declare const COLLAPSED_HEIGHT = 58;
44
+ export declare const CONTAINER_HEADER = 28;
45
+ /** Read geometry for an object, preferring a temporary override (preview, animation, brush). */
46
+ export type GeometryLookup = (id: string) => NodeLayout;
47
+ export declare function makeGeometry(doc: Diagram, override?: Record<string, NodeLayout> | null): (id: string) => NodeLayout;
48
+ /**
49
+ * Container frame as drawn: collapsed containers shrink to their title band plus a line for the
50
+ * shape count, so the title stays where it was in the open frame.
51
+ */
52
+ export declare function frameRect(doc: Diagram, geometry: GeometryLookup, id: string): NodeLayout;
53
+ export declare const objectRect: (doc: Diagram, geometry: GeometryLookup, id: string) => Rect;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The title and subtitle drawn above an exported diagram, shared by `inspect --svg` and the
3
+ * editor's export, so a copied image still says what it shows.
4
+ */
5
+ import type { Heading, ResolvedAppearance } from './appearance';
6
+ /** Title type: the theme's title face, larger; subtitle type: its annotation face. */
7
+ declare function types(look: ResolvedAppearance): {
8
+ title: import("./fonts").TypeProfile;
9
+ subtitle: import("./fonts").TypeProfile;
10
+ };
11
+ /**
12
+ * Width the subtitle wraps to over a drawing `room` wide: the drawing's width, held between 360px
13
+ * and about 70 characters, so a long subtitle wraps instead of widening the frame.
14
+ */
15
+ export declare function subtitleMeasure(look: ResolvedAppearance, room: number): number;
16
+ /** Size of the heading block, including the gap under it. Zero when there is nothing to draw. */
17
+ export declare function headingSize(look: ResolvedAppearance, heading: Heading | undefined, wrap: number): {
18
+ w: number;
19
+ h: number;
20
+ };
21
+ /**
22
+ * Each line of the heading with its own type and box, top-left corner at (x, y). The title is one
23
+ * line; the subtitle wraps to `wrap` (`subtitleMeasure`).
24
+ */
25
+ export declare function headingLines(look: ResolvedAppearance, heading: Heading | undefined, x: number, y: number, wrap: number): {
26
+ key: "title" | "subtitle";
27
+ text: string;
28
+ type: Type;
29
+ fill: string;
30
+ x: number;
31
+ y: number;
32
+ }[];
33
+ type Type = ReturnType<typeof types>['title'];
34
+ /** SVG markup for the heading with its top-left corner at (x, y). */
35
+ export declare function headingMarkup(look: ResolvedAppearance, heading: Heading | undefined, x: number, y: number, wrap: number): string;
36
+ /** Title and subtitle type, for drawing and editing them in the viewer. */
37
+ export declare const headingTypes: typeof types;
38
+ export {};
@@ -0,0 +1,2 @@
1
+ /** SVG path data for interface and shape icons, drawn on a 24×24 grid. */
2
+ export declare const ICONS: Record<string, string>;
@@ -0,0 +1,88 @@
1
+ import { type ResolvedAppearance } from './appearance';
2
+ import { type TypeProfile } from './fonts';
3
+ import type { Diagram, LabelMode, Rect, ShapeKind } from './types';
4
+ /**
5
+ * Room for a shape's title. Every size, wrap and overflow decision goes through these functions,
6
+ * in the compiler, the canvas and the CLI alike.
7
+ */
8
+ export interface TitleArea {
9
+ /** Line width available to the title. */
10
+ width: number;
11
+ /** Tallest text block that fits inside the shape at its current height. */
12
+ maxHeight: number;
13
+ }
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[];
19
+ /**
20
+ * Line width for a diamond's title, `s` its side. A text block W wide and H tall fits inside a
21
+ * square diamond when W + H <= s, so this takes the widest measure whose wrapped lines still fit:
22
+ * the title reads in as few lines as the shape allows. Undefined when no measure fits.
23
+ */
24
+ export declare function diamondMeasure(title: string, s: number, r: ResolvedAppearance): number | undefined;
25
+ /** Most lines a group title wraps to; past that the frame widens instead. */
26
+ export declare const GROUP_TITLE_LINES = 3;
27
+ /** A group's header height when its title takes `lines` lines. */
28
+ export declare const groupHeaderFor: (r: ResolvedAppearance, lines?: number) => number;
29
+ /**
30
+ * A group title as drawn in a frame `w` wide: wrapped to at most `lines` lines (the compiler
31
+ * reserves that many), the last shortened with an ellipsis if the text runs on.
32
+ */
33
+ export declare function groupTitleLines(title: string, w: number, r: ResolvedAppearance, lines?: number): string[];
34
+ export interface NodeLabel {
35
+ /** Center of the label, relative to the shape's top-left corner. */
36
+ x: number;
37
+ y: number;
38
+ width: number;
39
+ height: number;
40
+ lines: string[];
41
+ mode: LabelMode;
42
+ lineHeight: number;
43
+ leader: boolean;
44
+ }
45
+ /** Where a shape's title is drawn, relative to the shape. */
46
+ export declare function nodeLabel(doc: Diagram, id: string, title: string, r: Rect): NodeLabel;
47
+ /** Bounds of a shape together with its label. */
48
+ export declare function visualBounds(doc: Diagram, id: string, title: string, r: Rect): Rect;
49
+ /** Text width a sequence message's label wraps to; the gap between participants follows it. */
50
+ export declare const MESSAGE_WRAP = 168;
51
+ /**
52
+ * The type a connection label is set in: the annotation type, or a step larger for a sequence
53
+ * message (`message`), whose label is the main thing to read.
54
+ */
55
+ export declare const edgeLabelType: (r: ResolvedAppearance, message?: boolean) => TypeProfile;
56
+ /**
57
+ * Text width a flow line's label wraps to: about 24 characters of the annotation type, so a short
58
+ * phrase stays on one line and a sentence stacks instead of widening its gap.
59
+ */
60
+ export declare const LABEL_WRAP = 140;
61
+ /**
62
+ * A connection label's lines: split at each `\n` and wrapped to `LABEL_WRAP`, or a sequence
63
+ * message's to `MESSAGE_WRAP`.
64
+ */
65
+ export declare const edgeLabelLines: (label: string, r: ResolvedAppearance, message?: boolean) => string[];
66
+ /**
67
+ * A self-message's loop: its run out to the right of the lifeline and its drop. Its label sits
68
+ * `label` past the loop, centred on the drop.
69
+ */
70
+ export declare const SELF_LOOP: {
71
+ out: number;
72
+ down: number;
73
+ label: number;
74
+ };
75
+ /** Box for a connection label in the diagram's annotation type. */
76
+ export declare function edgeLabelSize(label: string, r?: ResolvedAppearance, message?: boolean): {
77
+ w: number;
78
+ h: number;
79
+ };
80
+ /**
81
+ * What a self-message draws, measured from where its row meets the lifeline: how far right, how
82
+ * far above the row and how far below it.
83
+ */
84
+ export declare function selfMessageInk(label: string | undefined, r: ResolvedAppearance): {
85
+ right: number;
86
+ above: number;
87
+ below: number;
88
+ };
@@ -0,0 +1,25 @@
1
+ /** The color key drawn under an exported diagram, shared by `inspect --svg` and the editor's export. */
2
+ import { type LegendEntry, type ResolvedAppearance } from './appearance';
3
+ import { type StatusCount } from './status';
4
+ /** The removed marker, a minus in a disc of radius `r`, centred at (x, y). */
5
+ export declare const markerMarkup: (x: number, y: number, ink: string, canvas: string, r?: number) => string;
6
+ /**
7
+ * Lay out the key: every `legend` entry, then every change mark used with its count, left to
8
+ * right, starting a new row only where the next entry would pass `max`. A short key is one row
9
+ * under the drawing. Zero size when there is nothing to show.
10
+ */
11
+ export declare function legendSize(look: ResolvedAppearance, legend?: LegendEntry[], statuses?: StatusCount[], max?: number): {
12
+ w: number;
13
+ h: number;
14
+ row: number;
15
+ items: {
16
+ x: number;
17
+ y: number;
18
+ legend?: LegendEntry;
19
+ status?: StatusCount;
20
+ label: string;
21
+ }[];
22
+ };
23
+ export type LegendLayout = ReturnType<typeof legendSize>;
24
+ /** SVG markup for the key laid out by `legendSize`, with its top-left corner at (x, y). */
25
+ export declare function legendMarkup(look: ResolvedAppearance, key: LegendLayout, x: number, y: number): string;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Links on objects (`href=`): what a link points at, and where its mark sits on a shape.
3
+ *
4
+ * A link is kept as the author wrote it. A URL (`https://…`, `mailto:`) opens as written; a
5
+ * `.draft` opens in the viewer; anything else is a file path that opens in the editor. A file
6
+ * path may end in `:line` or `:line:col`, and a relative one starts at the link base: the git
7
+ * root of the diagram, unless the file sets `config link-base=`.
8
+ */
9
+ import { type TypeProfile } from './fonts';
10
+ export type LinkKind = 'url' | 'draft' | 'file';
11
+ export interface LinkTarget {
12
+ kind: LinkKind;
13
+ /** The URL, or the file path without its line and column. */
14
+ path: string;
15
+ line?: number;
16
+ col?: number;
17
+ }
18
+ export declare function linkTarget(href: string): LinkTarget;
19
+ /**
20
+ * A relative `href` against a URL `link-base` (`https://host/org/repo/blob/<sha>/`), as the URL
21
+ * it opens; null when the base is a folder or unset, or `href` is already a URL.
22
+ */
23
+ export declare function baseUrl(href: string, base: string | undefined): string | null;
24
+ /** Join and normalize forward-slash paths; an absolute `path` ignores `base`. */
25
+ export declare function joinPath(base: string, path: string): string;
26
+ /** The path from folder `from` to `to`, both absolute with forward slashes. */
27
+ export declare function relativePath(from: string, to: string): string;
28
+ /**
29
+ * A link as a static SVG carries it. A URL stays as written; a file or `.draft` becomes a path
30
+ * from the diagram's folder (a first guess at where the SVG is saved), without its line.
31
+ */
32
+ export declare function staticHref(href: string, base: string, folder: string): string;
33
+ /** Editors that open `<scheme>://file/<path>:<line>:<col>`. */
34
+ export declare const EDITORS: readonly ["vscode", "vscode-insiders", "cursor", "windsurf", "zed"];
35
+ export type Editor = (typeof EDITORS)[number];
36
+ /** File settings from `config` statements. */
37
+ export interface FileConfig {
38
+ /**
39
+ * Where relative links start: a folder relative to the diagram's (its git root when unset), or
40
+ * a URL that relative links are joined to.
41
+ */
42
+ linkBase?: string;
43
+ /** The editor file links open in; VS Code when unset. */
44
+ editor?: Editor;
45
+ /** Strike removed titles through in a diff; off when unset. */
46
+ strikeRemoved?: boolean;
47
+ }
48
+ export declare const editorUrl: (editor: Editor, file: string, line?: number, col?: number) => string;
49
+ /** The link mark: a small arrow out of a shape's top-right corner, inside the shape when it can be. */
50
+ export interface LinkMark {
51
+ x: number;
52
+ y: number;
53
+ size: number;
54
+ /** Inside the shape's fill (draw in its ink), or on the canvas beside it. */
55
+ inside: boolean;
56
+ }
57
+ export declare const LINK_MARK_SIZE = 10;
58
+ /** `badge` is the width of a `count` badge on the corner, which the mark steps left of. */
59
+ export declare function linkMark(shape: string, w: number, h: number, badge?: number): LinkMark;
60
+ /** The mark next to a container's title. */
61
+ export declare const groupLinkMark: (title: string, font: TypeProfile, band: number) => LinkMark;
62
+ /** The arrow, in a 10×10 box. */
63
+ export declare const LINK_MARK_PATH = "M2 8L8 2M3.5 2H8V6.5";
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The marks at a connection's ends, `head=` and `tail=` in a file. The canvas and the CLI draw
3
+ * them from these definitions, so both give the same geometry.
4
+ */
5
+ import type { Point, Rect } from './types';
6
+ /** What `head=` and `tail=` accept. A plain edge is `head=arrow tail=none`. */
7
+ export declare const MARKERS: readonly ["arrow", "none", "triangle", "open-triangle", "diamond", "open-diamond", "circle", "cross"];
8
+ export type EndMarker = (typeof MARKERS)[number];
9
+ export interface MarkerShape {
10
+ w: number;
11
+ h: number;
12
+ /** Where the marker sits on its line, at the height of its middle. */
13
+ x: number;
14
+ d: string;
15
+ /** Solid marks take the line's ink. Open marks take the surface, which hides what is under. */
16
+ fill: 'ink' | 'surface' | 'none';
17
+ /** How far short of the end its line stops; the mark fills that gap, tip on the end. */
18
+ stop: number;
19
+ }
20
+ /** Each marker points along +x, in drawing units. `arrow` is the original open chevron. */
21
+ export declare const MARKER_SHAPES: Record<Exclude<EndMarker, 'none'>, MarkerShape>;
22
+ export declare const MARKER_STROKE = 1.5;
23
+ /** The `<marker>` box for an end. A tail's mark turns to face away from the line. */
24
+ export declare const markerBox: (kind: Exclude<EndMarker, "none">, tail: boolean) => {
25
+ markerWidth: number;
26
+ markerHeight: number;
27
+ refX: number;
28
+ refY: number;
29
+ orient: string;
30
+ };
31
+ /** The fill a marker takes on this line and surface. */
32
+ export declare function markerFill(kind: Exclude<EndMarker, 'none'>, ink: string, surface: string): string;
33
+ type Ends = {
34
+ directed?: boolean;
35
+ head?: EndMarker;
36
+ tail?: EndMarker;
37
+ };
38
+ /** An undirected edge has no marks, whatever else it holds. */
39
+ export declare const headOf: (e: Ends) => EndMarker;
40
+ export declare const tailOf: (e: Ends) => EndMarker;
41
+ /** Whether an edge has anything beyond the plain arrow, which labels and lines ignore. */
42
+ export declare const hasMarkers: (e: Ends) => boolean;
43
+ /** A route with each end pulled back to its marker's stop, so no line runs through a mark. */
44
+ export declare function stoppedPoints(points: Point[], e: Ends): Point[];
45
+ /** The room an edge's marks take. A label keeps off them. */
46
+ export declare function markerRects(points: Point[], e: Ends): Rect[];
47
+ export {};
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Notes: sentences on a diagram that never move its layout, from `note` statements. An anchored
3
+ * note (`near=`) is placed after layout in free space beside its object or sequence message; a
4
+ * note without one sits in the frame under the subtitle. The viewer, `inspect --svg` and the
5
+ * editor's exports all place and draw them from here.
6
+ */
7
+ import { type ResolvedAppearance } from './appearance';
8
+ import type { BoundedRoute } from './bounds';
9
+ import { type Run } from './rich-text';
10
+ import type { Diagram, Point, Rect } from './types';
11
+ export declare const NOTE_SIDES: readonly ["below", "above", "left", "right"];
12
+ export type NoteSide = (typeof NOTE_SIDES)[number];
13
+ export interface Note {
14
+ text: string;
15
+ /** The node, group or sequence message the note sits beside; the frame when unset. */
16
+ near?: string;
17
+ /** Which side of `near`; `below` when unset, or `right` for a message. */
18
+ side?: NoteSide;
19
+ /** Wrap width; `NOTE_WIDTH` when unset. */
20
+ width?: number;
21
+ }
22
+ /**
23
+ * A note with its box and wrapped lines. `inFrame` notes sit under the subtitle; `overlaps` is set
24
+ * on an anchored note when no clear spot was found.
25
+ */
26
+ export interface PlacedNote {
27
+ note: Note;
28
+ rect: Rect;
29
+ /** Wrapped lines, each as runs of plain text and links. */
30
+ lines: Run[][];
31
+ inFrame?: boolean;
32
+ overlaps?: boolean;
33
+ }
34
+ /** The side a note sits on: its `side`, or by default right of a message and below anything else. */
35
+ export declare const noteSide: (doc: Diagram, note: Note) => NoteSide;
36
+ /** Default wrap width, and the room between a note and its object. */
37
+ export declare const NOTE_WIDTH = 280;
38
+ /** Room between a note's panel and its text. */
39
+ export declare const NOTE_PAD: {
40
+ x: number;
41
+ y: number;
42
+ };
43
+ /** The note's wrapped lines and box size, at 0,0. */
44
+ export declare function noteBox(look: ResolvedAppearance, note: Note): PlacedNote;
45
+ /**
46
+ * Spots in a sequence's side margins, by index in `notes`, for notes on the messages in `rows`.
47
+ * `left` and `right` are the margins' inner edges and `top` the highest a note goes. A note is
48
+ * level with its row, or pushed down clear of the note above it. The panel grows round these, so
49
+ * no note here can cover the drawing.
50
+ */
51
+ export declare function marginNotes(look: ResolvedAppearance, notes: readonly Note[], rows: ReadonlyMap<string, number>, { left, right, top }: {
52
+ left: number;
53
+ right: number;
54
+ top: number;
55
+ }): (Rect | null)[];
56
+ /**
57
+ * Place every anchored note beside its object or message, clear of shapes, group borders, lines
58
+ * and earlier notes when a nearby spot allows it. Frame notes, and notes whose object is not drawn,
59
+ * come back `inFrame` at 0,0 for `diagramFrame` to place.
60
+ */
61
+ export declare function placeNotes(doc: Diagram, look: ResolvedAppearance, rects: ReadonlyMap<string, Rect>, routes: readonly BoundedRoute[]): PlacedNote[];
62
+ /**
63
+ * SVG markup for placed notes: a quiet panel with a bar on its left edge, then the text. Links
64
+ * are `<a>` elements with `href` as `write` gives it.
65
+ */
66
+ export declare function noteMarkup(look: ResolvedAppearance, notes: readonly PlacedNote[], write?: (href: string) => string | null): string;
67
+ /**
68
+ * Where a note dropped at `p` belongs: beside the closest object or (in a sequence) message, on
69
+ * the side of it `p` is on. Inside several objects the smallest wins, so a note dropped on a
70
+ * shape in a group goes to the shape. Undefined when nothing is within reach.
71
+ */
72
+ export declare function noteAnchor(p: Point, rects: ReadonlyMap<string, Rect>, messages?: readonly BoundedRoute[]): {
73
+ near: string;
74
+ side: NoteSide;
75
+ } | undefined;
@@ -0,0 +1,11 @@
1
+ import type { Diagram } from './types';
2
+ export type PanelSide = 'before' | 'after';
3
+ /**
4
+ * The merged document with each change as it was: old labels, shapes, counts, rows and phase
5
+ * bands. Every object stays in its place and every line stays in.
6
+ */
7
+ export declare function oldDoc(doc: Diagram): Diagram;
8
+ /** Panels repeat the drawing and put its notes in the frame, so a sequence drops its margins. */
9
+ export declare function withoutMargins(doc: Diagram): Diagram;
10
+ /** The document as it was (`before`: no added objects, old values) or is (`after`: no removed). */
11
+ export declare function panelDoc(doc: Diagram, side: PanelSide): Diagram;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Prose with links, for notes and footnotes: `[#1997](https://github.com/org/repo/pull/1997)`
3
+ * draws `#1997` as a link. Lines wrap exactly as the plain words would, so a link never moves
4
+ * anything; each line comes back as runs with their offsets, for SVG `<a>` and click targets.
5
+ */
6
+ import { type TypeProfile } from './fonts';
7
+ export interface Run {
8
+ text: string;
9
+ href?: string;
10
+ /** Offset from the line's start, and width. */
11
+ x: number;
12
+ w: number;
13
+ }
14
+ /** The text as drawn, with every link written as its label. */
15
+ export declare const plainText: (text: string) => string;
16
+ /** Every link's target, in order. */
17
+ export declare const textLinks: (text: string) => string[];
18
+ /** The text's lines at `width`, each as runs of plain text and links. */
19
+ export declare function richLines(text: string, width: number, type: TypeProfile): Run[][];
20
+ /**
21
+ * One line's content inside its `<text>`: plain runs as text, links as an underlined `<tspan>` in
22
+ * `link`, wrapped in `<a>` with `href` as `write` gives it.
23
+ */
24
+ export declare function runsMarkup(runs: Run[], link: string, write: (href: string) => string | null): string;
@@ -0,0 +1,170 @@
1
+ import { type PriorRoute, type Route, type RoutingResult } from '../engines/routing-engine.js';
2
+ import { type ResolvedAppearance } from './appearance';
3
+ import { type VisibleGraph } from './graph';
4
+ import type { Diagram, EdgeLayout, Point, Rect, ShapeKind, Side } from './types';
5
+ export declare const SIDE_VECTORS: Record<Side, Point>;
6
+ /** Where a connection meets a shape's outline on the given side. */
7
+ export declare function portPoint(shape: ShapeKind, r: Rect, side: Side, offset?: number): Point;
8
+ /** Side of `r` facing `p`. */
9
+ export declare function sideToward(r: Rect, p: Point): Side;
10
+ export interface EdgeLabelBox extends Point {
11
+ w: number;
12
+ h: number;
13
+ score?: number;
14
+ }
15
+ /** Lines and borders a label may sit on only when nothing better is free. */
16
+ export interface LabelAvoid {
17
+ /** Other edges' routes. Routes that leave from this edge's first point share its trunk. */
18
+ routes?: Point[][];
19
+ /** Thin strips along group borders. */
20
+ borders?: Rect[];
21
+ /** The marks at line ends. A spot that hides one costs as much as a line through the text. */
22
+ marks?: Rect[];
23
+ /** The open group that holds both ends. A spot not inside it costs a little less than a line. */
24
+ home?: Rect;
25
+ }
26
+ /** One edge label for `placeEdgeLabels`: its text, its route, and what it should keep clear of. */
27
+ export interface LabelJob {
28
+ label: string;
29
+ points: Point[];
30
+ avoid?: LabelAvoid;
31
+ /** The user's nudge, added to the chosen spot. */
32
+ dx?: number;
33
+ dy?: number;
34
+ }
35
+ /**
36
+ * Place every edge label at once, after all routes exist. Each label takes a spot on its own
37
+ * route that covers no shape, group title or other label, and crosses as few other lines as it
38
+ * can; a label closer to another than its own height costs as much as a line beside it. Labels
39
+ * with a clean spot go first, then one repair sweep moves each label to its best spot given all
40
+ * the others. Only a label with every spot near its route blocked moves away from it, and then as
41
+ * little as possible.
42
+ */
43
+ export declare function placeEdgeLabels(jobs: LabelJob[], obstacles: Rect[], look?: ResolvedAppearance, fixed?: Rect[]): EdgeLabelBox[];
44
+ /**
45
+ * Choose a label spot on the edge's own route, clear of the labels already placed: see
46
+ * `placeEdgeLabels`.
47
+ */
48
+ export declare function placeEdgeLabel(label: string, points: Point[], obstacles: Rect[], existing?: Rect[], look?: ResolvedAppearance, avoid?: LabelAvoid): EdgeLabelBox;
49
+ export interface DrawnRoute extends Route {
50
+ label: EdgeLabelBox | null;
51
+ path: string;
52
+ /**
53
+ * Short pieces of the line where it crosses another line or a group's frame: the only places a
54
+ * canvas-colored halo is drawn under it, so the crossing reads as a clean break. Empty when the
55
+ * line crosses nothing.
56
+ */
57
+ halo: string;
58
+ /**
59
+ * For a line to or from a cell with no side on its group's outline: the stretch from the outline
60
+ * across other cells to that cell's edge. It is drawn over the cells, with a dot at the edge.
61
+ */
62
+ cross?: [Point, Point];
63
+ }
64
+ export interface SceneRoutes {
65
+ routes: DrawnRoute[];
66
+ result: RoutingResult;
67
+ }
68
+ /**
69
+ * An edge's routing settings, with its lane turned into bends and sides: out of the source to
70
+ * the lane, along it, and into the target. A lane with `leave` or `enter` turns at that coordinate beside
71
+ * the source or target, to meet it side on. A lane that no longer lies between the two ends (a
72
+ * node was dragged across it) is ignored, as is any lane on an edge with its own bends.
73
+ */
74
+ export declare function laneBends(cfg: EdgeLayout, a: Rect, b: Rect): EdgeLayout;
75
+ export declare function routeInput(doc: Diagram, vg: VisibleGraph, all: Map<string, Rect>): {
76
+ nodes: ({
77
+ shape: ShapeKind;
78
+ x: number;
79
+ y: number;
80
+ w: number;
81
+ h: number;
82
+ id: string;
83
+ title: string;
84
+ was?: string;
85
+ wasShape?: ShapeKind;
86
+ wasCount?: number | string;
87
+ href?: string;
88
+ count?: number | string;
89
+ span?: number | "var";
90
+ parentId?: string;
91
+ image?: string;
92
+ lifeline?: number;
93
+ } | {
94
+ parentId?: string | undefined;
95
+ frame?: boolean | undefined;
96
+ shape: ShapeKind;
97
+ x: number;
98
+ y: number;
99
+ w: number;
100
+ h: number;
101
+ id: string;
102
+ title: string;
103
+ })[];
104
+ groups: {
105
+ titleHeight?: number | undefined;
106
+ collapsed: boolean;
107
+ titleWidth: number;
108
+ x: number;
109
+ y: number;
110
+ w: number;
111
+ h: number;
112
+ id: string;
113
+ title: string;
114
+ was?: string;
115
+ href?: string;
116
+ parentId?: string;
117
+ frame?: "soft" | "none";
118
+ titleLines?: number;
119
+ hull?: Rect[];
120
+ }[];
121
+ edges: {
122
+ from: string;
123
+ to: string;
124
+ cfg: EdgeLayout;
125
+ a: string;
126
+ b: string;
127
+ id: string;
128
+ label: string;
129
+ was?: string;
130
+ directed: boolean;
131
+ head?: import("./markers").EndMarker;
132
+ tail?: import("./markers").EndMarker;
133
+ emphasis: boolean;
134
+ dashed: boolean;
135
+ status?: import("./status.js").Status;
136
+ spot?: import("./status.js").Spot;
137
+ }[];
138
+ };
139
+ /**
140
+ * A labelled route with no run the label clears slides one bend along, so one run holds the label
141
+ * away from the bend; a label on the corner reads as part of neither run. `w` and `h` are the
142
+ * label's box. The bend moves the least that gives a run the label plus `LABEL_RUN` at each end,
143
+ * or as close to that as the route allows while the label still clears both ends. The move stands
144
+ * only when the route stays out of every box, crosses no more lines and runs beside no more.
145
+ */
146
+ export declare function roomForLabel(points: Point[], w: number, h: number, boxes: Rect[], others: Point[][]): Point[];
147
+ /** Add label positions and SVG paths to raw routes. */
148
+ export declare function drawRoutes(doc: Diagram, vg: VisibleGraph, rects: Map<string, Rect>, result: RoutingResult): DrawnRoute[];
149
+ /** Width of the halo under a connection, in drawing units. */
150
+ export declare const HALO_WIDTH = 8;
151
+ /** How far a halo piece runs each way from a crossing, along the line. */
152
+ export declare const HALO_REACH = 7;
153
+ /**
154
+ * The lines, by index, that another line's halo would cut at a mark. A halo there leaves half a
155
+ * mark, or on a short line almost nothing, so these lines stay whole.
156
+ */
157
+ export declare function wholeLines(lines: {
158
+ points: Point[];
159
+ marks: Rect[];
160
+ }[]): Set<number>;
161
+ /** The pieces of `points` around each place it crosses `others` or a frame's border, as a path. */
162
+ export declare function haloPath(points: Point[], others: Point[][], frames: Rect[]): string;
163
+ /** Full routes already computed for this drawing, or null. */
164
+ export declare function knownRoutes(doc: Diagram, vg: VisibleGraph, rects: Map<string, Rect>): RoutingResult | null;
165
+ /** Record full routes computed elsewhere (the engine worker) for this drawing. */
166
+ export declare function rememberRoutes(doc: Diagram, vg: VisibleGraph, rects: Map<string, Rect>, result: RoutingResult): void;
167
+ export declare function computeRoutes(doc: Diagram, vg: VisibleGraph, rects: Map<string, Rect>, options?: {
168
+ previous?: PriorRoute[];
169
+ quick?: boolean;
170
+ }): SceneRoutes;
@@ -0,0 +1,11 @@
1
+ import type { Diagram } from './types';
2
+ export interface SampleInfo {
3
+ id: string;
4
+ title: string;
5
+ icon: string;
6
+ }
7
+ /** Examples in menu order. Each id names a file in `src/samples/`. */
8
+ export declare const SAMPLES: SampleInfo[];
9
+ export declare const DEFAULT_SAMPLE = "automatic-ports";
10
+ /** A fresh copy of an example, with its own document id. */
11
+ export declare function loadSample(id: string): Diagram;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A node with `count` stands for many things: two offset outlines behind it read as a stack, and
3
+ * a badge on its corner says how many. Shared by the canvas and the SVG output.
4
+ */
5
+ /** Offsets of the outlines behind the node, back to front. */
6
+ export declare const STACK_OFFSETS: number[];
7
+ /** The badge's text: `×42`, `×1.2k` for large counts, or `×N` for a symbol. */
8
+ export declare function countText(count: number | string): string;