worldorbit 3.2.2 → 4.0.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 (73) hide show
  1. package/README.md +546 -545
  2. package/dist/browser/core/dist/atlas-edit.js +146 -1
  3. package/dist/browser/core/dist/atlas-validate.js +105 -10
  4. package/dist/browser/core/dist/draft-parse.js +341 -16
  5. package/dist/browser/core/dist/draft.d.ts +2 -1
  6. package/dist/browser/core/dist/draft.js +25 -3
  7. package/dist/browser/core/dist/format.js +86 -4
  8. package/dist/browser/core/dist/index.d.ts +1 -0
  9. package/dist/browser/core/dist/index.js +1 -0
  10. package/dist/browser/core/dist/load.js +7 -2
  11. package/dist/browser/core/dist/normalize.js +1 -0
  12. package/dist/browser/core/dist/scene.js +11 -2
  13. package/dist/browser/core/dist/schema.js +11 -1
  14. package/dist/browser/core/dist/solver.d.ts +26 -0
  15. package/dist/browser/core/dist/solver.js +27 -0
  16. package/dist/browser/core/dist/types.d.ts +57 -3
  17. package/dist/browser/editor/dist/editor.js +844 -719
  18. package/dist/browser/editor/dist/types.d.ts +2 -1
  19. package/dist/browser/viewer/dist/minimap.js +9 -7
  20. package/dist/browser/viewer/dist/render.js +23 -19
  21. package/dist/browser/viewer/dist/runtime-3d.js +2 -0
  22. package/dist/browser/viewer/dist/viewer.js +7 -3
  23. package/dist/obsidian-plugin/README.md +141 -124
  24. package/dist/obsidian-plugin/main.js +31 -31
  25. package/dist/unpkg/core/dist/atlas-edit.js +146 -1
  26. package/dist/unpkg/core/dist/atlas-validate.js +105 -10
  27. package/dist/unpkg/core/dist/draft-parse.js +341 -16
  28. package/dist/unpkg/core/dist/draft.d.ts +2 -1
  29. package/dist/unpkg/core/dist/draft.js +25 -3
  30. package/dist/unpkg/core/dist/format.js +86 -4
  31. package/dist/unpkg/core/dist/index.d.ts +1 -0
  32. package/dist/unpkg/core/dist/index.js +1 -0
  33. package/dist/unpkg/core/dist/load.js +7 -2
  34. package/dist/unpkg/core/dist/normalize.js +1 -0
  35. package/dist/unpkg/core/dist/scene.js +11 -2
  36. package/dist/unpkg/core/dist/schema.js +11 -1
  37. package/dist/unpkg/core/dist/solver.d.ts +26 -0
  38. package/dist/unpkg/core/dist/solver.js +27 -0
  39. package/dist/unpkg/core/dist/types.d.ts +57 -3
  40. package/dist/unpkg/editor/dist/editor.js +844 -719
  41. package/dist/unpkg/editor/dist/types.d.ts +2 -1
  42. package/dist/unpkg/viewer/dist/minimap.js +9 -7
  43. package/dist/unpkg/viewer/dist/render.js +23 -19
  44. package/dist/unpkg/viewer/dist/runtime-3d.js +2 -0
  45. package/dist/unpkg/viewer/dist/viewer.js +7 -3
  46. package/dist/unpkg/worldorbit-core.min.js +10 -10
  47. package/dist/unpkg/worldorbit-editor.min.js +359 -332
  48. package/dist/unpkg/worldorbit-markdown.min.js +28 -28
  49. package/dist/unpkg/worldorbit-viewer.min.js +214 -214
  50. package/dist/unpkg/worldorbit.js +758 -40
  51. package/dist/unpkg/worldorbit.min.js +223 -223
  52. package/package.json +5 -1
  53. package/packages/core/dist/atlas-edit.js +146 -1
  54. package/packages/core/dist/atlas-validate.js +105 -10
  55. package/packages/core/dist/draft-parse.js +341 -16
  56. package/packages/core/dist/draft.d.ts +2 -1
  57. package/packages/core/dist/draft.js +25 -3
  58. package/packages/core/dist/format.js +86 -4
  59. package/packages/core/dist/index.d.ts +1 -0
  60. package/packages/core/dist/index.js +1 -0
  61. package/packages/core/dist/load.js +7 -2
  62. package/packages/core/dist/normalize.js +1 -0
  63. package/packages/core/dist/scene.js +11 -2
  64. package/packages/core/dist/schema.js +11 -1
  65. package/packages/core/dist/solver.d.ts +26 -0
  66. package/packages/core/dist/solver.js +27 -0
  67. package/packages/core/dist/types.d.ts +57 -3
  68. package/packages/editor/dist/editor.js +844 -719
  69. package/packages/editor/dist/types.d.ts +2 -1
  70. package/packages/viewer/dist/minimap.js +9 -7
  71. package/packages/viewer/dist/render.js +23 -19
  72. package/packages/viewer/dist/runtime-3d.js +2 -0
  73. package/packages/viewer/dist/viewer.js +7 -3
