worldorbit 2.5.2

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 (113) hide show
  1. package/LICENSE.md +5 -0
  2. package/README.md +250 -0
  3. package/dist/browser/core/dist/index.js +4009 -0
  4. package/dist/browser/markdown/dist/index.js +3951 -0
  5. package/dist/browser/viewer/dist/index.js +5981 -0
  6. package/dist/constants.d.ts +8 -0
  7. package/dist/constants.js +84 -0
  8. package/dist/errors.d.ts +7 -0
  9. package/dist/errors.js +16 -0
  10. package/dist/index.d.ts +18 -0
  11. package/dist/index.js +25 -0
  12. package/dist/normalize.d.ts +2 -0
  13. package/dist/normalize.js +243 -0
  14. package/dist/parse.d.ts +2 -0
  15. package/dist/parse.js +126 -0
  16. package/dist/render.d.ts +6 -0
  17. package/dist/render.js +683 -0
  18. package/dist/tokenize.d.ts +4 -0
  19. package/dist/tokenize.js +68 -0
  20. package/dist/types.d.ts +208 -0
  21. package/dist/types.js +1 -0
  22. package/dist/unpkg/core/dist/index.js +4081 -0
  23. package/dist/unpkg/markdown/dist/index.js +3979 -0
  24. package/dist/unpkg/test.html +1 -0
  25. package/dist/unpkg/viewer/dist/index.js +6038 -0
  26. package/dist/unpkg/worldorbit-core.min.js +5 -0
  27. package/dist/unpkg/worldorbit-markdown.min.js +81 -0
  28. package/dist/unpkg/worldorbit-viewer.min.js +232 -0
  29. package/dist/unpkg/worldorbit.d.ts +2 -0
  30. package/dist/unpkg/worldorbit.js +2 -0
  31. package/dist/unpkg/worldorbit.min.js +236 -0
  32. package/dist/validate.d.ts +2 -0
  33. package/dist/validate.js +31 -0
  34. package/dist/viewer-state.d.ts +16 -0
  35. package/dist/viewer-state.js +130 -0
  36. package/dist/viewer.d.ts +2 -0
  37. package/dist/viewer.js +434 -0
  38. package/package.json +64 -0
  39. package/packages/core/README.md +13 -0
  40. package/packages/core/dist/atlas-edit.d.ts +11 -0
  41. package/packages/core/dist/atlas-edit.js +210 -0
  42. package/packages/core/dist/diagnostics.d.ts +10 -0
  43. package/packages/core/dist/diagnostics.js +109 -0
  44. package/packages/core/dist/draft-parse.d.ts +3 -0
  45. package/packages/core/dist/draft-parse.js +642 -0
  46. package/packages/core/dist/draft.d.ts +15 -0
  47. package/packages/core/dist/draft.js +343 -0
  48. package/packages/core/dist/errors.d.ts +7 -0
  49. package/packages/core/dist/errors.js +16 -0
  50. package/packages/core/dist/format.d.ts +4 -0
  51. package/packages/core/dist/format.js +364 -0
  52. package/packages/core/dist/index.d.ts +28 -0
  53. package/packages/core/dist/index.js +44 -0
  54. package/packages/core/dist/load.d.ts +4 -0
  55. package/packages/core/dist/load.js +130 -0
  56. package/packages/core/dist/markdown.d.ts +2 -0
  57. package/packages/core/dist/markdown.js +37 -0
  58. package/packages/core/dist/normalize.d.ts +2 -0
  59. package/packages/core/dist/normalize.js +304 -0
  60. package/packages/core/dist/parse.d.ts +2 -0
  61. package/packages/core/dist/parse.js +133 -0
  62. package/packages/core/dist/scene.d.ts +3 -0
  63. package/packages/core/dist/scene.js +1484 -0
  64. package/packages/core/dist/schema.d.ts +8 -0
  65. package/packages/core/dist/schema.js +298 -0
  66. package/packages/core/dist/tokenize.d.ts +4 -0
  67. package/packages/core/dist/tokenize.js +68 -0
  68. package/packages/core/dist/types.d.ts +382 -0
  69. package/packages/core/dist/types.js +1 -0
  70. package/packages/core/dist/validate.d.ts +2 -0
  71. package/packages/core/dist/validate.js +56 -0
  72. package/packages/editor/dist/editor.d.ts +2 -0
  73. package/packages/editor/dist/editor.js +2620 -0
  74. package/packages/editor/dist/index.d.ts +2 -0
  75. package/packages/editor/dist/index.js +1 -0
  76. package/packages/editor/dist/types.d.ts +53 -0
  77. package/packages/editor/dist/types.js +1 -0
  78. package/packages/markdown/README.md +9 -0
  79. package/packages/markdown/dist/html.d.ts +3 -0
  80. package/packages/markdown/dist/html.js +57 -0
  81. package/packages/markdown/dist/index.d.ts +4 -0
  82. package/packages/markdown/dist/index.js +3 -0
  83. package/packages/markdown/dist/rehype.d.ts +10 -0
  84. package/packages/markdown/dist/rehype.js +49 -0
  85. package/packages/markdown/dist/remark.d.ts +9 -0
  86. package/packages/markdown/dist/remark.js +28 -0
  87. package/packages/markdown/dist/types.d.ts +11 -0
  88. package/packages/markdown/dist/types.js +1 -0
  89. package/packages/viewer/README.md +12 -0
  90. package/packages/viewer/dist/atlas-state.d.ts +12 -0
  91. package/packages/viewer/dist/atlas-state.js +251 -0
  92. package/packages/viewer/dist/atlas-viewer.d.ts +2 -0
  93. package/packages/viewer/dist/atlas-viewer.js +448 -0
  94. package/packages/viewer/dist/custom-element.d.ts +1 -0
  95. package/packages/viewer/dist/custom-element.js +64 -0
  96. package/packages/viewer/dist/embed.d.ts +20 -0
  97. package/packages/viewer/dist/embed.js +138 -0
  98. package/packages/viewer/dist/index.d.ts +9 -0
  99. package/packages/viewer/dist/index.js +8 -0
  100. package/packages/viewer/dist/minimap.d.ts +3 -0
  101. package/packages/viewer/dist/minimap.js +63 -0
  102. package/packages/viewer/dist/render.d.ts +6 -0
  103. package/packages/viewer/dist/render.js +585 -0
  104. package/packages/viewer/dist/theme.d.ts +4 -0
  105. package/packages/viewer/dist/theme.js +98 -0
  106. package/packages/viewer/dist/tooltip.d.ts +3 -0
  107. package/packages/viewer/dist/tooltip.js +154 -0
  108. package/packages/viewer/dist/types.d.ts +256 -0
  109. package/packages/viewer/dist/types.js +1 -0
  110. package/packages/viewer/dist/viewer-state.d.ts +19 -0
  111. package/packages/viewer/dist/viewer-state.js +162 -0
  112. package/packages/viewer/dist/viewer.d.ts +2 -0
  113. package/packages/viewer/dist/viewer.js +1156 -0
