@found-in-space/skykit 0.2.0-alpha.0 → 0.2.0-dev.20260527.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.
Files changed (42) hide show
  1. package/README.md +223 -8
  2. package/examples/custom-object-layer/custom-object-layer.js +1 -24
  3. package/examples/xr-free-roam/index.html +62 -4
  4. package/examples/xr-free-roam/xr-free-roam.css +249 -18
  5. package/examples/xr-free-roam/xr-free-roam.js +644 -217
  6. package/package.json +46 -5
  7. package/src/__tests__/skykit-anchored-images.test.js +32 -4
  8. package/src/__tests__/skykit-browser.test.js +442 -0
  9. package/src/__tests__/skykit-data.test.js +131 -0
  10. package/src/__tests__/skykit-parallax.test.js +4 -4
  11. package/src/__tests__/skykit-touch-os.test.js +71 -0
  12. package/src/__tests__/skykit-xr.test.js +123 -2
  13. package/src/__tests__/skykit.test.js +138 -1
  14. package/src/anchored-images.js +14 -15
  15. package/src/browser-addons.d.ts +16 -0
  16. package/src/browser-addons.js +155 -0
  17. package/src/browser-constellations.d.ts +13 -0
  18. package/src/browser-constellations.js +387 -0
  19. package/src/browser-journey.d.ts +8 -0
  20. package/src/browser-journey.js +240 -0
  21. package/src/browser.d.ts +170 -0
  22. package/src/browser.js +369 -0
  23. package/src/data.d.ts +133 -0
  24. package/src/data.js +447 -0
  25. package/src/embed.d.ts +6 -0
  26. package/src/embed.js +119 -0
  27. package/src/hr-diagram.js +23 -5
  28. package/src/index.d.ts +32 -7
  29. package/src/plugins.js +87 -43
  30. package/src/story.d.ts +57 -0
  31. package/src/story.js +396 -0
  32. package/src/three-shim.d.ts +32 -0
  33. package/src/touch-os.d.ts +70 -0
  34. package/src/touch-os.js +275 -0
  35. package/src/utils.js +96 -6
  36. package/src/viewer-entry.d.ts +10 -0
  37. package/src/viewer-entry.js +4 -0
  38. package/src/viewer.js +110 -12
  39. package/src/xr/plugins.js +224 -13
  40. package/src/xr/session.js +60 -14
  41. package/src/xr.d.ts +22 -0
  42. package/src/xr.js +1 -0
package/src/index.d.ts CHANGED
@@ -67,6 +67,18 @@ export interface QuaternionLike {
67
67
  w: number;
68
68
  }
69
69
 
