worldorbit 2.5.17 → 3.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 (203) hide show
  1. package/README.md +91 -18
  2. package/dist/browser/core/dist/atlas-edit.d.ts +11 -0
  3. package/dist/browser/core/dist/atlas-edit.js +347 -0
  4. package/dist/browser/core/dist/atlas-utils.d.ts +22 -0
  5. package/dist/browser/core/dist/atlas-utils.js +189 -0
  6. package/dist/browser/core/dist/atlas-validate.d.ts +2 -0
  7. package/dist/browser/core/dist/atlas-validate.js +488 -0
  8. package/dist/browser/core/dist/diagnostics.d.ts +10 -0
  9. package/dist/browser/core/dist/diagnostics.js +109 -0
  10. package/dist/browser/core/dist/draft-parse.d.ts +3 -0
  11. package/dist/browser/core/dist/draft-parse.js +1654 -0
  12. package/dist/browser/core/dist/draft.d.ts +21 -0
  13. package/dist/browser/core/dist/draft.js +482 -0
  14. package/dist/browser/core/dist/errors.d.ts +7 -0
  15. package/dist/browser/core/dist/errors.js +16 -0
  16. package/dist/browser/core/dist/format.d.ts +4 -0
  17. package/dist/browser/core/dist/format.js +613 -0
  18. package/dist/browser/core/dist/index.d.ts +29 -0
  19. package/dist/browser/core/dist/index.js +35 -6101
  20. package/dist/browser/core/dist/load.d.ts +4 -0
  21. package/dist/browser/core/dist/load.js +182 -0
  22. package/dist/browser/core/dist/markdown.d.ts +2 -0
  23. package/dist/browser/core/dist/markdown.js +37 -0
  24. package/dist/browser/core/dist/normalize.d.ts +2 -0
  25. package/dist/browser/core/dist/normalize.js +312 -0
  26. package/dist/browser/core/dist/parse.d.ts +2 -0
  27. package/dist/browser/core/dist/parse.js +133 -0
  28. package/dist/browser/core/dist/scene.d.ts +3 -0
  29. package/dist/browser/core/dist/scene.js +1901 -0
  30. package/dist/browser/core/dist/schema.d.ts +8 -0
  31. package/dist/browser/core/dist/schema.js +298 -0
  32. package/dist/browser/core/dist/spatial-scene.d.ts +3 -0
  33. package/dist/browser/core/dist/spatial-scene.js +420 -0
  34. package/dist/browser/core/dist/tokenize.d.ts +4 -0
  35. package/dist/browser/core/dist/tokenize.js +68 -0
  36. package/dist/browser/core/dist/types.d.ts +637 -0
  37. package/dist/browser/core/dist/types.js +1 -0
  38. package/dist/browser/core/dist/validate.d.ts +2 -0
  39. package/dist/browser/core/dist/validate.js +56 -0
  40. package/dist/browser/editor/dist/editor.d.ts +2 -0
  41. package/dist/browser/editor/dist/editor.js +3700 -0
  42. package/dist/browser/editor/dist/index.d.ts +2 -0
  43. package/dist/browser/editor/dist/index.js +1 -11702
  44. package/dist/browser/editor/dist/types.d.ts +59 -0
  45. package/dist/browser/editor/dist/types.js +1 -0
  46. package/dist/browser/markdown/dist/html.d.ts +3 -0
  47. package/dist/browser/markdown/dist/html.js +64 -0
  48. package/dist/browser/markdown/dist/index.d.ts +4 -0
  49. package/dist/browser/markdown/dist/index.js +3 -5766
  50. package/dist/browser/markdown/dist/rehype.d.ts +10 -0
  51. package/dist/browser/markdown/dist/rehype.js +49 -0
  52. package/dist/browser/markdown/dist/remark.d.ts +9 -0
  53. package/dist/browser/markdown/dist/remark.js +28 -0
  54. package/dist/browser/markdown/dist/types.d.ts +11 -0
  55. package/dist/browser/markdown/dist/types.js +1 -0
  56. package/dist/browser/viewer/dist/atlas-state.d.ts +12 -0
  57. package/dist/browser/viewer/dist/atlas-state.js +269 -0
  58. package/dist/browser/viewer/dist/atlas-viewer.d.ts +2 -0
  59. package/dist/browser/viewer/dist/atlas-viewer.js +495 -0
  60. package/dist/browser/viewer/dist/custom-element.d.ts +1 -0
  61. package/dist/browser/viewer/dist/custom-element.js +78 -0
  62. package/dist/browser/viewer/dist/embed.d.ts +24 -0
  63. package/dist/browser/viewer/dist/embed.js +172 -0
  64. package/dist/browser/viewer/dist/errors.d.ts +6 -0
  65. package/dist/browser/viewer/dist/errors.js +12 -0
  66. package/dist/browser/viewer/dist/index.d.ts +10 -0
  67. package/dist/browser/viewer/dist/index.js +9 -7901
  68. package/dist/browser/viewer/dist/minimap.d.ts +3 -0
  69. package/dist/browser/viewer/dist/minimap.js +63 -0
  70. package/dist/browser/viewer/dist/render.d.ts +6 -0
  71. package/dist/browser/viewer/dist/render.js +670 -0
  72. package/dist/browser/viewer/dist/runtime-3d.d.ts +19 -0
  73. package/dist/browser/viewer/dist/runtime-3d.js +494 -0
  74. package/dist/browser/viewer/dist/theme.d.ts +4 -0
  75. package/dist/browser/viewer/dist/theme.js +103 -0
  76. package/dist/browser/viewer/dist/tooltip.d.ts +3 -0
  77. package/dist/browser/viewer/dist/tooltip.js +198 -0
  78. package/dist/browser/viewer/dist/types.d.ts +292 -0
  79. package/dist/browser/viewer/dist/types.js +1 -0
  80. package/dist/browser/viewer/dist/vendor/three.module.js +53032 -0
  81. package/dist/browser/viewer/dist/viewer-state.d.ts +19 -0
  82. package/dist/browser/viewer/dist/viewer-state.js +162 -0
  83. package/dist/browser/viewer/dist/viewer.d.ts +2 -0
  84. package/dist/browser/viewer/dist/viewer.js +1662 -0
  85. package/dist/unpkg/core/dist/atlas-edit.d.ts +11 -0
  86. package/dist/unpkg/core/dist/atlas-edit.js +347 -0
  87. package/dist/unpkg/core/dist/atlas-utils.d.ts +22 -0
  88. package/dist/unpkg/core/dist/atlas-utils.js +189 -0
  89. package/dist/unpkg/core/dist/atlas-validate.d.ts +2 -0
  90. package/dist/unpkg/core/dist/atlas-validate.js +488 -0
  91. package/dist/unpkg/core/dist/diagnostics.d.ts +10 -0
  92. package/dist/unpkg/core/dist/diagnostics.js +109 -0
  93. package/dist/unpkg/core/dist/draft-parse.d.ts +3 -0
  94. package/dist/unpkg/core/dist/draft-parse.js +1654 -0
  95. package/dist/unpkg/core/dist/draft.d.ts +21 -0
  96. package/dist/unpkg/core/dist/draft.js +482 -0
  97. package/dist/unpkg/core/dist/errors.d.ts +7 -0
  98. package/dist/unpkg/core/dist/errors.js +16 -0
  99. package/dist/unpkg/core/dist/format.d.ts +4 -0
  100. package/dist/unpkg/core/dist/format.js +613 -0
  101. package/dist/unpkg/core/dist/index.d.ts +29 -0
  102. package/dist/unpkg/core/dist/index.js +35 -6173
  103. package/dist/unpkg/core/dist/load.d.ts +4 -0
  104. package/dist/unpkg/core/dist/load.js +182 -0
  105. package/dist/unpkg/core/dist/markdown.d.ts +2 -0
  106. package/dist/unpkg/core/dist/markdown.js +37 -0
  107. package/dist/unpkg/core/dist/normalize.d.ts +2 -0
  108. package/dist/unpkg/core/dist/normalize.js +312 -0
  109. package/dist/unpkg/core/dist/parse.d.ts +2 -0
  110. package/dist/unpkg/core/dist/parse.js +133 -0
  111. package/dist/unpkg/core/dist/scene.d.ts +3 -0
  112. package/dist/unpkg/core/dist/scene.js +1901 -0
  113. package/dist/unpkg/core/dist/schema.d.ts +8 -0
  114. package/dist/unpkg/core/dist/schema.js +298 -0
  115. package/dist/unpkg/core/dist/spatial-scene.d.ts +3 -0
  116. package/dist/unpkg/core/dist/spatial-scene.js +420 -0
  117. package/dist/unpkg/core/dist/tokenize.d.ts +4 -0
  118. package/dist/unpkg/core/dist/tokenize.js +68 -0
  119. package/dist/unpkg/core/dist/types.d.ts +637 -0
  120. package/dist/unpkg/core/dist/types.js +1 -0
  121. package/dist/unpkg/core/dist/validate.d.ts +2 -0
  122. package/dist/unpkg/core/dist/validate.js +56 -0
  123. package/dist/unpkg/editor/dist/editor.d.ts +2 -0
  124. package/dist/unpkg/editor/dist/editor.js +3700 -0
  125. package/dist/unpkg/editor/dist/index.d.ts +2 -0
  126. package/dist/unpkg/editor/dist/index.js +1 -11727
  127. package/dist/unpkg/editor/dist/types.d.ts +59 -0
  128. package/dist/unpkg/editor/dist/types.js +1 -0
  129. package/dist/unpkg/markdown/dist/html.d.ts +3 -0
  130. package/dist/unpkg/markdown/dist/html.js +64 -0
  131. package/dist/unpkg/markdown/dist/index.d.ts +4 -0
  132. package/dist/unpkg/markdown/dist/index.js +3 -5794
  133. package/dist/unpkg/markdown/dist/rehype.d.ts +10 -0
  134. package/dist/unpkg/markdown/dist/rehype.js +49 -0
  135. package/dist/unpkg/markdown/dist/remark.d.ts +9 -0
  136. package/dist/unpkg/markdown/dist/remark.js +28 -0
  137. package/dist/unpkg/markdown/dist/types.d.ts +11 -0
  138. package/dist/unpkg/markdown/dist/types.js +1 -0
  139. package/dist/unpkg/viewer/dist/atlas-state.d.ts +12 -0
  140. package/dist/unpkg/viewer/dist/atlas-state.js +269 -0
  141. package/dist/unpkg/viewer/dist/atlas-viewer.d.ts +2 -0
  142. package/dist/unpkg/viewer/dist/atlas-viewer.js +495 -0
  143. package/dist/unpkg/viewer/dist/custom-element.d.ts +1 -0
  144. package/dist/unpkg/viewer/dist/custom-element.js +78 -0
  145. package/dist/unpkg/viewer/dist/embed.d.ts +24 -0
  146. package/dist/unpkg/viewer/dist/embed.js +172 -0
  147. package/dist/unpkg/viewer/dist/errors.d.ts +6 -0
  148. package/dist/unpkg/viewer/dist/errors.js +12 -0
  149. package/dist/unpkg/viewer/dist/index.d.ts +10 -0
  150. package/dist/unpkg/viewer/dist/index.js +9 -7958
  151. package/dist/unpkg/viewer/dist/minimap.d.ts +3 -0
  152. package/dist/unpkg/viewer/dist/minimap.js +63 -0
  153. package/dist/unpkg/viewer/dist/render.d.ts +6 -0
  154. package/dist/unpkg/viewer/dist/render.js +670 -0
  155. package/dist/unpkg/viewer/dist/runtime-3d.d.ts +19 -0
  156. package/dist/unpkg/viewer/dist/runtime-3d.js +494 -0
  157. package/dist/unpkg/viewer/dist/theme.d.ts +4 -0
  158. package/dist/unpkg/viewer/dist/theme.js +103 -0
  159. package/dist/unpkg/viewer/dist/tooltip.d.ts +3 -0
  160. package/dist/unpkg/viewer/dist/tooltip.js +198 -0
  161. package/dist/unpkg/viewer/dist/types.d.ts +292 -0
  162. package/dist/unpkg/viewer/dist/types.js +1 -0
  163. package/dist/unpkg/viewer/dist/vendor/three.module.js +53032 -0
  164. package/dist/unpkg/viewer/dist/viewer-state.d.ts +19 -0
  165. package/dist/unpkg/viewer/dist/viewer-state.js +162 -0
  166. package/dist/unpkg/viewer/dist/viewer.d.ts +2 -0
  167. package/dist/unpkg/viewer/dist/viewer.js +1662 -0
  168. package/dist/unpkg/worldorbit-core.min.js +1 -12
  169. package/dist/unpkg/worldorbit-editor.min.js +1 -894
  170. package/dist/unpkg/worldorbit-markdown.min.js +1 -103
  171. package/dist/unpkg/worldorbit-viewer.min.js +1 -259
  172. package/dist/unpkg/worldorbit.js +2 -9243
  173. package/dist/unpkg/worldorbit.min.js +2 -263
  174. package/package.json +1 -1
  175. package/packages/core/dist/atlas-edit.js +1 -1
  176. package/packages/core/dist/atlas-validate.js +99 -10
  177. package/packages/core/dist/draft-parse.js +190 -15
  178. package/packages/core/dist/draft.js +50 -11
  179. package/packages/core/dist/format.js +36 -5
  180. package/packages/core/dist/index.d.ts +1 -0
  181. package/packages/core/dist/index.js +1 -0
  182. package/packages/core/dist/load.js +9 -2
  183. package/packages/core/dist/scene.js +158 -24
  184. package/packages/core/dist/spatial-scene.d.ts +3 -0
  185. package/packages/core/dist/spatial-scene.js +420 -0
  186. package/packages/core/dist/types.d.ts +124 -2
  187. package/packages/editor/dist/editor.js +130 -8
  188. package/packages/editor/dist/types.d.ts +4 -0
  189. package/packages/markdown/dist/html.js +10 -3
  190. package/packages/viewer/dist/atlas-state.js +8 -2
  191. package/packages/viewer/dist/atlas-viewer.js +20 -8
  192. package/packages/viewer/dist/custom-element.js +18 -4
  193. package/packages/viewer/dist/embed.d.ts +5 -1
  194. package/packages/viewer/dist/embed.js +58 -24
  195. package/packages/viewer/dist/errors.d.ts +6 -0
  196. package/packages/viewer/dist/errors.js +12 -0
  197. package/packages/viewer/dist/index.d.ts +1 -0
  198. package/packages/viewer/dist/index.js +1 -0
  199. package/packages/viewer/dist/runtime-3d.d.ts +19 -0
  200. package/packages/viewer/dist/runtime-3d.js +494 -0
  201. package/packages/viewer/dist/types.d.ts +25 -3
  202. package/packages/viewer/dist/vendor/three.module.js +53032 -0
  203. package/packages/viewer/dist/viewer.js +517 -41
