@viamrobotics/motion-tools 0.12.0 → 0.14.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 (39) hide show
  1. package/dist/Detail.svelte.d.ts +46 -0
  2. package/dist/Detail.svelte.js +155 -0
  3. package/dist/WorldObject.svelte.d.ts +20 -8
  4. package/dist/WorldObject.svelte.js +84 -19
  5. package/dist/color.d.ts +6 -1
  6. package/dist/color.js +60 -0
  7. package/dist/components/App.svelte +45 -1
  8. package/dist/components/App.svelte.d.ts +8 -0
  9. package/dist/components/Details.svelte +315 -105
  10. package/dist/components/LiveUpdatesBanner.svelte +39 -0
  11. package/dist/components/LiveUpdatesBanner.svelte.d.ts +3 -0
  12. package/dist/components/Selected.svelte +10 -10
  13. package/dist/components/Tree/AddFrames.svelte +28 -0
  14. package/dist/components/Tree/AddFrames.svelte.d.ts +18 -0
  15. package/dist/components/Tree/TreeContainer.svelte +6 -1
  16. package/dist/components/Tree/buildTree.js +1 -1
  17. package/dist/components/WorldObjects.svelte +3 -1
  18. package/dist/components/WorldState.svelte +53 -1
  19. package/dist/components/weblab/WeblabActive.svelte +3 -2
  20. package/dist/components/weblab/WeblabActive.svelte.d.ts +1 -0
  21. package/dist/hooks/useDrawAPI.svelte.js +2 -2
  22. package/dist/hooks/useEnvironment.svelte.d.ts +10 -0
  23. package/dist/hooks/useEnvironment.svelte.js +19 -0
  24. package/dist/hooks/useFramelessComponents.svelte.d.ts +6 -0
  25. package/dist/hooks/useFramelessComponents.svelte.js +30 -0
  26. package/dist/hooks/useFrames.svelte.d.ts +1 -0
  27. package/dist/hooks/useFrames.svelte.js +163 -5
  28. package/dist/hooks/useGeometries.svelte.js +4 -1
  29. package/dist/hooks/usePartConfig.svelte.d.ts +101 -0
  30. package/dist/hooks/usePartConfig.svelte.js +316 -0
  31. package/dist/hooks/usePose.svelte.js +6 -1
  32. package/dist/three/BatchedArrow.d.ts +5 -2
  33. package/dist/three/BatchedArrow.js +34 -23
  34. package/dist/three/OBBHelper.d.ts +14 -0
  35. package/dist/three/OBBHelper.js +71 -0
  36. package/dist/transform.d.ts +7 -1
  37. package/dist/transform.js +70 -1
  38. package/dist/workers/worldStateWorker.js +7 -2
  39. package/package.json +2 -1
