@nebula-spatial/viewer 0.3.0 → 0.4.1

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,235 @@
1
+ import type { Object3D, WebGLRenderer, Scene, Camera, Box3, ColorRepresentation } from 'three';
2
+ import type { CadLoadSource, CadOpenOptions } from '../cad/types';
3
+ import type { ViewerViewMode } from '../types';
4
+ export type AssetPreviewKind = 'cad' | 'model' | 'simulation';
5
+ export type BuiltinAssetFormat = 'cad' | 'glb' | 'gltf' | 'fbx' | 'urdf' | 'mjcf' | 'usd' | 'usda' | 'usdc' | 'usdz';
6
+ export type AssetFormat = BuiltinAssetFormat | (string & {});
7
+ export interface AssetFileEntry {
8
+ readonly path: string;
9
+ readonly file: File | Blob;
10
+ }
11
+ export type AssetSource = CadLoadSource | {
12
+ readonly kind: 'files';
13
+ readonly files: readonly AssetFileEntry[];
14
+ readonly entry?: string;
15
+ readonly signal?: AbortSignal;
16
+ };
17
+ export interface AssetResourceResolver {
18
+ resolve(path: string, options?: {
19
+ readonly signal?: AbortSignal;
20
+ }): string | URL | Blob | Promise<string | URL | Blob>;
21
+ }
22
+ export interface ViewerAssetOptions {
23
+ readonly dracoDecoderPath?: string;
24
+ readonly openUsdBaseUrl?: string;
25
+ readonly mujocoBaseUrl?: string;
26
+ }
27
+ export interface OpenAssetOptions {
28
+ readonly simulationTheme?: 'dark' | 'light';
29
+ readonly format?: AssetFormat | 'auto';
30
+ readonly filename?: string;
31
+ readonly signal?: AbortSignal;
32
+ readonly baseUrl?: string;
33
+ readonly resourceResolver?: AssetResourceResolver;
34
+ /** @deprecated Auto detection never falls back to CAD; pass an explicit format instead. */
35
+ readonly unknownFormatPolicy?: 'cad' | 'error';
36
+ readonly dracoDecoderPath?: string;
37
+ readonly mujocoBaseUrl?: string;
38
+ readonly openUsdBaseUrl?: string;
39
+ /**
40
+ * Which authored axis is "up" for a plain model asset. FBX first uses a
41
+ * supported positive Y/Z declaration from GlobalSettings, then falls back to
42
+ * 'y' when it is missing or cannot be read. GLB/glTF retain the existing 'z'
43
+ * default for CAD-kernel compatibility. An explicit value always wins. 'y'
44
+ * rotates the model root into world +Z.
45
+ * Ignored by simulation assets, whose formats declare their own up axis.
46
+ */
47
+ readonly upAxis?: AssetUpAxis;
48
+ /** CAD-specific compatibility options. `filename` and `signal` above take precedence. */
49
+ readonly cad?: Omit<CadOpenOptions, 'filename' | 'signal'>;
50
+ }
51
+ /** World axis a model was authored to treat as up. See `OpenAssetOptions.upAxis`. */
52
+ export type AssetUpAxis = 'y' | 'z';
53
+ export interface AssetPreviewCapabilities {
54
+ readonly canPlay: boolean;
55
+ readonly canPause: boolean;
56
+ readonly canReset: boolean;
57
+ readonly hasPhysics: boolean;
58
+ /**
59
+ * A plain model whose authored up axis can be re-oriented at runtime.
60
+ * Optional (absent means false) so pre-existing third-party asset sessions
61
+ * that build this literal keep compiling; only the model session opts in.
62
+ */
63
+ readonly canReorientUpAxis?: boolean;
64
+ /**
65
+ * The staged ground (grid + reflection) can be hidden. Optional, same
66
+ * rationale as `canReorientUpAxis`; model and simulation sessions opt in.
67
+ */
68
+ readonly canToggleGround?: boolean;
69
+ /**
70
+ * Authored or runtime collision geometry can be shown independently of
71
+ * dynamic physics. Static USD stages may declare colliders without rigid
72
+ * bodies, so this must not be inferred from `hasPhysics`.
73
+ */
74
+ readonly canToggleCollision?: boolean;
75
+ }
76
+ export type AssetPreviewPhase = 'detecting' | 'loading' | 'ready' | 'failed' | 'closing' | 'closed';
77
+ export interface AssetErrorSnapshot {
78
+ readonly code: string;
79
+ readonly message: string;
80
+ readonly stage: string;
81
+ readonly fatal: boolean;
82
+ }
83
+ /** Non-fatal condition that changed how an otherwise usable asset is presented. */
84
+ export interface AssetWarningSnapshot {
85
+ readonly code: string;
86
+ readonly message: string;
87
+ readonly stage: string;
88
+ }
89
+ export interface AssetPreviewSnapshot {
90
+ readonly revision: number;
91
+ readonly phase: AssetPreviewPhase;
92
+ readonly stage?: string;
93
+ readonly message?: string;
94
+ readonly format: AssetFormat | null;
95
+ readonly kind: AssetPreviewKind | null;
96
+ readonly capabilities: AssetPreviewCapabilities | null;
97
+ readonly error: AssetErrorSnapshot | null;
98
+ /** Present when loading succeeded with an explicit, user-visible degradation. */
99
+ readonly warning?: AssetWarningSnapshot | null;
100
+ /**
101
+ * Display name for the asset (`OpenAssetOptions.filename`, else the file name
102
+ * derived from the normalized source). Hosts should render this rather than
103
+ * re-deriving a name from the raw URL.
104
+ */
105
+ readonly filename: string | null;
106
+ }
107
+ /**
108
+ * Control state of the active asset. Published on `asset-control-change` so UI
109
+ * (built-in or plugin) never has to keep its own copy of a pressed state —
110
+ * whichever entry point changed it, exactly one authoritative value is broadcast.
111
+ */
112
+ export interface AssetControlState {
113
+ readonly playing: boolean;
114
+ readonly groundVisible: boolean;
115
+ readonly collisionVisible: boolean;
116
+ readonly upAxis: AssetUpAxis | null;
117
+ }
118
+ export interface AssetPreview {
119
+ getPhysicsProperties(): Readonly<Record<string, unknown>> | null;
120
+ setCollisionVisible(visible: boolean): void;
121
+ /** Re-orients a plain model's authored up axis without moving the camera. */
122
+ setUpAxis(axis: AssetUpAxis): void;
123
+ getUpAxis(): AssetUpAxis | null;
124
+ /** Shows or hides the staged ground (grid + reflection). */
125
+ setGroundVisible(visible: boolean): void;
126
+ getGroundVisible(): boolean;
127
+ readonly id: string;
128
+ readonly ready: Promise<void>;
129
+ getSnapshot(): AssetPreviewSnapshot;
130
+ subscribe(listener: (snapshot: AssetPreviewSnapshot) => void): () => void;
131
+ close(): Promise<void>;
132
+ play(): void;
133
+ pause(): void;
134
+ reset(): void;
135
+ beginGrab?(event: PointerEvent, camera: import('three').Camera, canvas: HTMLElement): boolean;
136
+ moveGrab?(event: PointerEvent, camera: import('three').Camera, canvas: HTMLElement): void;
137
+ endGrab?(): void;
138
+ }
139
+ export interface AssetPresentationProfile {
140
+ readonly preferredViewMode?: ViewerViewMode;
141
+ /**
142
+ * Canvas clear color while this asset is active. Applied on profile change and
143
+ * reverted to the `createViewer({ clearColor })` value when no asset owns one,
144
+ * so hosts no longer need to encode "which asset is this" in their bootstrap.
145
+ */
146
+ readonly clearColor?: ColorRepresentation;
147
+ /** Staged dark/light variant for assets that own a themed stage. */
148
+ readonly stageTheme?: 'dark' | 'light';
149
+ readonly interactionMode?: 'cad-managed' | 'object' | 'simulation';
150
+ readonly uiProfile?: 'cad' | 'model' | 'simulation' | 'none';
151
+ readonly fitPolicy?: 'restore-then-fit' | 'fit-on-ready' | 'preserve';
152
+ readonly environmentProfile?: string;
153
+ }
154
+ export interface NormalizedAssetSource {
155
+ readonly original: AssetSource;
156
+ readonly entryPath: string | null;
157
+ readonly filename: string | null;
158
+ readonly url: string | null;
159
+ readonly signal: AbortSignal;
160
+ readonly baseUrl: string | null;
161
+ readonly hasResourceResolver: boolean;
162
+ resolve(path: string): Promise<string>;
163
+ release(): void;
164
+ }
165
+ export interface AssetDetectionResult {
166
+ readonly format: AssetFormat;
167
+ }
168
+ export interface AssetLoaderContext {
169
+ readonly signal: AbortSignal;
170
+ readonly options: OpenAssetOptions;
171
+ readonly source: NormalizedAssetSource;
172
+ readonly format: AssetFormat;
173
+ requestRender(): void;
174
+ fit(root: Object3D | Box3, options?: {
175
+ readonly viewMode?: ViewerViewMode;
176
+ readonly padding?: number;
177
+ }): boolean;
178
+ reportError(error: unknown, options: {
179
+ readonly stage: string;
180
+ readonly fatal: boolean;
181
+ readonly code?: string;
182
+ }): void;
183
+ reportProgress?(stage: string, message?: string): void;
184
+ }
185
+ export interface AssetSession {
186
+ setTheme?(theme: 'dark' | 'light'): void;
187
+ beforeRender?(renderer: WebGLRenderer, scene: Scene, camera: Camera): (() => void) | void;
188
+ getPhysicsProperties?(): Readonly<Record<string, unknown>> | null;
189
+ setCollisionVisible?(visible: boolean): void;
190
+ setUpAxis?(axis: AssetUpAxis): void;
191
+ getUpAxis?(): AssetUpAxis | null;
192
+ setGroundVisible?(visible: boolean): void;
193
+ getGroundVisible?(): boolean;
194
+ getFitBounds?(): Box3 | null;
195
+ readonly fitTarget?: Object3D | Box3 | null;
196
+ readonly id: string;
197
+ readonly opened: Promise<void>;
198
+ readonly root?: Object3D | null;
199
+ readonly capabilities?: AssetPreviewCapabilities;
200
+ readonly presentation?: AssetPresentationProfile;
201
+ /** Non-fatal degradation decided by the loader after the session opened. */
202
+ readonly warning?: AssetWarningSnapshot | null;
203
+ beginDispose(): void;
204
+ dispose(): Promise<void>;
205
+ play?(): void;
206
+ pause?(): void;
207
+ reset?(): void;
208
+ update?(deltaSeconds: number): boolean;
209
+ beginGrab?(event: PointerEvent, camera: import('three').Camera, canvas: HTMLElement): boolean;
210
+ moveGrab?(event: PointerEvent, camera: import('three').Camera, canvas: HTMLElement): void;
211
+ endGrab?(): void;
212
+ }
213
+ export interface AssetLoaderContribution {
214
+ readonly kind: 'asset-loader';
215
+ readonly id: string;
216
+ readonly assetKind: AssetPreviewKind;
217
+ readonly formats: readonly AssetFormat[];
218
+ readonly presentation?: AssetPresentationProfile;
219
+ match?(source: NormalizedAssetSource): AssetDetectionResult | null | Promise<AssetDetectionResult | null>;
220
+ createSession(source: NormalizedAssetSource, context: AssetLoaderContext): AssetSession | Promise<AssetSession>;
221
+ }
222
+ export interface AssetChangeEvent {
223
+ readonly sessionId: string;
224
+ readonly snapshot: AssetPreviewSnapshot;
225
+ }
226
+ export interface AssetControlChangeEvent {
227
+ readonly sessionId: string;
228
+ readonly state: AssetControlState;
229
+ }
230
+ export interface AssetErrorEvent extends AssetErrorSnapshot {
231
+ readonly sessionId: string;
232
+ readonly format: AssetFormat | null;
233
+ readonly error: Error;
234
+ }
235
+ export declare const EMPTY_ASSET_CAPABILITIES: AssetPreviewCapabilities;
@@ -1,6 +1,7 @@
1
1
  import type { RenderDebugBreakdownEntry, SplitBlockLayerDetail } from '@nebula-spatial/cad-loader';
