@viamrobotics/motion-tools 0.14.2 → 0.14.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/components/App.svelte +38 -40
- package/dist/components/Details.svelte +47 -3
- package/dist/components/LiveUpdatesBanner.svelte +1 -1
- package/dist/components/Tree/AddFrames.svelte +1 -1
- package/dist/components/Tree/Drawer.svelte +4 -2
- package/dist/components/Tree/Drawer.svelte.d.ts +1 -0
- package/dist/components/Tree/Logs.svelte +53 -4
- package/dist/components/Tree/TreeContainer.svelte +5 -2
- package/dist/components/weblab/WeblabActive.svelte +3 -3
- package/dist/hooks/useFrames.svelte.js +2 -2
- package/dist/hooks/useGeometries.svelte.d.ts +1 -0
- package/dist/hooks/useGeometries.svelte.js +12 -2
- package/dist/hooks/useLogs.svelte.d.ts +3 -0
- package/dist/hooks/useLogs.svelte.js +40 -9
- package/dist/hooks/usePartConfig.svelte.js +17 -20
- package/dist/hooks/usePointclouds.svelte.d.ts +1 -0
- package/dist/hooks/usePointclouds.svelte.js +5 -0
- package/dist/hooks/useWeblabs.svelte.d.ts +4 -7
- package/dist/hooks/useWeblabs.svelte.js +43 -18
- package/package.json +2 -2
- package/dist/components/weblab/WeblabProvider.svelte +0 -8
- package/dist/components/weblab/WeblabProvider.svelte.d.ts +0 -5
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools'
|
|
5
5
|
import { provideToast, ToastContainer } from '@viamrobotics/prime-core'
|
|
6
6
|
import type { Struct } from '@viamrobotics/sdk'
|
|
7
|
-
|
|
8
7
|
import Scene from './Scene.svelte'
|
|
9
8
|
import TreeContainer from './Tree/TreeContainer.svelte'
|
|
10
9
|
import Details from './Details.svelte'
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
import { domPortal } from '../portal'
|
|
17
16
|
import { provideSettings } from '../hooks/useSettings.svelte'
|
|
18
17
|
import FileDrop from './FileDrop.svelte'
|
|
19
|
-
import
|
|
18
|
+
import { provideWeblabs } from '../hooks/useWeblabs.svelte'
|
|
20
19
|
import { providePartConfig } from '../hooks/usePartConfig.svelte'
|
|
21
20
|
import { useViamClient } from '@viamrobotics/svelte-sdk'
|
|
22
21
|
import LiveUpdatesBanner from './LiveUpdatesBanner.svelte'
|
|
@@ -54,6 +53,7 @@
|
|
|
54
53
|
|
|
55
54
|
createPartIDContext(() => partID)
|
|
56
55
|
|
|
56
|
+
provideWeblabs()
|
|
57
57
|
provideToast()
|
|
58
58
|
|
|
59
59
|
let root = $state.raw<HTMLElement>()
|
|
@@ -83,41 +83,39 @@
|
|
|
83
83
|
<SvelteQueryDevtools initialIsOpen />
|
|
84
84
|
{/if}
|
|
85
85
|
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
>
|
|
91
|
-
<
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
{
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
</div>
|
|
123
|
-
</WeblabProvider>
|
|
86
|
+
<div
|
|
87
|
+
class="relative h-full w-full overflow-hidden"
|
|
88
|
+
bind:this={root}
|
|
89
|
+
>
|
|
90
|
+
<Canvas renderMode="always">
|
|
91
|
+
<World>
|
|
92
|
+
<SceneProviders>
|
|
93
|
+
{#snippet children({ focus })}
|
|
94
|
+
<Scene>
|
|
95
|
+
{@render appChildren?.()}
|
|
96
|
+
</Scene>
|
|
97
|
+
|
|
98
|
+
<XR {@attach domPortal(root)} />
|
|
99
|
+
|
|
100
|
+
<Dashboard {@attach domPortal(root)} />
|
|
101
|
+
<Details {@attach domPortal(root)} />
|
|
102
|
+
{#if environment.current.isStandalone}
|
|
103
|
+
<LiveUpdatesBanner {@attach domPortal(root)} />
|
|
104
|
+
{/if}
|
|
105
|
+
|
|
106
|
+
{#if !focus}
|
|
107
|
+
<TreeContainer {@attach domPortal(root)} />
|
|
108
|
+
{/if}
|
|
109
|
+
|
|
110
|
+
{#if !focus && settings.current.enableArmPositionsWidget}
|
|
111
|
+
<ArmPositions {@attach domPortal(root)} />
|
|
112
|
+
{/if}
|
|
113
|
+
|
|
114
|
+
<FileDrop {@attach domPortal(root)} />
|
|
115
|
+
{/snippet}
|
|
116
|
+
</SceneProviders>
|
|
117
|
+
</World>
|
|
118
|
+
</Canvas>
|
|
119
|
+
|
|
120
|
+
<ToastContainer />
|
|
121
|
+
</div>
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
import { useFrames } from '../hooks/useFrames.svelte'
|
|
27
27
|
import { usePartConfig } from '../hooks/usePartConfig.svelte'
|
|
28
28
|
import { DetailConfigUpdater } from '../Detail.svelte'
|
|
29
|
+
import { useWeblabs } from '../hooks/useWeblabs.svelte'
|
|
29
30
|
|
|
30
31
|
const { ...rest } = $props()
|
|
31
32
|
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
const partConfig = usePartConfig()
|
|
37
38
|
const selectedObject = useSelectedObject()
|
|
38
39
|
const selectedObject3d = useSelectedObject3d()
|
|
40
|
+
const weblab = useWeblabs()
|
|
39
41
|
|
|
40
42
|
const object = $derived(focusedObject.current ?? selectedObject.current)
|
|
41
43
|
const object3d = $derived(focusedObject3d.current ?? selectedObject3d.current)
|
|
@@ -99,6 +101,48 @@
|
|
|
99
101
|
stop()
|
|
100
102
|
}
|
|
101
103
|
})
|
|
104
|
+
|
|
105
|
+
const getCopyClipboardText = () => {
|
|
106
|
+
if (weblab.isActive('MOTION_TOOLS_EDIT_FRAME')) {
|
|
107
|
+
return JSON.stringify(
|
|
108
|
+
{
|
|
109
|
+
worldPosition: worldPosition,
|
|
110
|
+
worldOrientation: worldOrientation,
|
|
111
|
+
localPosition: {
|
|
112
|
+
x: localPose?.x,
|
|
113
|
+
y: localPose?.y,
|
|
114
|
+
z: localPose?.z,
|
|
115
|
+
},
|
|
116
|
+
localOrientation: {
|
|
117
|
+
x: localPose?.oX,
|
|
118
|
+
y: localPose?.oY,
|
|
119
|
+
z: localPose?.oZ,
|
|
120
|
+
th: localPose?.theta,
|
|
121
|
+
},
|
|
122
|
+
geometry: {
|
|
123
|
+
type: geometryType,
|
|
124
|
+
value: object?.geometry?.geometryType.value,
|
|
125
|
+
},
|
|
126
|
+
parentFrame: referenceFrame,
|
|
127
|
+
},
|
|
128
|
+
null,
|
|
129
|
+
2
|
|
130
|
+
)
|
|
131
|
+
} else {
|
|
132
|
+
return JSON.stringify(
|
|
133
|
+
{
|
|
134
|
+
worldPosition: worldPosition,
|
|
135
|
+
worldOrientation: worldOrientation,
|
|
136
|
+
geometry: {
|
|
137
|
+
type: geometryType,
|
|
138
|
+
value: object?.geometry?.geometryType.value,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
null,
|
|
142
|
+
2
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
102
146
|
</script>
|
|
103
147
|
|
|
104
148
|
{#snippet ImmutableField({
|
|
@@ -199,7 +243,7 @@
|
|
|
199
243
|
|
|
200
244
|
<button
|
|
201
245
|
onclick={async () => {
|
|
202
|
-
navigator.clipboard.writeText(
|
|
246
|
+
navigator.clipboard.writeText(getCopyClipboardText())
|
|
203
247
|
copied = true
|
|
204
248
|
setTimeout(() => (copied = false), 1000)
|
|
205
249
|
}}
|
|
@@ -430,7 +474,7 @@
|
|
|
430
474
|
<div class="flex items-center gap-2">
|
|
431
475
|
{@render GeometryAttribute({
|
|
432
476
|
label: 'r',
|
|
433
|
-
ariaLabel: 'sphere dimensions radius value
|
|
477
|
+
ariaLabel: 'sphere dimensions radius value',
|
|
434
478
|
value: radiusMm ? radiusMm.toFixed(2) : '-',
|
|
435
479
|
onInput: (value) =>
|
|
436
480
|
detailConfigUpdater.updateGeometry({ type: 'sphere', r: parseFloat(value) }),
|
|
@@ -523,7 +567,7 @@
|
|
|
523
567
|
<Button
|
|
524
568
|
variant="danger"
|
|
525
569
|
class="mt-2 w-full"
|
|
526
|
-
onclick={() => detailConfigUpdater.deleteFrame()}>Delete
|
|
570
|
+
onclick={() => detailConfigUpdater.deleteFrame()}>Delete frame</Button
|
|
527
571
|
>
|
|
528
572
|
{/if}
|
|
529
573
|
</WeblabActive>
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<Drawer name="Add frames">
|
|
12
|
-
<div class="flex h-64
|
|
12
|
+
<div class="flex max-h-64 flex-col gap-2 overflow-auto p-3">
|
|
13
13
|
{#if framelessComponents.current.length > 0}
|
|
14
14
|
<ul class="space-y-1">
|
|
15
15
|
{#each framelessComponents.current as component (component)}
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
name: string
|
|
8
8
|
defaultOpen?: boolean
|
|
9
9
|
children: Snippet
|
|
10
|
+
titleAlert?: Snippet
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
let { name, children, defaultOpen = false }: Props = $props()
|
|
13
|
+
let { name, children, titleAlert, defaultOpen = false }: Props = $props()
|
|
13
14
|
|
|
14
15
|
const expanded = $derived(new PersistedState(`${name}-expanded`, defaultOpen))
|
|
15
16
|
</script>
|
|
@@ -24,9 +25,10 @@
|
|
|
24
25
|
label="unfold more icon"
|
|
25
26
|
variant="ghost"
|
|
26
27
|
cx="size-6"
|
|
27
|
-
|
|
28
|
+
onclick={() => (expanded.current = !expanded.current)}
|
|
28
29
|
/>
|
|
29
30
|
{name}
|
|
31
|
+
{@render titleAlert?.()}
|
|
30
32
|
</h3>
|
|
31
33
|
</button>
|
|
32
34
|
|
|
@@ -1,14 +1,56 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { useFrames } from '../../hooks/useFrames.svelte'
|
|
3
|
+
import { useGeometries } from '../../hooks/useGeometries.svelte'
|
|
2
4
|
import { useLogs } from '../../hooks/useLogs.svelte'
|
|
5
|
+
import { usePointClouds } from '../../hooks/usePointclouds.svelte'
|
|
3
6
|
import Drawer from './Drawer.svelte'
|
|
4
7
|
|
|
8
|
+
const frames = useFrames()
|
|
9
|
+
const geometries = useGeometries()
|
|
10
|
+
const pointclouds = usePointClouds()
|
|
5
11
|
const logs = useLogs()
|
|
6
|
-
|
|
12
|
+
|
|
13
|
+
$effect(() => {
|
|
14
|
+
if (frames.error) {
|
|
15
|
+
const message = `Frames: ${frames.error.message}`
|
|
16
|
+
logs.add(message, 'error')
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
$effect(() => {
|
|
21
|
+
if (geometries.errors.length > 0) {
|
|
22
|
+
for (const error of geometries.errors) {
|
|
23
|
+
logs.add(`Geometries: ${error.message}`, 'error')
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
$effect(() => {
|
|
29
|
+
if (pointclouds.errors.length > 0) {
|
|
30
|
+
for (const error of pointclouds.errors) {
|
|
31
|
+
logs.add(`Pointclouds: ${error.message}`, 'error')
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})
|
|
7
35
|
</script>
|
|
8
36
|
|
|
9
37
|
<Drawer name="Logs">
|
|
10
|
-
|
|
11
|
-
{#
|
|
38
|
+
{#snippet titleAlert()}
|
|
39
|
+
{#if logs.warnings.length > 0}
|
|
40
|
+
<span class="mr-1 rounded bg-yellow-700 px-1 py-0.5 text-xs text-white">
|
|
41
|
+
{logs.warnings.length}
|
|
42
|
+
</span>
|
|
43
|
+
{/if}
|
|
44
|
+
|
|
45
|
+
{#if logs.errors.length > 0}
|
|
46
|
+
<span class="mr-1 rounded bg-red-700 px-1 py-0.5 text-xs text-white">
|
|
47
|
+
{logs.errors.length}
|
|
48
|
+
</span>
|
|
49
|
+
{/if}
|
|
50
|
+
{/snippet}
|
|
51
|
+
|
|
52
|
+
<div class="flex h-64 flex-col gap-2 overflow-auto p-3">
|
|
53
|
+
{#each logs.current as log (log.uuid)}
|
|
12
54
|
<div>
|
|
13
55
|
<div class="flex flex-wrap items-center gap-1.5">
|
|
14
56
|
<div
|
|
@@ -23,7 +65,14 @@
|
|
|
23
65
|
></div>
|
|
24
66
|
<div class="text-subtle-2">{log.timestamp}</div>
|
|
25
67
|
</div>
|
|
26
|
-
<div>
|
|
68
|
+
<div>
|
|
69
|
+
{#if log.count > 1}
|
|
70
|
+
<span class="mr-1 rounded bg-green-700 px-1 py-0.5 text-xs text-white">
|
|
71
|
+
{log.count}
|
|
72
|
+
</span>
|
|
73
|
+
{/if}
|
|
74
|
+
{log.message}
|
|
75
|
+
</div>
|
|
27
76
|
</div>
|
|
28
77
|
{:else}
|
|
29
78
|
No logs
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
import Widgets from './Widgets.svelte'
|
|
14
14
|
import AddFrames from './AddFrames.svelte'
|
|
15
15
|
import { useEnvironment } from '../../hooks/useEnvironment.svelte'
|
|
16
|
+
import { usePartID } from '../../hooks/usePartID.svelte'
|
|
16
17
|
const { ...rest } = $props()
|
|
17
18
|
|
|
18
19
|
provideTreeExpandedContext()
|
|
19
20
|
|
|
21
|
+
const partID = usePartID()
|
|
20
22
|
const selected = useSelected()
|
|
21
23
|
const objects = useObjects()
|
|
22
24
|
const draggable = useDraggable('treeview')
|
|
@@ -40,7 +42,7 @@
|
|
|
40
42
|
</script>
|
|
41
43
|
|
|
42
44
|
<div
|
|
43
|
-
class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 overflow-y-auto border text-xs"
|
|
45
|
+
class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 w-60 overflow-y-auto border text-xs"
|
|
44
46
|
style:transform="translate({draggable.current.x}px, {draggable.current.y}px)"
|
|
45
47
|
{...rest}
|
|
46
48
|
>
|
|
@@ -56,9 +58,10 @@
|
|
|
56
58
|
/>
|
|
57
59
|
{/key}
|
|
58
60
|
|
|
59
|
-
{#if environment.current.isStandalone}
|
|
61
|
+
{#if environment.current.isStandalone && partID.current}
|
|
60
62
|
<AddFrames />
|
|
61
63
|
{/if}
|
|
64
|
+
|
|
62
65
|
<Logs />
|
|
63
66
|
<Settings />
|
|
64
67
|
<Widgets />
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
}
|
|
10
10
|
let { experiment, children, renderIfActive = true }: Props = $props()
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const weblabs = useWeblabs()
|
|
13
13
|
|
|
14
14
|
$effect.pre(() => {
|
|
15
|
-
|
|
15
|
+
weblabs.load([experiment])
|
|
16
16
|
})
|
|
17
17
|
</script>
|
|
18
18
|
|
|
19
|
-
{#if
|
|
19
|
+
{#if weblabs.isActive(experiment) === renderIfActive}
|
|
20
20
|
{@render children()}
|
|
21
21
|
{/if}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { getContext, setContext, untrack } from 'svelte';
|
|
2
2
|
import { useRobotClient, createRobotQuery, useMachineStatus, useResourceNames, } from '@viamrobotics/svelte-sdk';
|
|
3
3
|
import { WorldObject } from '../WorldObject.svelte';
|
|
4
|
-
import { observe } from '@threlte/core';
|
|
5
4
|
import { useLogs } from './useLogs.svelte';
|
|
6
5
|
import { resourceColors } from '../color';
|
|
7
6
|
import { usePartConfig } from './usePartConfig.svelte';
|
|
8
7
|
import { Color } from 'three';
|
|
9
8
|
import { useEnvironment } from './useEnvironment.svelte';
|
|
10
9
|
import { createPoseFromFrame } from '../transform';
|
|
10
|
+
import { observe } from '@threlte/core';
|
|
11
11
|
const key = Symbol('frames-context');
|
|
12
12
|
export const provideFrames = (partID) => {
|
|
13
13
|
const resourceNames = useResourceNames(partID);
|
|
@@ -22,7 +22,7 @@ export const provideFrames = (partID) => {
|
|
|
22
22
|
untrack(() => query.current).refetch();
|
|
23
23
|
logs.add('Fetching frames...');
|
|
24
24
|
});
|
|
25
|
-
|
|
25
|
+
$effect.pre(() => {
|
|
26
26
|
if (partConfig.isDirty) {
|
|
27
27
|
environment.current.viewerMode = 'edit';
|
|
28
28
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArmClient, CameraClient, Geometry, GripperClient } from '@viamrobotics/sdk';
|
|
1
|
+
import { ArmClient, CameraClient, GantryClient, Geometry, GripperClient } from '@viamrobotics/sdk';
|
|
2
2
|
import { createQueries, queryOptions } from '@tanstack/svelte-query';
|
|
3
3
|
import { createResourceClient, useResourceNames } from '@viamrobotics/svelte-sdk';
|
|
4
4
|
import { setContext, getContext } from 'svelte';
|
|
@@ -9,18 +9,24 @@ import { usePersistentUUIDs } from './usePersistentUUIDs.svelte';
|
|
|
9
9
|
import { useLogs } from './useLogs.svelte';
|
|
10
10
|
import { resourceColors } from '../color';
|
|
11
11
|
import { Color } from 'three';
|
|
12
|
+
import { useFrames } from './useFrames.svelte';
|
|
12
13
|
const key = Symbol('geometries-context');
|
|
13
14
|
export const provideGeometries = (partID) => {
|
|
15
|
+
const frames = useFrames();
|
|
14
16
|
const resourceNames = useResourceNames(partID);
|
|
15
17
|
const arms = useResourceNames(partID, 'arm');
|
|
16
18
|
const cameras = useResourceNames(partID, 'camera');
|
|
17
19
|
const grippers = useResourceNames(partID, 'gripper');
|
|
20
|
+
const gantries = useResourceNames(partID, 'gantry');
|
|
18
21
|
const logs = useLogs();
|
|
19
22
|
const { refreshRates } = useMachineSettings();
|
|
20
23
|
const armClients = $derived(arms.current.map((arm) => createResourceClient(ArmClient, partID, () => arm.name)));
|
|
21
24
|
const gripperClients = $derived(grippers.current.map((gripper) => createResourceClient(GripperClient, partID, () => gripper.name)));
|
|
22
25
|
const cameraClients = $derived(cameras.current.map((camera) => createResourceClient(CameraClient, partID, () => camera.name)));
|
|
23
|
-
const
|
|
26
|
+
const gantryClients = $derived(gantries.current.map((gantry) => createResourceClient(GantryClient, partID, () => gantry.name)));
|
|
27
|
+
const clients = $derived([...armClients, ...gripperClients, ...cameraClients, ...gantryClients].filter((client) => {
|
|
28
|
+
return frames.current.some((frame) => frame.name === client.current?.name);
|
|
29
|
+
}));
|
|
24
30
|
const options = $derived.by(() => {
|
|
25
31
|
const interval = refreshRates.get(RefreshRates.poses);
|
|
26
32
|
const results = [];
|
|
@@ -44,6 +50,7 @@ export const provideGeometries = (partID) => {
|
|
|
44
50
|
});
|
|
45
51
|
const { updateUUIDs } = usePersistentUUIDs();
|
|
46
52
|
const queries = fromStore(createQueries({ queries: toStore(() => options) }));
|
|
53
|
+
const errors = $derived(queries.current.map((query) => query.error).filter((error) => error !== null));
|
|
47
54
|
const geometries = $derived.by(() => {
|
|
48
55
|
const results = [];
|
|
49
56
|
for (const query of queries.current) {
|
|
@@ -66,6 +73,9 @@ export const provideGeometries = (partID) => {
|
|
|
66
73
|
get current() {
|
|
67
74
|
return geometries;
|
|
68
75
|
},
|
|
76
|
+
get errors() {
|
|
77
|
+
return errors;
|
|
78
|
+
},
|
|
69
79
|
});
|
|
70
80
|
};
|
|
71
81
|
export const useGeometries = () => {
|
|
@@ -2,11 +2,14 @@ type Level = 'info' | 'warn' | 'error';
|
|
|
2
2
|
interface Log {
|
|
3
3
|
uuid: string;
|
|
4
4
|
message: string;
|
|
5
|
+
count: number;
|
|
5
6
|
level: Level;
|
|
6
7
|
timestamp: string;
|
|
7
8
|
}
|
|
8
9
|
interface Context {
|
|
9
10
|
current: Log[];
|
|
11
|
+
errors: Log[];
|
|
12
|
+
warnings: Log[];
|
|
10
13
|
add(message: string, level?: Level): void;
|
|
11
14
|
}
|
|
12
15
|
export declare const provideLogs: () => void;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { getContext, setContext } from 'svelte';
|
|
1
|
+
import { getContext, setContext, untrack } from 'svelte';
|
|
2
2
|
import { MathUtils } from 'three';
|
|
3
3
|
const key = Symbol('logs-context');
|
|
4
4
|
export const provideLogs = () => {
|
|
5
5
|
const logs = $state([]);
|
|
6
|
+
const warnings = $state([]);
|
|
7
|
+
const errors = $state([]);
|
|
6
8
|
const intl = new Intl.DateTimeFormat('en-US', {
|
|
7
9
|
dateStyle: 'short',
|
|
8
10
|
timeStyle: 'short',
|
|
@@ -11,16 +13,45 @@ export const provideLogs = () => {
|
|
|
11
13
|
get current() {
|
|
12
14
|
return logs;
|
|
13
15
|
},
|
|
16
|
+
get errors() {
|
|
17
|
+
return errors;
|
|
18
|
+
},
|
|
19
|
+
get warnings() {
|
|
20
|
+
return warnings;
|
|
21
|
+
},
|
|
14
22
|
add(message, level = 'info') {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
level
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
untrack(() => {
|
|
24
|
+
const timestamp = intl.format(Date.now());
|
|
25
|
+
const match = logs.find((log) => log.message === message && log.level === level && log.timestamp === timestamp);
|
|
26
|
+
if (match) {
|
|
27
|
+
match.count += 1;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const log = {
|
|
31
|
+
timestamp,
|
|
32
|
+
message,
|
|
33
|
+
count: 1,
|
|
34
|
+
level,
|
|
35
|
+
uuid: MathUtils.generateUUID(),
|
|
36
|
+
};
|
|
37
|
+
logs.unshift(log);
|
|
38
|
+
if (level === 'error') {
|
|
39
|
+
errors.unshift(log);
|
|
40
|
+
}
|
|
41
|
+
else if (level === 'warn') {
|
|
42
|
+
warnings.unshift(log);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (logs.length > 200) {
|
|
46
|
+
const log = logs.pop();
|
|
47
|
+
if (log && level === 'error') {
|
|
48
|
+
errors.splice(errors.indexOf(log), 1);
|
|
49
|
+
}
|
|
50
|
+
else if (log && level === 'warn') {
|
|
51
|
+
warnings.splice(errors.indexOf(log), 1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
20
54
|
});
|
|
21
|
-
if (logs.length > 1000) {
|
|
22
|
-
logs.shift();
|
|
23
|
-
}
|
|
24
55
|
},
|
|
25
56
|
});
|
|
26
57
|
};
|
|
@@ -257,32 +257,29 @@ export class StandalonePartConfig {
|
|
|
257
257
|
const fragmentRequests = [];
|
|
258
258
|
if (configJson.fragments) {
|
|
259
259
|
for (const fragmentId of configJson.fragments) {
|
|
260
|
-
|
|
260
|
+
//TODO: right now the json could be just a list of strings or an object with an id prop
|
|
261
|
+
const fragId = typeof fragmentId === 'string' ? fragmentId : fragmentId.id;
|
|
262
|
+
fragmentRequests.push(standalonePartConfigProps.viamClient()?.appClient.getFragment(fragId));
|
|
261
263
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
componentNameToFragmentId[componentName.value] = fragmentId;
|
|
276
|
-
}
|
|
264
|
+
const fragementResponses = await Promise.all(fragmentRequests);
|
|
265
|
+
for (const fragmentResponse of fragementResponses) {
|
|
266
|
+
const fragmentId = fragmentResponse?.id;
|
|
267
|
+
if (!fragmentId) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const components = fragmentResponse?.fragment?.fields['components'].kind;
|
|
271
|
+
if (components?.case === 'listValue') {
|
|
272
|
+
for (const component of components.value.values) {
|
|
273
|
+
if (component.kind.case === 'structValue') {
|
|
274
|
+
const componentName = component.kind.value.fields['name'].kind;
|
|
275
|
+
if (componentName.case === 'stringValue') {
|
|
276
|
+
componentNameToFragmentId[componentName.value] = fragmentId;
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
|
-
this._componentNameToFragmentId = componentNameToFragmentId;
|
|
282
|
-
}
|
|
283
|
-
catch {
|
|
284
|
-
/* Do nothing */
|
|
285
281
|
}
|
|
282
|
+
this._componentNameToFragmentId = componentNameToFragmentId;
|
|
286
283
|
}
|
|
287
284
|
};
|
|
288
285
|
initLocalConfig();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WorldObject, type PointsGeometry } from '../WorldObject.svelte';
|
|
2
2
|
interface Context {
|
|
3
3
|
current: WorldObject<PointsGeometry>[];
|
|
4
|
+
errors: Error[];
|
|
4
5
|
}
|
|
5
6
|
export declare const providePointclouds: (partID: () => string) => void;
|
|
6
7
|
export declare const usePointClouds: () => Context;
|
|
@@ -48,9 +48,11 @@ export const providePointclouds = (partID) => {
|
|
|
48
48
|
const data = results
|
|
49
49
|
.flatMap((result) => result.data)
|
|
50
50
|
.filter((data) => data !== null && data !== undefined);
|
|
51
|
+
const errors = results.flatMap((result) => result.error).filter((error) => error !== null);
|
|
51
52
|
updateUUIDs(data);
|
|
52
53
|
return {
|
|
53
54
|
data,
|
|
55
|
+
errors,
|
|
54
56
|
};
|
|
55
57
|
},
|
|
56
58
|
}));
|
|
@@ -58,6 +60,9 @@ export const providePointclouds = (partID) => {
|
|
|
58
60
|
get current() {
|
|
59
61
|
return queries.current.data;
|
|
60
62
|
},
|
|
63
|
+
get errors() {
|
|
64
|
+
return queries.current.errors;
|
|
65
|
+
},
|
|
61
66
|
});
|
|
62
67
|
};
|
|
63
68
|
export const usePointClouds = () => {
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
+
export declare const WEBLABS_CONTEXT_KEY: unique symbol;
|
|
1
2
|
interface Context {
|
|
2
|
-
|
|
3
|
+
load: (experiments: string[]) => void;
|
|
4
|
+
isActive(experiment: string): boolean;
|
|
3
5
|
}
|
|
6
|
+
export declare const createWeblabs: () => Context;
|
|
4
7
|
export declare const provideWeblabs: () => void;
|
|
5
8
|
export declare const useWeblabs: () => Context;
|
|
6
|
-
export declare class Weblab {
|
|
7
|
-
private activeExperiments;
|
|
8
|
-
constructor();
|
|
9
|
-
isActive(experiment: string): boolean;
|
|
10
|
-
load(experiments: string[]): void;
|
|
11
|
-
}
|
|
12
9
|
export {};
|
|
@@ -1,25 +1,50 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
|
-
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
export class Weblab {
|
|
11
|
-
activeExperiments;
|
|
12
|
-
constructor() {
|
|
13
|
-
this.activeExperiments = new Set();
|
|
2
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
3
|
+
export const WEBLABS_CONTEXT_KEY = Symbol('weblabs-context');
|
|
4
|
+
const getCookie = (name) => {
|
|
5
|
+
const value = `; ${document.cookie}`;
|
|
6
|
+
const parts = value.split(`; ${name}=`);
|
|
7
|
+
if (parts.length === 2) {
|
|
8
|
+
return parts.pop()?.split(';').shift() || null;
|
|
14
9
|
}
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
return null;
|
|
11
|
+
};
|
|
12
|
+
const addCookie = (name, value, days, path = '/') => {
|
|
13
|
+
let expires = '';
|
|
14
|
+
if (days !== undefined) {
|
|
15
|
+
const date = new Date();
|
|
16
|
+
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); // days in milliseconds
|
|
17
|
+
expires = '; expires=' + date.toUTCString();
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}${expires}; path=${path}`;
|
|
20
|
+
};
|
|
21
|
+
const getCookieExperiments = () => {
|
|
22
|
+
return getCookie('weblab_experiments')?.split(',') ?? [];
|
|
23
|
+
};
|
|
24
|
+
export const createWeblabs = () => {
|
|
25
|
+
const activeExperiments = new SvelteSet();
|
|
26
|
+
const load = (experiments) => {
|
|
27
|
+
const cookieExperiments = getCookieExperiments();
|
|
19
28
|
for (const experiment of experiments) {
|
|
20
|
-
if (
|
|
21
|
-
|
|
29
|
+
if (cookieExperiments.includes(experiment)) {
|
|
30
|
+
activeExperiments.add(experiment);
|
|
22
31
|
}
|
|
23
32
|
}
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
load,
|
|
36
|
+
isActive: (experiment) => {
|
|
37
|
+
return activeExperiments.has(experiment);
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export const provideWeblabs = () => {
|
|
42
|
+
const urlExperiment = new URLSearchParams(window.location.search).get('experiment');
|
|
43
|
+
if (urlExperiment) {
|
|
44
|
+
addCookie('weblab_experiments', [...getCookieExperiments(), urlExperiment].join(','));
|
|
24
45
|
}
|
|
25
|
-
|
|
46
|
+
setContext(WEBLABS_CONTEXT_KEY, createWeblabs());
|
|
47
|
+
};
|
|
48
|
+
export const useWeblabs = () => {
|
|
49
|
+
return getContext(WEBLABS_CONTEXT_KEY);
|
|
50
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viamrobotics/motion-tools",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"description": "Motion visualization with Viam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@typescript-eslint/parser": "8.42.0",
|
|
39
39
|
"@viamrobotics/prime-core": "0.1.5",
|
|
40
40
|
"@viamrobotics/sdk": "0.52.0",
|
|
41
|
-
"@viamrobotics/svelte-sdk": "0.
|
|
41
|
+
"@viamrobotics/svelte-sdk": "0.7.1",
|
|
42
42
|
"@vitejs/plugin-basic-ssl": "2.1.0",
|
|
43
43
|
"@zag-js/svelte": "1.22.1",
|
|
44
44
|
"@zag-js/tree-view": "1.22.1",
|