@@ -64,7 +64,11 @@ const OBJECT_NUMBER_FIELDS = ["albedo"];
64
64
  const FIELD_HELP = {
65
65
  "defaults-view": {
66
66
  description: "Sets the default camera projection for the atlas.",
67
- references: ["Topdown = map-like", "Isometric = angled overview"],
67
+ references: [
68
+ "Topdown = map-like",
69
+ "Isometric = angled overview",
70
+ "Orthographic/Perspective = 3D-ready semantic views",
71
+ ],
68
72
  },
69
73
  "defaults-scale": {
70
74
  description: "Chooses the overall spacing/style preset used by the renderer.",
@@ -76,15 +80,35 @@ const FIELD_HELP = {
76
80
  },
77
81
  "viewpoint-projection": {
78
82
  description: "Overrides the projection for this saved viewpoint.",
79
- references: ["Topdown = flat orbital map", "Isometric = angled scene"],
83
+ references: [
84
+ "Topdown = flat orbital map",
85
+ "Isometric = angled scene",
86
+ "Orthographic/Perspective = stored with current 2D fallback",
87
+ ],
80
88
  },
81
89
  "viewpoint-zoom": {
82
90
  description: "Controls how closely this viewpoint frames the system.",
83
91
  references: ["1 = scene fit", "2+ = close-up"],
84
92
  },
85
93
  "viewpoint-rotation": {
86
- description: "Rotates the saved camera angle in degrees.",
87
- references: ["90deg = quarter turn", "180deg = flip"],
94
+ description: "Legacy 2D screen rotation. This is separate from the Schema 2.5 camera block.",
95
+ references: ["90deg = quarter turn", "Use camera.azimuth for semantic view direction"],
96
+ },
97
+ "viewpoint-camera-azimuth": {
98
+ description: "Horizontal camera direction in degrees for Schema 2.5 viewpoints.",
99
+ references: ["0 = forward/default", "90 = quarter orbit around the scene"],
100
+ },
101
+ "viewpoint-camera-elevation": {
102
+ description: "Vertical camera tilt in degrees for 3D-ready viewpoints.",
103
+ references: ["0 = level", "30 = gentle look down"],
104
+ },
105
+ "viewpoint-camera-roll": {
106
+ description: "Rolls the camera around its forward axis.",
107
+ references: ["0 = upright", "15 = slight bank"],
108
+ },
109
+ "viewpoint-camera-distance": {
110
+ description: "Semantic camera distance for perspective viewpoints.",
111
+ references: ["4 = close", "12 = wide framing"],
88
112
  },
89
113
  "viewpoint-events": {
90
114
  description: "Lists event IDs that this viewpoint should feature in its detail panel.",
@@ -110,6 +134,14 @@ const FIELD_HELP = {
110
134
  description: "Notes where or how the event is visible.",
111
135
  references: ['"Visible from Naar"', '"Southern hemisphere only"'],
112
136
  },
137
+ "event-epoch": {
138
+ description: "Optional event-wide epoch that event poses inherit unless they override it.",
139
+ references: ['"JY-0001.0"', '"Naar bloom cycle year 18"'],
140
+ },
141
+ "event-referencePlane": {
142
+ description: "Optional event-wide reference plane for all poses in this snapshot.",
143
+ references: ["ecliptic", "naar-equatorial"],
144
+ },
113
145
  "event-viewpoints": {
114
146
  description: "Viewpoint IDs that should list this event prominently.",
115
147
  references: ["naar-system", "overview inner-system"],
@@ -150,6 +182,14 @@ const FIELD_HELP = {
150
182
  description: "Starting position of the object along its orbit.",
151
183
  references: ["0deg = start position", "180deg = opposite side"],
152
184
  },
185
+ "pose-epoch": {
186
+ description: "Overrides the effective epoch for this pose only.",
187
+ references: ['"JY-0001.0"', "Falls back to event, object, then system"],
188
+ },
189
+ "pose-referencePlane": {
190
+ description: "Overrides the effective reference plane for this pose only.",
191
+ references: ["naar-equatorial", "Falls back to event, object, then system"],
192
+ },
153
193
  "prop-radius": {
154
194
  description: "Visual body size or real-world-inspired radius value.",
155
195
  references: ["1re = Earth radius", "1sol = Sun radius"],
@@ -206,6 +246,7 @@ export function createWorldOrbitEditor(container, options = {}) {
206
246
  const showInspector = options.showInspector ?? true;
207
247
  const showPreview = options.showPreview ?? true;
208
248
  const shortcutsEnabled = options.shortcuts ?? true;
249
+ let viewMode = options.viewMode ?? "2d";
209
250
  container.classList.add("wo-editor");
210
251
  container.dataset.woShowInspector = String(showInspector);
211
252
  container.dataset.woShowTextPane = String(showTextPane);
@@ -231,6 +272,7 @@ export function createWorldOrbitEditor(container, options = {}) {
231
272
  height: options.viewerHeight ?? 680,
232
273
  preset: atlasDocument.system?.defaults.preset ?? "atlas-card",
233
274
  projection: "document",
275
+ viewMode,
234
276
  minimap: true,
235
277
  tooltipMode: "hover",
236
278
  onSelectionChange(selectedObject) {
@@ -297,6 +339,22 @@ export function createWorldOrbitEditor(container, options = {}) {
297
339
  getSelection() {
298
340
  return selection ? { path: { ...selection } } : null;
299
341
  },
342
+ getViewMode() {
343
+ return viewMode;
344
+ },
345
+ setViewMode(mode) {
346
+ const previousViewMode = viewMode;
347
+ try {
348
+ viewer.setViewMode(mode);
349
+ viewMode = mode;
350
+ renderStageOverlay();
351
+ renderPreviewNow();
352
+ }
353
+ catch (error) {
354
+ viewMode = previousViewMode;
355
+ throw error;
356
+ }
357
+ },
300
358
  isDirty() {
301
359
  return dirty;
302
360
  },
@@ -348,6 +406,8 @@ export function createWorldOrbitEditor(container, options = {}) {
348
406
  participantObjectIds: [],
349
407
  timing: null,
350
408
  visibility: null,
409
+ epoch: null,
410
+ referencePlane: null,
351
411
  tags: [],
352
412
  color: null,
353
413
  hidden: false,
@@ -372,6 +432,7 @@ export function createWorldOrbitEditor(container, options = {}) {
372
432
  preset: atlasDocument.system?.defaults.preset ?? null,
373
433
  zoom: null,
374
434
  rotationDeg: 0,
435
+ camera: null,
375
436
  layers: {},
376
437
  filter: null,
377
438
  };
@@ -418,7 +479,7 @@ export function createWorldOrbitEditor(container, options = {}) {
418
479
  return viewer.exportSvg();
419
480
  },
420
481
  exportEmbedMarkup() {
421
- return buildEmbedMarkup(getCurrentSourceForExport(), atlasDocument);
482
+ return buildEmbedMarkup(getCurrentSourceForExport(), atlasDocument, viewMode);
422
483
  },
423
484
  destroy() {
424
485
  if (destroyed) {
@@ -863,6 +924,9 @@ export function createWorldOrbitEditor(container, options = {}) {
863
924
  return;
864
925
  }
865
926
  overlay.innerHTML = "";
927
+ if (viewMode === "3d") {
928
+ return;
929
+ }
866
930
  const selectedObjectId = selection?.kind === "object"
867
931
  ? selection.id ?? null
868
932
  : selection?.kind === "event-pose"
@@ -1335,6 +1399,7 @@ export function createWorldOrbitEditor(container, options = {}) {
1335
1399
  null,
1336
1400
  zoom: parseNullableNumber(readOptionalTextInput(form, "viewpoint-zoom")),
1337
1401
  rotationDeg: parseNullableNumber(readOptionalTextInput(form, "viewpoint-rotation")) ?? 0,
1402
+ camera: buildViewCameraFromForm(form),
1338
1403
  layers: {
1339
1404
  background: readCheckbox(form, "layer-background"),
1340
1405
  guides: readCheckbox(form, "layer-guides"),
@@ -1380,6 +1445,8 @@ export function createWorldOrbitEditor(container, options = {}) {
1380
1445
  participantObjectIds: splitTokens(readOptionalTextInput(form, "event-participants")),
1381
1446
  timing: readOptionalTextInput(form, "event-timing"),
1382
1447
  visibility: readOptionalTextInput(form, "event-visibility"),
1448
+ epoch: readOptionalTextInput(form, "event-epoch"),
1449
+ referencePlane: readOptionalTextInput(form, "event-referencePlane"),
1383
1450
  tags: splitTokens(readOptionalTextInput(form, "event-tags")),
1384
1451
  color: readOptionalTextInput(form, "event-color"),
1385
1452
  hidden: readCheckbox(form, "event-hidden"),
@@ -1406,6 +1473,8 @@ export function createWorldOrbitEditor(container, options = {}) {
1406
1473
  const replacement = {
1407
1474
  objectId: nextObjectId,
1408
1475
  placement: buildPlacementFromPoseForm(form, currentPose),
1476
+ epoch: readOptionalTextInput(form, "pose-epoch"),
1477
+ referencePlane: readOptionalTextInput(form, "pose-referencePlane"),
1409
1478
  };
1410
1479
  const inner = parseOptionalUnit(readOptionalTextInput(form, "prop-inner"));
1411
1480
  const outer = parseOptionalUnit(readOptionalTextInput(form, "prop-outer"));
@@ -1529,7 +1598,7 @@ export function createWorldOrbitEditor(container, options = {}) {
1529
1598
  previewVisual.innerHTML = nextSvg;
1530
1599
  lastPreviewSvg = nextSvg;
1531
1600
  }
1532
- const nextMarkup = buildEmbedMarkup(getCurrentSourceForExport(), atlasDocument);
1601
+ const nextMarkup = buildEmbedMarkup(getCurrentSourceForExport(), atlasDocument, viewMode);
1533
1602
  if (previewMarkup && nextMarkup !== lastPreviewMarkup) {
1534
1603
  previewMarkup.textContent = nextMarkup;
1535
1604
  lastPreviewMarkup = nextMarkup;
@@ -1806,6 +1875,8 @@ function renderDefaultsInspector(formState) {
1806
1875
  ${renderInspectorSection("defaults", "basics", "Basics", `${renderSelectField("Projection", "defaults-view", [
1807
1876
  ["topdown", "Topdown"],
1808
1877
  ["isometric", "Isometric"],
1878
+ ["orthographic", "Orthographic"],
1879
+ ["perspective", "Perspective"],
1809
1880
  ], defaults?.view ?? "topdown")}
1810
1881
  ${renderTextField("Scale preset", "defaults-scale", defaults?.scale ?? "")}
1811
1882
  ${renderTextField("Units", "defaults-units", defaults?.units ?? "")}
@@ -1842,6 +1913,8 @@ function renderViewpointInspector(formState, id) {
1842
1913
  ${renderSelectField("Projection", "viewpoint-projection", [
1843
1914
  ["topdown", "Topdown"],
1844
1915
  ["isometric", "Isometric"],
1916
+ ["orthographic", "Orthographic"],
1917
+ ["perspective", "Perspective"],
1845
1918
  ], viewpoint.projection)}
1846
1919
  ${renderSelectField("Preset", "viewpoint-preset", [
1847
1920
  ["", "Document default"],
@@ -1852,6 +1925,11 @@ function renderViewpointInspector(formState, id) {
1852
1925
  ], viewpoint.preset ?? "")}
1853
1926
  ${renderTextField("Zoom", "viewpoint-zoom", viewpoint.zoom === null ? "" : String(viewpoint.zoom))}
1854
1927
  ${renderTextField("Rotation", "viewpoint-rotation", String(viewpoint.rotationDeg))}`, true)}
1928
+ ${renderInspectorSection("viewpoint", "camera", "Camera", `${renderTextField("Azimuth", "viewpoint-camera-azimuth", viewpoint.camera?.azimuth === null || viewpoint.camera?.azimuth === undefined ? "" : String(viewpoint.camera.azimuth))}
1929
+ ${renderTextField("Elevation", "viewpoint-camera-elevation", viewpoint.camera?.elevation === null || viewpoint.camera?.elevation === undefined ? "" : String(viewpoint.camera.elevation))}
1930
+ ${renderTextField("Roll", "viewpoint-camera-roll", viewpoint.camera?.roll === null || viewpoint.camera?.roll === undefined ? "" : String(viewpoint.camera.roll))}
1931
+ ${renderTextField("Distance", "viewpoint-camera-distance", viewpoint.camera?.distance === null || viewpoint.camera?.distance === undefined ? "" : String(viewpoint.camera.distance))}
1932
+ <p class="wo-editor-inline-note">Rotation stays a 2D screen-rotation hint. The camera block stores Schema 2.5 view direction and framing.</p>`)}
1855
1933
  ${renderInspectorSection("viewpoint", "layers", "Layers", `<fieldset class="wo-editor-fieldset">
1856
1934
  <legend>Layers</legend>
1857
1935
  ${renderCheckboxField("Background", "layer-background", viewpoint.layers.background !== false)}
@@ -1889,6 +1967,8 @@ function renderEventInspector(formState, id) {
1889
1967
  ${renderTextField("Participants", "event-participants", eventEntry.participantObjectIds.join(" "))}
1890
1968
  ${renderTextField("Timing", "event-timing", eventEntry.timing ?? "")}
1891
1969
  ${renderTextField("Visibility", "event-visibility", eventEntry.visibility ?? "")}
1970
+ ${renderTextField("Epoch", "event-epoch", eventEntry.epoch ?? "")}
1971
+ ${renderTextField("Reference plane", "event-referencePlane", eventEntry.referencePlane ?? "")}
1892
1972
  ${renderTextField("Tags", "event-tags", eventEntry.tags.join(" "))}
1893
1973
  ${renderTextField("Color", "event-color", eventEntry.color ?? "")}
1894
1974
  ${renderCheckboxField("Hidden", "event-hidden", eventEntry.hidden === true)}`, true)}
@@ -1943,6 +2023,9 @@ function renderEventPoseInspector(formState, eventId, objectId) {
1943
2023
  ${renderTextField("Phase", "placement-phase", pose.placement?.mode === "orbit" && pose.placement.phase ? formatUnitValue(pose.placement.phase) : "")}
1944
2024
  ${renderTextField("Inner", "prop-inner", pose.inner ? formatUnitValue(pose.inner) : "")}
1945
2025
  ${renderTextField("Outer", "prop-outer", pose.outer ? formatUnitValue(pose.outer) : "")}`, true)}
2026
+ ${renderInspectorSection("event-pose", "context", "Context", `${renderTextField("Epoch", "pose-epoch", pose.epoch ?? "")}
2027
+ ${renderTextField("Reference plane", "pose-referencePlane", pose.referencePlane ?? "")}
2028
+ <p class="wo-editor-inline-note">Falls back to event, then object, then system context when left empty.</p>`)}
1946
2029
  </form>`;
1947
2030
  }
1948
2031
  function renderAnnotationInspector(formState, id) {
@@ -2138,6 +2221,20 @@ function parseNullableNumber(value) {
2138
2221
  const parsed = Number(value);
2139
2222
  return Number.isFinite(parsed) ? parsed : null;
2140
2223
  }
2224
+ function buildViewCameraFromForm(form) {
2225
+ const camera = {
2226
+ azimuth: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-azimuth")),
2227
+ elevation: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-elevation")),
2228
+ roll: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-roll")),
2229
+ distance: parseNullableNumber(readOptionalTextInput(form, "viewpoint-camera-distance")),
2230
+ };
2231
+ return camera.azimuth !== null ||
2232
+ camera.elevation !== null ||
2233
+ camera.roll !== null ||
2234
+ camera.distance !== null
2235
+ ? camera
2236
+ : null;
2237
+ }
2141
2238
  function parseObjectTypes(value) {
2142
2239
  const tokens = splitTokens(value);
2143
2240
  return tokens.filter((token) => OBJECT_TYPES.includes(token));
@@ -2746,6 +2843,21 @@ function mapDiagnosticFieldToInputNames(selection, field) {
2746
2843
  return ["viewpoint-zoom"];
2747
2844
  case "rotationDeg":
2748
2845
  return ["viewpoint-rotation"];
2846
+ case "camera":
2847
+ return [
2848
+ "viewpoint-camera-azimuth",
2849
+ "viewpoint-camera-elevation",
2850
+ "viewpoint-camera-roll",
2851
+ "viewpoint-camera-distance",
2852
+ ];
2853
+ case "camera.azimuth":
2854
+ return ["viewpoint-camera-azimuth"];
2855
+ case "camera.elevation":
2856
+ return ["viewpoint-camera-elevation"];
2857
+ case "camera.roll":
2858
+ return ["viewpoint-camera-roll"];
2859
+ case "camera.distance":
2860
+ return ["viewpoint-camera-distance"];
2749
2861
  case "events":
2750
2862
  return ["viewpoint-events"];
2751
2863
  default:
@@ -2771,6 +2883,10 @@ function mapDiagnosticFieldToInputNames(selection, field) {
2771
2883
  return ["event-timing"];
2772
2884
  case "visibility":
2773
2885
  return ["event-visibility"];
2886
+ case "epoch":
2887
+ return ["event-epoch"];
2888
+ case "referencePlane":
2889
+ return ["event-referencePlane"];
2774
2890
  case "tags":
2775
2891
  return ["event-tags"];
2776
2892
  case "color":
@@ -2799,6 +2915,12 @@ function mapDiagnosticFieldToInputNames(selection, field) {
2799
2915
  if (field === "inner" || field === "outer") {
2800
2916
  return [`prop-${field}`];
2801
2917
  }
2918
+ if (field === "epoch") {
2919
+ return ["pose-epoch"];
2920
+ }
2921
+ if (field === "referencePlane") {
2922
+ return ["pose-referencePlane"];
2923
+ }
2802
2924
  return [];
2803
2925
  case "annotation":
2804
2926
  switch (field) {
@@ -2868,9 +2990,9 @@ function mapDiagnosticFieldToInputNames(selection, field) {
2868
2990
  return [`prop-${field}`];
2869
2991
  }
2870
2992
  }
2871
- function buildEmbedMarkup(source, document) {
2993
+ function buildEmbedMarkup(source, document, viewMode) {
2872
2994
  return renderWorldOrbitBlock(source, {
2873
- mode: "interactive",
2995
+ mode: viewMode === "3d" ? "interactive-3d" : "interactive-2d",
2874
2996
  preset: document.system?.defaults.preset ?? "atlas-card",
2875
2997
  projection: document.system?.defaults.view ?? "topdown",
2876
2998
  });
@@ -1,4 +1,5 @@
1
1
  import type { AtlasDocumentPath, AtlasResolvedDiagnostic, WorldOrbitAtlasDocument, WorldOrbitEvent, WorldOrbitAtlasSystem, WorldOrbitAtlasViewpoint, WorldOrbitObject } from "@worldorbit/core";
2
+ import type { WorldOrbitViewMode } from "@worldorbit/viewer";
2
3
  export interface WorldOrbitEditorSelection {
3
4
  path: AtlasDocumentPath | null;
4
5
  }
@@ -16,6 +17,7 @@ export interface WorldOrbitEditorOptions {
16
17
  showPreview?: boolean;
17
18
  viewerWidth?: number;
18
19
  viewerHeight?: number;
20
+ viewMode?: WorldOrbitViewMode;
19
21
  shortcuts?: boolean;
20
22
  onChange?: (snapshot: WorldOrbitEditorSnapshot) => void;
21
23
  onDiagnosticsChange?: (diagnostics: AtlasResolvedDiagnostic[]) => void;
@@ -29,6 +31,8 @@ export interface WorldOrbitEditor {
29
31
  getAtlasDocument(): WorldOrbitAtlasDocument;
30
32
  getDiagnostics(): AtlasResolvedDiagnostic[];
31
33
  getSelection(): WorldOrbitEditorSelection | null;
34
+ getViewMode(): WorldOrbitViewMode;
35
+ setViewMode(mode: WorldOrbitViewMode): void;
32
36
  isDirty(): boolean;
33
37
  markSaved(): void;
34
38
  selectPath(path: AtlasDocumentPath | null): void;
@@ -1,11 +1,17 @@
1
- import { loadWorldOrbitSource, renderDocumentToScene, } from "@worldorbit/core";
1
+ import { loadWorldOrbitSource, renderDocumentToScene, renderDocumentToSpatialScene, } from "@worldorbit/core";
2
2
  import { createEmbedPayload, createWorldOrbitEmbedMarkup, renderSceneToSvg, } from "@worldorbit/viewer";
3
3
  export function renderWorldOrbitBlock(source, options = {}) {
4
4
  try {
5
5
  const loaded = loadWorldOrbitSource(source);
6
6
  const scene = renderDocumentToScene(loaded.document, resolveSourceRenderOptions(loaded, options));
7
- if ((options.mode ?? "static") === "interactive") {
8
- return createWorldOrbitEmbedMarkup(createEmbedPayload(scene, "interactive", {
7
+ const mode = options.mode ?? "static";
8
+ if (mode === "interactive" || mode === "interactive-2d" || mode === "interactive-3d") {
9
+ const normalizedMode = mode === "interactive" ? "interactive-2d" : mode;
10
+ return createWorldOrbitEmbedMarkup(createEmbedPayload(scene, normalizedMode, {
11
+ spatialScene: normalizedMode === "interactive-3d"
12
+ ? renderDocumentToSpatialScene(loaded.document, resolveSourceRenderOptions(loaded, options))
13
+ : undefined,
14
+ viewMode: normalizedMode === "interactive-3d" ? "3d" : "2d",
9
15
  initialViewpointId: options.initialViewpointId,
10
16
  initialSelectionObjectId: options.initialSelectionObjectId,
11
17
  initialFilter: options.initialFilter ?? null,
@@ -17,6 +23,7 @@ export function renderWorldOrbitBlock(source, options = {}) {
17
23
  layers: options.layers,
18
24
  subtitle: options.subtitle,
19
25
  preset: options.preset,
26
+ viewMode: normalizedMode === "interactive-3d" ? "3d" : "2d",
20
27
  initialViewpointId: options.initialViewpointId,
21
28
  initialSelectionObjectId: options.initialSelectionObjectId,
22
29
  initialFilter: options.initialFilter ?? null,
@@ -62,16 +62,18 @@ export function searchSceneObjects(scene, query, limit = 12) {
62
62
  }
63
63
  export function createAtlasStateSnapshot(viewerState, renderOptions, filter, viewpointId) {
64
64
  return {
65
- version: "2.0",
65
+ version: "2.5",
66
66
  viewpointId,
67
67
  activeEventId: renderOptions.activeEventId ?? null,
68
68
  viewerState: { ...viewerState },
69
69
  renderOptions: {
70
70
  preset: renderOptions.preset,
71
71
  projection: renderOptions.projection,
72
+ camera: renderOptions.camera ? { ...renderOptions.camera } : null,
72
73
  layers: renderOptions.layers ? { ...renderOptions.layers } : undefined,
73
74
  scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : undefined,
74
75
  activeEventId: renderOptions.activeEventId ?? null,
76
+ viewMode: renderOptions.viewMode ?? "2d",
75
77
  },
76
78
  filter: normalizeViewerFilter(filter),
77
79
  };
@@ -82,7 +84,7 @@ export function serializeViewerAtlasState(state) {
82
84
  export function deserializeViewerAtlasState(serialized) {
83
85
  const raw = JSON.parse(decodeURIComponent(serialized));
84
86
  return {
85
- version: "2.0",
87
+ version: raw.version === "2.0" ? "2.0" : "2.5",
86
88
  viewpointId: raw.viewpointId ?? null,
87
89
  activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
88
90
  viewerState: {
@@ -95,11 +97,13 @@ export function deserializeViewerAtlasState(serialized) {
95
97
  renderOptions: {
96
98
  preset: raw.renderOptions?.preset,
97
99
  projection: raw.renderOptions?.projection,
100
+ camera: raw.renderOptions?.camera ? { ...raw.renderOptions.camera } : null,
98
101
  layers: raw.renderOptions?.layers ? { ...raw.renderOptions.layers } : undefined,
99
102
  scaleModel: raw.renderOptions?.scaleModel
100
103
  ? { ...raw.renderOptions.scaleModel }
101
104
  : undefined,
102
105
  activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
106
+ viewMode: raw.renderOptions?.viewMode ?? "2d",
103
107
  },
104
108
  filter: normalizeViewerFilter(raw.filter ?? null),
105
109
  };
@@ -117,11 +121,13 @@ export function createViewerBookmark(name, label, atlasState) {
117
121
  viewerState: { ...atlasState.viewerState },
118
122
  renderOptions: {
119
123
  ...atlasState.renderOptions,
124
+ camera: atlasState.renderOptions.camera ? { ...atlasState.renderOptions.camera } : null,
120
125
  layers: atlasState.renderOptions.layers ? { ...atlasState.renderOptions.layers } : undefined,
121
126
  scaleModel: atlasState.renderOptions.scaleModel
122
127
  ? { ...atlasState.renderOptions.scaleModel }
123
128
  : undefined,
124
129
  activeEventId: atlasState.renderOptions.activeEventId ?? null,
130
+ viewMode: atlasState.renderOptions.viewMode ?? "2d",
125
131
  },
126
132
  filter: atlasState.filter ? { ...atlasState.filter } : null,
127
133
  },
@@ -326,6 +326,8 @@ export function createAtlasViewer(container, options) {
326
326
  }
327
327
  function buildInspectorSnapshot() {
328
328
  const activeViewer = requireViewer();
329
+ const scene = activeViewer.getScene();
330
+ const camera = scene.camera;
329
331
  return {
330
332
  selection: activeViewer.getSelectionDetails(),
331
333
  activeViewpoint: activeViewer.getActiveViewpoint(),
@@ -333,14 +335,23 @@ export function createAtlasViewer(container, options) {
333
335
  atlasState: activeViewer.getAtlasState(),
334
336
  visibleObjectIds: activeViewer.getVisibleObjects().map((object) => object.objectId),
335
337
  scene: {
336
- title: activeViewer.getScene().title,
337
- projection: activeViewer.getScene().projection,
338
- renderPreset: activeViewer.getScene().renderPreset,
339
- groupCount: activeViewer.getScene().groups.length,
340
- semanticGroupCount: activeViewer.getScene().semanticGroups.length,
341
- relationCount: activeViewer.getScene().relations.length,
342
- eventCount: activeViewer.getScene().events.length,
343
- viewpointCount: activeViewer.getScene().viewpoints.length,
338
+ title: scene.title,
339
+ projection: scene.projection,
340
+ renderProjection: scene.renderProjection,
341
+ camera: camera
342
+ ? {
343
+ azimuth: camera.azimuth,
344
+ elevation: camera.elevation,
345
+ roll: camera.roll,
346
+ distance: camera.distance,
347
+ }
348
+ : null,
349
+ renderPreset: scene.renderPreset,
350
+ groupCount: scene.groups.length,
351
+ semanticGroupCount: scene.semanticGroups.length,
352
+ relationCount: scene.relations.length,
353
+ eventCount: scene.events.length,
354
+ viewpointCount: scene.viewpoints.length,
344
355
  },
345
356
  };
346
357
  }
@@ -469,6 +480,7 @@ function cloneBookmark(bookmark) {
469
480
  scaleModel: bookmark.atlasState.renderOptions.scaleModel
470
481
  ? { ...bookmark.atlasState.renderOptions.scaleModel }
471
482
  : undefined,
483
+ viewMode: bookmark.atlasState.renderOptions.viewMode ?? "2d",
472
484
  },
473
485
  filter: bookmark.atlasState.filter ? { ...bookmark.atlasState.filter } : null,
474
486
  },
@@ -1,4 +1,6 @@
1
1
  import { loadWorldOrbitSource, renderDocumentToScene, } from "@worldorbit/core";
2
+ import { WorldOrbit3DUnavailableError } from "./errors.js";
3
+ import { render3DUnavailableMarkup } from "./embed.js";
2
4
  import { renderSceneToSvg } from "./render.js";
3
5
  import { createAtlasViewer } from "./atlas-viewer.js";
4
6
  import { createInteractiveViewer } from "./viewer.js";
@@ -51,10 +53,22 @@ export function defineWorldOrbitViewerElement(tagName = "worldorbit-viewer") {
51
53
  });
52
54
  return;
53
55
  }
54
- this.viewer = createInteractiveViewer(this, {
55
- scene,
56
- theme,
57
- });
56
+ try {
57
+ this.viewer = createInteractiveViewer(this, {
58
+ source,
59
+ theme,
60
+ viewMode: mode === "interactive-3d"
61
+ ? "3d"
62
+ : "2d",
63
+ });
64
+ }
65
+ catch (error) {
66
+ if (error instanceof WorldOrbit3DUnavailableError && mode === "interactive-3d") {
67
+ this.innerHTML = render3DUnavailableMarkup(error.message);
68
+ return;
69
+ }
70
+ throw error;
71
+ }
58
72
  }
59
73
  }
60
74
  customElements.define(tagName, WorldOrbitViewerElement);
@@ -1,8 +1,10 @@
1
- import type { RenderScene } from "@worldorbit/core";
1
+ import type { RenderScene, SpatialScene } from "@worldorbit/core";
2
2
  import type { MountedWorldOrbitEmbeds, MountWorldOrbitEmbedsOptions, ViewerAtlasState, ViewerFilter, WorldOrbitEmbedPayload } from "./types.js";
3
3
  export declare function serializeWorldOrbitEmbedPayload(payload: WorldOrbitEmbedPayload): string;
4
4
  export declare function deserializeWorldOrbitEmbedPayload(serialized: string): WorldOrbitEmbedPayload;
5
5
  export declare function createEmbedPayload(scene: RenderScene, mode: WorldOrbitEmbedPayload["mode"], options?: {
6
+ spatialScene?: SpatialScene;
7
+ viewMode?: "2d" | "3d";
6
8
  initialViewpointId?: string;
7
9
  initialSelectionObjectId?: string;
8
10
  initialFilter?: ViewerFilter | null;
@@ -11,6 +13,7 @@ export declare function createEmbedPayload(scene: RenderScene, mode: WorldOrbitE
11
13
  }): WorldOrbitEmbedPayload;
12
14
  export declare function createWorldOrbitEmbedMarkup(payload: WorldOrbitEmbedPayload, options?: Pick<MountWorldOrbitEmbedsOptions, "theme" | "layers" | "subtitle" | "preset"> & {
13
15
  className?: string;
16
+ viewMode?: "2d" | "3d";
14
17
  initialViewpointId?: string;
15
18
  initialSelectionObjectId?: string;
16
19
  initialFilter?: ViewerFilter | null;
@@ -18,3 +21,4 @@ export declare function createWorldOrbitEmbedMarkup(payload: WorldOrbitEmbedPayl
18
21
  minimap?: boolean;
19
22
  }): string;
20
23
  export declare function mountWorldOrbitEmbeds(root?: ParentNode, options?: MountWorldOrbitEmbedsOptions): MountedWorldOrbitEmbeds;
24
+ export declare function render3DUnavailableMarkup(message: string): string;