worldorbit 2.5.15 → 2.5.17

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 (48) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/core/dist/index.js +2444 -342
  3. package/dist/browser/editor/dist/index.js +11702 -0
  4. package/dist/browser/markdown/dist/index.js +2207 -392
  5. package/dist/browser/viewer/dist/index.js +2302 -382
  6. package/dist/unpkg/core/dist/index.js +2447 -345
  7. package/dist/unpkg/editor/dist/index.js +11727 -0
  8. package/dist/unpkg/markdown/dist/index.js +2210 -395
  9. package/dist/unpkg/viewer/dist/index.js +2305 -385
  10. package/dist/unpkg/worldorbit-core.min.js +12 -12
  11. package/dist/unpkg/worldorbit-editor.min.js +894 -0
  12. package/dist/unpkg/worldorbit-markdown.min.js +66 -58
  13. package/dist/unpkg/worldorbit-viewer.min.js +76 -68
  14. package/dist/unpkg/worldorbit.js +797 -78
  15. package/dist/unpkg/worldorbit.min.js +80 -72
  16. package/package.json +3 -2
  17. package/packages/core/dist/atlas-edit.js +74 -0
  18. package/packages/core/dist/atlas-validate.js +122 -8
  19. package/packages/core/dist/draft-parse.js +212 -8
  20. package/packages/core/dist/draft.d.ts +5 -2
  21. package/packages/core/dist/draft.js +59 -3
  22. package/packages/core/dist/format.js +63 -1
  23. package/packages/core/dist/normalize.js +1 -0
  24. package/packages/core/dist/scene.js +248 -46
  25. package/packages/core/dist/types.d.ts +41 -2
  26. package/packages/editor/dist/editor.d.ts +2 -0
  27. package/packages/editor/dist/editor.js +3578 -0
  28. package/packages/editor/dist/index.d.ts +2 -0
  29. package/packages/editor/dist/index.js +1 -0
  30. package/packages/editor/dist/types.d.ts +55 -0
  31. package/packages/editor/dist/types.js +1 -0
  32. package/packages/markdown/dist/html.d.ts +3 -0
  33. package/packages/markdown/dist/html.js +57 -0
  34. package/packages/markdown/dist/index.d.ts +4 -0
  35. package/packages/markdown/dist/index.js +3 -0
  36. package/packages/markdown/dist/rehype.d.ts +10 -0
  37. package/packages/markdown/dist/rehype.js +49 -0
  38. package/packages/markdown/dist/remark.d.ts +9 -0
  39. package/packages/markdown/dist/remark.js +28 -0
  40. package/packages/markdown/dist/types.d.ts +11 -0
  41. package/packages/markdown/dist/types.js +1 -0
  42. package/packages/viewer/dist/atlas-state.js +6 -0
  43. package/packages/viewer/dist/atlas-viewer.js +1 -0
  44. package/packages/viewer/dist/render.js +31 -2
  45. package/packages/viewer/dist/theme.js +1 -0
  46. package/packages/viewer/dist/tooltip.js +9 -0
  47. package/packages/viewer/dist/types.d.ts +8 -1
  48. package/packages/viewer/dist/viewer.js +12 -1