@@ -0,0 +1,448 @@
1
+ import { normalizeViewerFilter } from "./atlas-state.js";
2
+ import { createInteractiveViewer } from "./viewer.js";
3
+ const STYLE_ID = "worldorbit-atlas-viewer-style";
4
+ export function createAtlasViewer(container, options) {
5
+ if (typeof document === "undefined") {
6
+ throw new Error("Atlas viewer requires a browser environment.");
7
+ }
8
+ installAtlasViewerStyles();
9
+ const controls = {
10
+ search: options.controls?.search ?? true,
11
+ typeFilter: options.controls?.typeFilter ?? true,
12
+ viewpointSelect: options.controls?.viewpointSelect ?? true,
13
+ inspector: options.controls?.inspector ?? true,
14
+ bookmarks: options.controls?.bookmarks ?? true,
15
+ };
16
+ container.classList.add("wo-atlas-viewer");
17
+ container.innerHTML = buildAtlasViewerMarkup(controls);
18
+ const toolbar = container.querySelector("[data-atlas-toolbar]");
19
+ const searchInput = container.querySelector("[data-atlas-search]");
20
+ const typeFilterSelect = container.querySelector("[data-atlas-type-filter]");
21
+ const viewpointSelect = container.querySelector("[data-atlas-viewpoint]");
22
+ const bookmarkButton = container.querySelector("[data-atlas-bookmark]");
23
+ const bookmarkList = container.querySelector("[data-atlas-bookmarks]");
24
+ const searchResults = container.querySelector("[data-atlas-results]");
25
+ const inspector = container.querySelector("[data-atlas-inspector]");
26
+ const stage = container.querySelector("[data-atlas-stage]");
27
+ if (!stage) {
28
+ throw new Error("Atlas viewer failed to initialize its stage container.");
29
+ }
30
+ const baseFilter = normalizeViewerFilter(options.initialFilter ?? null);
31
+ let searchQuery = options.initialQuery?.trim() ?? baseFilter?.query ?? "";
32
+ let objectTypeFilter = options.initialObjectType ??
33
+ (baseFilter?.objectTypes?.length === 1 ? baseFilter.objectTypes[0] : null);
34
+ let bookmarks = [];
35
+ let viewer;
36
+ viewer = createInteractiveViewer(stage, {
37
+ ...options,
38
+ initialFilter: null,
39
+ onSelectionChange(selection) {
40
+ if (viewer) {
41
+ updateInspector();
42
+ }
43
+ options.onSelectionChange?.(selection);
44
+ },
45
+ onSelectionDetailsChange(details) {
46
+ if (viewer) {
47
+ updateInspector();
48
+ }
49
+ options.onSelectionDetailsChange?.(details);
50
+ },
51
+ onFilterChange(filter, visibleObjects) {
52
+ if (viewer) {
53
+ syncControlsFromFilter(filter);
54
+ updateSearchResults();
55
+ updateInspector();
56
+ }
57
+ options.onFilterChange?.(filter, visibleObjects);
58
+ },
59
+ onViewpointChange(viewpoint) {
60
+ if (viewer) {
61
+ syncViewpointControl();
62
+ updateInspector();
63
+ }
64
+ options.onViewpointChange?.(viewpoint);
65
+ },
66
+ onAtlasStateChange(state) {
67
+ if (viewer) {
68
+ updateInspector();
69
+ }
70
+ options.onAtlasStateChange?.(state);
71
+ },
72
+ onViewChange(state) {
73
+ if (viewer) {
74
+ updateInspector();
75
+ }
76
+ options.onViewChange?.(state);
77
+ },
78
+ });
79
+ applyCurrentFilter();
80
+ populateViewpoints();
81
+ syncControlsFromFilter(viewer.getFilter());
82
+ renderBookmarks();
83
+ updateSearchResults();
84
+ updateInspector();
85
+ searchInput?.addEventListener("input", () => {
86
+ searchQuery = searchInput.value.trim();
87
+ applyCurrentFilter();
88
+ });
89
+ typeFilterSelect?.addEventListener("change", () => {
90
+ objectTypeFilter = (typeFilterSelect.value || null);
91
+ applyCurrentFilter();
92
+ });
93
+ viewpointSelect?.addEventListener("change", () => {
94
+ const activeViewer = requireViewer();
95
+ if (!viewpointSelect.value) {
96
+ activeViewer.resetView();
97
+ applyCurrentFilter();
98
+ return;
99
+ }
100
+ activeViewer.goToViewpoint(viewpointSelect.value);
101
+ updateInspector();
102
+ });
103
+ bookmarkButton?.addEventListener("click", () => {
104
+ const activeViewer = requireViewer();
105
+ const label = activeViewer.getActiveViewpoint()?.label ??
106
+ activeViewer.getSelectionDetails()?.objectId ??
107
+ `Bookmark ${bookmarks.length + 1}`;
108
+ bookmarks = [...bookmarks, activeViewer.captureBookmark(label, label)];
109
+ renderBookmarks();
110
+ updateInspector();
111
+ });
112
+ bookmarkList?.addEventListener("click", (event) => {
113
+ const button = event.target?.closest("[data-bookmark-id]");
114
+ if (!button) {
115
+ return;
116
+ }
117
+ const bookmark = bookmarks.find((entry) => entry.id === button.dataset.bookmarkId);
118
+ if (!bookmark) {
119
+ return;
120
+ }
121
+ const activeViewer = requireViewer();
122
+ activeViewer.applyBookmark(bookmark);
123
+ syncControlsFromFilter(activeViewer.getFilter());
124
+ updateSearchResults();
125
+ updateInspector();
126
+ });
127
+ searchResults?.addEventListener("click", (event) => {
128
+ const button = event.target?.closest("[data-object-id]");
129
+ if (!button) {
130
+ return;
131
+ }
132
+ requireViewer().focusObject(button.dataset.objectId ?? "");
133
+ updateInspector();
134
+ });
135
+ function requireViewer() {
136
+ if (!viewer) {
137
+ throw new Error("Atlas viewer is not initialized.");
138
+ }
139
+ return viewer;
140
+ }
141
+ const api = {
142
+ element: container,
143
+ get viewer() {
144
+ return requireViewer();
145
+ },
146
+ getViewer() {
147
+ return requireViewer();
148
+ },
149
+ setSource(source) {
150
+ requireViewer().setSource(source);
151
+ refreshAfterInputChange();
152
+ },
153
+ setDocument(document) {
154
+ requireViewer().setDocument(document);
155
+ refreshAfterInputChange();
156
+ },
157
+ setScene(scene) {
158
+ requireViewer().setScene(scene);
159
+ refreshAfterInputChange();
160
+ },
161
+ getAtlasState() {
162
+ return requireViewer().getAtlasState();
163
+ },
164
+ setAtlasState(state) {
165
+ const activeViewer = requireViewer();
166
+ activeViewer.setAtlasState(state);
167
+ syncControlsFromFilter(activeViewer.getFilter());
168
+ updateSearchResults();
169
+ updateInspector();
170
+ },
171
+ getInspectorSnapshot() {
172
+ return buildInspectorSnapshot();
173
+ },
174
+ getSearchQuery() {
175
+ return searchQuery;
176
+ },
177
+ setSearchQuery(query) {
178
+ searchQuery = query.trim();
179
+ if (searchInput) {
180
+ searchInput.value = searchQuery;
181
+ }
182
+ applyCurrentFilter();
183
+ },
184
+ getObjectTypeFilter() {
185
+ return objectTypeFilter;
186
+ },
187
+ setObjectTypeFilter(type) {
188
+ objectTypeFilter = type;
189
+ if (typeFilterSelect) {
190
+ typeFilterSelect.value = type ?? "";
191
+ }
192
+ applyCurrentFilter();
193
+ },
194
+ listSearchResults(limit = 6) {
195
+ return requireViewer().search(searchQuery, limit);
196
+ },
197
+ listBookmarks() {
198
+ return bookmarks.map(cloneBookmark);
199
+ },
200
+ captureBookmark(name, label) {
201
+ const bookmark = requireViewer().captureBookmark(name, label);
202
+ bookmarks = [...bookmarks, bookmark];
203
+ renderBookmarks();
204
+ updateInspector();
205
+ return cloneBookmark(bookmark);
206
+ },
207
+ applyBookmark(bookmark) {
208
+ const activeViewer = requireViewer();
209
+ const result = activeViewer.applyBookmark(bookmark);
210
+ if (result) {
211
+ syncControlsFromFilter(activeViewer.getFilter());
212
+ updateSearchResults();
213
+ updateInspector();
214
+ }
215
+ return result;
216
+ },
217
+ goToViewpoint(id) {
218
+ const result = requireViewer().goToViewpoint(id);
219
+ if (result) {
220
+ updateInspector();
221
+ }
222
+ return result;
223
+ },
224
+ exportSvg() {
225
+ return requireViewer().exportSvg();
226
+ },
227
+ destroy() {
228
+ requireViewer().destroy();
229
+ container.innerHTML = "";
230
+ container.classList.remove("wo-atlas-viewer");
231
+ },
232
+ };
233
+ return api;
234
+ function refreshAfterInputChange() {
235
+ populateViewpoints();
236
+ applyCurrentFilter();
237
+ renderBookmarks();
238
+ updateSearchResults();
239
+ updateInspector();
240
+ }
241
+ function applyCurrentFilter() {
242
+ requireViewer().setFilter(buildComposedFilter());
243
+ populateViewpoints();
244
+ updateSearchResults();
245
+ updateInspector();
246
+ }
247
+ function buildComposedFilter() {
248
+ return normalizeViewerFilter({
249
+ query: searchQuery || undefined,
250
+ objectTypes: objectTypeFilter ? [objectTypeFilter] : undefined,
251
+ tags: baseFilter?.tags,
252
+ groupIds: baseFilter?.groupIds,
253
+ includeAncestors: baseFilter?.includeAncestors ?? true,
254
+ });
255
+ }
256
+ function syncControlsFromFilter(filter) {
257
+ searchQuery = filter?.query?.trim() ?? "";
258
+ objectTypeFilter =
259
+ filter?.objectTypes?.length === 1 ? filter.objectTypes[0] : null;
260
+ if (searchInput && document.activeElement !== searchInput) {
261
+ searchInput.value = searchQuery;
262
+ }
263
+ if (typeFilterSelect) {
264
+ typeFilterSelect.value = objectTypeFilter ?? "";
265
+ }
266
+ }
267
+ function populateViewpoints() {
268
+ if (!viewpointSelect) {
269
+ return;
270
+ }
271
+ const activeViewer = requireViewer();
272
+ const active = activeViewer.getActiveViewpoint()?.id ?? "";
273
+ viewpointSelect.innerHTML = [
274
+ `<option value="">Scene default</option>`,
275
+ ...activeViewer
276
+ .listViewpoints()
277
+ .map((viewpoint) => `<option value="${escapeHtml(viewpoint.id)}">${escapeHtml(viewpoint.label)}</option>`),
278
+ ].join("");
279
+ viewpointSelect.value = active;
280
+ }
281
+ function syncViewpointControl() {
282
+ if (!viewpointSelect) {
283
+ return;
284
+ }
285
+ viewpointSelect.value = requireViewer().getActiveViewpoint()?.id ?? "";
286
+ }
287
+ function updateSearchResults() {
288
+ if (!searchResults) {
289
+ return;
290
+ }
291
+ const results = requireViewer().search(searchQuery, 6);
292
+ searchResults.innerHTML = results
293
+ .map((result) => `<button type="button" class="wo-atlas-pill" data-object-id="${escapeHtml(result.objectId)}">${escapeHtml(result.objectId)} - ${escapeHtml(result.type)}</button>`)
294
+ .join("");
295
+ }
296
+ function updateInspector() {
297
+ const snapshot = buildInspectorSnapshot();
298
+ if (inspector) {
299
+ inspector.textContent = JSON.stringify(snapshot, null, 2);
300
+ }
301
+ options.onInspectorChange?.(snapshot);
302
+ }
303
+ function buildInspectorSnapshot() {
304
+ const activeViewer = requireViewer();
305
+ return {
306
+ selection: activeViewer.getSelectionDetails(),
307
+ activeViewpoint: activeViewer.getActiveViewpoint(),
308
+ filter: activeViewer.getFilter(),
309
+ atlasState: activeViewer.getAtlasState(),
310
+ visibleObjectIds: activeViewer.getVisibleObjects().map((object) => object.objectId),
311
+ scene: {
312
+ title: activeViewer.getScene().title,
313
+ projection: activeViewer.getScene().projection,
314
+ renderPreset: activeViewer.getScene().renderPreset,
315
+ groupCount: activeViewer.getScene().groups.length,
316
+ viewpointCount: activeViewer.getScene().viewpoints.length,
317
+ },
318
+ };
319
+ }
320
+ function renderBookmarks() {
321
+ if (!bookmarkList) {
322
+ return;
323
+ }
324
+ bookmarkList.innerHTML = bookmarks
325
+ .map((bookmark) => `<button type="button" class="wo-atlas-pill" data-bookmark-id="${escapeHtml(bookmark.id)}">${escapeHtml(bookmark.label)}</button>`)
326
+ .join("");
327
+ }
328
+ }
329
+ function buildAtlasViewerMarkup(controls) {
330
+ const toolbarItems = [
331
+ controls.search
332
+ ? `<label class="wo-atlas-field">
333
+ <span>Search</span>
334
+ <input data-atlas-search type="text" placeholder="Search objects, tags, or types" />
335
+ </label>`
336
+ : "",
337
+ controls.typeFilter
338
+ ? `<label class="wo-atlas-field">
339
+ <span>Type</span>
340
+ <select data-atlas-type-filter>
341
+ <option value="">All types</option>
342
+ <option value="star">Star</option>
343
+ <option value="planet">Planet</option>
344
+ <option value="moon">Moon</option>
345
+ <option value="belt">Belt</option>
346
+ <option value="asteroid">Asteroid</option>
347
+ <option value="comet">Comet</option>
348
+ <option value="ring">Ring</option>
349
+ <option value="structure">Structure</option>
350
+ <option value="phenomenon">Phenomenon</option>
351
+ </select>
352
+ </label>`
353
+ : "",
354
+ controls.viewpointSelect
355
+ ? `<label class="wo-atlas-field">
356
+ <span>Viewpoint</span>
357
+ <select data-atlas-viewpoint>
358
+ <option value="">Scene default</option>
359
+ </select>
360
+ </label>`
361
+ : "",
362
+ controls.bookmarks
363
+ ? `<button type="button" class="wo-atlas-button" data-atlas-bookmark>Save bookmark</button>`
364
+ : "",
365
+ ]
366
+ .filter(Boolean)
367
+ .join("");
368
+ return `<section class="wo-atlas-shell">
369
+ ${toolbarItems ? `<div class="wo-atlas-toolbar" data-atlas-toolbar>${toolbarItems}</div>` : ""}
370
+ <div class="wo-atlas-workspace">
371
+ <div class="wo-atlas-stage" data-atlas-stage></div>
372
+ ${controls.inspector ? `<pre class="wo-atlas-inspector" data-atlas-inspector></pre>` : ""}
373
+ </div>
374
+ <div class="wo-atlas-footer">
375
+ <div class="wo-atlas-results" data-atlas-results></div>
376
+ ${controls.bookmarks ? `<div class="wo-atlas-bookmarks" data-atlas-bookmarks></div>` : ""}
377
+ </div>
378
+ </section>`;
379
+ }
380
+ function installAtlasViewerStyles() {
381
+ if (document.getElementById(STYLE_ID)) {
382
+ return;
383
+ }
384
+ const style = document.createElement("style");
385
+ style.id = STYLE_ID;
386
+ style.textContent = `
387
+ .wo-atlas-shell { display: grid; gap: 16px; min-width: 0; }
388
+ .wo-atlas-toolbar { display: flex; gap: 12px; flex-wrap: wrap; align-items: end; }
389
+ .wo-atlas-workspace { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); }
390
+ .wo-atlas-stage { min-height: 420px; min-width: 0; }
391
+ .wo-atlas-inspector {
392
+ margin: 0;
393
+ min-height: 420px;
394
+ padding: 16px;
395
+ border-radius: 18px;
396
+ border: 1px solid rgba(255,255,255,0.08);
397
+ background: rgba(7, 16, 25, 0.72);
398
+ color: #edf6ff;
399
+ overflow: auto;
400
+ font: 12px/1.5 "Cascadia Code", "Consolas", monospace;
401
+ white-space: pre-wrap;
402
+ overflow-wrap: anywhere;
403
+ }
404
+ .wo-atlas-footer { display: grid; gap: 12px; }
405
+ .wo-atlas-results, .wo-atlas-bookmarks { display: flex; gap: 8px; flex-wrap: wrap; }
406
+ .wo-atlas-field { display: grid; gap: 6px; min-width: 180px; color: #edf6ff; font: 600 12px/1.4 "Segoe UI Variable", "Segoe UI", sans-serif; text-transform: uppercase; letter-spacing: 0.08em; }
407
+ .wo-atlas-field input, .wo-atlas-field select, .wo-atlas-button, .wo-atlas-pill {
408
+ border: 1px solid rgba(240, 180, 100, 0.2);
409
+ border-radius: 999px;
410
+ background: rgba(240, 180, 100, 0.08);
411
+ color: #edf6ff;
412
+ font: 500 13px/1.4 "Segoe UI Variable", "Segoe UI", sans-serif;
413
+ padding: 10px 14px;
414
+ }
415
+ .wo-atlas-button, .wo-atlas-pill { cursor: pointer; }
416
+ @media (max-width: 1080px) {
417
+ .wo-atlas-workspace { grid-template-columns: 1fr; }
418
+ .wo-atlas-inspector { min-height: 220px; }
419
+ }
420
+ `;
421
+ document.head.append(style);
422
+ }
423
+ function cloneBookmark(bookmark) {
424
+ return {
425
+ ...bookmark,
426
+ atlasState: {
427
+ ...bookmark.atlasState,
428
+ viewerState: { ...bookmark.atlasState.viewerState },
429
+ renderOptions: {
430
+ ...bookmark.atlasState.renderOptions,
431
+ layers: bookmark.atlasState.renderOptions.layers
432
+ ? { ...bookmark.atlasState.renderOptions.layers }
433
+ : undefined,
434
+ scaleModel: bookmark.atlasState.renderOptions.scaleModel
435
+ ? { ...bookmark.atlasState.renderOptions.scaleModel }
436
+ : undefined,
437
+ },
438
+ filter: bookmark.atlasState.filter ? { ...bookmark.atlasState.filter } : null,
439
+ },
440
+ };
441
+ }
442
+ function escapeHtml(value) {
443
+ return value
444
+ .replaceAll("&", "&amp;")
445
+ .replaceAll("<", "&lt;")
446
+ .replaceAll(">", "&gt;")
447
+ .replaceAll('"', "&quot;");
448
+ }
@@ -0,0 +1 @@
1
+ export declare function defineWorldOrbitViewerElement(tagName?: string): void;
@@ -0,0 +1,64 @@
1
+ import { loadWorldOrbitSource, renderDocumentToScene, } from "@worldorbit/core";
2
+ import { renderSceneToSvg } from "./render.js";
3
+ import { createAtlasViewer } from "./atlas-viewer.js";
4
+ import { createInteractiveViewer } from "./viewer.js";
5
+ export function defineWorldOrbitViewerElement(tagName = "worldorbit-viewer") {
6
+ if (typeof window === "undefined" || typeof customElements === "undefined") {
7
+ return;
8
+ }
9
+ if (customElements.get(tagName)) {
10
+ return;
11
+ }
12
+ class WorldOrbitViewerElement extends HTMLElement {
13
+ static get observedAttributes() {
14
+ return ["source", "mode", "theme"];
15
+ }
16
+ viewer = null;
17
+ connectedCallback() {
18
+ this.renderCurrent();
19
+ }
20
+ disconnectedCallback() {
21
+ this.viewer?.destroy();
22
+ this.viewer = null;
23
+ }
24
+ attributeChangedCallback() {
25
+ if (this.isConnected) {
26
+ this.renderCurrent();
27
+ }
28
+ }
29
+ renderCurrent() {
30
+ this.viewer?.destroy();
31
+ this.viewer = null;
32
+ const source = this.getAttribute("source") ?? this.textContent ?? "";
33
+ const mode = this.getAttribute("mode") ?? "interactive";
34
+ if (!source.trim()) {
35
+ this.innerHTML = "";
36
+ return;
37
+ }
38
+ const documentModel = parseSource(source);
39
+ const scene = renderDocumentToScene(documentModel);
40
+ const theme = (this.getAttribute("theme") ?? undefined);
41
+ if (mode === "static") {
42
+ this.innerHTML = renderSceneToSvg(scene, {
43
+ theme,
44
+ });
45
+ return;
46
+ }
47
+ if (mode === "atlas") {
48
+ this.viewer = createAtlasViewer(this, {
49
+ scene,
50
+ theme,
51
+ });
52
+ return;
53
+ }
54
+ this.viewer = createInteractiveViewer(this, {
55
+ scene,
56
+ theme,
57
+ });
58
+ }
59
+ }
60
+ customElements.define(tagName, WorldOrbitViewerElement);
61
+ }
62
+ function parseSource(source) {
63
+ return loadWorldOrbitSource(source).document;
64
+ }
@@ -0,0 +1,20 @@
1
+ import type { RenderScene } from "@worldorbit/core";
2
+ import type { MountedWorldOrbitEmbeds, MountWorldOrbitEmbedsOptions, ViewerAtlasState, ViewerFilter, WorldOrbitEmbedPayload } from "./types.js";
3
+ export declare function serializeWorldOrbitEmbedPayload(payload: WorldOrbitEmbedPayload): string;
4
+ export declare function deserializeWorldOrbitEmbedPayload(serialized: string): WorldOrbitEmbedPayload;
5
+ export declare function createEmbedPayload(scene: RenderScene, mode: WorldOrbitEmbedPayload["mode"], options?: {
6
+ initialViewpointId?: string;
7
+ initialSelectionObjectId?: string;
8
+ initialFilter?: ViewerFilter | null;
9
+ atlasState?: ViewerAtlasState | null;
10
+ minimap?: boolean;
11
+ }): WorldOrbitEmbedPayload;
12
+ export declare function createWorldOrbitEmbedMarkup(payload: WorldOrbitEmbedPayload, options?: Pick<MountWorldOrbitEmbedsOptions, "theme" | "layers" | "subtitle" | "preset"> & {
13
+ className?: string;
14
+ initialViewpointId?: string;
15
+ initialSelectionObjectId?: string;
16
+ initialFilter?: ViewerFilter | null;
17
+ atlasState?: ViewerAtlasState | null;
18
+ minimap?: boolean;
19
+ }): string;
20
+ export declare function mountWorldOrbitEmbeds(root?: ParentNode, options?: MountWorldOrbitEmbedsOptions): MountedWorldOrbitEmbeds;
@@ -0,0 +1,138 @@
1
+ import { renderSceneToSvg } from "./render.js";
2
+ import { createInteractiveViewer } from "./viewer.js";
3
+ const EMBED_SELECTOR = "[data-worldorbit-embed]";
4
+ export function serializeWorldOrbitEmbedPayload(payload) {
5
+ return encodeURIComponent(JSON.stringify(payload));
6
+ }
7
+ export function deserializeWorldOrbitEmbedPayload(serialized) {
8
+ const raw = JSON.parse(decodeURIComponent(serialized));
9
+ return {
10
+ version: "2.0",
11
+ mode: raw.mode ?? "interactive",
12
+ scene: raw.scene,
13
+ options: raw.options
14
+ ? {
15
+ ...raw.options,
16
+ initialFilter: raw.options.initialFilter ?? null,
17
+ atlasState: raw.options.atlasState ?? null,
18
+ }
19
+ : undefined,
20
+ };
21
+ }
22
+ export function createEmbedPayload(scene, mode, options = {}) {
23
+ return {
24
+ version: "2.0",
25
+ mode,
26
+ scene,
27
+ options: {
28
+ initialViewpointId: options.initialViewpointId,
29
+ initialSelectionObjectId: options.initialSelectionObjectId,
30
+ initialFilter: options.initialFilter ?? null,
31
+ atlasState: options.atlasState ?? null,
32
+ minimap: options.minimap,
33
+ },
34
+ };
35
+ }
36
+ export function createWorldOrbitEmbedMarkup(payload, options = {}) {
37
+ const mergedPayload = {
38
+ ...payload,
39
+ options: {
40
+ ...payload.options,
41
+ theme: options.theme ?? payload.options?.theme,
42
+ layers: options.layers ?? payload.options?.layers,
43
+ subtitle: options.subtitle ?? payload.options?.subtitle,
44
+ preset: options.preset ?? payload.options?.preset,
45
+ initialViewpointId: options.initialViewpointId ?? payload.options?.initialViewpointId,
46
+ initialSelectionObjectId: options.initialSelectionObjectId ?? payload.options?.initialSelectionObjectId,
47
+ initialFilter: options.initialFilter ?? payload.options?.initialFilter ?? null,
48
+ atlasState: options.atlasState ?? payload.options?.atlasState ?? null,
49
+ minimap: options.minimap ?? payload.options?.minimap,
50
+ },
51
+ };
52
+ const html = renderSceneToSvg(payload.scene, {
53
+ theme: mergedPayload.options?.theme,
54
+ layers: mergedPayload.options?.layers,
55
+ filter: mergedPayload.options?.initialFilter ?? null,
56
+ selectedObjectId: mergedPayload.options?.initialSelectionObjectId ?? null,
57
+ subtitle: mergedPayload.options?.subtitle,
58
+ preset: mergedPayload.options?.preset,
59
+ });
60
+ return `<div class="${escapeAttribute(options.className ?? "worldorbit-embed")}" data-worldorbit-embed="true" data-worldorbit-mode="${payload.mode}" data-worldorbit-preset="${escapeAttribute(mergedPayload.options?.preset ?? payload.scene.renderPreset ?? "custom")}" data-worldorbit-viewpoint="${escapeAttribute(mergedPayload.options?.initialViewpointId ?? "")}" data-worldorbit-payload="${escapeAttribute(serializeWorldOrbitEmbedPayload(mergedPayload))}">${html}</div>`;
61
+ }
62
+ export function mountWorldOrbitEmbeds(root = document, options = {}) {
63
+ const viewers = new Map();
64
+ const elements = [...root.querySelectorAll(EMBED_SELECTOR)];
65
+ for (const element of elements) {
66
+ const payload = deserializePayloadFromElement(element);
67
+ const mode = options.mode ?? payload.mode;
68
+ const theme = options.theme ?? payload.options?.theme;
69
+ const layers = options.layers ?? payload.options?.layers;
70
+ const subtitle = options.subtitle ?? payload.options?.subtitle;
71
+ const preset = options.preset ?? payload.options?.preset ?? payload.scene.renderPreset ?? undefined;
72
+ const initialFilter = options.viewer?.initialFilter ?? payload.options?.initialFilter ?? null;
73
+ const initialViewpointId = options.viewer?.initialViewpointId ?? payload.options?.initialViewpointId;
74
+ const initialSelectionObjectId = options.viewer?.initialSelectionObjectId ?? payload.options?.initialSelectionObjectId;
75
+ const minimap = options.viewer?.minimap ?? payload.options?.minimap;
76
+ if (mode === "interactive") {
77
+ const viewer = createInteractiveViewer(element, {
78
+ ...options.viewer,
79
+ scene: payload.scene,
80
+ width: options.width ?? payload.scene.width,
81
+ height: options.height ?? payload.scene.height,
82
+ padding: options.padding ?? payload.scene.padding,
83
+ preset,
84
+ theme,
85
+ layers,
86
+ subtitle,
87
+ initialFilter,
88
+ initialViewpointId,
89
+ initialSelectionObjectId,
90
+ minimap,
91
+ });
92
+ if (payload.options?.atlasState) {
93
+ viewer.setAtlasState(payload.options.atlasState);
94
+ }
95
+ viewers.set(element, viewer);
96
+ options.onMount?.(viewer, element);
97
+ }
98
+ else {
99
+ element.innerHTML = renderSceneToSvg(payload.scene, {
100
+ width: options.width ?? payload.scene.width,
101
+ height: options.height ?? payload.scene.height,
102
+ padding: options.padding ?? payload.scene.padding,
103
+ preset,
104
+ theme,
105
+ layers,
106
+ filter: initialFilter,
107
+ selectedObjectId: initialSelectionObjectId ?? null,
108
+ subtitle,
109
+ });
110
+ options.onMount?.(null, element);
111
+ }
112
+ element.dataset.worldorbitMounted = "true";
113
+ }
114
+ return {
115
+ viewers: [...viewers.values()],
116
+ destroy() {
117
+ for (const [element, viewer] of viewers.entries()) {
118
+ viewer.destroy();
119
+ element.removeAttribute("data-worldorbit-mounted");
120
+ }
121
+ viewers.clear();
122
+ },
123
+ };
124
+ }
125
+ function deserializePayloadFromElement(element) {
126
+ const serialized = element.dataset.worldorbitPayload;
127
+ if (!serialized) {
128
+ throw new Error("WorldOrbit embed is missing data-worldorbit-payload.");
129
+ }
130
+ return deserializeWorldOrbitEmbedPayload(serialized);
131
+ }
132
+ function escapeAttribute(value) {
133
+ return value
134
+ .replaceAll("&", "&amp;")
135
+ .replaceAll("<", "&lt;")
136
+ .replaceAll(">", "&gt;")
137
+ .replaceAll("\"", "&quot;");
138
+ }
@@ -0,0 +1,9 @@
1
+ export type * from "./types.js";
2
+ export { getThemePreset, resolveLayers, resolveTheme } from "./theme.js";
3
+ export { deserializeViewerAtlasState, normalizeViewerFilter, sceneViewpointToLayerOptions, searchSceneObjects, serializeViewerAtlasState, viewpointToViewerFilter, } from "./atlas-state.js";
4
+ export { DEFAULT_VIEWER_STATE, clampScale, composeViewerTransform, fitViewerState, focusViewerState, getViewerVisibleBounds, invertViewerPoint, getSceneCenter, normalizeRotation, panViewerState, rotateViewerState, zoomViewerStateAt, } from "./viewer-state.js";
5
+ export { WORLD_LAYER_ID, renderDocumentToSvg, renderSceneToSvg, renderSourceToSvg, } from "./render.js";
6
+ export { createEmbedPayload, createWorldOrbitEmbedMarkup, deserializeWorldOrbitEmbedPayload, mountWorldOrbitEmbeds, serializeWorldOrbitEmbedPayload, } from "./embed.js";
7
+ export { defineWorldOrbitViewerElement } from "./custom-element.js";
8
+ export { createAtlasViewer } from "./atlas-viewer.js";
9
+ export { createInteractiveViewer } from "./viewer.js";