@@ -0,0 +1,46 @@
1
+ import type { WorldObject } from './lib';
2
+ import type { Geometries } from './WorldObject.svelte';
3
+ import type { Pose } from '@viamrobotics/sdk';
4
+ type UpdateFrameCallback = {
5
+ (componentName: string, referenceFrame: string, pose: Pose, geometry?: {
6
+ type: 'none' | 'box' | 'sphere' | 'capsule';
7
+ r?: number;
8
+ l?: number;
9
+ x?: number;
10
+ y?: number;
11
+ z?: number;
12
+ }): void;
13
+ };
14
+ type RemoveFrameCallback = {
15
+ (componentName: string): void;
16
+ };
17
+ export declare class DetailConfigUpdater {
18
+ private object;
19
+ private referenceFrame;
20
+ private updateFrame;
21
+ private removeFrame;
22
+ constructor(object: () => WorldObject<Geometries> | undefined, updateFrame: UpdateFrameCallback, removeFrame: RemoveFrameCallback, referenceFrame: () => string);
23
+ updateLocalPosition: ({ x, y, z }: {
24
+ x?: number;
25
+ y?: number;
26
+ z?: number;
27
+ }) => void;
28
+ updateLocalOrientation: ({ oX, oY, oZ, theta, }: {
29
+ oX?: number;
30
+ oY?: number;
31
+ oZ?: number;
32
+ theta?: number;
33
+ }) => void;
34
+ updateGeometry: (geometry: {
35
+ type: "none" | "box" | "sphere" | "capsule";
36
+ r?: number;
37
+ l?: number;
38
+ x?: number;
39
+ y?: number;
40
+ z?: number;
41
+ }) => void;
42
+ setFrameParent: (parentName: string) => void;
43
+ deleteFrame: () => void;
44
+ setGeometryType: (type: "none" | "box" | "sphere" | "capsule") => void;
45
+ }
46
+ export {};
@@ -0,0 +1,155 @@
1
+ export class DetailConfigUpdater {
2
+ object;
3
+ referenceFrame;
4
+ updateFrame;
5
+ removeFrame;
6
+ constructor(object, updateFrame, removeFrame, referenceFrame) {
7
+ this.referenceFrame = referenceFrame;
8
+ this.object = object;
9
+ this.updateFrame = updateFrame;
10
+ this.removeFrame = removeFrame;
11
+ }
12
+ updateLocalPosition = ({ x, y, z }) => {
13
+ const object = this.object();
14
+ if (!object)
15
+ return;
16
+ object.localEditedPose.x = x ?? object.localEditedPose.x;
17
+ object.localEditedPose.y = y ?? object.localEditedPose.y;
18
+ object.localEditedPose.z = z ?? object.localEditedPose.z;
19
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
20
+ x: x ?? object.localEditedPose.x,
21
+ y: y ?? object.localEditedPose.y,
22
+ z: z ?? object.localEditedPose.z,
23
+ oX: object.localEditedPose.oX,
24
+ oY: object.localEditedPose.oY,
25
+ oZ: object.localEditedPose.oZ,
26
+ theta: object.localEditedPose.theta,
27
+ });
28
+ };
29
+ updateLocalOrientation = ({ oX, oY, oZ, theta, }) => {
30
+ const object = this.object();
31
+ if (!object)
32
+ return;
33
+ object.localEditedPose.oX = oX ?? object.localEditedPose.oX;
34
+ object.localEditedPose.oY = oY ?? object.localEditedPose.oY;
35
+ object.localEditedPose.oZ = oZ ?? object.localEditedPose.oZ;
36
+ object.localEditedPose.theta = theta ?? object.localEditedPose.theta;
37
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
38
+ oX: oX ?? object.localEditedPose.oX,
39
+ oY: oY ?? object.localEditedPose.oY,
40
+ oZ: oZ ?? object.localEditedPose.oZ,
41
+ theta: theta ?? object.localEditedPose.theta,
42
+ x: object.localEditedPose.x,
43
+ y: object.localEditedPose.y,
44
+ z: object.localEditedPose.z,
45
+ });
46
+ };
47
+ updateGeometry = (geometry) => {
48
+ const object = this.object();
49
+ if (!object)
50
+ return;
51
+ let geometryObject;
52
+ if (geometry.type === 'box') {
53
+ const currentGeometry = object.geometry?.geometryType.value;
54
+ geometryObject = {
55
+ type: 'box',
56
+ x: geometry.x ?? currentGeometry?.dimsMm?.x,
57
+ y: geometry.y ?? currentGeometry?.dimsMm?.y,
58
+ z: geometry.z ?? currentGeometry?.dimsMm?.z,
59
+ };
60
+ }
61
+ else if (geometry.type === 'sphere') {
62
+ const currentGeometry = object.geometry?.geometryType.value;
63
+ geometryObject = {
64
+ type: 'sphere',
65
+ r: geometry.r ?? currentGeometry?.radiusMm,
66
+ };
67
+ }
68
+ else if (geometry.type === 'capsule') {
69
+ const currentGeometry = object.geometry?.geometryType.value;
70
+ geometryObject = {
71
+ type: 'capsule',
72
+ r: geometry.r ?? currentGeometry?.radiusMm,
73
+ l: geometry.l ?? currentGeometry?.lengthMm,
74
+ };
75
+ }
76
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
77
+ x: object.localEditedPose.x,
78
+ y: object.localEditedPose.y,
79
+ z: object.localEditedPose.z,
80
+ oX: object.localEditedPose.oX,
81
+ oY: object.localEditedPose.oY,
82
+ oZ: object.localEditedPose.oZ,
83
+ theta: object.localEditedPose.theta,
84
+ }, { ...geometryObject });
85
+ };
86
+ setFrameParent = (parentName) => {
87
+ const object = this.object();
88
+ if (!object)
89
+ return;
90
+ this.updateFrame(object.name ?? '', parentName, {
91
+ x: object.localEditedPose.x,
92
+ y: object.localEditedPose.y,
93
+ z: object.localEditedPose.z,
94
+ oX: object.localEditedPose.oX,
95
+ oY: object.localEditedPose.oY,
96
+ oZ: object.localEditedPose.oZ,
97
+ theta: object.localEditedPose.theta,
98
+ });
99
+ };
100
+ deleteFrame = () => {
101
+ const object = this.object();
102
+ if (!object)
103
+ return;
104
+ this.removeFrame(object.name ?? '');
105
+ };
106
+ setGeometryType = (type) => {
107
+ const object = this.object();
108
+ if (!object)
109
+ return;
110
+ if (type === 'none') {
111
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
112
+ x: object.localEditedPose.x,
113
+ y: object.localEditedPose.y,
114
+ z: object.localEditedPose.z,
115
+ oX: object.localEditedPose.oX,
116
+ oY: object.localEditedPose.oY,
117
+ oZ: object.localEditedPose.oZ,
118
+ theta: object.localEditedPose.theta,
119
+ }, { type: 'none' });
120
+ }
121
+ else if (type === 'box') {
122
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
123
+ x: object.localEditedPose.x,
124
+ y: object.localEditedPose.y,
125
+ z: object.localEditedPose.z,
126
+ oX: object.localEditedPose.oX,
127
+ oY: object.localEditedPose.oY,
128
+ oZ: object.localEditedPose.oZ,
129
+ theta: object.localEditedPose.theta,
130
+ }, { type: 'box', x: 100, y: 100, z: 100 });
131
+ }
132
+ else if (type === 'sphere') {
133
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
134
+ x: object.localEditedPose.x,
135
+ y: object.localEditedPose.y,
136
+ z: object.localEditedPose.z,
137
+ oX: object.localEditedPose.oX,
138
+ oY: object.localEditedPose.oY,
139
+ oZ: object.localEditedPose.oZ,
140
+ theta: object.localEditedPose.theta,
141
+ }, { type: 'sphere', r: 100 });
142
+ }
143
+ else if (type === 'capsule') {
144
+ this.updateFrame(object.name ?? '', this.referenceFrame(), {
145
+ x: object.localEditedPose.x,
146
+ y: object.localEditedPose.y,
147
+ z: object.localEditedPose.z,
148
+ oX: object.localEditedPose.oX,
149
+ oY: object.localEditedPose.oY,
150
+ oZ: object.localEditedPose.oZ,
151
+ theta: object.localEditedPose.theta,
152
+ }, { type: 'capsule', r: 20, l: 100 });
153
+ }
154
+ };
155
+ }
@@ -1,5 +1,7 @@
1
- import type { Geometry, Pose, TransformWithUUID } from '@viamrobotics/sdk';
2
- import { BatchedMesh, Box3, Object3D, Vector3, type ColorRepresentation } from 'three';
1
+ import type { Geometry, PlainMessage, Pose, Struct, TransformWithUUID } from '@viamrobotics/sdk';
2
+ import { BatchedMesh, Color, Object3D, Vector3 } from 'three';
3
+ import type { ValueOf } from 'type-fest';
4
+ import type { OBB } from 'three/addons/math/OBB.js';
3
5
  export type PointsGeometry = {
4
6
  center: undefined;
5
7
  geometryType: {
@@ -15,9 +17,14 @@ export type LinesGeometry = {
15
17
  };
16
18
  };
17
19
  export type Geometries = Geometry | PointsGeometry | LinesGeometry;
20
+ export declare const SupportedShapes: {
21
+ readonly points: "points";
22
+ readonly line: "line";
23
+ readonly arrow: "arrow";
24
+ };
18
25
  export type Metadata = {
19
26
  colors?: Float32Array;
20
- color?: ColorRepresentation;
27
+ color?: Color;
21
28
  opacity?: number;
22
29
  gltf?: {
23
30
  scene: Object3D;
@@ -25,20 +32,25 @@ export type Metadata = {
25
32
  points?: Vector3[];
26
33
  pointSize?: number;
27
34
  lineWidth?: number;
28
- lineDotColor?: ColorRepresentation;
35
+ lineDotColor?: Color;
29
36
  batched?: {
30
37
  id: number;
31
38
  object: BatchedMesh;
32
39
  };
33
- getBoundingBoxAt?: (box: Box3) => void;
40
+ shape?: ValueOf<typeof SupportedShapes>;
41
+ getBoundingBoxAt?: (box: OBB) => void;
34
42
  };
43
+ export declare const isMetadataKey: (key: string) => key is keyof Metadata;
35
44
  export declare class WorldObject<T extends Geometries = Geometries> {
36
45
  uuid: string;
37
46
  name: string;
38
- referenceFrame: string;
39
- pose: import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
47
+ referenceFrame: string | undefined;
48
+ pose: PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
40
49
  geometry?: T;
41
50
  metadata: Metadata;
51
+ localEditedPose: PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Pose>;
42
52
  constructor(name: string, pose?: Pose, parent?: string, geometry?: T, metadata?: Metadata);
43
53
  }
44
- export declare const fromTransform: (transform: TransformWithUUID) => WorldObject<import("@viamrobotics/sdk").PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Geometry>>;
54
+ export declare const parseMetadata: (fields?: PlainMessage<Struct>["fields"]) => Metadata;
55
+ export declare const fromTransform: (transform: TransformWithUUID) => WorldObject<PlainMessage<import("@viamrobotics/sdk/dist/gen/common/v1/common_pb").Geometry>>;
56
+ export declare const determinePose: (object: WorldObject, pose: Pose | undefined) => Pose;
@@ -1,23 +1,49 @@
1
- import { BatchedMesh, Box3, Color, MathUtils, Object3D, Vector3, } from 'three';
2
- import { createPose } from './transform';
1
+ import { BatchedMesh, Color, MathUtils, Object3D, Vector3 } from 'three';
2
+ import { createPose, matrixToPose, poseToMatrix } from './transform';
3
+ import { isColorRepresentation, isRGB, parseColor, parseOpacity, parseRGB } from './color';
4
+ export const SupportedShapes = {
5
+ points: 'points',
6
+ line: 'line',
7
+ arrow: 'arrow',
8
+ };
9
+ const METADATA_KEYS = [
10
+ 'colors',
11
+ 'color',
12
+ 'opacity',
13
+ 'gltf',
14
+ 'points',
15
+ 'pointSize',
16
+ 'lineWidth',
17
+ 'lineDotColor',
18
+ 'batched',
19
+ 'shape',
20
+ ];
21
+ export const isMetadataKey = (key) => {
22
+ return METADATA_KEYS.includes(key);
23
+ };
3
24
  export class WorldObject {
4
25
  uuid;
5
26
  name;
6
- referenceFrame;
27
+ referenceFrame = $state.raw();
7
28
  pose = $state.raw(createPose());
8
29
  geometry;
9
- metadata;
30
+ metadata = $state({});
31
+ localEditedPose = $state.raw(createPose());
10
32
  constructor(name, pose, parent = 'world', geometry, metadata) {
11
33
  this.uuid = MathUtils.generateUUID();
12
34
  this.name = name;
13
35
  this.referenceFrame = parent;
14
36
  this.geometry = geometry;
15
- this.metadata = metadata ?? {};
37
+ if (metadata) {
38
+ this.metadata = metadata;
39
+ }
16
40
  if (pose) {
17
41
  this.pose = pose;
42
+ this.localEditedPose = { ...pose };
18
43
  }
19
44
  }
20
45
  }
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
47
  const unwrapValue = (value) => {
22
48
  if (!value?.kind)
23
49
  return value;
@@ -27,9 +53,9 @@ const unwrapValue = (value) => {
27
53
  case 'boolValue':
28
54
  return value.kind.value;
29
55
  case 'structValue': {
30
- // Recursively unwrap nested struct
31
56
  const result = {};
32
57
  for (const [key, val] of Object.entries(value.kind.value.fields || {})) {
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
59
  result[key] = unwrapValue(val);
34
60
  }
35
61
  return result;
@@ -42,26 +68,65 @@ const unwrapValue = (value) => {
42
68
  return value.kind.value;
43
69
  }
44
70
  };
45
- const parseMetadata = (metadata) => {
46
- let json = {};
47
- for (const [k, v] of Object.entries(metadata)) {
71
+ export const parseMetadata = (fields = {}) => {
72
+ const json = {};
73
+ for (const [k, v] of Object.entries(fields)) {
74
+ if (!isMetadataKey(k))
75
+ continue;
48
76
  const unwrappedValue = unwrapValue(v);
49
- // TODO: Remove special case and add better handling for metadata
50
- if (k === 'color' && unwrappedValue && typeof unwrappedValue === 'object') {
51
- const { r, g, b } = unwrappedValue;
52
- json[k] = new Color().setRGB(r / 255, g / 255, b / 255);
53
- }
54
- else {
55
- json = { ...json, [k]: unwrappedValue };
77
+ switch (k) {
78
+ case 'color':
79
+ case 'lineDotColor':
80
+ json[k] = readColor(unwrappedValue);
81
+ break;
82
+ case 'opacity':
83
+ json[k] = parseOpacity(unwrappedValue);
84
+ break;
85
+ case 'gltf':
86
+ json[k] = unwrappedValue;
87
+ break;
88
+ case 'points':
89
+ json[k] = unwrappedValue;
90
+ break;
91
+ case 'pointSize':
92
+ json[k] = unwrappedValue;
93
+ break;
94
+ case 'lineWidth':
95
+ json[k] = unwrappedValue;
96
+ break;
97
+ case 'batched':
98
+ json[k] = unwrappedValue;
99
+ break;
100
+ case 'shape':
101
+ json[k] = unwrappedValue;
102
+ break;
56
103
  }
57
104
  }
58
105
  return json;
59
106
  };
107
+ const readColor = (color) => {
108
+ if (isColorRepresentation(color))
109
+ return parseColor(color);
110
+ if (isRGB(color))
111
+ return parseRGB(color);
112
+ return new Color('black');
113
+ };
60
114
  export const fromTransform = (transform) => {
61
- const metadata = transform.metadata
62
- ? parseMetadata(transform.metadata.fields)
63
- : {};
115
+ const metadata = transform.metadata ? parseMetadata(transform.metadata.fields) : {};
64
116
  const worldObject = new WorldObject(transform.referenceFrame, transform.poseInObserverFrame?.pose, transform.poseInObserverFrame?.referenceFrame, transform.physicalObject, metadata);
65
117
  worldObject.uuid = transform.uuidString;
66
118
  return worldObject;
67
119
  };
120
+ export const determinePose = (object, pose) => {
121
+ if (pose === undefined) {
122
+ return object.localEditedPose;
123
+ }
124
+ else {
125
+ const poseNetwork = poseToMatrix(object.pose);
126
+ const poseUsePose = poseToMatrix(pose);
127
+ const poseLocalEditedPose = poseToMatrix(object.localEditedPose);
128
+ const poseNetworkInverse = poseNetwork.invert();
129
+ const resultMatrix = poseUsePose.multiply(poseNetworkInverse).multiply(poseLocalEditedPose);
130
+ return matrixToPose(resultMatrix);
131
+ }
132
+ };
package/dist/color.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Color, type ColorRepresentation } from 'three';
1
+ import { Color, type ColorRepresentation, type RGB } from 'three';
2
2
  /**
3
3
  * Darkens a THREE.Color by a given percentage while preserving hue.
4
4
  * @param color The original THREE.Color instance.
@@ -27,3 +27,8 @@ export declare const resourceColors: {
27
27
  readonly switch: string;
28
28
  readonly webcam: string;
29
29
  };
30
+ export declare const isColorRepresentation: (color: unknown) => color is ColorRepresentation;
31
+ export declare const parseColor: (color: unknown, defaultColor?: ColorRepresentation) => Color;
32
+ export declare const isRGB: (color: unknown) => color is RGB;
33
+ export declare const parseRGB: (color: unknown, defaultColor?: RGB) => Color;
34
+ export declare const parseOpacity: (opacity: unknown, defaultOpacity?: number) => number;
package/dist/color.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Color } from 'three';
2
2
  import twColors from 'tailwindcss/colors';
3
+ import { isNumber } from 'lodash-es';
3
4
  // Step 3: linear sRGB → sRGB
4
5
  const linearToSrgb = (x) => {
5
6
  return x <= 0.0031308 ? 12.92 * x : 1.055 * Math.pow(x, 1 / 2.4) - 0.055;
@@ -73,3 +74,62 @@ export const resourceColors = {
73
74
  switch: oklchToHex(twColors.stone[darkness]),
74
75
  webcam: oklchToHex(twColors.sky[darkness]),
75
76
  };
77
+ export const isColorRepresentation = (color) => {
78
+ if (!color)
79
+ return false;
80
+ if (isColorString(color))
81
+ return true;
82
+ if (isColorHex(color))
83
+ return true;
84
+ if (isColor(color))
85
+ return true;
86
+ return false;
87
+ };
88
+ export const parseColor = (color, defaultColor = 'black') => {
89
+ if (!isColorRepresentation(color))
90
+ return new Color(defaultColor);
91
+ return new Color(color);
92
+ };
93
+ export const isRGB = (color) => {
94
+ if (!color ||
95
+ typeof color !== 'object' ||
96
+ !('r' in color) ||
97
+ !('g' in color) ||
98
+ !('b' in color)) {
99
+ return false;
100
+ }
101
+ return isNumber(color.r) && isNumber(color.g) && isNumber(color.b);
102
+ };
103
+ export const parseRGB = (color, defaultColor = { r: 0, g: 0, b: 0 }) => {
104
+ if (!isRGB(color))
105
+ return new Color().setRGB(defaultColor.r, defaultColor.g, defaultColor.b);
106
+ return new Color().setRGB(color.r, color.g, color.b);
107
+ };
108
+ export const parseOpacity = (opacity, defaultOpacity = 1) => {
109
+ if (!isNumber(opacity))
110
+ return defaultOpacity;
111
+ return opacity > 1 ? opacity / 100 : opacity;
112
+ };
113
+ const isColor = (color) => {
114
+ if (!color)
115
+ return false;
116
+ return color instanceof Color;
117
+ };
118
+ const isColorString = (color) => {
119
+ if (!color)
120
+ return false;
121
+ if (typeof color === 'string') {
122
+ const parsed = new Color(color);
123
+ return parsed.isColor;
124
+ }
125
+ return false;
126
+ };
127
+ const isColorHex = (color) => {
128
+ if (!color)
129
+ return false;
130
+ if (typeof color === 'number') {
131
+ const parsed = new Color(color);
132
+ return parsed.isColor;
133
+ }
134
+ return false;
135
+ };
@@ -3,6 +3,7 @@
3
3
  import { Canvas } from '@threlte/core'
4
4
  import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools'
5
5
  import { provideToast, ToastContainer } from '@viamrobotics/prime-core'
6
+ import type { Struct } from '@viamrobotics/sdk'
6
7
 
7
8
  import Scene from './Scene.svelte'
8
9
  import TreeContainer from './Tree/TreeContainer.svelte'
@@ -16,16 +17,36 @@
16
17
  import { provideSettings } from '../hooks/useSettings.svelte'
17
18
  import FileDrop from './FileDrop.svelte'
18
19
  import WeblabProvider from './weblab/WeblabProvider.svelte'
20
+ import { providePartConfig } from '../hooks/usePartConfig.svelte'
21
+ import { useViamClient } from '@viamrobotics/svelte-sdk'
22
+ import LiveUpdatesBanner from './LiveUpdatesBanner.svelte'
19
23
  import ArmPositions from './widgets/ArmPositions.svelte'
24
+ import { provideEnvironment } from '../hooks/useEnvironment.svelte'
25
+
26
+ interface LocalConfigProps {
27
+ getLocalPartConfig: () => Struct
28
+ setLocalPartConfig: (config: Struct) => void
29
+ isDirty: () => boolean
30
+ getComponentToFragId: () => Record<string, string>
31
+ }
32
+
20
33
  interface Props {
21
34
  partID?: string
22
35
  enableKeybindings?: boolean
23
36
  children?: Snippet
37
+ localConfigProps?: LocalConfigProps
24
38
  }
25
39
 
26
- let { partID = '', enableKeybindings = true, children: appChildren }: Props = $props()
40
+ let {
41
+ partID = '',
42
+ enableKeybindings = true,
43
+ children: appChildren,
44
+ localConfigProps,
45
+ }: Props = $props()
27
46
 
47
+ const appClient = useViamClient()
28
48
  const settings = provideSettings()
49
+ const environment = provideEnvironment()
29
50
 
30
51
  $effect(() => {
31
52
  settings.current.enableKeybindings = enableKeybindings
@@ -36,6 +57,26 @@
36
57
  provideToast()
37
58
 
38
59
  let root = $state.raw<HTMLElement>()
60
+
61
+ if (localConfigProps) {
62
+ environment.current.isStandalone = false
63
+ providePartConfig({
64
+ appEmbeddedPartConfigProps: {
65
+ isDirty: () => localConfigProps.isDirty(),
66
+ getLocalPartConfig: () => localConfigProps.getLocalPartConfig(),
67
+ setLocalPartConfig: (config: Struct) => localConfigProps.setLocalPartConfig(config),
68
+ getComponentToFragId: () => localConfigProps.getComponentToFragId(),
69
+ },
70
+ })
71
+ } else {
72
+ environment.current.isStandalone = true
73
+ providePartConfig({
74
+ standalonePartConfigProps: {
75
+ viamClient: () => appClient?.current,
76
+ partID: () => partID,
77
+ },
78
+ })
79
+ }
39
80
  </script>
40
81
 
41
82
  {#if settings.current.enableQueryDevtools}
@@ -59,6 +100,9 @@
59
100
 
60
101
  <Dashboard {@attach domPortal(root)} />
61
102
  <Details {@attach domPortal(root)} />
103
+ {#if environment.current.isStandalone}
104
+ <LiveUpdatesBanner {@attach domPortal(root)} />
105
+ {/if}
62
106
 
63
107
  {#if !focus}
64
108
  <TreeContainer {@attach domPortal(root)} />
@@ -1,8 +1,16 @@
1
1
  import type { Snippet } from 'svelte';
2
+ import type { Struct } from '@viamrobotics/sdk';
3
+ interface LocalConfigProps {
4
+ getLocalPartConfig: () => Struct;
5
+ setLocalPartConfig: (config: Struct) => void;
6
+ isDirty: () => boolean;
7
+ getComponentToFragId: () => Record<string, string>;
8
+ }
2
9
  interface Props {
3
10
  partID?: string;
4
11
  enableKeybindings?: boolean;
5
12
  children?: Snippet;
13
+ localConfigProps?: LocalConfigProps;
6
14
  }
7
15
  declare const App: import("svelte").Component<Props, {}, "">;
8
16
  type App = ReturnType<typeof App>;