@nebula-spatial/viewer 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,131 @@
1
+ import type { Box3, Object3D } from 'three';
2
+ import type { CadHit, CadSelection, CadViewerSnapshot, CadViewerEventMap } from './cad/types';
3
+ import type { ViewerEventMap, ViewerViewMode } from './types';
4
+ export interface Disposable {
5
+ dispose(): void;
6
+ }
7
+ export interface ToolbarItem {
8
+ readonly id: string;
9
+ readonly label: string;
10
+ readonly title?: string;
11
+ readonly disabled?: boolean;
12
+ readonly onClick: () => void;
13
+ }
14
+ export interface ViewerState {
15
+ readonly viewMode: ViewerViewMode;
16
+ readonly selectedObject: Object3D | null;
17
+ readonly highlightedObject: Object3D | null;
18
+ readonly isDisposed: boolean;
19
+ }
20
+ export interface ViewerPluginContext {
21
+ readonly cad: {
22
+ $read(): CadViewerSnapshot;
23
+ };
24
+ readonly viewer: {
25
+ $getState(): ViewerState;
26
+ };
27
+ readonly commands: {
28
+ setLayerVisible(index: number, visible: boolean): boolean;
29
+ setSelection(selection: CadSelection | null): void;
30
+ fitDocument(): boolean;
31
+ setViewMode(mode: ViewerViewMode): void;
32
+ };
33
+ readonly events: {
34
+ on<K extends keyof (ViewerEventMap & CadViewerEventMap)>(type: K, listener: (event: (ViewerEventMap & CadViewerEventMap)[K]) => void): () => void;
35
+ };
36
+ readonly layout: {
37
+ mount(node: Node, options?: {
38
+ position?: 'left' | 'right' | 'top' | 'bottom' | 'overlay';
39
+ }): Disposable;
40
+ onInsetsChange(listener: (insets: ViewerEventMap['ui-layout-change']['contentInsets']) => void): () => void;
41
+ };
42
+ contribute(point: ViewerContributionPoint): Disposable;
43
+ dispose(): void;
44
+ }
45
+ export interface ViewerInteractionHooks {
46
+ /** Return false to consume the interaction and stop the managed pipeline. */
47
+ onPick?: (hit: CadHit | null) => boolean | void;
48
+ onHover?: (hit: CadHit | null) => void;
49
+ onSelect?: (selection: CadSelection | null) => void;
50
+ onClear?: () => void;
51
+ }
52
+ export type ViewerContributionPoint = {
53
+ readonly kind: 'panel';
54
+ readonly id: string;
55
+ readonly factory: (ctx: ViewerPluginContext) => Node;
56
+ } | {
57
+ readonly kind: 'toolbar';
58
+ readonly id: string;
59
+ readonly items: readonly ToolbarItem[];
60
+ } | {
61
+ readonly kind: 'hud';
62
+ readonly id: string;
63
+ readonly factory: (ctx: ViewerPluginContext) => Node;
64
+ } | {
65
+ readonly kind: 'statusbar';
66
+ readonly id: string;
67
+ readonly factory: (ctx: ViewerPluginContext) => Node;
68
+ } | {
69
+ readonly kind: 'inspector';
70
+ readonly id: string;
71
+ readonly matcher: (selection: CadSelection) => boolean;
72
+ readonly render: (ctx: ViewerPluginContext, selection: CadSelection) => Node;
73
+ } | {
74
+ readonly kind: 'interaction';
75
+ readonly id: string;
76
+ readonly hooks: ViewerInteractionHooks;
77
+ } | {
78
+ readonly kind: 'command';
79
+ readonly id: string;
80
+ readonly handler: (ctx: ViewerPluginContext, args?: unknown) => void;
81
+ } | {
82
+ readonly kind: 'fit-policy';
83
+ readonly id: string;
84
+ readonly resolve: (ctx: ViewerPluginContext) => Box3 | null;
85
+ };
86
+ export interface ViewerPlugin {
87
+ readonly id: string;
88
+ activate(context: ViewerPluginContext): void | Promise<void>;
89
+ deactivate?(): void | Promise<void>;
90
+ }
91
+ type SnapshotReader = () => CadViewerSnapshot;
92
+ type StateReader = () => ViewerState;
93
+ type PluginEventMap = ViewerEventMap & CadViewerEventMap;
94
+ type EventSubscriber = <K extends keyof PluginEventMap>(type: K, listener: (event: PluginEventMap[K]) => void) => () => void;
95
+ type CommandApi = ViewerPluginContext['commands'];
96
+ export declare class ViewerPluginManager implements ViewerPluginRegistry {
97
+ private readonly readCad;
98
+ private readonly readState;
99
+ private readonly subscribe;
100
+ private readonly commands;
101
+ private readonly hostElement;
102
+ private readonly pluginsById;
103
+ private readonly contributions;
104
+ private readonly commandContributions;
105
+ private refreshingInspectors;
106
+ private disposed;
107
+ constructor(readCad: SnapshotReader, readState: StateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement);
108
+ register(plugin: ViewerPlugin): Disposable;
109
+ use(plugins: readonly ViewerPlugin[], options?: {
110
+ replace?: boolean;
111
+ }): void;
112
+ private deactivate;
113
+ disposeAll(): void;
114
+ dispose(): void;
115
+ dispatchInteraction(kind: keyof ViewerInteractionHooks, value: CadHit | CadSelection | null): boolean;
116
+ findFitPolicy(): Box3 | null;
117
+ executeCommand(id: string, args?: unknown): boolean;
118
+ hasCommand(id: string): boolean;
119
+ private removeContribution;
120
+ private mountContribution;
121
+ private isFirstMatchingInspector;
122
+ private refreshInspectorContributions;
123
+ private assertActive;
124
+ }
125
+ export interface ViewerPluginRegistry {
126
+ register(plugin: ViewerPlugin): Disposable;
127
+ use(plugins: readonly ViewerPlugin[], options?: {
128
+ replace?: boolean;
129
+ }): void;
130
+ }
131
+ export {};
package/dist/types.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type { Box3, ColorRepresentation, Object3D, Vector3 } from 'three';
2
2
  import type { CadViewerCapability, CameraStateStore, ViewerUiOptions } from './cad/types';
