@restless-dreams/orbit 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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +168 -2
  3. package/dist/bin/orbit.js +11666 -0
  4. package/dist/bin/orbit.js.map +60 -0
  5. package/dist/chunk-fkdfgtx9.js +17295 -0
  6. package/dist/chunk-fkdfgtx9.js.map +75 -0
  7. package/dist/chunk-y748hzcr.js +82 -0
  8. package/dist/chunk-y748hzcr.js.map +10 -0
  9. package/dist/chunk-zze4tc9n.js +408 -0
  10. package/dist/chunk-zze4tc9n.js.map +10 -0
  11. package/dist/core/auto-render.d.ts +85 -0
  12. package/dist/core/branch-colors.d.ts +57 -0
  13. package/dist/core/browser.d.ts +23 -0
  14. package/dist/core/create.d.ts +23 -0
  15. package/dist/core/diagnostics.d.ts +40 -0
  16. package/dist/core/dispatch.d.ts +23 -0
  17. package/dist/core/document-options.d.ts +18 -0
  18. package/dist/core/document-source.d.ts +128 -0
  19. package/dist/core/editing.d.ts +66 -0
  20. package/dist/core/engine/animation-clock.d.ts +40 -0
  21. package/dist/core/engine/camera.d.ts +70 -0
  22. package/dist/core/engine/colors.d.ts +51 -0
  23. package/dist/core/engine/display.d.ts +125 -0
  24. package/dist/core/engine/export.d.ts +32 -0
  25. package/dist/core/engine/glide.d.ts +34 -0
  26. package/dist/core/engine/labels.d.ts +106 -0
  27. package/dist/core/engine/node-drag.d.ts +49 -0
  28. package/dist/core/engine/scene.d.ts +160 -0
  29. package/dist/core/engine/spin.d.ts +62 -0
  30. package/dist/core/engine/surface-pattern.d.ts +20 -0
  31. package/dist/core/engine/text-measure.d.ts +116 -0
  32. package/dist/core/engine/views.d.ts +361 -0
  33. package/dist/core/extensions.d.ts +155 -0
  34. package/dist/core/format-upgrades.d.ts +33 -0
  35. package/dist/core/index.d.ts +46 -0
  36. package/dist/core/input-controller.d.ts +116 -0
  37. package/dist/core/languages.d.ts +201 -0
  38. package/dist/core/layout.d.ts +61 -0
  39. package/dist/core/map-tools/definitions.d.ts +71 -0
  40. package/dist/core/map-tools/input.d.ts +62 -0
  41. package/dist/core/map-tools/reading.d.ts +40 -0
  42. package/dist/core/map-tools/tools.d.ts +51 -0
  43. package/dist/core/markdown-writing.d.ts +87 -0
  44. package/dist/core/material.d.ts +25 -0
  45. package/dist/core/media.d.ts +42 -0
  46. package/dist/core/node-attributes.d.ts +31 -0
  47. package/dist/core/node-links.d.ts +25 -0
  48. package/dist/core/other-apps/freemind.d.ts +8 -0
  49. package/dist/core/other-apps/icons.d.ts +16 -0
  50. package/dist/core/other-apps/ideas.d.ts +76 -0
  51. package/dist/core/other-apps/import.d.ts +28 -0
  52. package/dist/core/other-apps/inflate.d.ts +22 -0
  53. package/dist/core/other-apps/markup.d.ts +51 -0
  54. package/dist/core/other-apps/notes.d.ts +80 -0
  55. package/dist/core/other-apps/opml.d.ts +26 -0
  56. package/dist/core/other-apps/reading.d.ts +36 -0
  57. package/dist/core/other-apps/xmind.d.ts +14 -0
  58. package/dist/core/other-apps/zip.d.ts +37 -0
  59. package/dist/core/parser-markdown.d.ts +42 -0
  60. package/dist/core/parser-yaml.d.ts +8 -0
  61. package/dist/core/print-layout.d.ts +70 -0
  62. package/dist/core/render-to-svg.d.ts +57 -0
  63. package/dist/core/renderer/content-painter.d.ts +30 -0
  64. package/dist/core/renderer/dom.d.ts +15 -0
  65. package/dist/core/renderer/families.d.ts +35 -0
  66. package/dist/core/renderer/label-painter.d.ts +93 -0
  67. package/dist/core/renderer/node-painter.d.ts +113 -0
  68. package/dist/core/renderer/numbers.d.ts +2 -0
  69. package/dist/core/renderer/options.d.ts +12 -0
  70. package/dist/core/renderer/rings-and-links.d.ts +23 -0
  71. package/dist/core/renderer/scene-helpers.d.ts +33 -0
  72. package/dist/core/renderer/trees.d.ts +22 -0
  73. package/dist/core/serializer-markdown.d.ts +18 -0
  74. package/dist/core/serializer-yaml.d.ts +2 -0
  75. package/dist/core/source-patch.d.ts +24 -0
  76. package/dist/core/source.d.ts +27 -0
  77. package/dist/core/styles.d.ts +6 -0
  78. package/dist/core/suggest.d.ts +8 -0
  79. package/dist/core/svg-renderer.d.ts +626 -0
  80. package/dist/core/text-layout.d.ts +54 -0
  81. package/dist/core/themes.d.ts +62 -0
  82. package/dist/core/types.d.ts +471 -0
  83. package/dist/core/ui/actions.d.ts +75 -0
  84. package/dist/core/ui/controls.d.ts +61 -0
  85. package/dist/core/ui/detail-sheet.d.ts +88 -0
  86. package/dist/core/ui/frames.d.ts +11 -0
  87. package/dist/core/ui/icons.d.ts +14 -0
  88. package/dist/core/ui/inline-editor.d.ts +79 -0
  89. package/dist/core/ui/media-players.d.ts +65 -0
  90. package/dist/core/ui/media-viewer.d.ts +169 -0
  91. package/dist/core/ui/media-zoom.d.ts +51 -0
  92. package/dist/core/ui/note-markdown.d.ts +19 -0
  93. package/dist/core/ui/outline.d.ts +44 -0
  94. package/dist/core/url-guard.d.ts +40 -0
  95. package/dist/core/utils.d.ts +23 -0
  96. package/dist/core/web-page/page.d.ts +59 -0
  97. package/dist/core/web-page/runtime.d.ts +13 -0
  98. package/dist/core/web-page/start.d.ts +1 -0
  99. package/dist/core/yaml-mini.d.ts +54 -0
  100. package/dist/index.d.ts +1 -0
  101. package/dist/index.js +4357 -0
  102. package/dist/index.js.map +29 -0
  103. package/dist/marked/index.d.ts +31 -0
  104. package/dist/marked/index.js +110 -0
  105. package/dist/marked/index.js.map +10 -0
  106. package/dist/react/index.d.ts +55 -0
  107. package/dist/react/index.js +352 -0
  108. package/dist/react/index.js.map +1 -0
  109. package/dist/web-page.d.ts +2 -0
  110. package/dist/web-page.js +130 -0
  111. package/llms-full.txt +2785 -0
  112. package/llms.txt +36 -0
  113. package/package.json +118 -4
  114. package/skills/orbit/SKILL.md +140 -0
  115. package/skills/orbit/references/editing.md +195 -0
  116. package/skills/orbit/references/extending.md +247 -0
  117. package/skills/orbit/references/format.md +331 -0
  118. package/skills/orbit/references/guide.md +546 -0
  119. package/skills/orbit/references/import-export.md +115 -0
  120. package/skills/orbit/references/languages.md +160 -0
  121. package/skills/orbit/references/map-tools.md +188 -0
  122. package/skills/orbit/references/media.md +160 -0
  123. package/skills/orbit/references/recipes.md +236 -0
  124. package/skills/orbit/references/theming.md +199 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * How wide label text is, in ems of the font it's drawn with. In a browser,
