@viamrobotics/motion-tools 1.12.2 → 1.12.3
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.
- package/dist/assets/ferndale_studio_11_1k.hdr +0 -0
- package/dist/components/App.svelte +7 -24
- package/dist/components/App.svelte.d.ts +3 -3
- package/dist/components/Geometry.svelte +2 -2
- package/dist/components/Scene.svelte +4 -1
- package/dist/components/overlay/LiveUpdatesBanner.svelte +3 -3
- package/dist/components/overlay/__tests__/__fixtures__/resource.d.ts +10 -1
- package/dist/components/overlay/__tests__/__fixtures__/resource.js +1 -0
- package/dist/components/overlay/dashboard/Dashboard.svelte +1 -1
- package/dist/hooks/use3DModels.svelte.js +11 -0
- package/dist/hooks/useFramelessComponents.svelte.js +1 -1
- package/dist/hooks/useFrames.svelte.js +2 -2
- package/dist/hooks/usePartConfig.svelte.d.ts +10 -19
- package/dist/hooks/usePartConfig.svelte.js +137 -164
- package/package.json +1 -1
|
Binary file
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
import FileDrop from './FileDrop/FileDrop.svelte'
|
|
18
18
|
import { provideWeblabs } from '../hooks/useWeblabs.svelte'
|
|
19
19
|
import { providePartConfig } from '../hooks/usePartConfig.svelte'
|
|
20
|
-
import { useViamClient } from '@viamrobotics/svelte-sdk'
|
|
21
20
|
import LiveUpdatesBanner from './overlay/LiveUpdatesBanner.svelte'
|
|
22
21
|
import ArmPositions from './overlay/widgets/ArmPositions.svelte'
|
|
23
22
|
import { provideEnvironment } from '../hooks/useEnvironment.svelte'
|
|
@@ -33,10 +32,10 @@
|
|
|
33
32
|
import { useXR } from '@threlte/xr'
|
|
34
33
|
|
|
35
34
|
interface LocalConfigProps {
|
|
36
|
-
|
|
35
|
+
current: Struct
|
|
36
|
+
isDirty: boolean
|
|
37
|
+
componentToFragId: Record<string, string>
|
|
37
38
|
setLocalPartConfig: (config: Struct) => void
|
|
38
|
-
isDirty: () => boolean
|
|
39
|
-
getComponentToFragId: () => Record<string, string>
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
interface Props {
|
|
@@ -65,7 +64,6 @@
|
|
|
65
64
|
|
|
66
65
|
provideWorld()
|
|
67
66
|
|
|
68
|
-
const appClient = useViamClient()
|
|
69
67
|
const settings = provideSettings()
|
|
70
68
|
const environment = provideEnvironment()
|
|
71
69
|
const currentRobotCameraWidgets = $derived(settings.current.openCameraWidgets[partID] || [])
|
|
@@ -82,25 +80,10 @@
|
|
|
82
80
|
|
|
83
81
|
let root = $state.raw<HTMLElement>()
|
|
84
82
|
|
|
85
|
-
providePartConfig(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
isDirty: () => localConfigProps.isDirty(),
|
|
90
|
-
getLocalPartConfig: () => localConfigProps.getLocalPartConfig(),
|
|
91
|
-
setLocalPartConfig: (config: Struct) => localConfigProps.setLocalPartConfig(config),
|
|
92
|
-
getComponentToFragId: () => localConfigProps.getComponentToFragId(),
|
|
93
|
-
},
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
return {
|
|
97
|
-
standalonePartConfigProps: {
|
|
98
|
-
viamClient: () => appClient?.current,
|
|
99
|
-
partID: () => partID,
|
|
100
|
-
},
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
})
|
|
83
|
+
providePartConfig(
|
|
84
|
+
() => partID,
|
|
85
|
+
() => localConfigProps
|
|
86
|
+
)
|
|
104
87
|
|
|
105
88
|
$effect.pre(() => {
|
|
106
89
|
if (localConfigProps) {
|
|
@@ -3,10 +3,10 @@ import type { Struct } from '@viamrobotics/sdk';
|
|
|
3
3
|
import type { CameraPose } from '../hooks/useControls.svelte';
|
|
4
4
|
import { type DrawConnectionConfig } from '../hooks/useDrawConnectionConfig.svelte';
|
|
5
5
|
interface LocalConfigProps {
|
|
6
|
-
|
|
6
|
+
current: Struct;
|
|
7
|
+
isDirty: boolean;
|
|
8
|
+
componentToFragId: Record<string, string>;
|
|
7
9
|
setLocalPartConfig: (config: Struct) => void;
|
|
8
|
-
isDirty: () => boolean;
|
|
9
|
-
getComponentToFragId: () => Record<string, string>;
|
|
10
10
|
}
|
|
11
11
|
interface Props {
|
|
12
12
|
partID?: string;
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
is={LineMaterial}
|
|
176
176
|
{color}
|
|
177
177
|
width={lineWidth.current ? lineWidth.current * 0.001 : 0.5}
|
|
178
|
-
depthTest={materialProps.current?.depthTest}
|
|
178
|
+
depthTest={materialProps.current?.depthTest ?? true}
|
|
179
179
|
/>
|
|
180
180
|
{:else}
|
|
181
181
|
{@const currentOpacity = opacity.current ?? 0.7}
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
transparent={currentOpacity < 1}
|
|
186
186
|
depthWrite={currentOpacity === 1}
|
|
187
187
|
opacity={currentOpacity}
|
|
188
|
-
depthTest={materialProps.current?.depthTest}
|
|
188
|
+
depthTest={materialProps.current?.depthTest ?? true}
|
|
189
189
|
/>
|
|
190
190
|
|
|
191
191
|
{#if geo && (renderMode.includes('colliders') || !model)}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { ShaderMaterial, Vector3 } from 'three'
|
|
3
3
|
import { T } from '@threlte/core'
|
|
4
|
-
import { Grid, interactivity, PerfMonitor, PortalTarget } from '@threlte/extras'
|
|
4
|
+
import { Environment, Grid, interactivity, PerfMonitor, PortalTarget } from '@threlte/extras'
|
|
5
5
|
import Entities from './Entities.svelte'
|
|
6
6
|
import Selected from './Selected.svelte'
|
|
7
7
|
import Focus from './Focus.svelte'
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import PointerMissBox from './PointerMissBox.svelte'
|
|
19
19
|
import BatchedArrows from './BatchedArrows.svelte'
|
|
20
20
|
import Arrows from './Arrows/ArrowGroups.svelte'
|
|
21
|
+
import hdrImage from '../assets/ferndale_studio_11_1k.hdr'
|
|
21
22
|
|
|
22
23
|
interface Props {
|
|
23
24
|
children?: Snippet
|
|
@@ -54,6 +55,8 @@
|
|
|
54
55
|
<PerfMonitor anchorX="right" />
|
|
55
56
|
{/if}
|
|
56
57
|
|
|
58
|
+
<Environment url={hdrImage} />
|
|
59
|
+
|
|
57
60
|
<T.Group
|
|
58
61
|
position={origin.position}
|
|
59
62
|
rotation.x={$isPresenting ? -Math.PI / 2 : 0}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
if (event.key.toLowerCase() === 's') {
|
|
18
18
|
event.preventDefault()
|
|
19
19
|
event.stopImmediatePropagation()
|
|
20
|
-
partConfig.
|
|
20
|
+
partConfig.save()
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
<Button
|
|
44
44
|
class="cursor-pointer text-blue-600"
|
|
45
45
|
onclick={() => {
|
|
46
|
-
partConfig.
|
|
46
|
+
partConfig.discardChanges()
|
|
47
47
|
}}
|
|
48
48
|
>
|
|
49
49
|
Discard
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
aria-label="Save"
|
|
55
55
|
class="cursor-pointer text-blue-600"
|
|
56
56
|
onclick={() => {
|
|
57
|
-
partConfig.
|
|
57
|
+
partConfig.save()
|
|
58
58
|
}}
|
|
59
59
|
>
|
|
60
60
|
<div class="flex gap-2">
|
|
@@ -7,8 +7,17 @@ export declare const resource: {
|
|
|
7
7
|
y: number;
|
|
8
8
|
z: number;
|
|
9
9
|
};
|
|
10
|
+
orientation: {
|
|
11
|
+
type: "ov_degrees";
|
|
12
|
+
value: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
z: number;
|
|
16
|
+
th: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
10
19
|
geometry: {
|
|
11
|
-
type:
|
|
20
|
+
type: "box";
|
|
12
21
|
x: number;
|
|
13
22
|
y: number;
|
|
14
23
|
z: number;
|
|
@@ -4,6 +4,7 @@ import { getContext, setContext } from 'svelte';
|
|
|
4
4
|
import { useSettings } from './useSettings.svelte';
|
|
5
5
|
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
6
6
|
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
7
|
+
import { isInstanceOf } from '@threlte/core';
|
|
7
8
|
const gltfLoader = new GLTFLoader();
|
|
8
9
|
const dracoLoader = new DRACOLoader();
|
|
9
10
|
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
|
|
@@ -38,6 +39,16 @@ export const provide3DModels = (partID) => {
|
|
|
38
39
|
const arrayBuffer = model.mesh.buffer.slice(model.mesh.byteOffset, model.mesh.byteOffset + model.mesh.byteLength);
|
|
39
40
|
const gltfModel = await gltfLoader.parseAsync(arrayBuffer, '');
|
|
40
41
|
next[prefix][id] = gltfModel.scene;
|
|
42
|
+
gltfModel.scene.traverse((object) => {
|
|
43
|
+
if (isInstanceOf(object, 'Mesh')) {
|
|
44
|
+
const { material } = object;
|
|
45
|
+
console.log(material);
|
|
46
|
+
if (isInstanceOf(material, 'MeshStandardMaterial')) {
|
|
47
|
+
material.roughness = 0.3;
|
|
48
|
+
material.metalness = 0.1;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
41
52
|
}
|
|
42
53
|
current = next;
|
|
43
54
|
}
|
|
@@ -6,7 +6,7 @@ export const provideFramelessComponents = () => {
|
|
|
6
6
|
const partConfig = usePartConfig();
|
|
7
7
|
const frames = useFrames();
|
|
8
8
|
const current = $derived.by(() => {
|
|
9
|
-
const components = partConfig.
|
|
9
|
+
const { components } = partConfig.current;
|
|
10
10
|
const partComponentsWIthNoFrame = components
|
|
11
11
|
?.filter((component) => component.frame === undefined)
|
|
12
12
|
.map((component) => component.name) ?? [];
|
|
@@ -52,7 +52,7 @@ export const provideFrames = (partID) => {
|
|
|
52
52
|
return frames;
|
|
53
53
|
});
|
|
54
54
|
const [configFrames, configUnsetFrameNames] = $derived.by(() => {
|
|
55
|
-
const components = partConfig.
|
|
55
|
+
const { components } = partConfig.current;
|
|
56
56
|
const results = {};
|
|
57
57
|
const unsetResults = [];
|
|
58
58
|
for (const { name, frame } of components ?? []) {
|
|
@@ -68,7 +68,7 @@ export const provideFrames = (partID) => {
|
|
|
68
68
|
return [results, unsetResults];
|
|
69
69
|
});
|
|
70
70
|
const [fragmentFrames, fragmentUnsetFrameNames] = $derived.by(() => {
|
|
71
|
-
const { fragment_mods: fragmentMods = [] } = partConfig.
|
|
71
|
+
const { fragment_mods: fragmentMods = [] } = partConfig.current;
|
|
72
72
|
const fragmentDefinedComponents = Object.keys(partConfig.componentNameToFragmentId);
|
|
73
73
|
const results = {};
|
|
74
74
|
const unsetResults = [];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type Frame } from '../frame';
|
|
2
2
|
import { Struct, Pose } from '@viamrobotics/sdk';
|
|
3
|
-
import type { ViamClient } from '@viamrobotics/sdk';
|
|
4
3
|
export interface PartConfig {
|
|
5
4
|
components: {
|
|
6
5
|
name: string;
|
|
@@ -11,31 +10,23 @@ export interface PartConfig {
|
|
|
11
10
|
mods: any[];
|
|
12
11
|
}[];
|
|
13
12
|
}
|
|
14
|
-
interface PartConfigParams {
|
|
15
|
-
appEmbeddedPartConfigProps?: AppEmbeddedPartConfigProps;
|
|
16
|
-
standalonePartConfigProps?: StandalonePartConfigProps;
|
|
17
|
-
}
|
|
18
13
|
interface PartConfigContext {
|
|
14
|
+
current: PartConfig;
|
|
15
|
+
isDirty: boolean;
|
|
16
|
+
hasEditPermissions: boolean;
|
|
17
|
+
componentNameToFragmentId: Record<string, string>;
|
|
19
18
|
updateFrame: (componentName: string, referenceFrame: string, pose: Pose, geometry?: Frame['geometry']) => void;
|
|
20
|
-
saveLocalPartConfig: () => void;
|
|
21
|
-
resetLocalPartConfig: () => void;
|
|
22
19
|
deleteFrame: (componentName: string) => void;
|
|
23
20
|
createFrame: (componentName: string) => void;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
isDirty: boolean;
|
|
27
|
-
hasEditPermissions: boolean;
|
|
21
|
+
save: () => void;
|
|
22
|
+
discardChanges: () => void;
|
|
28
23
|
}
|
|
29
|
-
export declare const providePartConfig: (params: () =>
|
|
24
|
+
export declare const providePartConfig: (partID: () => string, params: () => AppEmbeddedPartConfigProps | undefined) => void;
|
|
30
25
|
export declare const usePartConfig: () => PartConfigContext;
|
|
31
26
|
interface AppEmbeddedPartConfigProps {
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
current: Struct;
|
|
28
|
+
isDirty: boolean;
|
|
29
|
+
componentToFragId: Record<string, string>;
|
|
34
30
|
setLocalPartConfig: (config: Struct) => void;
|
|
35
|
-
getComponentToFragId: () => Record<string, string>;
|
|
36
|
-
}
|
|
37
|
-
interface StandalonePartConfigProps {
|
|
38
|
-
viamClient: () => ViamClient | undefined;
|
|
39
|
-
partID: () => string;
|
|
40
31
|
}
|
|
41
32
|
export {};
|
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import { createFrame } from '../frame';
|
|
2
2
|
import { createPoseFromFrame } from '../transform';
|
|
3
3
|
import { Struct, Pose } from '@viamrobotics/sdk';
|
|
4
|
+
import { createAppMutation, createAppQuery } from '@viamrobotics/svelte-sdk';
|
|
4
5
|
import { getContext, setContext } from 'svelte';
|
|
5
6
|
const key = Symbol('part-config-context');
|
|
6
|
-
export const providePartConfig = (params) => {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
_localPartConfig = new StandalonePartConfig(standalonePartConfigProps);
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
throw new Error('No part config provided');
|
|
17
|
-
}
|
|
7
|
+
export const providePartConfig = (partID, params) => {
|
|
8
|
+
const props = $derived(params());
|
|
9
|
+
const config = $derived(props ? useEmbeddedPartConfig(props) : useStandalonePartConfig(partID));
|
|
10
|
+
const getCurrent = () => {
|
|
11
|
+
return (config.current.toJson?.() ?? { components: [] });
|
|
12
|
+
};
|
|
13
|
+
const current = $derived(getCurrent());
|
|
18
14
|
const createFragmentFrame = (fragmentId, componentName) => {
|
|
19
|
-
const newConfig =
|
|
20
|
-
|
|
21
|
-
newConfig.fragment_mods = [];
|
|
22
|
-
}
|
|
15
|
+
const newConfig = getCurrent();
|
|
16
|
+
newConfig.fragment_mods ??= [];
|
|
23
17
|
let fragmentMod = newConfig.fragment_mods.find((mod) => mod.fragment_id === fragmentId);
|
|
24
18
|
if (fragmentMod === undefined) {
|
|
25
19
|
fragmentMod = {
|
|
@@ -35,23 +29,19 @@ export const providePartConfig = (params) => {
|
|
|
35
29
|
},
|
|
36
30
|
};
|
|
37
31
|
fragmentMod.mods.push(frame);
|
|
38
|
-
|
|
39
|
-
_localPartConfig.setLocalPartConfig(configStruct);
|
|
32
|
+
config.set(newConfig);
|
|
40
33
|
};
|
|
41
34
|
const createPartFrame = (componentName) => {
|
|
42
|
-
const newConfig =
|
|
35
|
+
const newConfig = getCurrent();
|
|
43
36
|
const component = newConfig?.components?.find((comp) => comp.name === componentName);
|
|
44
37
|
if (component) {
|
|
45
38
|
component.frame = createFrame();
|
|
46
39
|
}
|
|
47
|
-
|
|
48
|
-
_localPartConfig.setLocalPartConfig(configStruct);
|
|
40
|
+
config.set(newConfig);
|
|
49
41
|
};
|
|
50
42
|
const updateFragmentFrame = (fragmentId, componentName, referenceFrame, framePosition, frameGeometry) => {
|
|
51
|
-
const newConfig =
|
|
52
|
-
|
|
53
|
-
newConfig.fragment_mods = [];
|
|
54
|
-
}
|
|
43
|
+
const newConfig = getCurrent();
|
|
44
|
+
newConfig.fragment_mods ??= [];
|
|
55
45
|
let fragmentMod = newConfig.fragment_mods.find((mod) => mod.fragment_id === fragmentId);
|
|
56
46
|
if (fragmentMod === undefined) {
|
|
57
47
|
fragmentMod = {
|
|
@@ -99,11 +89,12 @@ export const providePartConfig = (params) => {
|
|
|
99
89
|
else {
|
|
100
90
|
fragmentMod.mods.push(frame);
|
|
101
91
|
}
|
|
102
|
-
|
|
92
|
+
config.set(newConfig);
|
|
103
93
|
};
|
|
104
94
|
const updatePartFrame = (componentName, referenceFrame, pose, geometry) => {
|
|
105
|
-
const newConfig =
|
|
106
|
-
const component = newConfig
|
|
95
|
+
const newConfig = getCurrent();
|
|
96
|
+
const component = newConfig.components?.find(({ name }) => name === componentName);
|
|
97
|
+
console.log('hi', newConfig, componentName);
|
|
107
98
|
if (!component) {
|
|
108
99
|
return;
|
|
109
100
|
}
|
|
@@ -131,24 +122,20 @@ export const providePartConfig = (params) => {
|
|
|
131
122
|
}
|
|
132
123
|
}
|
|
133
124
|
}
|
|
134
|
-
|
|
135
|
-
_localPartConfig.setLocalPartConfig(configStruct);
|
|
125
|
+
config.set(newConfig);
|
|
136
126
|
};
|
|
137
127
|
const deletePartFrame = (componentName) => {
|
|
138
|
-
const newConfig =
|
|
128
|
+
const newConfig = getCurrent();
|
|
139
129
|
const component = newConfig?.components?.find((comp) => comp.name === componentName);
|
|
140
130
|
if (!component) {
|
|
141
131
|
return;
|
|
142
132
|
}
|
|
143
133
|
delete component.frame;
|
|
144
|
-
|
|
145
|
-
_localPartConfig.setLocalPartConfig(configStruct);
|
|
134
|
+
config.set(newConfig);
|
|
146
135
|
};
|
|
147
136
|
const deleteFragmentFrame = (fragmentId, componentName) => {
|
|
148
|
-
const newConfig =
|
|
149
|
-
|
|
150
|
-
newConfig.fragment_mods = [];
|
|
151
|
-
}
|
|
137
|
+
const newConfig = getCurrent();
|
|
138
|
+
newConfig.fragment_mods ??= [];
|
|
152
139
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
140
|
let fragmentMod = newConfig.fragment_mods.find((mod) => mod.fragment_id === fragmentId);
|
|
154
141
|
if (fragmentMod === undefined) {
|
|
@@ -164,12 +151,23 @@ export const providePartConfig = (params) => {
|
|
|
164
151
|
[modUnSetPath]: '',
|
|
165
152
|
},
|
|
166
153
|
});
|
|
167
|
-
|
|
168
|
-
_localPartConfig.setLocalPartConfig(configStruct);
|
|
154
|
+
config.set(newConfig);
|
|
169
155
|
};
|
|
170
156
|
setContext(key, {
|
|
157
|
+
get current() {
|
|
158
|
+
return current;
|
|
159
|
+
},
|
|
160
|
+
get componentNameToFragmentId() {
|
|
161
|
+
return config.componentNameToFragmentId;
|
|
162
|
+
},
|
|
163
|
+
get isDirty() {
|
|
164
|
+
return config.isDirty;
|
|
165
|
+
},
|
|
166
|
+
get hasEditPermissions() {
|
|
167
|
+
return config.hasEditPermissions;
|
|
168
|
+
},
|
|
171
169
|
updateFrame: (componentName, referenceFrame, framePosition, frameGeometry) => {
|
|
172
|
-
const fragmentId =
|
|
170
|
+
const fragmentId = config.componentNameToFragmentId[componentName];
|
|
173
171
|
if (fragmentId !== undefined) {
|
|
174
172
|
updateFragmentFrame(fragmentId, componentName, referenceFrame, framePosition, frameGeometry);
|
|
175
173
|
}
|
|
@@ -178,7 +176,7 @@ export const providePartConfig = (params) => {
|
|
|
178
176
|
}
|
|
179
177
|
},
|
|
180
178
|
deleteFrame: (componentName) => {
|
|
181
|
-
const fragmentId =
|
|
179
|
+
const fragmentId = config.componentNameToFragmentId[componentName];
|
|
182
180
|
if (fragmentId !== undefined) {
|
|
183
181
|
deleteFragmentFrame(fragmentId, componentName);
|
|
184
182
|
}
|
|
@@ -187,7 +185,7 @@ export const providePartConfig = (params) => {
|
|
|
187
185
|
}
|
|
188
186
|
},
|
|
189
187
|
createFrame: (componentName) => {
|
|
190
|
-
const fragmentId =
|
|
188
|
+
const fragmentId = config.componentNameToFragmentId[componentName];
|
|
191
189
|
if (fragmentId !== undefined) {
|
|
192
190
|
createFragmentFrame(fragmentId, componentName);
|
|
193
191
|
}
|
|
@@ -195,137 +193,112 @@ export const providePartConfig = (params) => {
|
|
|
195
193
|
createPartFrame(componentName);
|
|
196
194
|
}
|
|
197
195
|
},
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
save: () => config.save?.(),
|
|
197
|
+
discardChanges: () => config.discardChanges?.(),
|
|
198
|
+
});
|
|
199
|
+
};
|
|
200
|
+
export const usePartConfig = () => {
|
|
201
|
+
return getContext(key);
|
|
202
|
+
};
|
|
203
|
+
const useEmbeddedPartConfig = (props) => {
|
|
204
|
+
return {
|
|
205
|
+
hasEditPermissions: true,
|
|
206
|
+
get isDirty() {
|
|
207
|
+
return props.isDirty;
|
|
203
208
|
},
|
|
204
|
-
get
|
|
205
|
-
return
|
|
209
|
+
get current() {
|
|
210
|
+
return props.current ?? new Struct();
|
|
206
211
|
},
|
|
207
212
|
get componentNameToFragmentId() {
|
|
208
|
-
return
|
|
209
|
-
},
|
|
210
|
-
get isDirty() {
|
|
211
|
-
return _localPartConfig.isDirty();
|
|
213
|
+
return props.componentToFragId;
|
|
212
214
|
},
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
set(config) {
|
|
216
|
+
const struct = Struct.fromJson(config);
|
|
217
|
+
return props.setLocalPartConfig(struct);
|
|
215
218
|
},
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
export const usePartConfig = () => {
|
|
219
|
-
return getContext(key);
|
|
219
|
+
};
|
|
220
220
|
};
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
this._hasEditPermissions = true;
|
|
262
|
-
const configJson = JSON.parse(partResponse?.configJson ?? '{}');
|
|
263
|
-
this._networkPartConfig = Struct.fromJson(configJson);
|
|
264
|
-
this._localPartConfig = Struct.fromJson(configJson);
|
|
265
|
-
this._partName = partResponse?.part?.name;
|
|
266
|
-
const componentNameToFragmentId = {};
|
|
267
|
-
const fragmentRequests = [];
|
|
268
|
-
if (configJson.fragments) {
|
|
269
|
-
for (const fragmentId of configJson.fragments) {
|
|
270
|
-
//TODO: right now the json could be just a list of strings or an object with an id prop
|
|
271
|
-
const fragId = typeof fragmentId === 'string' ? fragmentId : fragmentId.id;
|
|
272
|
-
fragmentRequests.push(standalonePartConfigProps.viamClient()?.appClient.getFragment(fragId));
|
|
273
|
-
}
|
|
274
|
-
const fragementResponses = await Promise.all(fragmentRequests);
|
|
275
|
-
for (const fragmentResponse of fragementResponses) {
|
|
276
|
-
const fragmentId = fragmentResponse?.id;
|
|
277
|
-
if (!fragmentId) {
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
const components = fragmentResponse?.fragment?.fields['components']?.kind;
|
|
281
|
-
if (components?.case === 'listValue') {
|
|
282
|
-
for (const component of components.value.values) {
|
|
283
|
-
if (component.kind.case === 'structValue') {
|
|
284
|
-
const componentName = component.kind.value.fields['name'].kind;
|
|
285
|
-
if (componentName.case === 'stringValue') {
|
|
286
|
-
componentNameToFragmentId[componentName.value] = fragmentId;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
221
|
+
const useStandalonePartConfig = (partID) => {
|
|
222
|
+
const partQuery = createAppQuery('getRobotPart', () => [partID()], {
|
|
223
|
+
refetchInterval: false,
|
|
224
|
+
});
|
|
225
|
+
const partName = $derived(partQuery.data?.part?.name);
|
|
226
|
+
const configJSON = $derived.by(() => {
|
|
227
|
+
if (!partQuery.data?.configJson) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return JSON.parse(partQuery.data.configJson);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
let networkPartConfig = $derived(configJSON ? Struct.fromJson(configJSON) : undefined);
|
|
238
|
+
let current = $state.raw();
|
|
239
|
+
let isDirty = $state(false);
|
|
240
|
+
const hasEditPermissions = $derived(networkPartConfig !== undefined);
|
|
241
|
+
const fragmentQueries = $derived((configJSON?.fragments ?? []).map((fragmentId) => {
|
|
242
|
+
const id = typeof fragmentId === 'string' ? fragmentId : fragmentId.id;
|
|
243
|
+
return createAppQuery('getFragment', () => [id], { refetchInterval: false });
|
|
244
|
+
}));
|
|
245
|
+
const componentNameToFragmentId = $derived.by(() => {
|
|
246
|
+
const results = {};
|
|
247
|
+
for (const query of fragmentQueries) {
|
|
248
|
+
if (!query.data) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const fragmentId = query.data.id;
|
|
252
|
+
const components = query.data?.fragment?.fields['components']?.kind;
|
|
253
|
+
if (components?.case === 'listValue') {
|
|
254
|
+
for (const component of components.value.values) {
|
|
255
|
+
if (component.kind.case === 'structValue') {
|
|
256
|
+
const componentName = component.kind.value.fields['name']?.kind;
|
|
257
|
+
if (componentName.case === 'stringValue') {
|
|
258
|
+
results[componentName.value] = fragmentId;
|
|
290
259
|
}
|
|
291
260
|
}
|
|
292
|
-
this._componentNameToFragmentId = componentNameToFragmentId;
|
|
293
261
|
}
|
|
294
|
-
}
|
|
295
|
-
initLocalConfig();
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
getLocalPartConfig() {
|
|
299
|
-
return this._localPartConfig ?? new Struct();
|
|
300
|
-
}
|
|
301
|
-
setLocalPartConfig(config) {
|
|
302
|
-
this._localPartConfig = config;
|
|
303
|
-
this._isDirty = true;
|
|
304
|
-
}
|
|
305
|
-
isDirty() {
|
|
306
|
-
return this._isDirty;
|
|
307
|
-
}
|
|
308
|
-
hasEditPermissions() {
|
|
309
|
-
return this._hasEditPermissions;
|
|
310
|
-
}
|
|
311
|
-
componentNameToFragmentId() {
|
|
312
|
-
return this._componentNameToFragmentId ?? {};
|
|
313
|
-
}
|
|
314
|
-
async saveLocalPartConfig() {
|
|
315
|
-
if (!this._localPartConfig || !this._partName) {
|
|
316
|
-
return;
|
|
262
|
+
}
|
|
317
263
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
async resetLocalPartConfig() {
|
|
325
|
-
if (!this._networkPartConfig) {
|
|
264
|
+
return results;
|
|
265
|
+
});
|
|
266
|
+
$effect.pre(() => {
|
|
267
|
+
if (!networkPartConfig) {
|
|
268
|
+
// no config returned here indicates this api key has no permission to update config
|
|
326
269
|
return;
|
|
327
270
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
271
|
+
current = networkPartConfig;
|
|
272
|
+
});
|
|
273
|
+
const updateRobotPartMutation = createAppMutation('updateRobotPart');
|
|
274
|
+
return {
|
|
275
|
+
get current() {
|
|
276
|
+
return current ?? new Struct();
|
|
277
|
+
},
|
|
278
|
+
get isDirty() {
|
|
279
|
+
return isDirty;
|
|
280
|
+
},
|
|
281
|
+
get hasEditPermissions() {
|
|
282
|
+
return hasEditPermissions;
|
|
283
|
+
},
|
|
284
|
+
get componentNameToFragmentId() {
|
|
285
|
+
return componentNameToFragmentId;
|
|
286
|
+
},
|
|
287
|
+
set(config) {
|
|
288
|
+
current = Struct.fromJson(config);
|
|
289
|
+
isDirty = true;
|
|
290
|
+
},
|
|
291
|
+
async save() {
|
|
292
|
+
if (!current || !partName) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
networkPartConfig = current;
|
|
296
|
+
await updateRobotPartMutation.mutateAsync([partID(), partName, current]);
|
|
297
|
+
isDirty = false;
|
|
298
|
+
},
|
|
299
|
+
discardChanges() {
|
|
300
|
+
current = networkPartConfig;
|
|
301
|
+
isDirty = false;
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
};
|