3
- export type { CadBlockModel, CadDocumentHandle, CadFontResolver, CadResolvedTextFont, CadHudModel, CadInspectorModel, CadLayerModel, CadLoadSource, CadOpenOptions, CadSessionLoadTiming, CadTextDetails, CadTextSelection, CadTextSelectionId, CadViewerCapability, ResolveTextFont, CadViewerEventMap, CameraStateStore, ViewerUiOptions, ViewerUiParts, ViewerUiPreset, ViewerUiTheme, } from './cad/types';
3
+ export type { CadBlockModel, CadDocumentHandle, CadDocumentUnits, CadFontResolver, CadFontRequest, CadResolvedFont, CadResolvedTextFont, CadTextWarmupOptions, CadHudModel, CadHudSnapshot, CadInspectorModel, CadInspectorSnapshot, CadLayerModel, CadLayerRow, CadBlockUsageRow, CadSessionPhase, CadWorldBounds, CadScaleRulerState, CadInsertDetails, CadInsertSelection, CadInsertSelectionId, CadTextDetails, CadTextSelection, CadTextSelectionId, CadSelection, CadHit, CadViewerSnapshot, CadLoadProgress, CadViewerErrorCode, CadViewerErrorEvent, CadLoadSource, CadOpenOptions, CadSessionLoadTiming, RenderDebugBreakdownEntry, SplitBlockLayerDetail, CadViewerCapability, ResolveTextFont, CadViewerEventMap, CameraStateStore, ViewerUiKeyboardOptions, ViewerUiOptions, ViewerUiParts, ViewerUiPreset, ViewerUiStateStore, ViewerUiTheme, ViewerUiTokens, } from './cad/types';
4
4
  export type ViewerViewMode = '2d' | '3d';
