@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,125 @@
1
+ /**
2
+ * What is on screen this frame. Pure functions: a scene plus the orbit's
3
+ * rotation gives the resting display; during a flight, the current display
4
+ * morphs into the target scene placed in today's world coordinates.
5
+ */
6
+ import type { OrbitNode } from '../types.js';
7
+ import { type Camera, type CameraPath, type Similarity } from './camera.js';
8
+ import { type NodeRole, type Scene, type SceneRing } from './scene.js';
9
+ export interface DisplayNode {
10
+ id: string;
11
+ node: OrbitNode;
12
+ role: NodeRole;
13
+ x: number;
14
+ y: number;
15
+ radius: number;
16
+ opacity: number;
17
+ /** Multiplies the label's own visibility rules (level of detail, role). */
18
+ labelOpacity: number;
19
+ color: string;
20
+ depth: number;
21
+ hiddenChildren: number;
22
+ parentId?: string;
23
+ hostId?: string;
24
+ /** A planet of a crowded orbit, drawn flat (see `drawsFlat`). */
25
+ crowded?: boolean;
26
+ /** During a flight, how the node comes or goes with its siblings (see `Cluster`). */
27
+ cluster?: Cluster;
28
+ /** A body that turns with its orbit: where it is in the unturned orbit (see `OrbitTurn`). */
29
+ unturned?: Point;
30
+ }
31
+ /**
32
+ * A focused orbit at rest, turned `rotation` degrees about its sun at
33
+ * (`cx`, `cy`). Each body that turns with it (those with `unturned`) is drawn
34
+ * where it is in the unturned orbit, in a group that turns, so spinning the
35
+ * orbit moves the group instead of every body in it.
36
+ */
37
+ export interface OrbitTurn {
38
+ cx: number;
39
+ cy: number;
40
+ rotation: number;
41
+ }
42
+ interface Point {
43
+ readonly x: number;
44
+ readonly y: number;
45
+ }
46
+ /**
47
+ * During a flight, nodes that come or go together: the children of one
48
+ * parent growing out of it, shrinking into it, or fading where they are.
49
+ * Each is drawn at its own `x`, `y`, `radius` and `nodeOpacity`, which hold
50
+ * still, scaled by `scale` about the pivot with `opacity` on top, which are
51
+ * the same for all of them, so a family can be moved as one.
52
+ */
53
+ export interface Cluster {
54
+ pivotX: number;
55
+ pivotY: number;
56
+ scale: number;
57
+ opacity: number;
58
+ x: number;
59
+ y: number;
60
+ radius: number;
61
+ nodeOpacity: number;
62
+ }
63
+ export interface DisplayRing {
64
+ id: string;
65
+ centerId: string;
66
+ cx: number;
67
+ cy: number;
68
+ radius: number;
69
+ color: string;
70
+ gaps: SceneRing['gaps'];
71
+ /** Degrees added to every gap angle (the orbit's spin). */
72
+ rotation: number;
73
+ opacity: number;
74
+ }
75
+ export interface DisplayLink {
76
+ id: string;
77
+ fromId: string;
78
+ toId: string;
79
+ color: string;
80
+ opacity: number;
81
+ }
82
+ /** The free place in the orbit for a new idea (see `SceneSlot`), where it is this frame. */
83
+ export interface DisplaySlot {
84
+ parentId: string;
85
+ index: number;
86
+ x: number;
87
+ y: number;
88
+ radius: number;
89
+ opacity: number;
90
+ /** Where the slot is in the unturned orbit (see `OrbitTurn`). */
91
+ unturned?: Point;
92
+ }
93
+ export interface Display {
94
+ nodes: Map<string, DisplayNode>;
95
+ rings: DisplayRing[];
96
+ links: DisplayLink[];
97
+ camera: Camera;
98
+ addSlot?: DisplaySlot;
99
+ /** A focused orbit at rest. */
100
+ turn?: OrbitTurn;
101
+ }
102
+ /** A scene at rest, with its orbit turned by `rotation` degrees. */
103
+ export declare const sceneDisplay: (scene: Scene, rotation: number, camera: Camera) => Display;
104
+ export interface Transition {
105
+ /** The display when the flight started, in the current world. */
106
+ from: Display;
107
+ /** Fade out and in without moving anything, for readers who prefer reduced motion. */
108
+ fade?: boolean;
109
+ /** The destination scene in its own coordinates. */
110
+ target: Scene;
111
+ /** Places `target` in the current world. */
112
+ placement: Similarity;
113
+ cameraPath: CameraPath;
114
+ startedAt: number;
115
+ duration: number;
116
+ }
117
+ /** The camera that frames `scene` once it is placed by `placement`. */
118
+ export declare const placedFrame: (scene: Scene, placement: Similarity) => Camera;
119
+ export declare const transitionDisplay: (transition: Transition, progress: number) => Display;
120
+ /**
121
+ * A view change without movement: the old view fades out, then the new one
122
+ * fades in already in place (in its own coordinates, with its own camera).
123
+ */
124
+ export declare const fadeDisplay: (transition: Transition, progress: number) => Display;
125
+ export {};
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Standalone SVG for a display. Every color is resolved to a plain value,
3
+ * labels are SVG text and nothing depends on the page's stylesheet, so the
4
+ * file looks the same in any viewer or editor.
5
+ */
6
+ import type { Display, DisplayNode } from './display.js';
7
+ import { type Viewport } from './camera.js';
8
+ import type { MapExtensions } from '../extensions.js';
9
+ import type { OrbitWords } from '../languages.js';
10
+ import { type LabelMeasures } from './text-measure.js';
11
+ export interface ExportOptions {
12
+ viewport: Viewport;
13
+ /** Concrete theme values, keyed by token name (`surface`, `color-1`, …). */
14
+ tokens: Record<string, string>;
15
+ showRings: boolean;
16
+ title: string;
17
+ /** The fill the page resolved for a node, when it is on screen. */
18
+ fills?: (node: DisplayNode) => string;
19
+ baseUrl?: string;
20
+ /** Size the SVG to its container (100% wide and tall) instead of in pixels. */
21
+ responsive?: boolean;
22
+ /** Prefix for the SVG's ids; give each SVG inlined in one page its own. */
23
+ idPrefix?: string;
24
+ /** The map's node types and status words, for icons, status colors and type tokens. */
25
+ extensions?: MapExtensions;
26
+ /** Measures names in the fonts the map draws them with, so labels break as on the live map; by default, widths are estimated. */
27
+ measures?: LabelMeasures;
28
+ /** The map's words, for the name of an idea without text. Default: English. */
29
+ words?: OrbitWords;
30
+ }
31
+ export declare const exportDisplay: (display: Display | null, options: ExportOptions) => string;
32
+ export declare const escapeXml: (value: string) => string;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pan momentum for the overview. Like the orbit spin, the release velocity
3
+ * is the least-squares slope of the last 80 ms of pointer movement, and the
4
+ * glide slows down exponentially using the exact solution for each step.
5
+ */
6
+ export interface Velocity {
7
+ /** Pixels per millisecond. */
8
+ x: number;
9
+ y: number;
10
+ }
11
+ /** Tracks a dragged position over time and reports the release velocity. */
12
+ export declare class PointVelocityTracker {
13
+ private samples;
14
+ private x;
15
+ private y;
16
+ reset(time: number): void;
17
+ /** Records a movement of (`deltaX`, `deltaY`) pixels at `time` (milliseconds). */
18
+ add(time: number, deltaX: number, deltaY: number): void;
19
+ velocityAt(releaseTime: number): Velocity;
20
+ }
21
+ export declare class Glide {
22
+ velocity: Velocity;
23
+ private readonly timeConstant;
24
+ /** `timeConstant` in milliseconds: how long the glide takes to lose about two thirds of its speed. */
25
+ constructor(timeConstant?: number);
26
+ get isMoving(): boolean;
27
+ start(velocity: Velocity): void;
28
+ stop(): void;
29
+ /** Advances by `elapsed` milliseconds and returns the distance travelled, in pixels. */
30
+ step(elapsed: number): {
31
+ x: number;
32
+ y: number;
33
+ };
34
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Where every label goes, frame by frame: inside its node, on a chip, as a
3
+ * pill beside the node, or nowhere. Pills try the sides of their node in
4
+ * order of preference and take the first one that covers no other label,
5
+ * no other node and doesn't run off the map. The browser renderer and the
6
+ * SVG export both use this, so an export looks like the live map.
7
+ */
8
+ import type { Viewport } from './camera.js';
9
+ import type { Display, DisplayNode } from './display.js';
10
+ import { type LabelPlacement } from './views.js';
11
+ import { type LabelMeasures, type TextMeasure } from './text-measure.js';
12
+ export interface Point {
13
+ x: number;
14
+ y: number;
15
+ }
16
+ export interface Box {
17
+ left: number;
18
+ right: number;
19
+ top: number;
20
+ bottom: number;
21
+ }
22
+ export interface PlannedLabel {
23
+ entry: DisplayNode;
24
+ /** The node's center on screen. */
25
+ x: number;
26
+ y: number;
27
+ screenRadius: number;
28
+ placement: Exclude<LabelPlacement, 'hidden'>;
29
+ /** Which side of the node an outside label sits on (a unit vector). */
30
+ direction: Point;
31
+ /** How far from the node's center a pill sits (px; see `pillDistance`). */
32
+ distance: number;
33
+ /** Pixels a pill above or below its node slid sideways to stay on the map. */
34
+ offset: number;
35
+ /** A pill's lines: two when one line fits nowhere. */
36
+ lines: 1 | 2;
37
+ /** A pill's estimated width. */
38
+ width: number;
39
+ opacity: number;
40
+ /** What a pill says: the node's name, after its icon when `pillIcon` gives one. */
41
+ text: string;
42
+ /** The width (px) of the node's "+N", which a name inside it keeps clear of; 0 when it shows none. */
43
+ countWidth: number;
44
+ }
45
+ export interface LabelPlanOptions {
46
+ viewport: Viewport;
47
+ /** Whether the node shows a picture, which pushes its label outside. */
48
+ hasImage(entry: DisplayNode): boolean;
49
+ /** Whether the node has an icon to show above its name (the fit is the label's own, cached). */
50
+ hasIcon?(entry: DisplayNode): boolean;
51
+ /**
52
+ * An icon to show on the node's pill, before its name: for a node whose
53
+ * picture or content fills it, so the icon has no place inside.
54
+ */
55
+ pillIcon?(entry: DisplayNode): string | undefined;
56
+ /** The side a label sat on last frame, so labels don't hop around while the map moves. */
57
+ previousSide?(id: string): Point | null;
58
+ /** While nodes fly between views, pills stay hidden; they'd flicker as nodes grow and shrink. */
59
+ inFlight?: boolean;
60
+ /** Measures names in the fonts the map draws them with; by default, widths are estimated. */
61
+ measures?: LabelMeasures;
62
+ /**
63
+ * While the map moves, names that don't fit at their font's width don't
64
+ * try narrower ones, and names not yet showing only start to when they'd
65
+ * fit by the estimate too (`isShown` says which show): measured names fit
66
+ * more often, and hundreds starting to show mid-zoom would make frames
67
+ * late. Once the map is still (and by default), every name that fits shows.
68
+ */
69
+ moving?: boolean;
70
+ /** Whether a node's label shows now. */
71
+ isShown?(id: string): boolean;
72
+ /** Whether nodes show a "+N" for children that aren't drawn, which names inside them keep clear of: the map does, an export doesn't. */
73
+ counts?: boolean;
74
+ }
75
+ /** The order labels are placed in, so the important ones win when they would overlap. */
76
+ export declare const labelOrder: (nodes: Iterable<DisplayNode>) => DisplayNode[];
77
+ /** The labels to show this frame, in `order`. Nodes missing from the result show no label. */
78
+ export declare const planLabels: (display: Display, order: DisplayNode[], options: LabelPlanOptions) => PlannedLabel[];
79
+ /**
80
+ * The sides a node's pill may go, best first. Toward the parent is usually
81
+ * free (a planet's moons and a branch's children sit on the far side); a
82
+ * leaf in the overview has its far side free too. Moons look away from
83
+ * their planet. Below and above are the fallbacks.
84
+ */
85
+ export declare const labelSides: (entry: DisplayNode, display: Display) => Point[];
86
+ /**
87
+ * Where an outside label will be, from its text's width in `measure` (by
88
+ * default, an estimate), slid `offset` pixels sideways, on `lines` lines.
89
+ * The pill is drawn no bigger than this box: its width holds the text, the
90
+ * pill's padding (16 px) and 2 px to spare, and the stylesheet counts the
91
+ * padding in a pill's width, capped at the same 160 px (280 px for the
92
+ * focused node).
93
+ */
94
+ export declare const outsideLabelBox: (text: string, point: Point, screenRadius: number, direction: Point, maximumWidth: number, offset?: number, lines?: 1 | 2, measure?: TextMeasure) => Box;
95
+ /**
96
+ * How far from its node's center a pill toward `direction` sits. A pill is
97
+ * placed by its point that matches its side: the middle of its top when it's
98
+ * below the node, the middle of its left end when it's to the right, and on a
99
+ * slant a point between them, inside the pill. That point sits past the node's
100
+ * edge by the gap, which keeps a pill on a side clear of its node, but on a
101
+ * slant the pill's nearest end would reach back over it: there the pill sits
102
+ * farther out, as far as that end keeps the same gap.
103
+ */
104
+ export declare const pillDistance: (screenRadius: number, direction: Point, width: number, lines: 1 | 2) => number;
105
+ /** A pill's text width (px): on two lines, its longer line when it breaks where the two balance best. Estimated, unless `measure` measures it. */
106
+ export declare const pillTextWidth: (text: string, lines: 1 | 2, measure?: TextMeasure) => number;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Where a node being dragged would go if it were let go now: into another
3
+ * node, at a place in the focused orbit, or nowhere. Pure functions over what
4
+ * is on screen, so the renderer only tracks the finger.
5
+ */
6
+ import type { OrbitNode } from '../types.js';
7
+ import type { Display, DisplayNode } from './display.js';
8
+ import { type Scene } from './scene.js';
9
+ /** Where a dragged node would go: under `parentId`, at `index` among its children (null: after the last). */
10
+ export interface DropPlace {
11
+ parentId: string;
12
+ index: number | null;
13
+ /** Dropped on a node rather than on a place in the orbit. */
14
+ into: boolean;
15
+ }
16
+ interface Point {
17
+ readonly x: number;
18
+ readonly y: number;
19
+ }
20
+ /**
21
+ * The ids a node can't be dropped into: itself and everything under it
22
+ * (a node can't go inside itself), and its parent, which it is already in.
23
+ */
24
+ export declare const unreachableIds: (node: OrbitNode, parentId: string | undefined) => Set<string>;
25
+ /**
26
+ * The node under `point` (world coordinates) to drop into, or null. Each
27
+ * node catches the point within `catchRadius` of its center (world units),
28
+ * given whether it's the target so far, so a target can hold on a little
29
+ * further than it caught; the most central catch wins.
30
+ */
31
+ export declare const nodeUnder: (display: Display, point: Point, unreachable: ReadonlySet<string>, catchRadius: (node: DisplayNode, current: boolean) => number, current: string | null) => DisplayNode | null;
32
+ /**
33
+ * The place in a focused orbit for node `draggedId` dropped at `point`
34
+ * (relative to the sun, in an orbit turned `rotation` degrees), counted among
35
+ * the sun's children without it, or null when the point is off the orbit.
36
+ * `layout` is the orbit as it's drawn, with the place it's held at (it, laid
37
+ * out there, or the slot), so the place under the press is the one on
38
+ * screen: it stays put while the press stays near it, and moves past a
39
+ * planet the press crosses. `ringRadius` is used when the orbit is empty.
40
+ * Orbits of more than one ring take drops on their planets only.
41
+ *
42
+ * The first place and the last are the same gap, between the last planet
43
+ * and the first: there, the place held now (`current`) stays, rather than
44
+ * turning the whole orbit a step.
45
+ */
46
+ export declare const orbitPlaceAt: (layout: Scene, point: Point, rotation: number, ringRadius: number, band: number, draggedId: string, current?: number | null) => number | null;
47
+ /** Whether two places are the same. */
48
+ export declare const samePlace: (first: DropPlace | null, second: DropPlace | null) => boolean;
49
+ export {};
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Scenes: what each view of a map contains, in world coordinates.
3
+ *
4
+ * A focus scene puts the focused node (the sun) at the origin with its
5
+ * children (planets) on rings and its grandchildren as small moons around
6
+ * them. An overview scene lays out the whole tree around the root. Every
7
+ * node keeps one color in every scene, and nodes are keyed by id so a
8
+ * transition can morph the same node from one scene into the next: the
9
+ * moons of a planet grow into the planets of the next view.
10
+ */
11
+ import type { OrbitNode } from '../types.js';
12
+ import { MOON_LIMIT } from '../layout.js';
13
+ import { type Camera, type Insets, type Similarity, type Viewport } from './camera.js';
14
+ export type NodeRole = 'sun' | 'planet' | 'moon' | 'node';
15
+ export interface SceneNode {
16
+ id: string;
17
+ node: OrbitNode;
18
+ role: NodeRole;
19
+ /** World position of the center, before any orbit spin. */
20
+ x: number;
21
+ y: number;
22
+ radius: number;
23
+ color: string;
24
+ depth: number;
25
+ parentId?: string;
26
+ /** Planets: angle around the sun (degrees, −90 = top) and distance from it. */
27
+ orbitAngle?: number;
28
+ orbitRadius?: number;
29
+ /** Moons: the planet they circle, their angle relative to the direction from the sun to the planet, and their distance. */
30
+ hostId?: string;
31
+ hostOffset?: number;
32
+ hostDistance?: number;
33
+ /** Children that exist but aren't drawn: beyond the moon limit, or under a collapsed node. */
34
+ hiddenChildren: number;
35
+ /** Turns with the orbit when it spins. */
36
+ spins: boolean;
37
+ /** A planet of an orbit too crowded for each planet's shadow to read: drawn flat. */
38
+ crowded?: boolean;
39
+ }
40
+ export interface SceneRing {
41
+ id: string;
42
+ centerId: string;
43
+ cx: number;
44
+ cy: number;
45
+ radius: number;
46
+ color: string;
47
+ /** Openings around each body on the ring, so the line never runs through a node. */
48
+ gaps: {
49
+ angle: number;
50
+ halfWidth: number;
51
+ }[];
52
+ spins: boolean;
53
+ }
54
+ export interface SceneLink {
55
+ id: string;
56
+ fromId: string;
57
+ toId: string;
58
+ color: string;
59
+ }
60
+ export interface Scene {
61
+ kind: 'focus' | 'overview';
62
+ focusId: string;
63
+ nodes: Map<string, SceneNode>;
64
+ rings: SceneRing[];
65
+ links: SceneLink[];
66
+ /** The camera that fits the scene into the viewport. */
67
+ frame: Camera;
68
+ /**
69
+ * How far the scene reaches from its focus node on each axis, in world
70
+ * units: what `frame` fits. A focus scene reaches as far on both, the edge
71
+ * of its orbit at every turn.
72
+ */
73
+ extent: {
74
+ x: number;
75
+ y: number;
76
+ };
77
+ /** Focus scenes of editable maps: the free place in the orbit where a new idea goes. */
78
+ addSlot?: SceneSlot;
79
+ }
80
+ /** A free place in the sun's orbit, laid out like a planet, for the next child. */
81
+ export interface SceneSlot {
82
+ /** The node a new idea there becomes a child of: the sun. */
83
+ parentId: string;
84
+ /** Its position among the sun's children. */
85
+ index: number;
86
+ x: number;
87
+ y: number;
88
+ radius: number;
89
+ orbitAngle: number;
90
+ orbitRadius: number;
91
+ }
92
+ export interface SceneOptions {
93
+ viewport: Viewport;
94
+ /** The parts of the viewport the page's own controls cover: views are fitted into the rest. */
95
+ insets?: Insets;
96
+ colors: Map<string, string>;
97
+ maxDepth?: number;
98
+ /** Keep a place in the orbit for a new idea, after the sun's children (or at `index`). Focus scenes only. */
99
+ addSlot?: {
100
+ index?: number;
101
+ };
102
+ /**
103
+ * One of the sun's children being dragged to another place among them: it's
104
+ * laid out at `index` among the others, so they make room for it there.
105
+ * Focus scenes only.
106
+ */
107
+ reorder?: {
108
+ id: string;
109
+ index: number;
110
+ };
111
+ /**
112
+ * While a node is carried along the orbit of `focusId`: the radii its rings
113
+ * had when the drag began. They keep them until the drop, so the orbit
114
+ * doesn't grow or shrink as the places in it move. Focus scenes only.
115
+ */
116
+ keptRings?: {
117
+ focusId: string;
118
+ radii: number[];
119
+ };
120
+ }
121
+ export { MOON_LIMIT };
122
+ /** More planets than this in one view, and their shadows overlap into a haze: they're drawn flat. */
123
+ export declare const CROWDED_ORBIT = 48;
124
+ /**
125
+ * The camera that fits `scene` into `viewport` as its `frame` does, inside
126
+ * what `insets` leave of it, and with at least `below` px under it: room for
127
+ * something drawn there, such as the focused node's buttons.
128
+ */
129
+ export declare const fitScene: (scene: Scene, viewport: Viewport, insets?: Insets, below?: number) => Camera;
130
+ /**
131
+ * One color per node, the same in every view. The root uses the first
132
+ * palette entry; top-level branches take the others in turn (see
133
+ * `branchNumbers`); deeper nodes use their branch's color, mixed toward the
134
+ * theme's `shade-light` or `shade-dark` by position so siblings stay
135
+ * distinguishable. A node's own color always wins: its `color`, or its
136
+ * type's (see `ownColor`); then its `paletteColor`.
137
+ */
138
+ export declare const assignNodeColors: (tree: OrbitNode, palette: string[], ownColor?: (node: OrbitNode) => string | undefined) => Map<string, string>;
139
+ /** How much of the branch color each sibling keeps, and what the rest is mixed with. */
140
+ export declare const SHADES: [number, 'light' | 'dark'][];
141
+ export declare const buildFocusScene: (tree: OrbitNode, focusId: string, options: SceneOptions) => Scene;
142
+ export declare const buildOverviewScene: (tree: OrbitNode, options: SceneOptions) => Scene;
143
+ /**
144
+ * The transform that places `target` so the node `anchorId` lands exactly
145
+ * where it is displayed now (`current`): used to fly from one view to the
146
+ * next without any jump.
147
+ */
148
+ export declare const alignScene: (target: Scene, anchorId: string, current: {
149
+ x: number;
150
+ y: number;
151
+ radius: number;
152
+ }) => Similarity;
153
+ /** Applies a placement to a scene node's position and size. */
154
+ export declare const placeNode: (node: SceneNode, transform: Similarity) => {
155
+ x: number;
156
+ y: number;
157
+ radius: number;
158
+ };
159
+ export declare const toRadians: (degrees: number) => number;
160
+ export declare const toDegrees: (radians: number) => number;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Spinner physics for the focus-mode orbit.
3
+ *
4
+ * Release speed comes from where the pointer actually was over the last
5
+ * 80 ms, not from the last pointer event, so a lazy drag barely coasts and a
6
+ * hard flick spins for several turns on any device. Coasting decays
7
+ * exponentially using the exact solution for each step, so the result is the
8
+ * same at 60 Hz, 120 Hz or any other frame rate.
9
+ */
10
+ /** Degrees. `-180 < result <= 180`. */
11
+ export declare const normalizeDelta: (delta: number) => number;
12
+ /** Tracks a dragged angle over time and reports the release velocity. */
13
+ export declare class AngularVelocityTracker {
14
+ private samples;
15
+ private lastRawAngle;
16
+ private unwrappedAngle;
17
+ reset(time: number, rawAngle: number): void;
18
+ /** Records the pointer's angle (degrees) at `time` (milliseconds). Returns the change since the last sample. */
19
+ add(time: number, rawAngle: number): number;
20
+ /** Degrees per millisecond at `releaseTime`: the least-squares slope of the last 80 ms. */
21
+ velocityAt(releaseTime: number): number;
22
+ }
23
+ export interface SpinSettings {
24
+ /** Time constant of the exponential slow-down, in seconds. */
25
+ decay: number;
26
+ /** Fastest coast, in degrees per second. */
27
+ maxSpeed: number;
28
+ /** Below this speed (degrees per second) a coast hands over to settling. */
29
+ settleSpeed: number;
30
+ /** Snap a planet to the top when settling. */
31
+ snap: boolean;
32
+ }
33
+ export declare const DEFAULT_SPIN_SETTINGS: SpinSettings;
34
+ export type SpinMode = 'idle' | 'dragging' | 'coasting' | 'settling';
35
+ export declare class SpinPhysics {
36
+ /** Rotation of the orbit, in degrees. */
37
+ angle: number;
38
+ /** Degrees per millisecond. */
39
+ velocity: number;
40
+ mode: SpinMode;
41
+ settings: SpinSettings;
42
+ private settleTarget;
43
+ /** Angles (degrees) that put a planet at the top; used for snapping. */
44
+ private slots;
45
+ constructor(settings?: Partial<SpinSettings>);
46
+ /** The finger is on the orbit: stops any coast. */
47
+ grab(): void;
48
+ /** Rotates directly while dragging. */
49
+ drag(delta: number): void;
50
+ /** Lets go with a velocity in degrees per millisecond. */
51
+ release(velocity: number, slots?: number[]): void;
52
+ /** Eases the orbit to the turn equivalent to `angle` (degrees) that is nearest to where it is now. */
53
+ settleTo(angle: number): void;
54
+ /** Stops immediately, wherever the orbit is. */
55
+ stop(): void;
56
+ get isMoving(): boolean;
57
+ /** Advances by `elapsed` milliseconds. Returns whether the orbit is still moving. */
58
+ step(elapsed: number): boolean;
59
+ private beginSettling;
60
+ private nearestSlot;
61
+ private stepSettling;
62
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A theme's `surface-pattern` (CSS backgrounds) drawn as SVG, so exported
3
+ * pictures keep the wallpaper glass shows through, a starfield or a grid.
4
+ * Reads the kinds of layers Orbit's own themes use: radial gradients of one
5
+ * color fading out, `linear-gradient` lines tiled by a size, and tiled
6
+ * images. A layer of any other kind is left out of the picture.
7
+ */
8
+ export interface PatternLayers {
9
+ /** `<defs>` entries the layers use. */
10
+ definitions: string[];
11
+ /** One full-size rectangle per layer, bottom layer first. */
12
+ layers: string[];
13
+ }
14
+ /**
15
+ * The layers of `pattern` for a picture `width` × `height`, with ids made
16
+ * from `prefix`. `color` resolves a theme color to a plain value.
17
+ */
18
+ export declare const patternLayers: (pattern: string, width: number, height: number, prefix: string, color: (value: string) => string) => PatternLayers;
19
+ /** The layers of a CSS background list, split at the commas between them. */
20
+ export declare const splitLayers: (value: string) => string[];