3
+ * names are measured with the fonts the page gives the map's labels (each
4
+ * theme's, a node type's own, a page's brand font), so a name fills its
5
+ * node as far as it really reaches and never spills out of it. Where there's
6
+ * no canvas to measure with (a server, tests), widths are estimated from the
7
+ * kind of each character.
8
+ */
9
+ import type { OrbitNode } from '../types.js';
10
+ import type { MapExtensions } from '../extensions.js';
11
+ import type { NodeRole } from './scene.js';
12
+ /** Text widths in one font. */
13
+ export interface TextMeasure {
14
+ /** Equal keys measure alike: caches of fitted labels are kept by it. */
15
+ readonly key: string;
16
+ /** Whether widths in ems differ by font size (fonts with optical sizes); estimates don't. */
17
+ readonly bySize: boolean;
18
+ /** The width of `text` on one line at `fontSize` px, in ems. */
19
+ ems(text: string, fontSize: number): number;
20
+ /** The space between two words, in ems. */
21
+ space(fontSize: number): number;
22
+ /** The ellipsis a cut name ends with, in ems. */
23
+ ellipsis(fontSize: number): number;
24
+ /** How wide the font draws, as `font-stretch` (a percentage of its normal width). */
25
+ readonly stretch: number;
26
+ /**
27
+ * The same font drawn narrower, for names that don't fit at its width,
28
+ * or null when it has no narrower width (most fonts don't: only those
29
+ * with a width axis or condensed faces do).
30
+ */
31
+ narrower(): TextMeasure | null;
32
+ }
33
+ /** The measures a map's labels are fitted with: names inside nodes, pills beside them, and the "+N" names keep clear of. */
34
+ export interface LabelMeasures {
35
+ /** Equal keys measure alike. */
36
+ readonly key: string;
37
+ /** For a node's name inside it. */
38
+ inside(role: NodeRole, node?: OrbitNode): TextMeasure;
39
+ /** For a node's name on its pill. */
40
+ pill(role: NodeRole, node?: OrbitNode): TextMeasure;
41
+ /** For the "+N" a node shows for children that aren't drawn. */
42
+ count(): TextMeasure;
43
+ }
44
+ /** A name inside its node: semibold UI fonts average a little under 0.58 em a character. */
45
+ export declare const INSIDE_ESTIMATE: TextMeasure;
46
+ /** A name on a pill: 6.4 px a character in its 11.5 px text. */
47
+ export declare const PILL_ESTIMATE: TextMeasure;
48
+ /** A "+N": its plus sign and figures, in semibold text, take a little over two thirds of an em each. */
49
+ export declare const COUNT_ESTIMATE: TextMeasure;
50
+ /** Estimates for every label, where nothing can be measured. */
51
+ export declare const ESTIMATED_LABELS: LabelMeasures;
52
+ /** What a label's font is, as the page computes it. */
53
+ export interface LabelFont {
54
+ family: string;
55
+ weight: string;
56
+ style: string;
57
+ /** `font-stretch`, as a percentage of the normal width. */
58
+ stretch: number;
59
+ /** Space after each character, in ems. */
60
+ letterSpacing: number;
61
+ }
62
+ /** The measure for a font, or null when nothing can measure it. */
63
+ export declare const measureFor: (font: LabelFont) => TextMeasure | null;
64
+ /**
65
+ * Calls `listener` whenever measurements start over: a font finished loading,
66
+ * so text measured while the page drew it with a fallback measures anew.
67
+ * Returns a function that stops listening.
68
+ */
69
+ export declare const onMeasurementsReset: (listener: () => void) => () => void;
70
+ /** Forgets every measurement, and tells whoever keeps fits made from them. */
71
+ export declare const resetMeasurements: () => void;
72
+ /**
73
+ * The fonts a map's labels are drawn with, read from the page: a hidden
74
+ * label of each kind in the map's label layer, so the theme, the page's
75
+ * own CSS and a node type's tokens all count. Read again when `refresh` is
76
+ * called (the theme or the page may have changed), not on every frame.
77
+ */
78
+ export declare class LabelFonts implements LabelMeasures {
79
+ private readonly layer;
80
+ private readonly extensions;
81
+ /** A hidden box in the label layer holding a label of each kind, to read its font from. */
82
+ private shelf;
83
+ private probes;
84
+ /** By kind of label (and node type with a font of its own); null where nothing measures. */
85
+ private measures;
86
+ /** The sun's and other nodes' measures, for nodes without a type: looked up for every label on every frame. */
87
+ private sunMeasure;
88
+ private nameMeasure;
89
+ /** The "+N"'s measure: looked up for every label on every frame. */
90
+ private countMeasure;
91
+ private stale;
92
+ private currentKey;
93
+ /** A recheck waiting for the page to be idle (see `recheckWhenIdle`). */
94
+ private recheck;
95
+ constructor(layer: HTMLElement, extensions: () => MapExtensions | undefined);
96
+ /** Equal keys measure alike: a change of font changes the key. */
97
+ get key(): string;
98
+ /** The theme, the page or the fonts may have changed: read the fonts again before the next fit. */
99
+ refresh(): void;
100
+ inside(role: NodeRole, node?: OrbitNode): TextMeasure;
101
+ pill(role: NodeRole, node?: OrbitNode): TextMeasure;
102
+ count(): TextMeasure;
103
+ /**
104
+ * Once the page is idle, reads the fonts again and calls `changed` if they
105
+ * differ: a page may switch its fonts at any time (a class, a brand font).
106
+ * Off the frames that draw, so reading doesn't make one late.
107
+ */
108
+ recheckWhenIdle(changed: () => void): void;
109
+ /** Takes the hidden labels out of the page. */
110
+ destroy(): void;
111
+ private read;
112
+ private measure;
113
+ private measureOf;
114
+ /** The font of a hidden element of `className` (a label, or a "+N") in the label layer, with a node type's `tokens`. */
115
+ private computedFont;
116
+ }
@@ -0,0 +1,361 @@
1
+ /**
2
+ * DOM views for the map content. Nodes are SVG groups drawn at unit radius
3
+ * and positioned with a single `translate() scale()`, so moving or resizing
4
+ * a node is one attribute write per frame; spinning an orbit turns the group
5
+ * its bodies are in instead (see `OrbitTurn`). Labels are HTML in screen
6
+ * space, so text never scales with the camera and keeps a readable size.
7
+ */
8
+ import type { OrbitNode } from '../types.js';
9
+ import type { DisplayNode, DisplayRing, DisplaySlot } from './display.js';
10
+ import type { NodeRole } from './scene.js';
11
+ import type { MapExtensions, OrbitNodeType } from '../extensions.js';
12
+ import { type TextMeasure } from './text-measure.js';
13
+ import { OrbitWords } from '../languages.js';
14
+ export declare const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
15
+ export interface ViewIds {
16
+ /** Unit-circle clip path shared by every node image. */
17
+ clip: string;
18
+ /** Sphere shading gradient. */
19
+ shade: string;
20
+ /**
21
+ * The shading of specks: the same, turned back by the turn they're drawn in
22
+ * once their orbit rests, so it comes from the top left (see `turnShading`).
23
+ */
24
+ speckShade: string;
25
+ /** Blur for the shadow (or glow) of planets and moons. */
26
+ shadow: string;
27
+ /** A wider blur for the sun's. */
28
+ sunShadow: string;
29
+ }
30
+ export declare const svgElement: <Name extends keyof SVGElementTagNameMap>(name: Name, attributes?: Record<string, string>) => SVGElementTagNameMap[Name];
31
+ /**
32
+ * Where the sphere shading changes, from the lit side (top left) to the far
33
+ * edge. The theme colors each stop (`node-highlight`, `node-terminator`,
34
+ * `node-shading`); the stylesheet and the SVG export share these offsets.
35
+ */
36
+ export declare const SHADE_STOPS: readonly [{
37
+ readonly offset: 0;
38
+ readonly part: "highlight";
39
+ }, {
40
+ readonly offset: 0.45;
41
+ readonly part: "highlight-fade";
42
+ }, {
43
+ readonly offset: 0.55;
44
+ readonly part: "terminator-start";
45
+ }, {
46
+ readonly offset: 0.78;
47
+ readonly part: "terminator";
48
+ }, {
49
+ readonly offset: 1;
50
+ readonly part: "shading";
51
+ }];
52
+ /**
53
+ * The circle the shading spreads over, in fractions of a node's box: its
54
+ * center, then its radius. The default lights spheres from the top left; a
55
+ * theme moves it with the `node-shade-circle` token, such as to the bottom
56
+ * right so the far stops catch light along the top left rim, as glass does.
57
+ */
58
+ export interface ShadeCircle {
59
+ x: number;
60
+ y: number;
61
+ radius: number;
62
+ }
63
+ export declare const DEFAULT_SHADE_CIRCLE: ShadeCircle;
64
+ /** A `node-shade-circle` value (`0.34 0.3 0.78`) read, or the default when it isn't three numbers with a radius above 0. */
65
+ export declare const readShadeCircle: (value: string | undefined) => ShadeCircle;
66
+ /** Moves the shading of every node (specks follow it) to the theme's circle. */
67
+ export declare const placeShading: (defs: SVGDefsElement, ids: ViewIds, circle: ShadeCircle) => void;
68
+ /** Blur radius of a shadow, in node radii: planets and moons, then the sun. */
69
+ export declare const SHADOW_BLUR: {
70
+ readonly planet: 0.16;
71
+ readonly sun: 0.3;
72
+ };
73
+ /** Shared definitions: the image clip, sphere shading and shadow blurs, with instance-scoped ids. */
74
+ export declare const createDefinitions: (ids: ViewIds) => SVGDefsElement;
75
+ /**
76
+ * Turns the shading of specks back by `turn` degrees, the turn they're drawn
77
+ * in, so their light comes from the top left. One write for every speck, but
78
+ * the browser draws each of them again: not on every frame of a spin.
79
+ */
80
+ export declare const turnShading: (defs: SVGDefsElement, ids: ViewIds, turn: number) => void;
81
+ /** Labels and the assistive name of a node, in the map's language (English without one). */
82
+ export declare const describeNode: (node: OrbitNode, hiddenChildren: number, visibleChildren: number, extensions?: MapExtensions, words?: OrbitWords) => string;
83
+ /**
84
+ * The on-screen radius (px) below which flat bodies are specks, at
85
+ * `pixelRatio` device pixels per px: bigger for a `plainCrowded` planet, one
86
+ * of a crowded orbit without badges (a status, a note's marker, a play button).
87
+ */
88
+ export declare const speckRadius: (pixelRatio: number, plainCrowded?: boolean) => number;
89
+ /** What every node is drawn with this frame, besides its own place (see `NodeView.update`). */
90
+ export interface NodeDrawing {
91
+ /** In an orbit turned as one, the degrees it's turned (see `OrbitTurn`). */
92
+ turn?: number;
93
+ /** The on-screen radius (px) below which flat bodies are specks (see `speckRadius`). */
94
+ speckRadius: number;
95
+ /** The same for a crowded orbit's planets without badges. */
96
+ crowdedSpeckRadius: number;
97
+ /**
98
+ * How many more nodes may become specks, or stop being them, this frame.
99
+ * The rest wait for the next: a zoom across the size where a whole orbit
100
+ * becomes specks doesn't redraw all of it at once.
101
+ */
102
+ switches: number;
103
+ /** Set when a node is waiting for its turn to switch. */
104
+ waiting: boolean;
105
+ /**
106
+ * During a flight bodies don't become specks: one shrinking as the camera
107
+ * pulls away costs less to leave as it is until the flight lands.
108
+ */
109
+ flight: boolean;
110
+ }
111
+ /**
112
+ * Whether a body leaves out its blurred shadow (and hidden halo): when it's
113
+ * too small on screen for a shadow to show, a planet of a crowded orbit,
114
+ * where shadows overlap into a haze, or one coming or going in a flight,
115
+ * fading in or out while the camera sweeps past it (and may draw it huge).
116
+ * Blurring hundreds of shadows on every frame is also what makes a crowded
117
+ * orbit or a flight slow.
118
+ */
119
+ export declare const drawsFlat: (display: DisplayNode, screenRadius: number) => boolean;
120
+ export declare class NodeView {
121
+ readonly element: SVGGElement;
122
+ readonly id: string;
123
+ readonly node: OrbitNode;
124
+ /** The type the node was drawn as. One given for display can change with the map around it, and the view is drawn again then. */
125
+ readonly typeName: string | undefined;
126
+ /** Shows a picture (an image, or a video's thumbnail) rather than a plain color. */
127
+ hasImage: boolean;
128
+ /** Shows a status, a note's marker or a play button: a crowded orbit's planet with one stays a body down to the usual size (see `speckRadius`). */
129
+ private readonly hasBadges;
130
+ private readonly body;
131
+ private readonly disc;
132
+ private readonly shade;
133
+ private readonly shadeFills;
134
+ private readonly shadowFilters;
135
+ /**
136
+ * Created once a node shows them: a shadow when it's drawn with one (not
137
+ * the hundreds of flat planets of a crowded orbit, or overview nodes), the
138
+ * halo when it becomes the sun, the focus ring when it's focused.
139
+ */
140
+ private shadow;
141
+ private halo;
142
+ private focusRing;
143
+ private status;
144
+ private role;
145
+ private color;
146
+ private visible;
147
+ /** Where the node was last drawn, and how: what its transform (or a speck's circles) is worked out from. */
148
+ private drawn;
149
+ private transform;
150
+ private opacity;
151
+ private editing;
152
+ private lifted;
153
+ private dropTarget;
154
+ private flat;
155
+ private crowded;
156
+ private speck;
157
+ private turnedBack;
158
+ constructor(display: DisplayNode, screenRadius: number, ids: ViewIds, baseUrl: string | undefined, extensions: MapExtensions, drawing?: NodeDrawing, onImageError?: () => void);
159
+ /**
160
+ * Draws the node as `display` places it, `screenRadius` px across on screen.
161
+ *
162
+ * A speck (see `speckRadius`) is drawn with its circles' own coordinates
163
+ * instead of a transform, its badges left out: the browser handles a
164
+ * thousand of them far faster that way, as long as they hold still. A node
165
+ * `moving` on its own (one travelling in a flight) is drawn as a body:
166
+ * moving it is one write instead of one per circle.
167
+ *
168
+ * In an orbit turned as one (see `OrbitTurn`), a body that turns with it is
169
+ * drawn where it is in the unturned orbit: a speck turns with the orbit,
170
+ * shading and all, and anything bigger is turned back upright.
171
+ */
172
+ update(display: DisplayNode, screenRadius?: number, drawing?: NodeDrawing, moving?: boolean): void;
173
+ /** Whether it was last drawn turned back upright in an orbit turned as one: it moves as the orbit turns. */
174
+ get isTurnedBack(): boolean;
175
+ /** Draws the node as a speck from now on, or as a body again. */
176
+ private setSpeck;
177
+ /** Puts a speck's circles at (`x`, `y`), `radius` in size, in the map's own units. */
178
+ private placeSpeck;
179
+ /** The circles a body draws at unit radius around its center, and a speck at its own place. */
180
+ private unitCircles;
181
+ /** The fill as the browser resolved it (theme tokens and color mixes included). */
182
+ resolvedFill(): string;
183
+ /** Marks the node whose text is being edited on the map. */
184
+ setEditing(editing: boolean): void;
185
+ /** Held up by a press, to be dragged somewhere else: it grows, its shadow deepens. */
186
+ setLifted(lifted: boolean): void;
187
+ /** Where a node being dragged would go if let go now: it grows to take it, ringed. */
188
+ setDropTarget(target: boolean): void;
189
+ /** Adds the ring that shows while the node has keyboard focus or is being edited. */
190
+ showFocusRing(): void;
191
+ }
192
+ /**
193
+ * The free place in the orbit for a new idea: a dashed circle with a plus,
194
+ * drawn at unit radius like a node. While a new idea is typed there, it
195
+ * fills in around the field.
196
+ */
197
+ export declare class AddSlotView {
198
+ readonly element: SVGGElement;
199
+ private shown;
200
+ private pending;
201
+ private drop;
202
+ private label;
203
+ private transform;
204
+ private opacity;
205
+ get isShown(): boolean;
206
+ /** `held`: the place held for a node being dragged, which only shows, rather than the + to press. */
207
+ constructor(held?: boolean);
208
+ /**
209
+ * Draws the slot where `slot` says, or hides it. `label` names it for
210
+ * assistive technology. `drop` shows it as the place held open for a node
211
+ * being dragged, without its plus: the `target`, or `waiting` while the
212
+ * node is over another one. In an orbit turned `turn` degrees, it's drawn
213
+ * where it is in the unturned orbit, turned back upright.
214
+ */
215
+ update(slot: DisplaySlot | undefined, label: string, pending: boolean, turn?: number, drop?: 'target' | 'waiting' | false): void;
216
+ }
217
+ /** CSS hooks for a node's element and label: `data-orbit-type`, `data-orbit-status` and `data-orbit-tags`. */
218
+ export declare const markNode: (element: HTMLElement | SVGElement, node: OrbitNode, type?: string | undefined) => void;
219
+ /** A node type's theme tokens, set on its node's elements so they win over the map's theme there. */
220
+ export declare const applyTypeTokens: (element: HTMLElement | SVGElement, type: OrbitNodeType | undefined) => void;
221
+ /** The picture a node shows: its image, or its video's thumbnail. */
222
+ export declare const nodeThumbnail: (node: OrbitNode) => string | undefined;
223
+ /** Inside the node, as a pill outside it, or not shown. */
224
+ export type LabelPlacement = 'inside' | 'outside' | 'hidden';
225
+ /** The width (px) of a label typed into a node of this on-screen radius. Snapped to 6 px steps so zooming doesn't re-wrap text every frame. */
226
+ export declare const insideLabelWidth: (role: NodeRole, screenRadius: number) => number;
227
+ /**
228
+ * The font size (px) of text typed into a node: the role's size, reduced
229
+ * (down to its minimum) until the longest word fits on one line.
230
+ */
231
+ export declare const insideFontSize: (role: NodeRole, width: number, text: string, measure?: TextMeasure) => number;
232
+ export interface InsideLabelLayout {
233
+ /** Width of the text block (px): its widest line's room. */
234
+ width: number;
235
+ fontSize: number;
236
+ /** The name as it shows, line by line; the last ends with an ellipsis when the name is cut short. */
237
+ lines: string[];
238
+ /** Whether the node's icon shows above its name. */
239
+ icon: boolean;
240
+ /** The name doesn't fit whole. */
241
+ truncated: boolean;
242
+ /** A narrower width of the font the name is drawn in, as `font-stretch` (a percentage), when it needs one to fit. */
243
+ stretch?: number;
244
+ /** How far above the node's center (px) the lines and icon are centered: they rise to clear the node's "+N". */
245
+ lift: number;
246
+ }
247
+ /**
248
+ * The "+N" of a node with children that aren't drawn (the renderer's
249
+ * `paintCounts`) is centered this share of the node's radius right of and
250
+ * below the node's center.
251
+ */
252
+ export declare const COUNT_POSITION = 0.74;
253
+ /** A "+N" is this tall (px): its line and padding, as the stylesheet sets them. */
254
+ export declare const COUNT_HEIGHT = 15;
255
+ /** Whether a node of this role and on-screen radius shows a "+N" for children that aren't drawn (flights and faded nodes hide it for a while). */
256
+ export declare const showsCount: (role: NodeRole, screenRadius: number, hiddenChildren: number) => boolean;
257
+ /** How wide (px) a node's "+N" is for `hiddenChildren` children, in `measure` (the font it's drawn in). */
258
+ export declare const measureCount: (hiddenChildren: number, measure?: TextMeasure) => number;
259
+ /** How far below its node's center (px) a name laid out inside it reaches: its lines and icon are centered `lift` above it. */
260
+ export declare const insideLabelReach: (layout: InsideLabelLayout) => number;
261
+ /**
262
+ * How a node's name fits inside it at this on-screen radius, or null when it
263
+ * doesn't. The name shrinks (down to the role's minimum) and wraps (at
264
+ * spaces, and inside long words where they read), its lines balanced and
265
+ * each as wide as the circle is where it sits. The icon above it stays while
266
+ * the name only shrinks a little. A name too long to fit whole even then
267
+ * shows as much as fits, ending with an ellipsis, as long as that's enough
268
+ * to read. `measure` measures the text in the font it's drawn with (by
269
+ * default, widths are estimated).
270
+ *
271
+ * A name that doesn't fit at its font's width tries narrower ones (see
272
+ * `fitInside`) only when `narrower` allows, as it does while the map is
273
+ * still: while the map moves, trying hundreds of names in each width would
274
+ * make frames late. A fit already made with them is kept, though.
275
+ *
276
+ * A node showing a "+N" `countWidth` px wide (see `measureCount`) keeps its
277
+ * name clear of it: the name rises above it, or gets smaller.
278
+ */
279
+ export declare const insideLabelLayout: (role: NodeRole, screenRadius: number, text: string, hasIcon?: boolean, measure?: TextMeasure, narrower?: boolean, countWidth?: number) => InsideLabelLayout | null;
280
+ /**
281
+ * Where a node's label goes at this on-screen size. A name goes inside its
282
+ * node whenever it fits there (see `insideLabelLayout`). A pill beside the
283
+ * node is for the nodes that need one: nodes showing a picture, the focused
284
+ * node when its name doesn't fit inside, and overview nodes too small for
285
+ * their names. Any other node too small for its name shows none until you
286
+ * zoom in. A name inside a node keeps clear of its "+N", `countWidth` px wide.
287
+ */
288
+ export declare const labelPlacement: (role: NodeRole, screenRadius: number, text: string, hasImage: boolean, hasIcon?: boolean, measure?: TextMeasure, narrower?: boolean, countWidth?: number) => LabelPlacement;
289
+ /** How a node's label is drawn on a frame (see `NodeLabel.update`). */
290
+ export interface LabelDrawing {
291
+ /** The node's center on screen (px). */
292
+ x: number;
293
+ y: number;
294
+ /** The node's radius on screen (px). */
295
+ screenRadius: number;
296
+ role: NodeRole;
297
+ placement: LabelPlacement;
298
+ opacity: number;
299
+ /** Whether a name inside its node takes the theme's light text. */
300
+ light?: boolean;
301
+ /** The font a name inside its node is fitted in, as its placement was (default: estimated widths). */
302
+ measure?: TextMeasure;
303
+ /** Whether a name inside its node may try narrower widths of its font, as it does while the map is still (default: true). */
304
+ narrower?: boolean;
305
+ /** The width (px) of the node's "+N", which a name inside it keeps clear of (default: 0, when it shows none). */
306
+ countWidth?: number;
307
+ /** The side of its node a pill touches, a unit vector (default: below). */
308
+ direction?: {
309
+ x: number;
310
+ y: number;
311
+ };
312
+ /** How far from the node's center a pill sits (px; see `pillDistance` in `labels.ts`; default: 5 px past its edge). */
313
+ distance?: number;
314
+ /** Pixels a pill above or below its node slid sideways to stay on the map. */
315
+ offset?: number;
316
+ /** A pill's lines, and the width (px) it wraps at on two. */
317
+ lines?: 1 | 2;
318
+ width?: number;
319
+ /** What a pill says (default: the node's name). */
320
+ pillText?: string;
321
+ }
322
+ export declare class NodeLabel {
323
+ readonly element: HTMLDivElement;
324
+ readonly node: OrbitNode;
325
+ /** The type the node was drawn as, as for its view. */
326
+ readonly typeName: string | undefined;
327
+ private readonly text;
328
+ /** Whether the node has an icon, shown above its name when there's room. */
329
+ private readonly hasIcon;
330
+ /** The side an outside label sat on last (a unit vector), or null. */
331
+ direction: {
332
+ x: number;
333
+ y: number;
334
+ } | null;
335
+ private placement;
336
+ private role;
337
+ private fontSize;
338
+ /** The `font-stretch` written for a name drawn narrower to fit (0: the font's own width). */
339
+ private stretch;
340
+ /** The lines written into the label inside its node, or null while it holds the whole name (pills wrap it themselves). */
341
+ private shownLines;
342
+ /** What the label's pill says, while it's a pill. */
343
+ private pillText;
344
+ private iconHidden;
345
+ private light;
346
+ /** An outside pill on two lines, and the width it wraps at. */
347
+ private wrapped;
348
+ private pillWidth;
349
+ /** What was last written, so a label that didn't move isn't touched: the map repaints every frame while it moves. */
350
+ private shown;
351
+ private transform;
352
+ private opacity;
353
+ constructor(node: OrbitNode, extensions: MapExtensions);
354
+ get currentPlacement(): LabelPlacement;
355
+ /** Whether the label shows: placed somewhere and not hidden. */
356
+ get isShown(): boolean;
357
+ /** Draws the label as `drawing` says: inside its node, fitted as its placement was, or as a pill beside it. */
358
+ update(drawing: LabelDrawing): void;
359
+ }
360
+ /** Dashed ring arcs between the bodies on a ring (centered on the origin). */
361
+ export declare const ringPath: (radius: number, gaps: DisplayRing["gaps"]) => string;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Ways to extend Orbit: node types, actions and status words. Each works for
3
+ * one map, through its options, or for every map once registered, which also
4
+ * covers `<orbit-map>` elements and maps in markdown pages. A map's own
5
+ * options win over registered ones.
6
+ */
7
+ import type { OrbitNode } from './types.js';
8
+ import type { OrbitThemeToken } from './themes.js';
9
+ /** The colored dots statuses get. */
10
+ export type OrbitStatusTone = 'active' | 'done' | 'blocked' | 'todo' | 'neutral';
11
+ /** How a status word looks: one of the dot tones, or any CSS color. */
12
+ export type OrbitStatusStyle = OrbitStatusTone | (string & {});
13
+ /** A button under the focused node and in its detail sheet. */
14
+ export interface OrbitAction {
15
+ /** Names the action in the `action` event: `event.action === 'assign'`. */
16
+ id: string;
17
+ /** The button's name, read by screen readers, shown as its tooltip and in the detail sheet. */
18
+ label: string | ((node: OrbitNode) => string);
19
+ /**
20
+ * The icon: an emoji or short symbol (`'✏️'`), SVG path data on a 24-unit
21
+ * grid (`'M4 20h4L20 8l-4-4L4 16z'`), or one of Orbit's icons (`'note'`,
22
+ * `'link'`, `'goto'`, `'image'`, `'video'`, `'list'`). Default: the
23
+ * label's first letter.
24
+ */
25
+ icon?: string;
26
+ /** Which nodes offer the action. Default: every node. */
27
+ when?: (node: OrbitNode) => boolean;
28
+ /** Runs when the button is pressed, unless an `action` listener prevents it. */
29
+ run: (node: OrbitNode, event: MouseEvent | KeyboardEvent) => void;
30
+ }
31
+ /** How nodes of one type look and behave. Nodes pick a type with `type: name`. */
32
+ export interface OrbitNodeType {
33
+ /** The type's name for people, shown in the detail sheet and the tooltip: `'Milestone'`. */
34
+ label?: string;
35
+ /** The icon these nodes get unless they set their own. */
36
+ icon?: string;
37
+ /** The color these nodes get unless they set their own; their children take shades of it. */
38
+ color?: string;
39
+ /**
40
+ * Theme tokens for these nodes only, without `--orbit-`: their rim,
41
+ * shadow, glow, label and status colors, such as
42
+ * `{ 'node-stroke': '#B45309', 'node-stroke-width': '3' }`.
43
+ */
44
+ tokens?: Partial<Record<OrbitThemeToken, string>>;
45
+ /**
46
+ * Draws inside the node, the way a picture does: `element` is a 100 × 100 px
47
+ * box clipped to the node's circle and scaled with it, and the label moves
48
+ * outside. Return a function to clean up when the node leaves the map.
49
+ */
50
+ render?: (node: OrbitNode, element: HTMLElement) => void | (() => void);
51
+ /** Adds a section to the detail sheet, above the note; the node gets a "Details" button. */
52
+ details?: (node: OrbitNode, element: HTMLElement) => void;
53
+ /** Actions only nodes of this type offer, after the map's own. */
54
+ actions?: OrbitAction[];
55
+ /**
56
+ * The version of the fields these nodes keep in their attributes, once
57
+ * the type changes them: 1 at first. Nodes say which version they were
58
+ * written for (`type: task@2`; none means 1), and older ones are brought
59
+ * up to date with `upgrade` when a map is drawn.
60
+ */
61
+ version?: number;
62
+ /**
63
+ * Brings a node written for an older version of the type up to date:
64
+ * renames or reshapes its attributes, in place. `from` is the version it
65
+ * was written for. Runs once per node when a map is drawn (or with
66
+ * `upgradeNodeTypes`), and the next save writes the node as upgraded.
67
+ * Nodes written for a newer version than this one are left as they are.
68
+ */
69
+ upgrade?: (node: OrbitNode, from: number) => void;
70
+ }
71
+ /** The extensions a map is created with, before the registered ones. */
72
+ export interface OrbitExtensionOptions {
73
+ /** Node types by name. */
74
+ nodeTypes?: Record<string, OrbitNodeType>;
75
+ /** Actions every node offers (or the ones `when` picks). */
76
+ actions?: OrbitAction[];
77
+ /** Status words and how they look: `{ review: 'active', 'on hold': '#A855F7' }`. */
78
+ statuses?: Record<string, OrbitStatusStyle>;
79
+ /**
80
+ * The type a node is drawn as when it names none: its look, content and
81
+ * actions, as if it said `type: name`, for display only. Orbit never writes
82
+ * it to the map, never upgrades a node for it, and asks again whenever it
83
+ * draws, so return undefined to draw the node as it is. Called often: keep
84
+ * it quick.
85
+ */
86
+ nodeTypeFor?: (node: OrbitNode) => string | undefined;
87
+ }
88
+ /**
89
+ * Defines a node type for every map: nodes with `type: name` (any case) take
90
+ * it. Returns a function that removes it again.
91
+ */
92
+ export declare const registerNodeType: (name: string, definition: OrbitNodeType) => () => void;
93
+ /** Adds an action to every map. Returns a function that removes it again. */
94
+ export declare const registerAction: (action: OrbitAction) => () => void;
95
+ /**
96
+ * Gives a status word its look on every map: one of the tones (`active`,
97
+ * `done`, `blocked`, `todo`, `neutral`) or any CSS color. Returns a function
98
+ * that removes it again.
99
+ */
100
+ export declare const registerStatus: (word: string, style: OrbitStatusStyle) => () => void;
101
+ /** How a status looks on a map: its tone, and its own color when it has one. */
102
+ export interface StatusLook {
103
+ tone: OrbitStatusTone;
104
+ color?: string;
105
+ }
106
+ /**
107
+ * One map's view of the extensions: its own node types, actions and status
108
+ * words first, then the registered ones. Lookups read the registries each
109
+ * time, so a type registered after the map was created applies on its next
110
+ * redraw.
111
+ */
112
+ export declare class MapExtensions {
113
+ private readonly types;
114
+ private readonly statuses;
115
+ private readonly actionList;
116
+ private readonly typeFor;
117
+ /** `nodeTypeFor` threw: it's asked on every frame, so the console hears of it once. */
118
+ private typeForFailed;
119
+ constructor(options?: OrbitExtensionOptions);
120
+ /**
121
+ * The name of the type the node is drawn as: its own `type`, or for a node
122
+ * that names none, the one `nodeTypeFor` gives it for display.
123
+ */
124
+ typeName(node: OrbitNode): string | undefined;
125
+ /** The type the node is drawn as, if this map knows it: its own, or one given for display (see `typeName`). */
126
+ nodeType(node: OrbitNode): OrbitNodeType | undefined;
127
+ /** The node's own type (`type`), never one given for display: what upgrades go by. */
128
+ ownType(node: OrbitNode): OrbitNodeType | undefined;
129
+ private typeNamed;
130
+ /** The node's icon: its own, or its type's. */
131
+ icon(node: OrbitNode): string | undefined;
132
+ /** The node's own color, or its type's. */
133
+ color(node: OrbitNode): string | undefined;
134
+ /** The actions a node offers: the map's, the registered ones, then its type's, each id once. */
135
+ actions(node: OrbitNode): OrbitAction[];
136
+ /** How a status looks: a word this map or a registration names, or one of the built-in words. */
137
+ status(word: string): StatusLook;
138
+ }
139
+ /**
140
+ * Brings nodes written for older versions of their types up to date (see
141
+ * `OrbitNodeType.version`), with the types `nodeTypes` defines or the
142
+ * registered ones. Maps do this when they're drawn; this is for documents
143
+ * read without one, on a server or in a script. Returns how many nodes
144
+ * were upgraded.
145
+ */
146
+ export declare const upgradeNodeTypes: (root: OrbitNode, nodeTypes?: Record<string, OrbitNodeType>) => number;
147
+ /** Upgrades the nodes under `root` with the types `extensions` knows. */
148
+ export declare const upgradeTypes: (root: OrbitNode, extensions: MapExtensions) => number;
149
+ /** An action's label for a node. */
150
+ export declare const actionLabel: (action: OrbitAction, node: OrbitNode) => string;
151
+ /**
152
+ * Runs code a map was extended with. If it throws, the map carries on and
153
+ * the console says which extension failed.
154
+ */
155
+ export declare const guarded: <Result>(what: string, fallback: Result, run: () => Result) => Result;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The format's versions. A document says which version it's written for
3
+ * with `schemaVersion` (none means 1). When the format changes, the change
4
+ * goes here with a step that brings older documents up to date, and
5
+ * `SUPPORTED_SCHEMA_VERSION` goes up: documents are upgraded as they're
6
+ * read, and the next save writes them in the new format, changing only
7
+ * what the upgrade changed.
8
+ *
9
+ * A document written for a newer version than this one is read as well as
10
+ * it can be, and reported (`unsupported-schema-version`, an error), so apps
11
+ * can open it without editing it: everything it says is kept on save, but
12
+ * what this version doesn't know of it isn't understood.
13
+ */
14
+ import type { OrbitDocument } from './types.js';
15
+ import type { DiagnosticCollector } from './diagnostics.js';
16
+ /** A change to the format: how a document written for version `from` becomes one for `from + 1`. */
17
+ export interface FormatUpgrade {
18
+ from: number;
19
+ /** Changes the document in place: its options, and its nodes' fields. */
20
+ upgrade: (document: OrbitDocument) => void;
21
+ }
22
+ /**
23
+ * The format's changes, oldest first. Empty while the format is at its
24
+ * first version; tests check it ends at `SUPPORTED_SCHEMA_VERSION`.
25
+ */
26
+ export declare const FORMAT_UPGRADES: FormatUpgrade[];
27
+ /** The newest `schemaVersion` this version of Orbit reads and writes. */
28
+ export declare const SUPPORTED_SCHEMA_VERSION = 1;
29
+ /**
30
+ * Upgrades a document written for an older version of the format, one step
31
+ * at a time, and says so. A document for a newer version is reported.
32
+ */
33
+ export declare const upgradeFormat: (document: OrbitDocument, diagnostics: DiagnosticCollector, line: number | undefined, upgrades?: readonly FormatUpgrade[]) => void;