5
+ export type CadInteractionMode = 'managed' | 'manual' | 'disabled';
6
+ export type CameraChangeReason = 'pointer' | 'wheel' | 'fit' | 'restore' | 'api' | 'view-mode';
5
7
  export type ViewerEnvironment = 'room' | {
6
8
  /** Load an external equirectangular HDR texture for image-based lighting. */
7
9
  readonly type: 'hdr';
@@ -50,6 +52,22 @@ export interface ViewerOptions {
50
52
  environment?: ViewerEnvironment;
51
53
  autoSelectOnPick?: boolean;
52
54
  pointerPick?: boolean;
55
+ /** CAD click policy. Defaults to managed. */
56
+ cadInteraction?: CadInteractionMode;
57
+ cadInteractionStyle?: {
58
+ hover?: {
59
+ color?: ColorRepresentation;
60
+ fillOpacity?: number;
61
+ edgeOpacity?: number;
62
+ enabled?: boolean;
63
+ };
64
+ selected?: {
65
+ color?: ColorRepresentation;
66
+ fillOpacity?: number;
67
+ edgeOpacity?: number;
68
+ enabled?: boolean;
69
+ };
70
+ };
53
71
  cameraStateStore?: CameraStateStore | false;
54
72
  /** Max delay for merged viewport demands while the interaction is active. Default 160ms. */
55
73
  viewportDemandCoalesceMs?: number;
@@ -75,6 +93,10 @@ export interface ViewerPickResult {
75
93
  export interface ViewerHighlightOptions {
76
94
  color?: ColorRepresentation;
77
95
  }
96
+ export interface ViewerObjectOptions {
97
+ /** Viewer only mounts the object; the caller retains responsibility for GPU resource disposal. */
98
+ readonly ownership?: 'borrowed';
99
+ }
78
100
  export interface ViewerRenderStats {
79
101
  readonly drawCalls: number;
80
102
  readonly submittedVertexRefs: number;
@@ -121,6 +143,18 @@ export interface ViewerEventMap {
121
143
  };
122
144
  'camera-change': {
123
145
  viewMode: ViewerViewMode;
146
+ reason: CameraChangeReason;
147
+ interaction: 'active' | 'idle';
148
+ };
149
+ 'ui-layout-change': {
150
+ contentInsets: {
151
+ readonly top: number;
152
+ readonly right: number;
153
+ readonly bottom: number;
154
+ readonly left: number;
155
+ };
156
+ layerPanelOpen: boolean;
157
+ propertyPanelOpen: boolean;
124
158
  };
125
159
  }
126
160
  export interface Viewer {
@@ -131,13 +165,33 @@ export interface Viewer {
131
165
  readonly highlightedObject: Object3D | null;
132
166
  readonly isDisposed: boolean;
133
167
  readonly cad: CadViewerCapability;
134
- addObject(object: Object3D): void;
168
+ readonly overlays: {
169
+ set(key: string, object: Object3D, options?: {
170
+ owned?: boolean;
171
+ }): void;
172
+ remove(key: string, options?: {
173
+ dispose?: boolean;
174
+ }): Object3D | null;
175
+ };
176
+ readonly camera: {
177
+ getState(): ViewerCameraSnapshot | null;
178
+ /** Applies a validated snapshot immediately. Animated transitions are not part of the current contract. */
179
+ setState(snapshot: ViewerCameraSnapshot): boolean;
180
+ reset(): boolean;
181
+ };
182
+ readonly plugins: import('./plugins').ViewerPluginRegistry;
183
+ readonly commands: {
184
+ execute(id: string, args?: unknown): boolean;
185
+ has(id: string): boolean;
186
+ };
187
+ addObject(object: Object3D, options?: ViewerObjectOptions): void;
135
188
  removeObject(object: Object3D): boolean;
136
189
  setVisible(object: Object3D, visible: boolean): void;
137
190
  setViewMode(mode: ViewerViewMode, options?: {
138
191
  preserveOrbit?: boolean;
139
192
  }): void;
140
193
  fitView(target?: Object3D | Box3, options?: ViewerFitViewOptions): boolean;
194
+ fitAll(options?: ViewerFitViewOptions): boolean;
141
195
  screenToWorldOnPlane(clientX: number, clientY: number, planeZ?: number): Vector3 | null;
142
196
  pick(clientX: number, clientY: number, options?: ViewerPickOptions): ViewerPickResult | null;
143
197
  setSelection(object: Object3D | null): void;
@@ -146,4 +200,7 @@ export interface Viewer {
146
200
  resize(): boolean;
147
201
  requestRender(): void;
148
202
  dispose(): void;
203
+ use(plugins: readonly import('./plugins').ViewerPlugin[], options?: {
204
+ replace?: boolean;
205
+ }): void;
149
206
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebula-spatial/viewer",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Read-only Three.js viewer facade",
5
5
  "license": "ISC",
6
6
  "engines": {
@@ -36,7 +36,7 @@
36
36
  "prepack": "npm run build"
37
37
  },
38
38
  "dependencies": {
39
- "@nebula-spatial/cad-loader": "^0.2.4"
39
+ "@nebula-spatial/cad-loader": "^0.3.0"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "three": "0.182.0"