worldorbit 3.2.0 → 3.2.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 (103) hide show
  1. package/dist/browser/core/dist/scene.js +98 -16
  2. package/dist/browser/core/dist/spatial-scene.js +1 -0
  3. package/dist/browser/core/dist/types.d.ts +3 -0
  4. package/dist/browser/editor/dist/editor.js +55 -12
  5. package/dist/browser/obsidian-plugin/dist/diagnostics.d.ts +3 -0
  6. package/dist/browser/obsidian-plugin/dist/diagnostics.js +23 -0
  7. package/dist/browser/obsidian-plugin/dist/examples.d.ts +3 -0
  8. package/dist/browser/obsidian-plugin/dist/examples.js +77 -0
  9. package/dist/browser/obsidian-plugin/dist/index.d.ts +9 -0
  10. package/dist/browser/obsidian-plugin/dist/index.js +8 -0
  11. package/dist/browser/obsidian-plugin/dist/main.d.ts +2 -0
  12. package/dist/browser/obsidian-plugin/dist/main.js +2 -0
  13. package/dist/browser/obsidian-plugin/dist/navigation.d.ts +6 -0
  14. package/dist/browser/obsidian-plugin/dist/navigation.js +44 -0
  15. package/dist/browser/obsidian-plugin/dist/plugin.d.ts +8 -0
  16. package/dist/browser/obsidian-plugin/dist/plugin.js +508 -0
  17. package/dist/browser/obsidian-plugin/dist/positions.d.ts +7 -0
  18. package/dist/browser/obsidian-plugin/dist/positions.js +14 -0
  19. package/dist/browser/obsidian-plugin/dist/settings.d.ts +2 -0
  20. package/dist/browser/obsidian-plugin/dist/settings.js +5 -0
  21. package/dist/browser/obsidian-plugin/dist/theme.d.ts +2 -0
  22. package/dist/browser/obsidian-plugin/dist/theme.js +31 -0
  23. package/dist/browser/obsidian-plugin/dist/types.d.ts +42 -0
  24. package/dist/browser/obsidian-plugin/dist/types.js +1 -0
  25. package/dist/browser/obsidian-plugin/dist/viewer-host.d.ts +14 -0
  26. package/dist/browser/obsidian-plugin/dist/viewer-host.js +110 -0
  27. package/dist/browser/viewer/dist/atlas-state.js +3 -0
  28. package/dist/browser/viewer/dist/index.d.ts +1 -0
  29. package/dist/browser/viewer/dist/index.js +1 -0
  30. package/dist/browser/viewer/dist/interactive-2d.d.ts +21 -0
  31. package/dist/browser/viewer/dist/interactive-2d.js +201 -0
  32. package/dist/browser/viewer/dist/render.d.ts +1 -1
  33. package/dist/browser/viewer/dist/render.js +2 -1
  34. package/dist/browser/viewer/dist/types.d.ts +1 -0
  35. package/dist/browser/viewer/dist/viewer-state.d.ts +1 -1
  36. package/dist/browser/viewer/dist/viewer-state.js +1 -1
  37. package/dist/browser/viewer/dist/viewer.js +2 -0
  38. package/dist/obsidian-plugin/LICENSE +21 -0
  39. package/dist/obsidian-plugin/README.md +124 -0
  40. package/dist/obsidian-plugin/main.js +108 -0
  41. package/dist/obsidian-plugin/manifest.json +9 -0
  42. package/dist/obsidian-plugin/obsidian_scsh_1.png +0 -0
  43. package/dist/obsidian-plugin/obsidian_scsh_2.png +0 -0
  44. package/dist/obsidian-plugin/styles.css +164 -0
  45. package/dist/unpkg/core/dist/scene.js +98 -16
  46. package/dist/unpkg/core/dist/spatial-scene.js +1 -0
  47. package/dist/unpkg/core/dist/types.d.ts +3 -0
  48. package/dist/unpkg/editor/dist/editor.js +55 -12
  49. package/dist/unpkg/obsidian-plugin/dist/diagnostics.d.ts +3 -0
  50. package/dist/unpkg/obsidian-plugin/dist/diagnostics.js +23 -0
  51. package/dist/unpkg/obsidian-plugin/dist/examples.d.ts +3 -0
  52. package/dist/unpkg/obsidian-plugin/dist/examples.js +77 -0
  53. package/dist/unpkg/obsidian-plugin/dist/index.d.ts +9 -0
  54. package/dist/unpkg/obsidian-plugin/dist/index.js +8 -0
  55. package/dist/unpkg/obsidian-plugin/dist/main.d.ts +2 -0
  56. package/dist/unpkg/obsidian-plugin/dist/main.js +2 -0
  57. package/dist/unpkg/obsidian-plugin/dist/navigation.d.ts +6 -0
  58. package/dist/unpkg/obsidian-plugin/dist/navigation.js +44 -0
  59. package/dist/unpkg/obsidian-plugin/dist/plugin.d.ts +8 -0
  60. package/dist/unpkg/obsidian-plugin/dist/plugin.js +508 -0
  61. package/dist/unpkg/obsidian-plugin/dist/positions.d.ts +7 -0
  62. package/dist/unpkg/obsidian-plugin/dist/positions.js +14 -0
  63. package/dist/unpkg/obsidian-plugin/dist/settings.d.ts +2 -0
  64. package/dist/unpkg/obsidian-plugin/dist/settings.js +5 -0
  65. package/dist/unpkg/obsidian-plugin/dist/theme.d.ts +2 -0
  66. package/dist/unpkg/obsidian-plugin/dist/theme.js +31 -0
  67. package/dist/unpkg/obsidian-plugin/dist/types.d.ts +42 -0
  68. package/dist/unpkg/obsidian-plugin/dist/types.js +1 -0
  69. package/dist/unpkg/obsidian-plugin/dist/viewer-host.d.ts +14 -0
  70. package/dist/unpkg/obsidian-plugin/dist/viewer-host.js +110 -0
  71. package/dist/unpkg/viewer/dist/atlas-state.js +3 -0
  72. package/dist/unpkg/viewer/dist/index.d.ts +1 -0
  73. package/dist/unpkg/viewer/dist/index.js +1 -0
  74. package/dist/unpkg/viewer/dist/interactive-2d.d.ts +21 -0
  75. package/dist/unpkg/viewer/dist/interactive-2d.js +201 -0
  76. package/dist/unpkg/viewer/dist/render.d.ts +1 -1
  77. package/dist/unpkg/viewer/dist/render.js +2 -1
  78. package/dist/unpkg/viewer/dist/types.d.ts +1 -0
  79. package/dist/unpkg/viewer/dist/viewer-state.d.ts +1 -1
  80. package/dist/unpkg/viewer/dist/viewer-state.js +1 -1
  81. package/dist/unpkg/viewer/dist/viewer.js +2 -0
  82. package/dist/unpkg/worldorbit-core.min.js +12 -12
  83. package/dist/unpkg/worldorbit-editor.min.js +342 -342
  84. package/dist/unpkg/worldorbit-markdown.min.js +23 -23
  85. package/dist/unpkg/worldorbit-viewer.min.js +197 -197
  86. package/dist/unpkg/worldorbit.js +297 -21
  87. package/dist/unpkg/worldorbit.min.js +201 -201
  88. package/package.json +18 -1
  89. package/packages/core/dist/scene.js +98 -16
  90. package/packages/core/dist/spatial-scene.js +1 -0
  91. package/packages/core/dist/types.d.ts +3 -0
  92. package/packages/editor/dist/editor.js +55 -12
  93. package/packages/viewer/dist/atlas-state.js +3 -0
  94. package/packages/viewer/dist/index.d.ts +1 -0
  95. package/packages/viewer/dist/index.js +1 -0
  96. package/packages/viewer/dist/interactive-2d.d.ts +21 -0
  97. package/packages/viewer/dist/interactive-2d.js +201 -0
  98. package/packages/viewer/dist/render.d.ts +1 -1
  99. package/packages/viewer/dist/render.js +2 -1
  100. package/packages/viewer/dist/types.d.ts +1 -0
  101. package/packages/viewer/dist/viewer-state.d.ts +1 -1
  102. package/packages/viewer/dist/viewer-state.js +1 -1
  103. package/packages/viewer/dist/viewer.js +2 -0