2
2
  import type { CadSessionLoadTiming } from '@nebula-spatial/cad-loader';
3
3
  import type { ViewerViewMode } from '../types';
4
+ export type { ViewerUiOptions, ViewerSceneUiOptions, ViewerSceneUiParts, ViewerUiPreset, ViewerUiTheme, ViewerUiTokens } from '../ui/types';
4
5
  export type { CadSessionLoadTiming } from '@nebula-spatial/cad-loader';
5
6
  export type { RenderDebugBreakdownEntry, SplitBlockLayerDetail } from '@nebula-spatial/cad-loader';
6
7
  export type CadSessionPhase = 'created' | 'header-ready' | 'initial-loading' | 'interactive' | 'failed' | 'disposed';
@@ -303,7 +304,14 @@ export interface CadViewerCapability {
303
304
  readonly insertPreview: true;
304
305
  readonly batchVisibility: true;
305
306
  };
307
+ /**
308
+ * @deprecated Use `viewer.openAsset(source, { format: 'cad', cad: options })`
309
+ * and read `viewer.cad.document` after `asset.ready`.
310
+ */
306
311
  open(source: CadLoadSource, options?: CadOpenOptions): Promise<CadDocumentHandle>;
312
+ /**
313
+ * @deprecated Use `viewer.closeAsset()` to close the active asset.
314
+ */
307
315
  close(): void;
