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.
- package/README.md +1 -1
- package/dist/browser/core/dist/index.js +2444 -342
- package/dist/browser/editor/dist/index.js +11702 -0
- package/dist/browser/markdown/dist/index.js +2207 -392
- package/dist/browser/viewer/dist/index.js +2302 -382
- package/dist/unpkg/core/dist/index.js +2447 -345
- package/dist/unpkg/editor/dist/index.js +11727 -0
- package/dist/unpkg/markdown/dist/index.js +2210 -395
- package/dist/unpkg/viewer/dist/index.js +2305 -385
- package/dist/unpkg/worldorbit-core.min.js +12 -12
- package/dist/unpkg/worldorbit-editor.min.js +894 -0
- package/dist/unpkg/worldorbit-markdown.min.js +66 -58
- package/dist/unpkg/worldorbit-viewer.min.js +76 -68
- package/dist/unpkg/worldorbit.js +797 -78
- package/dist/unpkg/worldorbit.min.js +80 -72
- package/package.json +3 -2
- package/packages/core/dist/atlas-edit.js +74 -0
- package/packages/core/dist/atlas-validate.js +122 -8
- package/packages/core/dist/draft-parse.js +212 -8
- package/packages/core/dist/draft.d.ts +5 -2
- package/packages/core/dist/draft.js +59 -3
- package/packages/core/dist/format.js +63 -1
- package/packages/core/dist/normalize.js +1 -0
- package/packages/core/dist/scene.js +248 -46
- package/packages/core/dist/types.d.ts +41 -2
- package/packages/editor/dist/editor.d.ts +2 -0
- package/packages/editor/dist/editor.js +3578 -0
- package/packages/editor/dist/index.d.ts +2 -0
- package/packages/editor/dist/index.js +1 -0
- package/packages/editor/dist/types.d.ts +55 -0
- package/packages/editor/dist/types.js +1 -0
- package/packages/markdown/dist/html.d.ts +3 -0
- package/packages/markdown/dist/html.js +57 -0
- package/packages/markdown/dist/index.d.ts +4 -0
- package/packages/markdown/dist/index.js +3 -0
- package/packages/markdown/dist/rehype.d.ts +10 -0
- package/packages/markdown/dist/rehype.js +49 -0
- package/packages/markdown/dist/remark.d.ts +9 -0
- package/packages/markdown/dist/remark.js +28 -0
- package/packages/markdown/dist/types.d.ts +11 -0
- package/packages/markdown/dist/types.js +1 -0
- package/packages/viewer/dist/atlas-state.js +6 -0
- package/packages/viewer/dist/atlas-viewer.js +1 -0
- package/packages/viewer/dist/render.js +31 -2
- package/packages/viewer/dist/theme.js +1 -0
- package/packages/viewer/dist/tooltip.js +9 -0
- package/packages/viewer/dist/types.d.ts +8 -1
- package/packages/viewer/dist/viewer.js +12 -1
|
@@ -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,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("&", "&")
|
|
42
|
+
.replaceAll("<", "<")
|
|
43
|
+
.replaceAll(">", ">");
|
|
44
|
+
}
|
|
45
|
+
function escapeAttribute(value) {
|
|
46
|
+
return escapeHtml(value).replaceAll("\"", """);
|
|
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,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
|
}
|
|
@@ -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;
|