@@ -9,6 +9,7 @@ export interface WorldOrbitEditorSnapshot {
9
9
  diagnostics: AtlasResolvedDiagnostic[];
10
10
  selection: WorldOrbitEditorSelection | null;
11
11
  }
12
+ export type WorldOrbitEditorObjectType = WorldOrbitObject["type"] | "craft";
12
13
  export interface WorldOrbitEditorOptions {
13
14
  source?: string;
14
15
  atlasDocument?: WorldOrbitAtlasDocument;
@@ -40,7 +41,7 @@ export interface WorldOrbitEditor {
40
41
  canRedo(): boolean;
41
42
  undo(): boolean;
42
43
  redo(): boolean;
43
- addObject(type?: WorldOrbitObject["type"]): string;
44
+ addObject(type?: WorldOrbitEditorObjectType): string;
44
45
  addEvent(): string;
45
46
  addViewpoint(): string;
46
47
  addAnnotation(): string;
@@ -28,13 +28,13 @@ export function renderViewerMinimap(scene, state, visibleObjects) {
28
28
  return `<circle cx="${formatNumber(x)}" cy="${formatNumber(y)}" r="${formatNumber(radius)}" fill="${fill}" fill-opacity="0.92" />`;
29
29
  })
30
30
  .join("");
31
- return `<div data-worldorbit-minimap="true" style="position:absolute;right:16px;bottom:16px;width:${MINIMAP_WIDTH}px;height:${MINIMAP_HEIGHT}px;padding:8px;border-radius:16px;background:rgba(5, 14, 22, 0.78);border:1px solid rgba(179, 216, 255, 0.16);box-shadow:0 14px 28px rgba(0, 0, 0, 0.24);backdrop-filter:blur(8px);pointer-events:none;">
32
- <svg width="${MINIMAP_WIDTH}" height="${MINIMAP_HEIGHT}" viewBox="0 0 ${MINIMAP_WIDTH} ${MINIMAP_HEIGHT}" role="presentation" aria-hidden="true">
33
- <rect x="0.5" y="0.5" width="${MINIMAP_WIDTH - 1}" height="${MINIMAP_HEIGHT - 1}" rx="12" ry="12" fill="rgba(7, 17, 27, 0.85)" stroke="rgba(179, 216, 255, 0.18)" />
34
- <rect x="${formatNumber(bounds.minX * scale + translateX)}" y="${formatNumber(bounds.minY * scale + translateY)}" width="${formatNumber(bounds.width * scale)}" height="${formatNumber(bounds.height * scale)}" rx="10" ry="10" fill="rgba(163, 209, 255, 0.04)" stroke="rgba(163, 209, 255, 0.16)" />
35
- ${objectsMarkup}
36
- <rect x="${formatNumber(viewport.minX * scale + translateX)}" y="${formatNumber(viewport.minY * scale + translateY)}" width="${formatNumber(viewport.width * scale)}" height="${formatNumber(viewport.height * scale)}" rx="8" ry="8" fill="rgba(255, 180, 100, 0.09)" stroke="rgba(255, 180, 100, 0.88)" stroke-width="1.4" />
37
- </svg>
31
+ return `<div data-worldorbit-minimap="true" style="position:absolute;right:16px;bottom:16px;width:${MINIMAP_WIDTH}px;height:${MINIMAP_HEIGHT}px;padding:8px;border-radius:16px;background:rgba(5, 14, 22, 0.78);border:1px solid rgba(179, 216, 255, 0.16);box-shadow:0 14px 28px rgba(0, 0, 0, 0.24);backdrop-filter:blur(8px);pointer-events:none;">
32
+ <svg width="${MINIMAP_WIDTH}" height="${MINIMAP_HEIGHT}" viewBox="0 0 ${MINIMAP_WIDTH} ${MINIMAP_HEIGHT}" role="presentation" aria-hidden="true">
33
+ <rect x="0.5" y="0.5" width="${MINIMAP_WIDTH - 1}" height="${MINIMAP_HEIGHT - 1}" rx="12" ry="12" fill="rgba(7, 17, 27, 0.85)" stroke="rgba(179, 216, 255, 0.18)" />
34
+ <rect x="${formatNumber(bounds.minX * scale + translateX)}" y="${formatNumber(bounds.minY * scale + translateY)}" width="${formatNumber(bounds.width * scale)}" height="${formatNumber(bounds.height * scale)}" rx="10" ry="10" fill="rgba(163, 209, 255, 0.04)" stroke="rgba(163, 209, 255, 0.16)" />
35
+ ${objectsMarkup}
36
+ <rect x="${formatNumber(viewport.minX * scale + translateX)}" y="${formatNumber(viewport.minY * scale + translateY)}" width="${formatNumber(viewport.width * scale)}" height="${formatNumber(viewport.height * scale)}" rx="8" ry="8" fill="rgba(255, 180, 100, 0.09)" stroke="rgba(255, 180, 100, 0.88)" stroke-width="1.4" />
37
+ </svg>
38
38
  </div>`;
39
39
  }
40
40
  function minimapColorForObject(type) {
@@ -52,6 +52,8 @@ function minimapColorForObject(type) {
52
52
  return "#a7a5b8";
53
53
  case "comet":
54
54
  return "#9ce7ff";
55
+ case "craft":
56
+ return "#ffb47f";
55
57
  case "structure":
56
58
  return "#ff7f5f";
57
59
  case "phenomenon":
@@ -89,14 +89,14 @@ export function renderSceneToSvg(scene, options = {}) {
89
89
  .wo-bg-glow { fill: url(#wo-bg-glow); }
90
90
  .wo-grid { fill: none; stroke: ${theme.guide}; stroke-width: 1; }
91
91
  .wo-orbit { fill: none; stroke: ${theme.orbit}; stroke-width: 1.5; }
92
- .wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
93
- .wo-orbit-front { opacity: 0.9; }
94
- .wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
95
- .wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
96
- .wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
97
- .wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
98
- .wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
99
- .wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
92
+ .wo-orbit-back { opacity: 0.38; stroke-dasharray: 8 6; }
93
+ .wo-orbit-front { opacity: 0.9; }
94
+ .wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
95
+ .wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
96
+ .wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
97
+ .wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
98
+ .wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
99
+ .wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
100
100
  .wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
101
101
  .wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
102
102
  .wo-title { fill: ${theme.ink}; font: 700 24px ${theme.displayFont}; letter-spacing: 0.06em; text-transform: uppercase; }
@@ -127,13 +127,13 @@ export function renderSceneToSvg(scene, options = {}) {
127
127
  <g data-worldorbit-world="true">
128
128
  <g data-worldorbit-camera-root="${WORLD_LAYER_ID}" id="${WORLD_LAYER_ID}">
129
129
  <g data-worldorbit-world-content="true">
130
- ${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
131
- ${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
132
- ${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
133
- ${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
134
- ${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
135
- ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
136
- ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
130
+ ${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
131
+ ${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
132
+ ${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
133
+ ${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
134
+ ${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
135
+ ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
136
+ ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
137
137
  </g>
138
138
  </g>
139
139
  </g>
@@ -152,10 +152,10 @@ function renderSceneEventOverlay(scene, event, visibleObjectIds, theme) {
152
152
  const lineMarkup = participants
153
153
  .map((object) => `<line class="wo-event-line" x1="${event.x}" y1="${event.y}" x2="${object.x}" y2="${object.y}" stroke="${escapeAttribute(stroke)}" data-event-id="${escapeAttribute(event.eventId)}" data-object-id="${escapeAttribute(object.objectId)}" />`)
154
154
  .join("");
155
- return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
156
- ${lineMarkup}
157
- <circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
158
- <text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
155
+ return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
156
+ ${lineMarkup}
157
+ <circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
158
+ <text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
159
159
  </g>`;
160
160
  }