70
+ export interface SkykitLookAtInput {
71
+ targetPc?: Vector3Like | [number, number, number];
72
+ raDeg?: number;
73
+ raHours?: number;
74
+ decDeg?: number;
75
+ distancePc?: number;
76
+ star?: string | StarObjectRef | StarPickMeta | unknown;
77
+ orientationIcrs?: QuaternionLike;
78
+ positionAngleDeg?: number;
79
+ [key: string]: unknown;
80
+ }
81
+
70
82
  export interface SkykitObserverMotion {
71
83
  velocityPcPerSec: Vector3Like;
72
84
  speedPcPerSec: number;
@@ -76,8 +88,8 @@ export interface SkykitViewState {
76
88
  revision: number;
77
89
  observerPc: Vector3Like;
78
90
  renderObserverPosition: Vector3Like;
91
+ lookAt?: SkykitLookAtInput | null;
79
92
  targetPc?: Vector3Like | null;
80
- directionIcrs?: Vector3Like | null;
81
93
  orientationIcrs?: QuaternionLike | null;
82
94
  limitingMagnitude: number;
83
95
  verticalFovDeg?: number;
@@ -370,6 +382,8 @@ export interface SkykitViewerOptions {
370
382
  parts?: Iterable<SkykitThreePart>;
371
383
  plugins?: Iterable<SkykitPluginInput>;
372
384
  view?: Partial<SkykitViewState>;
385
+ resolveLookAtStar?: (star: unknown, lookAt: SkykitLookAtInput) => SkykitLookAtInput | Vector3Like | [number, number, number] | Promise<SkykitLookAtInput | Vector3Like | [number, number, number] | null> | null;
386
+ resolveLookAtBookmark?: (bookmarkId: string, lookAt: unknown) => SkykitLookAtInput | Vector3Like | [number, number, number] | Promise<SkykitLookAtInput | Vector3Like | [number, number, number] | null> | null;
373
387
  autoMountRenderer?: boolean;
374
388
  }
375
389
 
@@ -412,6 +426,7 @@ export interface SkykitViewer {
412
426
  readonly observerRig: SkykitObserverRig;
413
427
  readonly actions: SkykitActionRegistry;
414
428
  addPart(part: SkykitThreePart): SkykitPluginTeardown;
429
+ addPlugin(plugin: SkykitPluginInput): Promise<SkykitPluginTeardown>;
415
430
  getViewState(): SkykitViewState;
416
431
  requestViewState(patch: Partial<SkykitViewState>, reason?: string): void;
417
432
  update(deltaSeconds?: number, frameOptions?: SkykitFrameOptions): void;
@@ -501,11 +516,11 @@ export interface AnchoredImageCatalog {
501
516
  resolveTargetPc(key: string, options?: AnchoredImageTargetOptions): Vector3Like | null;
502
517
  resolveLookAt(key: string, options?: AnchoredImageLookAtOptions): AnchoredImageLookAtResult | null;
503
518
  resolveNearest(
504
- directionIcrs: Vector3Like | [number, number, number],
519
+ lookDirection: Vector3Like | [number, number, number],
505
520
  options?: AnchoredImageResolveNearestOptions
506
521
  ): AnchoredImageMatch | null;
507
522
  resolveWithinAngle(
508
- directionIcrs: Vector3Like | [number, number, number],
523
+ lookDirection: Vector3Like | [number, number, number],
509
524
  options: AnchoredImageResolveWithinAngleOptions
510
525
  ): AnchoredImageMatch[];
511
526
  }
@@ -553,6 +568,8 @@ export interface AnchoredImageSkyPluginOptions {
553
568
  catalog: AnchoredImageCatalog;
554
569
  controller: AnchoredImageController;
555
570
  loading?: AnchoredImageSkyLoading;
571
+ anchorMode?: SkykitLayerAnchorMode;
572
+ scaleBandId?: string;
556
573
  fixedAtInfinity?: boolean;
557
574
  radius?: number;
558
575
  opacity?: number;
@@ -674,7 +691,7 @@ export interface SkykitStarSourceRestartRetentionPolicy {
674
691
  }
675
692
 
676
693
  export interface SkykitHrDiagramTouchOsOptions {
677
- surfaces?: EmbeddedSurfaceService;
694
+ surfaces?: EmbeddedSurfaceService | (() => EmbeddedSurfaceService | null | undefined);
678
695
  sourceId?: string;
679
696
  componentId?: string;
680
697
  width?: number;
@@ -938,6 +955,16 @@ export interface SkykitJourneyPluginOptions {
938
955
  ) => void;
939
956
  }
940
957
 
958
+ export interface SkykitJourneyPlugin extends SkykitPlugin {
959
+ goTo(payload: unknown): Promise<JourneySceneSpec | null>;
960
+ next(): Promise<JourneySceneSpec | null>;
961
+ previous(): Promise<JourneySceneSpec | null>;
962
+ seek(payload: unknown): number;
963
+ play(payload?: unknown): number;
964
+ pause(): number;
965
+ getSnapshot(): unknown;
966
+ }
967
+
941
968
  export interface SkykitSpatialPreloadStrategyOptions {
942
969
  combine?: boolean;
943
970
  baseStrategy?: StarCellStrategy;
@@ -1170,9 +1197,7 @@ export declare function createKeyboardNavigationPlugin(options?: SkykitKeyboardN
1170
1197
  export declare function createSkykitNavigationPlugin(options?: SkykitNavigationPluginOptions): SkykitPlugin & {
1171
1198
  getSnapshot(): unknown;
1172
1199
  };
1173
- export declare function createSkykitJourneyPlugin(options?: SkykitJourneyPluginOptions): SkykitPlugin & {
1174
- getSnapshot(): unknown;
1175
- };
1200
+ export declare function createSkykitJourneyPlugin(options?: SkykitJourneyPluginOptions): SkykitJourneyPlugin;
1176
1201
  export declare function createSkykitStarPreloadRequestsFromSpatialHints(
1177
1202
  hints: Iterable<SpatialPreloadHint>,
1178
1203
  options?: SkykitSpatialPreloadStrategyOptions
package/src/plugins.js CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  createOrbitTransferRoute,
20
20
  createSpatialPoseTransition,
21
21
  createSpatialNavigationAutomation,
22
+ resolveSpatialLookAt,
22
23
  resolveSpatialTarget,
23
24
  } from '@found-in-space/spatial';
24
25
 
@@ -219,8 +220,12 @@ export function createSkykitNavigationPlugin(options = {}) {
219
220
  id,
220
221
  setup(context) {
221
222
  const threeContext = /** @type {import('./index.d.ts').SkykitThreePluginContext} */ (context);
222
- threeContext.addPart(part);
223
- threeContext.addDisposable(registerNavigationActions(threeContext));
223
+ const removePart = threeContext.addPart(part);
224
+ const unregisterActions = registerNavigationActions(threeContext);
225
+ return () => {
226
+ unregisterActions();
227
+ removePart();
228
+ };
224
229
  },
225
230
  getSnapshot: () => part.getSnapshot?.() ?? null,
226
231
  };
@@ -366,10 +371,24 @@ export function createSkykitNavigationPlugin(options = {}) {
366
371
  const position = positionInput === undefined
367
372
  ? current.observerPc
368
373
  : await resolveTarget(positionInput, context) ?? current.observerPc;
374
+ const lookAtInput = targetSource.lookAt;
375
+ const resolvedLook = lookAtInput !== undefined
376
+ ? await resolveSpatialLookAt(lookAtInput, {
377
+ observerPc: position,
378
+ resolveBookmark: typeof options.resolveBookmark === 'function'
379
+ ? (bookmarkId, original) => options.resolveBookmark?.(
380
+ bookmarkId,
381
+ /** @type {import('@found-in-space/spatial').SpatialTargetInput} */ (original),
382
+ context,
383
+ ) ?? null
384
+ : undefined,
385
+ })
386
+ : null;
369
387
  const orientationInput = targetSource.orientationIcrs
370
388
  ?? (targetSource.orientation && isQuaternionLike(targetSource.orientation) ? targetSource.orientation : undefined)
371
389
  ?? source.orientationIcrs;
372
- const orientation = normalizeQuaternion(orientationInput, current.orientationIcrs ?? IDENTITY_QUATERNION);
390
+ const orientation = resolvedLook?.orientationIcrs
391
+ ?? normalizeQuaternion(orientationInput, current.orientationIcrs ?? IDENTITY_QUATERNION);
373
392
  return createSpatialPoseTransition({
374
393
  from: {
375
394
  position: current.observerPc,
@@ -391,7 +410,7 @@ export function createSkykitNavigationPlugin(options = {}) {
391
410
 
392
411
  /**
393
412
  * @param {import('./index.d.ts').SkykitJourneyPluginOptions} [options]
394
- * @returns {SkykitPlugin & { getSnapshot(): unknown }}
413
+ * @returns {import('./index.d.ts').SkykitJourneyPlugin}
395
414
  */
396
415
  export function createSkykitJourneyPlugin(options = {}) {
397
416
  const id = options.id ?? 'journey';
@@ -458,56 +477,81 @@ export function createSkykitJourneyPlugin(options = {}) {
458
477
  id,
459
478
  setup(context) {
460
479
  const threeContext = /** @type {import('./index.d.ts').SkykitThreePluginContext} */ (context);
461
- threeContext.addPart(part);
462
- threeContext.addDisposable(registerJourneyActions(threeContext));
480
+ const removePart = threeContext.addPart(part);
481
+ const unregisterActions = registerJourneyActions(threeContext);
482
+ return () => {
483
+ unregisterActions();
484
+ removePart();
485
+ };
463
486
  },
487
+ goTo,
488
+ next,
489
+ previous,
490
+ seek,
491
+ play,
492
+ pause,
464
493
  getSnapshot,
465
494
  };
466
495
 
496
+ /** @param {unknown} payload */
497
+ async function goTo(payload) {
498
+ await pendingSceneApplication;
499
+ const sceneId = resolveSceneId(payload);
500
+ const spec = sceneId && controller ? controller.goTo(sceneId, { source: SKYKIT_ACTIONS.journey.goToChapter }) : null;
501
+ await pendingSceneApplication;
502
+ return spec;
503
+ }
504
+
505
+ async function next() {
506
+ await pendingSceneApplication;
507
+ const spec = controller?.next({ source: SKYKIT_ACTIONS.journey.next }) ?? null;
508
+ await pendingSceneApplication;
509
+ return spec;
510
+ }
511
+
512
+ async function previous() {
513
+ await pendingSceneApplication;
514
+ const spec = controller?.previous({ source: SKYKIT_ACTIONS.journey.previous }) ?? null;
515
+ await pendingSceneApplication;
516
+ return spec;
517
+ }
518
+
519
+ /** @param {unknown} payload */
520
+ function seek(payload) {
521
+ currentTimeSecs = clampTime(resolveTimeSecs(payload), evaluator?.durationSecs ?? Number.POSITIVE_INFINITY);
522
+ if (evaluator && pluginContext) applyTimedFrame(evaluator.evaluate(currentTimeSecs), { viewer: pluginContext.viewer, view: pluginContext.getViewState() });
523
+ if (pluginContext) emitTimedPreloadHints(pluginContext, 'seek');
524
+ return currentTimeSecs;
525
+ }
526
+
527
+ /** @param {unknown} [payload] */
528
+ function play(payload) {
529
+ if (payload && typeof payload === 'object' && 'timeSecs' in payload) {
530
+ currentTimeSecs = clampTime(resolveTimeSecs(payload), evaluator?.durationSecs ?? Number.POSITIVE_INFINITY);
531
+ }
532
+ playing = true;
533
+ if (pluginContext) emitTimedPreloadHints(pluginContext, 'play');
534
+ return currentTimeSecs;
535
+ }
536
+
537
+ function pause() {
538
+ playing = false;
539
+ return currentTimeSecs;
540
+ }
541
+
467
542
  /** @param {import('./index.d.ts').SkykitThreePluginContext} context */
468
543
  function registerJourneyActions(context) {
469
544
  const unregisters = [
470
- context.actions.registerAction(SKYKIT_ACTIONS.journey.goToChapter, async ({ payload }) => {
471
- await pendingSceneApplication;
472
- const sceneId = resolveSceneId(payload);
473
- const spec = sceneId && controller ? controller.goTo(sceneId, { source: SKYKIT_ACTIONS.journey.goToChapter }) : null;
474
- await pendingSceneApplication;
475
- return spec;
476
- }, { label: 'Go to journey chapter' }),
477
- context.actions.registerAction(SKYKIT_ACTIONS.journey.next, async () => {
478
- await pendingSceneApplication;
479
- const spec = controller?.next({ source: SKYKIT_ACTIONS.journey.next }) ?? null;
480
- await pendingSceneApplication;
481
- return spec;
482
- }, {
545
+ context.actions.registerAction(SKYKIT_ACTIONS.journey.goToChapter, ({ payload }) => goTo(payload), { label: 'Go to journey chapter' }),
546
+ context.actions.registerAction(SKYKIT_ACTIONS.journey.next, () => next(), {
483
547
  label: 'Next journey chapter',
484
548
  }),
485
- context.actions.registerAction(SKYKIT_ACTIONS.journey.previous, async () => {
486
- await pendingSceneApplication;
487
- const spec = controller?.previous({ source: SKYKIT_ACTIONS.journey.previous }) ?? null;
488
- await pendingSceneApplication;
489
- return spec;
490
- }, {
549
+ context.actions.registerAction(SKYKIT_ACTIONS.journey.previous, () => previous(), {
491
550
  label: 'Previous journey chapter',
492
551
  }),
493
- context.actions.registerAction(SKYKIT_ACTIONS.journey.seek, ({ payload }) => {
494
- currentTimeSecs = clampTime(resolveTimeSecs(payload), evaluator?.durationSecs ?? Number.POSITIVE_INFINITY);
495
- if (evaluator) applyTimedFrame(evaluator.evaluate(currentTimeSecs), { viewer: context.viewer, view: context.getViewState() });
496
- emitTimedPreloadHints(context, 'seek');
497
- return currentTimeSecs;
498
- }, { label: 'Seek journey time' }),
499
- context.actions.registerAction(SKYKIT_ACTIONS.journey.play, ({ payload }) => {
500
- if (payload && typeof payload === 'object' && 'timeSecs' in payload) {
501
- currentTimeSecs = clampTime(resolveTimeSecs(payload), evaluator?.durationSecs ?? Number.POSITIVE_INFINITY);
502
- }
503
- playing = true;
504
- emitTimedPreloadHints(context, 'play');
505
- return currentTimeSecs;
506
- }, { label: 'Play journey' }),
507
- context.actions.registerAction(SKYKIT_ACTIONS.journey.pause, () => {
508
- playing = false;
509
- return currentTimeSecs;
510
- }, { label: 'Pause journey' }),
552
+ context.actions.registerAction(SKYKIT_ACTIONS.journey.seek, ({ payload }) => seek(payload), { label: 'Seek journey time' }),
553
+ context.actions.registerAction(SKYKIT_ACTIONS.journey.play, ({ payload }) => play(payload), { label: 'Play journey' }),
554
+ context.actions.registerAction(SKYKIT_ACTIONS.journey.pause, () => pause(), { label: 'Pause journey' }),
511
555
  ];
512
556
  return () => {
513
557
  for (const unregister of unregisters.reverse()) unregister();
package/src/story.d.ts ADDED
@@ -0,0 +1,57 @@
1
+ import type { SkykitBrowser, SkykitBrowserHost, SkykitBrowserOptions } from './browser.js';
2
+ import type { SkykitViewState, SkykitViewer, Vector3Like } from './index.js';
3
+
4
+ export type SkykitStoryHost = SkykitBrowserHost;
5
+
6
+ export interface SkykitStoryChapterInput {
7
+ id?: string;
8
+ title?: string;
9
+ body?: string;
10
+ bodyHtml?: string | null;
11
+ targetPc?: Vector3Like | null;
12
+ observerPc?: Vector3Like | null;
13
+ view?: Partial<SkykitViewState> | null;
14
+ annotations?: unknown[];
15
+ }
16
+
17
+ export interface SkykitStoryChapter {
18
+ id: string;
19
+ title: string;
20
+ body: string;
21
+ bodyHtml: string | null;
22
+ targetPc: Vector3Like | null;
23
+ observerPc: Vector3Like | null;
24
+ view: Partial<SkykitViewState> | null;
25
+ annotations: unknown[];
26
+ }
27
+
28
+ export interface SkykitStoryOptions extends SkykitBrowserOptions {
29
+ host?: SkykitStoryHost;
30
+ src?: string;
31
+ chapters?: Iterable<SkykitStoryChapterInput>;
32
+ initialChapter?: string | number;
33
+ controls?: boolean;
34
+ previousLabel?: string;
35
+ nextLabel?: string;
36
+ }
37
+
38
+ export interface SkykitStory {
39
+ browser: SkykitBrowser;
40
+ viewer: SkykitViewer;
41
+ chapters: SkykitStoryChapter[];
42
+ readonly currentIndex: number;
43
+ readonly currentChapter: SkykitStoryChapter | null;
44
+ next(): SkykitStoryChapter | null;
45
+ previous(): SkykitStoryChapter | null;
46
+ goTo(indexOrId: number | string): SkykitStoryChapter | null;
47
+ dispose(): Promise<void>;
48
+ }
49
+
50
+ export declare function createSkykitStory(
51
+ host: SkykitStoryHost,
52
+ options?: SkykitStoryOptions
53
+ ): Promise<SkykitStory>;
54
+
55
+ export declare function createSkykitStory(
56
+ options?: SkykitStoryOptions
57
+ ): Promise<SkykitStory>;