@@ -0,0 +1,14 @@
1
+ export function inferFenceContentStartLine(section) {
2
+ const lines = section.text.split(/\r?\n/);
3
+ const openerIndex = lines.findIndex((line) => /^\s*(```+|~~~+)\s*worldorbit(?:\s+.*)?$/i.test(line));
4
+ return section.lineStart + (openerIndex >= 0 ? openerIndex + 1 : 1);
5
+ }
6
+ export function resolveDiagnosticEditorPosition(contentStartLine, diagnostic) {
7
+ if (!diagnostic.line || diagnostic.line < 1) {
8
+ return null;
9
+ }
10
+ return {
11
+ line: contentStartLine + diagnostic.line - 1,
12
+ ch: Math.max((diagnostic.column ?? 1) - 1, 0),
13
+ };
14
+ }
@@ -0,0 +1,2 @@
1
+ import type { WorldOrbitObsidianPluginSettings } from "./types.js";
2
+ export declare const DEFAULT_SETTINGS: WorldOrbitObsidianPluginSettings;
@@ -0,0 +1,5 @@
1
+ export const DEFAULT_SETTINGS = {
2
+ embeddedInteraction: "locked",
3
+ showWarnings: false,
4
+ showFullscreenButton: true,
5
+ };
@@ -0,0 +1,2 @@
1
+ import type { WorldOrbitTheme } from "@worldorbit/viewer/interactive-2d";
2
+ export declare function createObsidianViewerTheme(): WorldOrbitTheme;
@@ -0,0 +1,31 @@
1
+ import { resolveTheme } from "@worldorbit/viewer/interactive-2d";
2
+ export function createObsidianViewerTheme() {
3
+ const base = resolveTheme("atlas");
4
+ return {
5
+ ...base,
6
+ name: "obsidian",
7
+ backgroundStart: "var(--background-primary, #10131a)",
8
+ backgroundEnd: "var(--background-secondary, #171b24)",
9
+ backgroundGlow: "var(--interactive-accent-hover, rgba(143, 202, 255, 0.18))",
10
+ panel: "var(--background-secondary-alt, rgba(10, 16, 24, 0.92))",
11
+ panelLine: "var(--background-modifier-border, rgba(168, 207, 242, 0.2))",
12
+ relation: "var(--text-accent-hover, rgba(240, 180, 100, 0.42))",
13
+ orbit: "var(--text-faint, rgba(163, 209, 255, 0.24))",
14
+ guide: "var(--background-modifier-border-hover, rgba(255, 255, 255, 0.08))",
15
+ leader: "var(--text-muted, rgba(225, 238, 255, 0.4))",
16
+ ink: "var(--text-normal, #e8f0ff)",
17
+ muted: "var(--text-muted, rgba(232, 240, 255, 0.7))",
18
+ accent: "var(--interactive-accent, #f0b464)",
19
+ accentStrong: "var(--text-accent-hover, #ff7f5f)",
20
+ selected: "var(--color-cyan, rgba(255, 214, 139, 0.92))",
21
+ starCore: "var(--color-yellow, #ffcc67)",
22
+ starStroke: "var(--text-normal, rgba(255, 245, 203, 0.85))",
23
+ starGlow: "var(--color-orange, #ffe8a3)",
24
+ objectSpecular: "var(--text-normal, #f5f8ff)",
25
+ selectionHalo: "var(--interactive-accent, rgba(255, 214, 139, 0.9))",
26
+ atmosphere: "var(--color-cyan, rgba(143, 202, 255, 0.4))",
27
+ cometTail: "var(--color-cyan, rgba(193, 243, 255, 0.7))",
28
+ fontFamily: "var(--font-interface, \"Segoe UI\", sans-serif)",
29
+ displayFont: "var(--font-text, var(--font-interface, \"Segoe UI\", sans-serif))",
30
+ };
31
+ }
@@ -0,0 +1,42 @@
1
+ import type { EditorPosition } from "obsidian";
2
+ import type { RenderScene, WorldOrbitDiagnostic } from "@worldorbit/core/types";
3
+ import type { WorldOrbitTheme, WorldOrbitViewer2D } from "@worldorbit/viewer/interactive-2d";
4
+ export type EmbeddedInteractionMode = "locked" | "enabled";
5
+ export interface WorldOrbitObsidianPluginSettings {
6
+ embeddedInteraction: EmbeddedInteractionMode;
7
+ showWarnings: boolean;
8
+ showFullscreenButton: boolean;
9
+ }
10
+ export interface BlockNavigationContext {
11
+ sourcePath: string;
12
+ contentStartLine: number;
13
+ }
14
+ export interface WorldOrbitEmbeddedViewOptions {
15
+ container: HTMLElement;
16
+ scene: RenderScene;
17
+ theme: WorldOrbitTheme;
18
+ interactive: boolean;
19
+ enablePointer: boolean;
20
+ enableTouch: boolean;
21
+ createViewer?: (container: HTMLElement, options: {
22
+ scene: RenderScene;
23
+ theme: WorldOrbitTheme;
24
+ pointer: boolean;
25
+ touch: boolean;
26
+ width?: number;
27
+ height?: number;
28
+ }) => WorldOrbitViewer2D;
29
+ renderStatic?: (scene: RenderScene, options: {
30
+ theme: WorldOrbitTheme;
31
+ width?: number;
32
+ height?: number;
33
+ }) => string;
34
+ }
35
+ export interface WorldOrbitEmbeddedViewState {
36
+ interactive: boolean;
37
+ destroyed: boolean;
38
+ }
39
+ export interface DiagnosticNavigationTarget {
40
+ diagnostic: WorldOrbitDiagnostic;
41
+ position: EditorPosition | null;
42
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { WorldOrbitEmbeddedViewOptions, WorldOrbitEmbeddedViewState } from "./types.js";
2
+ export declare class WorldOrbitEmbeddedView {
3
+ private readonly options;
4
+ private viewer;
5
+ private state;
6
+ constructor(options: WorldOrbitEmbeddedViewOptions);
7
+ getState(): WorldOrbitEmbeddedViewState;
8
+ mount(): void;
9
+ setInteractive(interactive: boolean): void;
10
+ resize(): void;
11
+ destroy(): void;
12
+ private renderCurrent;
13
+ private destroyViewer;
14
+ }
@@ -0,0 +1,110 @@
1
+ import { createInteractiveViewer2D, renderSceneToSvg, } from "@worldorbit/viewer/interactive-2d";
2
+ export class WorldOrbitEmbeddedView {
3
+ options;
4
+ viewer = null;
5
+ state;
6
+ constructor(options) {
7
+ this.options = {
8
+ ...options,
9
+ createViewer: options.createViewer ?? defaultCreateViewer,
10
+ renderStatic: options.renderStatic ?? defaultRenderStatic,
11
+ };
12
+ this.state = {
13
+ interactive: options.interactive,
14
+ destroyed: false,
15
+ };
16
+ }
17
+ getState() {
18
+ return { ...this.state };
19
+ }
20
+ mount() {
21
+ if (this.state.destroyed) {
22
+ return;
23
+ }
24
+ this.renderCurrent();
25
+ }
26
+ setInteractive(interactive) {
27
+ if (this.state.destroyed || this.state.interactive === interactive) {
28
+ return;
29
+ }
30
+ this.state.interactive = interactive;
31
+ this.renderCurrent();
32
+ }
33
+ resize() {
34
+ if (this.state.destroyed) {
35
+ return;
36
+ }
37
+ if (this.viewer) {
38
+ const viewport = measureViewport(this.options.container, this.options.scene);
39
+ this.viewer.setRenderOptions(viewport);
40
+ return;
41
+ }
42
+ this.renderCurrent();
43
+ }
44
+ destroy() {
45
+ if (this.state.destroyed) {
46
+ return;
47
+ }
48
+ this.state.destroyed = true;
49
+ this.destroyViewer();
50
+ clearElement(this.options.container);
51
+ }
52
+ renderCurrent() {
53
+ this.destroyViewer();
54
+ clearElement(this.options.container);
55
+ if (this.state.interactive) {
56
+ const viewport = measureViewport(this.options.container, this.options.scene);
57
+ this.viewer = this.options.createViewer(this.options.container, {
58
+ scene: this.options.scene,
59
+ theme: this.options.theme,
60
+ pointer: this.options.enablePointer,
61
+ touch: this.options.enableTouch,
62
+ ...viewport,
63
+ });
64
+ return;
65
+ }
66
+ const viewport = measureViewport(this.options.container, this.options.scene);
67
+ this.options.container.innerHTML = this.options.renderStatic(this.options.scene, {
68
+ theme: this.options.theme,
69
+ ...viewport,
70
+ });
71
+ }
72
+ destroyViewer() {
73
+ this.viewer?.destroy();
74
+ this.viewer = null;
75
+ }
76
+ }
77
+ function defaultCreateViewer(container, options) {
78
+ return createInteractiveViewer2D(container, options.scene, {
79
+ theme: options.theme,
80
+ pointer: options.pointer,
81
+ touch: options.touch,
82
+ selection: true,
83
+ width: options.width,
84
+ height: options.height,
85
+ });
86
+ }
87
+ function defaultRenderStatic(scene, options) {
88
+ return renderSceneToSvg(scene, {
89
+ theme: options.theme,
90
+ width: options.width,
91
+ height: options.height,
92
+ });
93
+ }
94
+ function measureViewport(container, scene) {
95
+ const rect = container.getBoundingClientRect();
96
+ const width = sanitizeDimension(container.clientWidth || rect.width) ?? scene.width;
97
+ const height = sanitizeDimension(container.clientHeight || rect.height) ??
98
+ Math.max(Math.round(width * (scene.height / Math.max(scene.width, 1))), 280);
99
+ return { width, height };
100
+ }
101
+ function sanitizeDimension(value) {
102
+ return Number.isFinite(value) && value > 0 ? Math.round(value) : undefined;
103
+ }
104
+ function clearElement(element) {
105
+ if (typeof element.empty === "function") {
106
+ element.empty();
107
+ return;
108
+ }
109
+ element.replaceChildren();
110
+ }
@@ -72,6 +72,7 @@ export function createAtlasStateSnapshot(viewerState, renderOptions, filter, vie
72
72
  camera: renderOptions.camera ? { ...renderOptions.camera } : null,
73
73
  layers: renderOptions.layers ? { ...renderOptions.layers } : undefined,
74
74
  scaleModel: renderOptions.scaleModel ? { ...renderOptions.scaleModel } : undefined,
75
+ bodyScaleMode: renderOptions.bodyScaleMode,
75
76
  activeEventId: renderOptions.activeEventId ?? null,
76
77
  viewMode: renderOptions.viewMode ?? "2d",
77
78
  },
@@ -102,6 +103,7 @@ export function deserializeViewerAtlasState(serialized) {
102
103
  scaleModel: raw.renderOptions?.scaleModel
103
104
  ? { ...raw.renderOptions.scaleModel }
104
105
  : undefined,
106
+ bodyScaleMode: raw.renderOptions?.bodyScaleMode,
105
107
  activeEventId: raw.activeEventId ?? raw.renderOptions?.activeEventId ?? null,
106
108
  viewMode: raw.renderOptions?.viewMode ?? "2d",
107
109
  },
@@ -126,6 +128,7 @@ export function createViewerBookmark(name, label, atlasState) {
126
128
  scaleModel: atlasState.renderOptions.scaleModel
127
129
  ? { ...atlasState.renderOptions.scaleModel }
128
130
  : undefined,
131
+ bodyScaleMode: atlasState.renderOptions.bodyScaleMode,
129
132
  activeEventId: atlasState.renderOptions.activeEventId ?? null,
130
133
  viewMode: atlasState.renderOptions.viewMode ?? "2d",
131
134
  },
@@ -8,3 +8,4 @@ export { createEmbedPayload, createWorldOrbitEmbedMarkup, deserializeWorldOrbitE
8
8
  export { defineWorldOrbitViewerElement } from "./custom-element.js";
9
9
  export { createAtlasViewer } from "./atlas-viewer.js";
10
10
  export { createInteractiveViewer } from "./viewer.js";
11
+ export { createInteractiveViewer2D } from "./interactive-2d.js";
@@ -7,3 +7,4 @@ export { createEmbedPayload, createWorldOrbitEmbedMarkup, deserializeWorldOrbitE
7
7
  export { defineWorldOrbitViewerElement } from "./custom-element.js";
8
8
  export { createAtlasViewer } from "./atlas-viewer.js";
9
9
  export { createInteractiveViewer } from "./viewer.js";
10
+ export { createInteractiveViewer2D } from "./interactive-2d.js";
@@ -0,0 +1,21 @@
1
+ import type { RenderScene } from "@worldorbit/core/types";
2
+ import type { SvgRenderOptions, ViewerState } from "./types.js";
3
+ export interface InteractiveViewer2DOptions extends Pick<SvgRenderOptions, "width" | "height" | "padding" | "preset" | "theme" | "layers" | "subtitle"> {
4
+ pointer?: boolean;
5
+ touch?: boolean;
6
+ selection?: boolean;
7
+ minScale?: number;
8
+ maxScale?: number;
9
+ fitPadding?: number;
10
+ }
11
+ export interface WorldOrbitViewer2D {
12
+ getState(): ViewerState;
13
+ setState(state: Partial<ViewerState>): void;
14
+ setRenderOptions(options: Partial<InteractiveViewer2DOptions>): void;
15
+ fitToSystem(): void;
16
+ destroy(): void;
17
+ }
18
+ export { renderSceneToSvg } from "./render.js";
19
+ export { resolveTheme } from "./theme.js";
20
+ export type { WorldOrbitTheme } from "./types.js";
21
+ export declare function createInteractiveViewer2D(container: HTMLElement, scene: RenderScene, options?: InteractiveViewer2DOptions): WorldOrbitViewer2D;
@@ -0,0 +1,201 @@
1
+ import { renderSceneToSvg, WORLD_LAYER_ID } from "./render.js";
2
+ import { DEFAULT_VIEWER_STATE, composeViewerTransform, fitViewerState, panViewerState, zoomViewerStateAt, } from "./viewer-state.js";
3
+ export { renderSceneToSvg } from "./render.js";
4
+ export { resolveTheme } from "./theme.js";
5
+ const DEFAULT_VIEWER_LIMITS = {
6
+ minScale: 0.2,
7
+ maxScale: 8,
8
+ fitPadding: 48,
9
+ };
10
+ export function createInteractiveViewer2D(container, scene, options = {}) {
11
+ const constraints = {
12
+ minScale: options.minScale ?? DEFAULT_VIEWER_LIMITS.minScale,
13
+ maxScale: options.maxScale ?? DEFAULT_VIEWER_LIMITS.maxScale,
14
+ fitPadding: options.fitPadding ?? DEFAULT_VIEWER_LIMITS.fitPadding,
15
+ };
16
+ const behavior = {
17
+ pointer: options.pointer ?? true,
18
+ touch: options.touch ?? true,
19
+ selection: options.selection ?? true,
20
+ };
21
+ let renderOptions = {
22
+ width: options.width,
23
+ height: options.height,
24
+ padding: options.padding,
25
+ preset: options.preset,
26
+ theme: options.theme,
27
+ layers: options.layers,
28
+ subtitle: options.subtitle,
29
+ pointer: behavior.pointer,
30
+ touch: behavior.touch,
31
+ selection: behavior.selection,
32
+ minScale: constraints.minScale,
33
+ maxScale: constraints.maxScale,
34
+ fitPadding: constraints.fitPadding,
35
+ };
36
+ let state = fitViewerState(scene, DEFAULT_VIEWER_STATE, constraints);
37
+ let svgElement = null;
38
+ let cameraRoot = null;
39
+ let destroyed = false;
40
+ let activePointerId = null;
41
+ let lastPointerClientPoint = null;
42
+ let dragDistance = 0;
43
+ const previousTabIndex = container.getAttribute("tabindex");
44
+ const previousTouchAction = container.style.touchAction;
45
+ if (previousTabIndex === null) {
46
+ container.tabIndex = 0;
47
+ }
48
+ container.classList.add("wo-viewer-container");
49
+ container.style.touchAction = behavior.touch ? "none" : previousTouchAction;
50
+ const handleWheel = (event) => {
51
+ if (!behavior.pointer || destroyed || !svgElement) {
52
+ return;
53
+ }
54
+ event.preventDefault();
55
+ container.focus();
56
+ const anchor = getScenePointFromClient(event.clientX, event.clientY);
57
+ const factor = clamp(Math.exp(-event.deltaY * 0.002), 0.6, 1.6);
58
+ updateState(zoomViewerStateAt(scene, state, factor, anchor, constraints));
59
+ };
60
+ const handlePointerDown = (event) => {
61
+ if (destroyed) {
62
+ return;
63
+ }
64
+ const isTouch = event.pointerType === "touch";
65
+ if ((isTouch && !behavior.touch) || (!isTouch && !behavior.pointer)) {
66
+ return;
67
+ }
68
+ if (!isTouch && event.button !== 0) {
69
+ return;
70
+ }
71
+ activePointerId = event.pointerId;
72
+ lastPointerClientPoint = { x: event.clientX, y: event.clientY };
73
+ dragDistance = 0;
74
+ container.setPointerCapture?.(event.pointerId);
75
+ container.focus();
76
+ };
77
+ const handlePointerMove = (event) => {
78
+ if (destroyed || activePointerId !== event.pointerId || !lastPointerClientPoint) {
79
+ return;
80
+ }
81
+ const rect = svgElement?.getBoundingClientRect();
82
+ if (!rect || rect.width <= 0 || rect.height <= 0) {
83
+ return;
84
+ }
85
+ const dx = event.clientX - lastPointerClientPoint.x;
86
+ const dy = event.clientY - lastPointerClientPoint.y;
87
+ lastPointerClientPoint = { x: event.clientX, y: event.clientY };
88
+ dragDistance += Math.hypot(dx, dy);
89
+ updateState(panViewerState(state, dx * (scene.width / rect.width), dy * (scene.height / rect.height)));
90
+ };
91
+ const stopPointer = (event) => {
92
+ if (activePointerId !== event.pointerId) {
93
+ return;
94
+ }
95
+ activePointerId = null;
96
+ lastPointerClientPoint = null;
97
+ container.releasePointerCapture?.(event.pointerId);
98
+ };
99
+ const handleClick = (event) => {
100
+ if (destroyed || !behavior.selection || dragDistance > 6) {
101
+ return;
102
+ }
103
+ const objectEl = event.target?.closest(".wo-object[data-object-id]");
104
+ if (!objectEl) {
105
+ return;
106
+ }
107
+ updateState({
108
+ ...state,
109
+ selectedObjectId: objectEl.dataset.objectId ?? null,
110
+ });
111
+ renderSvg();
112
+ };
113
+ container.addEventListener("wheel", handleWheel, { passive: false });
114
+ container.addEventListener("pointerdown", handlePointerDown);
115
+ container.addEventListener("pointermove", handlePointerMove);
116
+ container.addEventListener("pointerup", stopPointer);
117
+ container.addEventListener("pointercancel", stopPointer);
118
+ container.addEventListener("click", handleClick);
119
+ renderSvg();
120
+ return {
121
+ getState() {
122
+ return { ...state };
123
+ },
124
+ setState(nextState) {
125
+ updateState({
126
+ ...state,
127
+ ...nextState,
128
+ });
129
+ if ("selectedObjectId" in nextState) {
130
+ renderSvg();
131
+ }
132
+ },
133
+ setRenderOptions(nextOptions) {
134
+ renderOptions = {
135
+ ...renderOptions,
136
+ ...nextOptions,
137
+ };
138
+ renderSvg();
139
+ },
140
+ fitToSystem() {
141
+ updateState(fitViewerState(scene, state, constraints));
142
+ },
143
+ destroy() {
144
+ if (destroyed) {
145
+ return;
146
+ }
147
+ destroyed = true;
148
+ container.removeEventListener("wheel", handleWheel);
149
+ container.removeEventListener("pointerdown", handlePointerDown);
150
+ container.removeEventListener("pointermove", handlePointerMove);
151
+ container.removeEventListener("pointerup", stopPointer);
152
+ container.removeEventListener("pointercancel", stopPointer);
153
+ container.removeEventListener("click", handleClick);
154
+ if (previousTabIndex === null) {
155
+ container.removeAttribute("tabindex");
156
+ }
157
+ container.style.touchAction = previousTouchAction;
158
+ container.replaceChildren();
159
+ svgElement = null;
160
+ cameraRoot = null;
161
+ },
162
+ };
163
+ function renderSvg() {
164
+ if (destroyed) {
165
+ return;
166
+ }
167
+ container.innerHTML = renderSceneToSvg(scene, {
168
+ width: renderOptions.width,
169
+ height: renderOptions.height,
170
+ padding: renderOptions.padding,
171
+ preset: renderOptions.preset,
172
+ theme: renderOptions.theme,
173
+ layers: renderOptions.layers,
174
+ subtitle: renderOptions.subtitle,
175
+ selectedObjectId: state.selectedObjectId,
176
+ });
177
+ svgElement = container.querySelector("svg");
178
+ cameraRoot = container.querySelector(`[data-worldorbit-camera-root="${WORLD_LAYER_ID}"]`);
179
+ applyTransform();
180
+ }
181
+ function applyTransform() {
182
+ cameraRoot?.setAttribute("transform", composeViewerTransform(scene, state));
183
+ }
184
+ function updateState(nextState) {
185
+ state = nextState;
186
+ applyTransform();
187
+ }
188
+ function getScenePointFromClient(clientX, clientY) {
189
+ const rect = svgElement?.getBoundingClientRect();
190
+ if (!rect || rect.width <= 0 || rect.height <= 0) {
191
+ return { x: scene.width / 2, y: scene.height / 2 };
192
+ }
193
+ return {
194
+ x: ((clientX - rect.left) / rect.width) * scene.width,
195
+ y: ((clientY - rect.top) / rect.height) * scene.height,
196
+ };
197
+ }
198
+ }
199
+ function clamp(value, min, max) {
200
+ return Math.min(Math.max(value, min), max);
201
+ }
@@ -1,4 +1,4 @@
1
- import { type RenderScene, type WorldOrbitDocument } from "@worldorbit/core";
1
+ import { type RenderScene, type WorldOrbitDocument } from "@worldorbit/core/types";
2
2
  import type { SvgRenderOptions } from "./types.js";
3
3
  export declare const WORLD_LAYER_ID = "worldorbit-camera-root";
4
4
  export declare function renderSceneToSvg(scene: RenderScene, options?: SvgRenderOptions): string;
@@ -1,4 +1,5 @@
1
- import { loadWorldOrbitSource, renderDocumentToScene, } from "@worldorbit/core";
1
+ import { loadWorldOrbitSource } from "@worldorbit/core/load";
2
+ import { renderDocumentToScene } from "@worldorbit/core/scene";
2
3
  import { computeVisibleObjectIds } from "./atlas-state.js";
3
4
  import { resolveLayers, resolveTheme } from "./theme.js";
4
5
  export const WORLD_LAYER_ID = "worldorbit-camera-root";
@@ -129,6 +129,7 @@ export interface ViewerAtlasState {
129
129
  camera?: WorldOrbitViewCamera | null;
130
130
  layers?: ViewerLayerOptions;
131
131
  scaleModel?: Partial<RenderScaleModel>;
132
+ bodyScaleMode?: SceneRenderOptions["bodyScaleMode"];
132
133
  activeEventId?: string | null;
133
134
  viewMode?: WorldOrbitViewMode;
134
135
  quality?: WorldOrbit3DQuality;
@@ -1,4 +1,4 @@
1
- import { type CoordinatePoint, type RenderBounds, type RenderScene } from "@worldorbit/core";
1
+ import { type CoordinatePoint, type RenderBounds, type RenderScene } from "@worldorbit/core/types";
2
2
  import type { ViewerState } from "./types.js";
3
3
  export interface ViewerConstraints {
4
4
  minScale: number;
@@ -1,4 +1,4 @@
1
- import { rotatePoint, } from "@worldorbit/core";
1
+ import { rotatePoint } from "@worldorbit/core/scene";
2
2
  export const DEFAULT_VIEWER_STATE = {
3
3
  scale: 1,
4
4
  rotationDeg: 0,
@@ -44,6 +44,7 @@ export function createInteractiveViewer(container, options) {
44
44
  padding: options.padding,
45
45
  preset: options.preset,
46
46
  projection: options.projection,
47
+ bodyScaleMode: options.bodyScaleMode,
47
48
  viewMode: options.viewMode ?? "2d",
48
49
  quality: options.quality ?? "balanced",
49
50
  style3d: options.style3d ?? "symbolic",
@@ -1595,6 +1596,7 @@ function hasSceneAffectingRenderOptions(options) {
1595
1596
  options.projection !== undefined ||
1596
1597
  options.camera !== undefined ||
1597
1598
  options.scaleModel !== undefined ||
1599
+ options.bodyScaleMode !== undefined ||
1598
1600
  options.activeEventId !== undefined);
1599
1601
  }
1600
1602
  function resolveSourceRenderOptions(loaded, renderOptions) {