@@ -0,0 +1,2 @@
1
+ export type * from "./types.js";
2
+ export { createWorldOrbitEditor } from "./editor.js";
@@ -0,0 +1 @@
1
+ export { createWorldOrbitEditor } from "./editor.js";
@@ -0,0 +1,55 @@
1
+ import type { AtlasDocumentPath, AtlasResolvedDiagnostic, WorldOrbitAtlasDocument, WorldOrbitEvent, WorldOrbitAtlasSystem, WorldOrbitAtlasViewpoint, WorldOrbitObject } from "@worldorbit/core";
2
+ export interface WorldOrbitEditorSelection {
3
+ path: AtlasDocumentPath | null;
4
+ }
5
+ export interface WorldOrbitEditorSnapshot {
6
+ source: string;
7
+ atlasDocument: WorldOrbitAtlasDocument;
8
+ diagnostics: AtlasResolvedDiagnostic[];
9
+ selection: WorldOrbitEditorSelection | null;
10
+ }
11
+ export interface WorldOrbitEditorOptions {
12
+ source?: string;
13
+ atlasDocument?: WorldOrbitAtlasDocument;
14
+ showTextPane?: boolean;
15
+ showInspector?: boolean;
16
+ showPreview?: boolean;
17
+ viewerWidth?: number;
18
+ viewerHeight?: number;
19
+ shortcuts?: boolean;
20
+ onChange?: (snapshot: WorldOrbitEditorSnapshot) => void;
21
+ onDiagnosticsChange?: (diagnostics: AtlasResolvedDiagnostic[]) => void;
22
+ onSelectionChange?: (selection: WorldOrbitEditorSelection | null) => void;
23
+ onDirtyChange?: (dirty: boolean) => void;
24
+ }
25
+ export interface WorldOrbitEditor {
26
+ setSource(source: string): void;
27
+ setAtlasDocument(document: WorldOrbitAtlasDocument): void;
28
+ getSource(): string;
29
+ getAtlasDocument(): WorldOrbitAtlasDocument;
30
+ getDiagnostics(): AtlasResolvedDiagnostic[];
31
+ getSelection(): WorldOrbitEditorSelection | null;
32
+ isDirty(): boolean;
33
+ markSaved(): void;
34
+ selectPath(path: AtlasDocumentPath | null): void;
35
+ canUndo(): boolean;
36
+ canRedo(): boolean;
37
+ undo(): boolean;
38
+ redo(): boolean;
39
+ addObject(type?: WorldOrbitObject["type"]): string;
40
+ addEvent(): string;
41
+ addViewpoint(): string;
42
+ addAnnotation(): string;
43
+ addMetadata(key?: string, value?: string): string;
44
+ removeSelection(): boolean;
45
+ exportSvg(): string;
46
+ exportEmbedMarkup(): string;
47
+ destroy(): void;
48
+ }
49
+ export interface WorldOrbitEditorFormState {
50
+ selection: WorldOrbitEditorSelection | null;
51
+ system: WorldOrbitAtlasSystem | null;
52
+ viewpoints: WorldOrbitAtlasViewpoint[];
53
+ events: WorldOrbitEvent[];
54
+ objects: WorldOrbitObject[];
55
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { WorldOrbitMarkdownOptions } from "./types.js";
2
+ export declare function renderWorldOrbitBlock(source: string, options?: WorldOrbitMarkdownOptions): string;
3
+ export declare function renderWorldOrbitError(message: string): string;
@@ -0,0 +1,57 @@
1
+ import { loadWorldOrbitSource, renderDocumentToScene, } from "@worldorbit/core";
2
+ import { createEmbedPayload, createWorldOrbitEmbedMarkup, renderSceneToSvg, } from "@worldorbit/viewer";
3
+ export function renderWorldOrbitBlock(source, options = {}) {
4
+ try {
5
+ const loaded = loadWorldOrbitSource(source);
6
+ const scene = renderDocumentToScene(loaded.document, resolveSourceRenderOptions(loaded, options));
7
+ if ((options.mode ?? "static") === "interactive") {
8
+ return createWorldOrbitEmbedMarkup(createEmbedPayload(scene, "interactive", {
9
+ initialViewpointId: options.initialViewpointId,
10
+ initialSelectionObjectId: options.initialSelectionObjectId,
11
+ initialFilter: options.initialFilter ?? null,
12
+ atlasState: options.atlasState ?? null,
13
+ minimap: options.minimap,
14
+ }), {
15
+ className: options.className ?? "worldorbit-block worldorbit-interactive",
16
+ theme: options.theme,
17
+ layers: options.layers,
18
+ subtitle: options.subtitle,
19
+ preset: options.preset,
20
+ initialViewpointId: options.initialViewpointId,
21
+ initialSelectionObjectId: options.initialSelectionObjectId,
22
+ initialFilter: options.initialFilter ?? null,
23
+ atlasState: options.atlasState ?? null,
24
+ minimap: options.minimap,
25
+ });
26
+ }
27
+ return `<figure class="${escapeAttribute(options.className ?? "worldorbit-block worldorbit-static")}">${renderSceneToSvg(scene, options)}</figure>`;
28
+ }
29
+ catch (error) {
30
+ if (options.strict) {
31
+ throw error;
32
+ }
33
+ return renderWorldOrbitError(error instanceof Error ? error.message : String(error));
34
+ }
35
+ }
36
+ export function renderWorldOrbitError(message) {
37
+ return `<pre class="worldorbit-error">WorldOrbit error: ${escapeHtml(message)}</pre>`;
38
+ }
39
+ function escapeHtml(value) {
40
+ return value
41
+ .replaceAll("&", "&amp;")
42
+ .replaceAll("<", "&lt;")
43
+ .replaceAll(">", "&gt;");
44
+ }
45
+ function escapeAttribute(value) {
46
+ return escapeHtml(value).replaceAll("\"", "&quot;");
47
+ }
48
+ function resolveSourceRenderOptions(loaded, options) {
49
+ const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;
50
+ if (options.preset || !atlasDocument?.system?.defaults.preset) {
51
+ return options;
52
+ }
53
+ return {
54
+ ...options,
55
+ preset: atlasDocument.system.defaults.preset,
56
+ };
57
+ }
@@ -0,0 +1,4 @@
1
+ export type * from "./types.js";
2
+ export { renderWorldOrbitBlock, renderWorldOrbitError } from "./html.js";
3
+ export { remarkWorldOrbit } from "./remark.js";
4
+ export { rehypeWorldOrbit } from "./rehype.js";
@@ -0,0 +1,3 @@
1
+ export { renderWorldOrbitBlock, renderWorldOrbitError } from "./html.js";
2
+ export { remarkWorldOrbit } from "./remark.js";
3
+ export { rehypeWorldOrbit } from "./rehype.js";
@@ -0,0 +1,10 @@
1
+ import type { WorldOrbitMarkdownOptions } from "./types.js";
2
+ interface HastNode {
3
+ type: string;
4
+ tagName?: string;
5
+ value?: string;
6
+ properties?: Record<string, unknown>;
7
+ children?: HastNode[];
8
+ }
9
+ export declare function rehypeWorldOrbit(options?: WorldOrbitMarkdownOptions): (tree: HastNode) => void;
10
+ export {};
@@ -0,0 +1,49 @@
1
+ import { renderWorldOrbitBlock } from "./html.js";
2
+ export function rehypeWorldOrbit(options = {}) {
3
+ return function transform(tree) {
4
+ visitHastNodes(tree, (node, index, parent) => {
5
+ if (!parent || index === -1 || node.type !== "element" || node.tagName !== "pre") {
6
+ return;
7
+ }
8
+ const codeElement = node.children?.[0];
9
+ if (!codeElement || codeElement.type !== "element" || codeElement.tagName !== "code") {
10
+ return;
11
+ }
12
+ const classNames = normalizeClassNames(codeElement.properties?.className);
13
+ if (!classNames.includes("language-worldorbit")) {
14
+ return;
15
+ }
16
+ const source = collectText(codeElement);
17
+ parent.children[index] = {
18
+ type: "raw",
19
+ value: renderWorldOrbitBlock(source, options),
20
+ };
21
+ });
22
+ };
23
+ }
24
+ function visitHastNodes(node, visitor, parent = null) {
25
+ const children = node.children ?? [];
26
+ for (let index = 0; index < children.length; index += 1) {
27
+ const child = children[index];
28
+ visitor(child, index, node);
29
+ visitHastNodes(child, visitor, node);
30
+ }
31
+ if (!parent) {
32
+ visitor(node, -1, null);
33
+ }
34
+ }
35
+ function normalizeClassNames(value) {
36
+ if (Array.isArray(value)) {
37
+ return value.map((entry) => String(entry));
38
+ }
39
+ if (typeof value === "string") {
40
+ return value.split(/\s+/).filter(Boolean);
41
+ }
42
+ return [];
43
+ }
44
+ function collectText(node) {
45
+ if (node.type === "text") {
46
+ return node.value ?? "";
47
+ }
48
+ return (node.children ?? []).map((child) => collectText(child)).join("");
49
+ }
@@ -0,0 +1,9 @@
1
+ import type { WorldOrbitMarkdownOptions } from "./types.js";
2
+ interface MdNode {
3
+ type: string;
4
+ lang?: string | null;
5
+ value?: string;
6
+ children?: MdNode[];
7
+ }
8
+ export declare function remarkWorldOrbit(options?: WorldOrbitMarkdownOptions): (tree: MdNode) => void;
9
+ export {};
@@ -0,0 +1,28 @@
1
+ import { renderWorldOrbitBlock } from "./html.js";
2
+ export function remarkWorldOrbit(options = {}) {
3
+ return function transform(tree) {
4
+ visitMdNodes(tree, (node, index, parent) => {
5
+ if (!parent || index === -1 || node.type !== "code" || !isWorldOrbitLanguage(node.lang)) {
6
+ return;
7
+ }
8
+ parent.children[index] = {
9
+ type: "html",
10
+ value: renderWorldOrbitBlock(node.value ?? "", options),
11
+ };
12
+ });
13
+ };
14
+ }
15
+ function visitMdNodes(node, visitor, parent = null) {
16
+ const children = node.children ?? [];
17
+ for (let index = 0; index < children.length; index += 1) {
18
+ const child = children[index];
19
+ visitor(child, index, node);
20
+ visitMdNodes(child, visitor, node);
21
+ }
22
+ if (!parent) {
23
+ visitor(node, -1, null);
24
+ }
25
+ }
26
+ function isWorldOrbitLanguage(language) {
27
+ return (language ?? "").trim().toLowerCase() === "worldorbit";
28
+ }
@@ -0,0 +1,11 @@
1
+ import type { SvgRenderOptions, ViewerAtlasState, ViewerFilter, WorldOrbitEmbedMode } from "@worldorbit/viewer";
2
+ export interface WorldOrbitMarkdownOptions extends SvgRenderOptions {
3
+ mode?: WorldOrbitEmbedMode;
4
+ strict?: boolean;
5
+ className?: string;
6
+ initialViewpointId?: string;
7
+ initialSelectionObjectId?: string;
8
+ initialFilter?: ViewerFilter | null;
9
+ atlasState?: ViewerAtlasState | null;
10
+ minimap?: boolean;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -64,12 +64,14 @@ export function createAtlasStateSnapshot(viewerState, renderOptions, filter, vie
64
64
  return {
65
65
  version: "2.0",
66
66
  viewpointId,
67
+ activeEventId: renderOptions.activeEventId ?? null,
67
68
  viewerState: { ...viewerState },
68
69
  renderOptions: {
69
70
  preset: renderOptions.preset,
70
71
  projection: renderOptions.projection,
71
72
  layers: renderOptions.layers ? { ...renderOptions.layers } : undefined,
72
73
  scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : undefined,
74
+ activeEventId: renderOptions.activeEventId ?? null,
73
75
  },
74
76
  filter: normalizeViewerFilter(filter),
75
77
  };
@@ -82,6 +84,7 @@ export function deserializeViewerAtlasState(serialized) {
82
84
  return {
83
85
  version: "2.0",
84
86
  viewpointId: raw.viewpointId ?? null,
87
+ activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
85
88
  viewerState: {
86
89
  scale: raw.viewerState?.scale ?? 1,
87
90
  rotationDeg: raw.viewerState?.rotationDeg ?? 0,
@@ -96,6 +99,7 @@ export function deserializeViewerAtlasState(serialized) {
96
99
  scaleModel: raw.renderOptions?.scaleModel
97
100
  ? { ...raw.renderOptions.scaleModel }
98
101
  : undefined,
102
+ activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
99
103
  },
100
104
  filter: normalizeViewerFilter(raw.filter ?? null),
101
105
  };
@@ -117,6 +121,7 @@ export function createViewerBookmark(name, label, atlasState) {
117
121
  scaleModel: atlasState.renderOptions.scaleModel
118
122
  ? { ...atlasState.renderOptions.scaleModel }
119
123
  : undefined,
124
+ activeEventId: atlasState.renderOptions.activeEventId ?? null,
120
125
  },
121
126
  filter: atlasState.filter ? { ...atlasState.filter } : null,
122
127
  },
@@ -134,6 +139,7 @@ export function sceneViewpointToLayerOptions(viewpoint) {
134
139
  background: viewpoint.layers.background,
135
140
  guides: viewpoint.layers.guides,
136
141
  relations: viewpoint.layers.relations,
142
+ events: viewpoint.layers.events,
137
143
  orbits: viewpoint.layers["orbits-front"] === undefined && viewpoint.layers["orbits-back"] === undefined
138
144
  ? undefined
139
145
  : viewpoint.layers["orbits-front"] !== false || viewpoint.layers["orbits-back"] !== false,
@@ -339,6 +339,7 @@ export function createAtlasViewer(container, options) {
339
339
  groupCount: activeViewer.getScene().groups.length,
340
340
  semanticGroupCount: activeViewer.getScene().semanticGroups.length,
341
341
  relationCount: activeViewer.getScene().relations.length,
342
+ eventCount: activeViewer.getScene().events.length,
342
343
  viewpointCount: activeViewer.getScene().viewpoints.length,
343
344
  },
344
345
  };
@@ -39,6 +39,12 @@ export function renderSceneToSvg(scene, options = {}) {
39
39
  .map((relation) => `<line class="wo-relation" x1="${relation.x1}" y1="${relation.y1}" x2="${relation.x2}" y2="${relation.y2}" data-render-id="${escapeXml(relation.renderId)}" data-relation-id="${escapeAttribute(relation.relationId)}" />`)
40
40
  .join("")
41
41
  : "";
42
+ const eventMarkup = layers.events
43
+ ? scene.events
44
+ .filter((event) => !event.hidden)
45
+ .map((event) => renderSceneEventOverlay(scene, event, visibleObjectIds, theme))
46
+ .join("")
47
+ : "";
42
48
  const objectMarkup = layers.objects
43
49
  ? visibleObjects
44
50
  .map((object) => renderSceneObject(object, options.selectedObjectId ?? null, theme))
@@ -86,6 +92,9 @@ export function renderSceneToSvg(scene, options = {}) {
86
92
  .wo-orbit-front { opacity: 0.9; }
87
93
  .wo-orbit-band { stroke: ${theme.orbitBand}; stroke-linecap: round; }
88
94
  .wo-relation { stroke: ${theme.relation}; stroke-width: 2; stroke-dasharray: 10 6; }
95
+ .wo-event-line { stroke: ${theme.accent}; stroke-width: 1.6; stroke-dasharray: 5 5; opacity: 0.72; }
96
+ .wo-event-node { fill: ${theme.accent}; stroke: ${theme.selected}; stroke-width: 1.4; opacity: 0.92; }
97
+ .wo-event-label { fill: ${theme.accent}; font-family: ${theme.fontFamily}; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; }
89
98
  .wo-leader { stroke: ${theme.leader}; stroke-width: 1.5; stroke-dasharray: 6 5; }
90
99
  .wo-label { fill: ${theme.ink}; font-family: ${theme.fontFamily}; font-weight: 600; letter-spacing: 0.02em; }
91
100
  .wo-label-secondary { fill: ${theme.muted}; font-family: ${theme.fontFamily}; font-weight: 500; }
@@ -120,14 +129,34 @@ export function renderSceneToSvg(scene, options = {}) {
120
129
  ${layers.orbits ? `<g data-layer-id="orbits-back">${orbitMarkup.back}</g>` : ""}
121
130
  ${layers.guides ? `<g data-layer-id="guides">${leaderMarkup}</g>` : ""}
122
131
  ${layers.relations ? `<g data-layer-id="relations">${relationMarkup}</g>` : ""}
132
+ ${layers.events ? `<g data-layer-id="events">${eventMarkup}</g>` : ""}
123
133
  ${layers.objects ? `<g data-layer-id="objects">${objectMarkup}</g>` : ""}
124
- ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
125
- ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
134
+ ${layers.orbits ? `<g data-layer-id="orbits-front">${orbitMarkup.front}</g>` : ""}
135
+ ${layers.labels ? `<g data-layer-id="labels">${labelMarkup}</g>` : ""}
126
136
  </g>
127
137
  </g>
128
138
  </g>
129
139
  </svg>`;
130
140
  }
141
+ function renderSceneEventOverlay(scene, event, visibleObjectIds, theme) {
142
+ const participants = event.objectIds
143
+ .filter((objectId) => visibleObjectIds.has(objectId))
144
+ .map((objectId) => scene.objects.find((object) => object.objectId === objectId && !object.hidden))
145
+ .filter(Boolean);
146
+ if (participants.length === 0) {
147
+ return "";
148
+ }
149
+ const stroke = event.event.color || theme.accent;
150
+ const label = event.event.label || event.event.id;
151
+ const lineMarkup = participants
152
+ .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)}" />`)
153
+ .join("");
154
+ return `<g class="wo-event" data-render-id="${escapeXml(event.renderId)}" data-event-id="${escapeAttribute(event.eventId)}">
155
+ ${lineMarkup}
156
+ <circle class="wo-event-node" cx="${event.x}" cy="${event.y}" r="5" fill="${escapeAttribute(stroke)}" />
157
+ <text class="wo-event-label" x="${event.x}" y="${event.y - 10}" text-anchor="middle" font-size="10">${escapeXml(label)}</text>
158
+ </g>`;
159
+ }
131
160
  export function renderDocumentToSvg(document, options = {}) {
132
161
  return renderSceneToSvg(renderDocumentToScene(document, options), options);
133
162
  }
@@ -2,6 +2,7 @@ const DEFAULT_LAYERS = {
2
2
  background: true,
3
3
  guides: true,
4
4
  relations: true,
5
+ events: true,
5
6
  orbits: true,
6
7
  objects: true,
7
8
  labels: true,
@@ -111,6 +111,15 @@ function buildTooltipFields(details) {
111
111
  value: `${details.object.resonance.targetObjectId} ${details.object.resonance.ratio}`,
112
112
  });
113
113
  }
114
+ if (details.relatedEvents.length > 0) {
115
+ fields.set("events", {
116
+ key: "events",
117
+ label: "Events",
118
+ value: details.relatedEvents
119
+ .map((event) => event.event.label || event.event.id)
120
+ .join(", "),
121
+ });
122
+ }
114
123
  if (placement?.mode === "at") {
115
124
  fields.set("placement", {
116
125
  key: "placement",
@@ -1,4 +1,4 @@
1
- import type { CoordinatePoint, RenderOrbitVisual, RenderPresetName, RenderSceneGroup, RenderSceneLabel, RenderScaleModel, RenderScene, RenderSceneObject, RenderSceneViewpoint, SceneRenderOptions, ViewProjection, WorldOrbitObject, WorldOrbitDocument } from "@worldorbit/core";
1
+ import type { CoordinatePoint, RenderOrbitVisual, RenderPresetName, RenderSceneEvent, RenderSceneGroup, RenderSceneLabel, RenderScaleModel, RenderScene, RenderSceneObject, RenderSceneViewpoint, SceneRenderOptions, ViewProjection, WorldOrbitObject, WorldOrbitDocument } from "@worldorbit/core";
2
2
  export type WorldOrbitThemeName = "atlas" | "nightglass" | "ember";
3
3
  export type WorldOrbitEmbedMode = "static" | "interactive";
4
4
  export type TooltipMode = "hover" | "pinned" | "disabled";
@@ -29,6 +29,7 @@ export interface ViewerLayerOptions {
29
29
  background?: boolean;
30
30
  guides?: boolean;
31
31
  relations?: boolean;
32
+ events?: boolean;
32
33
  orbits?: boolean;
33
34
  objects?: boolean;
34
35
  labels?: boolean;
@@ -79,6 +80,7 @@ export interface ViewerObjectDetails {
79
80
  orbit: RenderOrbitVisual | null;
80
81
  relatedOrbits: RenderOrbitVisual[];
81
82
  relations: RenderScene["relations"];
83
+ relatedEvents: RenderSceneEvent[];
82
84
  parent: RenderSceneObject | null;
83
85
  children: RenderSceneObject[];
84
86
  ancestors: RenderSceneObject[];
@@ -104,12 +106,14 @@ export interface ViewerTooltipDetails {
104
106
  export interface ViewerAtlasState {
105
107
  version: "2.0";
106
108
  viewpointId: string | null;
109
+ activeEventId?: string | null;
107
110
  viewerState: ViewerState;
108
111
  renderOptions: {
109
112
  preset?: RenderPresetName;
110
113
  projection?: "document" | ViewProjection;
111
114
  layers?: ViewerLayerOptions;
112
115
  scaleModel?: Partial<RenderScaleModel>;
116
+ activeEventId?: string | null;
113
117
  };
114
118
  filter: ViewerFilter | null;
115
119
  }
@@ -139,6 +143,7 @@ export interface AtlasInspectorSnapshot {
139
143
  groupCount: number;
140
144
  semanticGroupCount: number;
141
145
  relationCount: number;
146
+ eventCount: number;
142
147
  viewpointCount: number;
143
148
  };
144
149
  }
@@ -189,6 +194,8 @@ export interface WorldOrbitViewer {
189
194
  listViewpoints(): RenderSceneViewpoint[];
190
195
  getActiveViewpoint(): RenderSceneViewpoint | null;
191
196
  goToViewpoint(id: string): boolean;
197
+ getActiveEventId(): string | null;
198
+ setActiveEvent(id: string | null): void;
192
199
  search(query: string, limit?: number): ViewerSearchResult[];
193
200
  getFilter(): ViewerFilter | null;
194
201
  setFilter(filter: ViewerFilter | null): void;
@@ -355,6 +355,12 @@ export function createInteractiveViewer(container, options) {
355
355
  emitAtlasStateChange();
356
356
  return true;
357
357
  },
358
+ getActiveEventId() {
359
+ return renderOptions.activeEventId ?? null;
360
+ },
361
+ setActiveEvent(id) {
362
+ api.setRenderOptions({ activeEventId: id });
363
+ },
358
364
  search(query, limit = 12) {
359
365
  return searchSceneObjects(scene, query, limit);
360
366
  },
@@ -653,6 +659,9 @@ export function createInteractiveViewer(container, options) {
653
659
  relations: scene.relations.filter((relation) => !relation.hidden &&
654
660
  (relation.fromObjectId === renderObject.objectId ||
655
661
  relation.toObjectId === renderObject.objectId)),
662
+ relatedEvents: scene.events.filter((event) => !event.hidden &&
663
+ (event.targetObjectId === renderObject.objectId ||
664
+ event.objectIds.includes(renderObject.objectId))),
656
665
  parent: getObjectById(renderObject.parentId),
657
666
  children: renderObject.childIds.map((childId) => getObjectById(childId)).filter(Boolean),
658
667
  ancestors: renderObject.ancestorIds
@@ -989,6 +998,7 @@ function cloneRenderOptions(renderOptions) {
989
998
  theme: renderOptions.theme && typeof renderOptions.theme === "object"
990
999
  ? { ...renderOptions.theme }
991
1000
  : renderOptions.theme,
1001
+ activeEventId: renderOptions.activeEventId ?? null,
992
1002
  };
993
1003
  }
994
1004
  function mergeRenderOptions(current, next) {
@@ -1027,7 +1037,8 @@ function hasSceneAffectingRenderOptions(options) {
1027
1037
  options.padding !== undefined ||
1028
1038
  options.preset !== undefined ||
1029
1039
  options.projection !== undefined ||
1030
- options.scaleModel !== undefined);
1040
+ options.scaleModel !== undefined ||
1041
+ options.activeEventId !== undefined);
1031
1042
  }
1032
1043
  function resolveSourceRenderOptions(loaded, renderOptions) {
1033
1044
  const atlasDocument = loaded.atlasDocument ?? loaded.draftDocument;