161
161
  export function renderDocumentToSvg(document, options = {}) {
@@ -259,6 +259,8 @@ function renderObjectBody(object, x, y, radius, palette, options = {}) {
259
259
  ? `<circle cx="${x}" cy="${y}" r="${radius}" fill="transparent" stroke="${palette.stroke}" stroke-width="1.4" />`
260
260
  : `<path d="M ${x - radius * 2} ${y + radius * 1.3} Q ${x - radius * 0.7} ${y + radius * 0.3} ${x - radius * 0.45} ${y}" fill="none" stroke="${tail}" stroke-width="${Math.max(2, radius * 0.8)}" stroke-linecap="round" opacity="0.85" />
261
261
  <circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
262
+ case "craft":
263
+ return `<polygon points="${diamondPoints(x, y, radius * 0.85)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
262
264
  case "structure":
263
265
  return `<polygon points="${diamondPoints(x, y, radius)}" fill="${fill}" stroke="${palette.stroke}" stroke-width="1.4" />`;
264
266
  case "phenomenon": {
@@ -443,6 +445,8 @@ function basePaletteForType(type, kind, theme) {
443
445
  return { fill: "#9ce7ff", stroke: "#e7fbff" };
444
446
  case "ring":
445
447
  return { fill: "#e59f7d", stroke: "#fff0d3" };
448
+ case "craft":
449
+ return { fill: "#ffb47f", stroke: "#fff0d3" };
446
450
  case "structure":
447
451
  return { fill: theme.accentStrong, stroke: "#fff2ea" };
448
452
  case "phenomenon":
@@ -686,6 +686,8 @@ function colorForObject(object) {
686
686
  return "#b8926a";
687
687
  case "ring":
688
688
  return "#cdbf9a";
689
+ case "craft":
690
+ return "#ffce8a";
689
691
  case "structure":
690
692
  return "#ffce8a";
691
693
  case "phenomenon":
@@ -1086,7 +1086,7 @@ export function createInteractiveViewer(container, options) {
1086
1086
  tooltipRoot.hidden = false;
1087
1087
  tooltipRoot.dataset.mode = resolved.mode;
1088
1088
  tooltipRoot.classList.toggle("is-pinned", resolved.mode === "pinned");
1089
- tooltipRoot.style.pointerEvents = "auto";
1089
+ tooltipRoot.style.pointerEvents = resolved.mode === "pinned" ? "auto" : "none";
1090
1090
  tooltipRoot.style.visibility = "hidden";
1091
1091
  renderTooltipContent(tooltipRoot, tooltipDetails, resolved.mode);
1092
1092
  positionTooltip(tooltipRoot, details.renderObject);
@@ -1732,7 +1732,11 @@ function getClosestObjectId(target) {
1732
1732
  if (!(target instanceof Element)) {
1733
1733
  return null;
1734
1734
  }
1735
- return target.closest("[data-object-id]")?.dataset.objectId ?? null;
1735
+ const selectionTarget = target.closest("[data-object-id], [data-orbit-object-id]");
1736
+ if (!selectionTarget) {
1737
+ return null;
1738
+ }
1739
+ return selectionTarget.dataset.objectId ?? selectionTarget.dataset.orbitObjectId ?? null;
1736
1740
  }
1737
1741
  function ensureBrowserEnvironment(container) {
1738
1742
  if (typeof window === "undefined" || typeof document === "undefined") {
@@ -1864,7 +1868,7 @@ function installViewerOverlayStyles() {
1864
1868
  backdrop-filter: blur(12px);
1865
1869
  font: 500 13px/1.5 "Segoe UI Variable", "Segoe UI", sans-serif;
1866
1870
  }
1867
- .wo-viewer-tooltip-root[data-mode="hover"] { pointer-events: auto; }
1871
+ .wo-viewer-tooltip-root[data-mode="hover"] { pointer-events: none; }
1868
1872
  .wo-viewer-tooltip-root[data-mode="pinned"] { pointer-events: auto; }
1869
1873
  .wo-tooltip-card { display: grid; gap: 10px; }
1870
1874
  .wo-tooltip-head { display: grid; grid-template-columns: 52px minmax(0, 1fr); gap: 12px; align-items: center; }
@@ -1,124 +1,141 @@
1
-
2
-
3
- Treat your Obsidian Vault as a stellar atlas. WorldOrbit provides a text-first DSL (Domain Specific Language) to render fictional star systems, orbital mechanics, and space infrastructure directly inside your notes.
4
-
5
- See the **Examples** section below for a quick start, or the [full reference](https://github.com/negurvulkan/worldorbit) for all the details.
6
-
7
- ## Examples
8
-
9
- Render a basic star system with a star and a planet:
10
-
11
- Code-Snippet
12
-
13
- ```
14
- schema 2.6
15
- system Sol
16
-
17
- object star Sun
18
- radius 695700km
19
-
20
- object planet Earth
21
- orbit Sun
22
- semiMajor 1au
23
- color #6fa8ff
24
- ```
25
-
26
-
27
- *Visualisierung eines einfachen Sternensystems in der Obsidian Live-Vorschau.*
28
-
29
- Visualize complex orbital hierarchies including moons and rings:
30
-
31
- Code-Snippet
32
-
33
- ```
34
- schema 2.6
35
- system Sol
36
-
37
- object star Sun
38
- radius 695700km
39
-
40
- object planet Earth
41
- orbit Sun
42
- semiMajor 1au
43
- color #6fa8ff
44
-
45
- object planet Saturn
46
- orbit Sun
47
- semiMajor 9.58au
48
-
49
- object ring Saturn-Rings
50
- orbit Saturn
51
- inner 67000km
52
- outer 140000km
53
-
54
- object moon Titan
55
- orbit Saturn
56
- distance 1221870km
57
- ```
58
-
59
-
60
- *Darstellung komplexer orbitaler Hierarchien mit Ringen und Monden.*
61
-
62
- ## Usage
63
-
64
- WorldOrbit consists of two major aspects: **Data (DSL)** and **Visualizing**.
65
-
66
- ### Data
67
-
68
- WorldOrbit generates diagrams from fenced code blocks using the `.ks` (KeplerScript) syntax. It is designed for fictional worldbuilding, focusing on readability and logic rather than strict astrophysical simulation.
69
-
70
- - **Objects**: Define stars, planets, moons, or space stations.
71
- - **Placement**: Use `orbit`, `at` (for Lagrange points or anchors), or `surface` to position objects.
72
- - **Physical Traits**: Assign `mass`, `radius`, `color`, and `atmosphere` to enrich the simulation.
73
-
74
- ### Visualizing
75
-
76
- Once you've defined your system, the plugin renders it using three modes:
77
-
78
- 1. **Reading View & Live Preview**: Automatically turns code blocks into interactive 2D diagrams.
79
- 2. **Lazy Loading**: Previews are only rendered when they become visible, keeping your Obsidian vault fast and responsive.
80
- 3. **Locked Mode**: By default, diagrams are locked to allow safe scrolling. Click "Activate Interaction" to enable pan and zoom.
81
-
82
- ## Features
83
-
84
- - **2D Interactive Viewer**: Pan, zoom, and explore your systems.
85
- - **Diagnostics**: Real-time validation of your orbital data. If a planet's period doesn't match its mass (Kepler's Laws), the plugin will tell you.
86
- - **Command Palette**: Use `WorldOrbit: Insert Solar System Example` to quickly bootstrap a new system.
87
- - **Fullscreen Mode**: Open any diagram in a high-resolution modal view for detailed inspection.
88
-
89
- ## Installation
90
-
91
- ### Community Plugin
92
-
93
- Search for **WorldOrbit** in the Obsidian Community Plugins settings and click Install. (Note: Submission pending).
94
-
95
- ### Manual Installation
96
-
97
- 1. Download `main.js`, `manifest.json`, and `styles.css` from the [latest release](https://github.com/negurvulkan/worldorbit/releases).
98
- 2. Create a folder `.obsidian/plugins/worldorbit` in your vault.
99
- 3. Move the files into that folder and enable the plugin in Obsidian.
100
-
101
- ## Contributing
102
-
103
- Contributions via bug reports, documentation, and improvements are welcome.
104
-
105
- ### Local Development
106
-
107
- The codebase is part of a TypeScript monorepo. To set up locally:
108
-
109
- Bash
110
-
111
- ```
112
- git clone https://github.com/negurvulkan/worldorbit.git
113
- cd worldorbit
114
- npm install
115
- npm run build
116
- ```
117
-
118
- ## License
119
-
120
- Licensed under the **MIT License**.
121
-
122
- NPM Version|97
123
- Downloads
124
- GitHub stars
1
+ # WorldOrbit for Obsidian
2
+
3
+ <p align="center">
4
+ <img src="https://img.shields.io/badge/version-1.0.0-gold?style=for-the-badge" alt="Version 1.0.0">
5
+ <img src="https://img.shields.io/badge/license-MIT-blue?style=for-the-badge" alt="License MIT">
6
+ <img src="https://img.shields.io/badge/Obsidian-v1.5.0+-purple?style=for-the-badge&logo=obsidian" alt="Obsidian v1.5.0+">
7
+ </p>
8
+
9
+ ---
10
+
11
+ Treat your Obsidian Vault as a stellar atlas. WorldOrbit provides a text-first DSL (Domain Specific Language) to render fictional star systems, orbital mechanics, and space infrastructure directly inside your notes.
12
+
13
+ See the **Examples** section below for a quick start, or the [full reference](https://github.com/negurvulkan/worldorbit) for all the details.
14
+
15
+ ## Examples
16
+
17
+ Render a basic star system with a star and a planet:
18
+
19
+ Code-Snippet
20
+
21
+ ```
22
+ schema 2.6
23
+ system Sol
24
+
25
+ object star Sun
26
+ radius 695700km
27
+
28
+ object planet Earth
29
+ orbit Sun
30
+ semiMajor 1au
31
+ color #6fa8ff
32
+ ```
33
+
34
+
35
+ *.*
36
+ <p align="center">
37
+ <img src="https://negurvulkan.github.io/worldorbit/obsidian_scsh_1.png" width="700" alt="WorldOrbit Simple System Preview">
38
+ <br>
39
+ <em>WorldOrbit Simple System Preview</em>
40
+ </p>
41
+
42
+ Visualize complex orbital hierarchies including moons and rings:
43
+
44
+ Code-Snippet
45
+
46
+ ```
47
+ schema 2.6
48
+ system Sol
49
+
50
+ object star Sun
51
+ radius 695700km
52
+
53
+ object planet Earth
54
+ orbit Sun
55
+ semiMajor 1au
56
+ color #6fa8ff
57
+
58
+ object planet Saturn
59
+ orbit Sun
60
+ semiMajor 9.58au
61
+
62
+ object ring Saturn-Rings
63
+ orbit Saturn
64
+ inner 67000km
65
+ outer 140000km
66
+
67
+ object moon Titan
68
+ orbit Saturn
69
+ distance 1221870km
70
+ ```
71
+
72
+
73
+ <p align="center">
74
+ <img src="https://negurvulkan.github.io/worldorbit/obsidian_scsh_2.png" width="700" alt="WorldOrbit Advanced System Preview">
75
+ <br>
76
+ <em>WorldOrbit Advanced System</em>
77
+ </p>
78
+
79
+ ## Usage
80
+
81
+ WorldOrbit consists of two major aspects: **Data (DSL)** and **Visualizing**.
82
+
83
+ ### Data
84
+
85
+ WorldOrbit generates diagrams from fenced code blocks using the `.ks` (KeplerScript) syntax. It is designed for fictional worldbuilding, focusing on readability and logic rather than strict astrophysical simulation.
86
+
87
+ - **Objects**: Define stars, planets, moons, or space stations.
88
+ - **Placement**: Use `orbit`, `at` (for Lagrange points or anchors), or `surface` to position objects.
89
+ - **Physical Traits**: Assign `mass`, `radius`, `color`, and `atmosphere` to enrich the simulation.
90
+
91
+ ### Visualizing
92
+
93
+ Once you've defined your system, the plugin renders it using three modes:
94
+
95
+ 1. **Reading View & Live Preview**: Automatically turns code blocks into interactive 2D diagrams.
96
+ 2. **Lazy Loading**: Previews are only rendered when they become visible, keeping your Obsidian vault fast and responsive.
97
+ 3. **Locked Mode**: By default, diagrams are locked to allow safe scrolling. Click "Activate Interaction" to enable pan and zoom.
98
+
99
+ ## Features
100
+
101
+ - **2D Interactive Viewer**: Pan, zoom, and explore your systems.
102
+ - **Diagnostics**: Real-time validation of your orbital data. If a planet's period doesn't match its mass (Kepler's Laws), the plugin will tell you.
103
+ - **Command Palette**: Use `WorldOrbit: Insert Solar System Example` to quickly bootstrap a new system.
104
+ - **Fullscreen Mode**: Open any diagram in a high-resolution modal view for detailed inspection.
105
+
106
+ ## Installation
107
+
108
+ ### Community Plugin
109
+
110
+ Search for **WorldOrbit** in the Obsidian Community Plugins settings and click Install. (Note: Submission pending).
111
+
112
+ ### Manual Installation
113
+
114
+ 1. Download `main.js`, `manifest.json`, and `styles.css` from the [latest release](https://github.com/negurvulkan/worldorbit/releases).
115
+ 2. Create a folder `.obsidian/plugins/worldorbit` in your vault.
116
+ 3. Move the files into that folder and enable the plugin in Obsidian.
117
+
118
+ ## Contributing
119
+
120
+ Contributions via bug reports, documentation, and improvements are welcome.
121
+
122
+ ### Local Development
123
+
124
+ The codebase is part of a TypeScript monorepo. To set up locally:
125
+
126
+ Bash
127
+
128
+ ```
129
+ git clone https://github.com/negurvulkan/worldorbit.git
130
+ cd worldorbit
131
+ npm install
132
+ npm run build
133
+ ```
134
+
135
+ ## License
136
+
137
+ Licensed under the **MIT License**.
138
+
139
+ ![NPM Version](https://img.shields.io/npm/v/worldorbit?style=flat-square&color=cb3837&logo=npm)
140
+ ![Downloads](https://img.shields.io/npm/dm/worldorbit?style=flat-square&color=blue)
141
+ ![GitHub stars](https://img.shields.io/github/stars/negurvulkan/worldorbit?style=flat-square&color=gold)