308
316
  renderInsertPreview(canvas: HTMLCanvasElement): boolean;
309
317
  hitTest(clientX: number, clientY: number, options?: {
@@ -316,9 +324,7 @@ export interface CadViewerCapability {
316
324
  fitSelection(): boolean;
317
325
  on<K extends keyof CadViewerEventMap>(type: K, listener: (event: CadViewerEventMap[K]) => void): () => void;
318
326
  }
319
- export type ViewerUiPreset = 'cad-readonly';
320
- export type ViewerUiTheme = 'dark' | 'light';
321
- export interface ViewerUiParts {
327
+ export interface ViewerCadUiParts {
322
328
  layerPanel?: boolean;
323
329
  propertyPanel?: boolean;
324
330
  hud?: boolean;
@@ -332,30 +338,23 @@ export interface ViewerUiParts {
332
338
  /** Show the Debug details card in the infobar. Default: true. */
333
339
  infobarDebug?: boolean;
334
340
  }
335
- export interface ViewerUiOptions {
336
- root: HTMLElement;
337
- preset?: ViewerUiPreset;
338
- parts?: ViewerUiParts;
339
- theme?: ViewerUiTheme;
340
- tokens?: ViewerUiTokens;
341
- style?: string | ((root: ShadowRoot) => void);
341
+ /** Options belonging only to the built-in CAD UI group. */
342
+ export interface ViewerCadUiOptions {
343
+ parts?: ViewerCadUiParts;
342
344
  persistence?: boolean;
343
345
  persistenceKey?: string;
344
- stateStore?: ViewerUiStateStore;
345
- keyboard?: ViewerUiKeyboardOptions;
346
- locale?: string;
347
- messages?: Readonly<Record<string, string>>;
348
- messageResolver?: (key: string, params?: Readonly<Record<string, string | number>>) => string | undefined;
346
+ stateStore?: ViewerCadUiStateStore;
347
+ keyboard?: ViewerCadUiKeyboardOptions;
349
348
  unitFormatter?: (value: number, context: {
350
349
  readonly unit: string;
351
350
  readonly viewMode: ViewerViewMode;
352
351
  }) => string;
353
352
  }
354
- export interface ViewerUiStateStore {
353
+ export interface ViewerCadUiStateStore {
355
354
  read(key: string): string | null;
356
355
  write(key: string, value: string): void;
357
356
  }
358
- export interface ViewerUiKeyboardOptions {
357
+ export interface ViewerCadUiKeyboardOptions {
359
358
  enabled?: boolean;
360
359
  /**
361
360
  * Restrict shortcuts to this element. A pointer interaction inside it focuses
@@ -367,15 +366,6 @@ export interface ViewerUiKeyboardOptions {
367
366
  fit?: string | false;
368
367
  };
369
368
  }
370
- export interface ViewerUiTokens {
371
- accent?: string;
372
- panelBackground?: string;
373
- background?: string;
374
- text?: string;
375
- textDim?: string;
376
- border?: string;
377
- hudBackground?: string;
378
- }
379
369
  export interface CameraStateStore {
380
370
  read(key: string): unknown | null;
381
371
  write(key: string, value: unknown): void;