@viamrobotics/test-widgets 0.6.1 → 0.7.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 (51) hide show
  1. package/README.md +1 -1
  2. package/dist/api-docs-href.d.ts +8 -0
  3. package/dist/api-docs-href.js +17 -0
  4. package/dist/components/angle-unit-toggle.svelte +1 -4
  5. package/dist/components/api-section.svelte +21 -23
  6. package/dist/components/api-section.svelte.d.ts +2 -0
  7. package/dist/components/index.d.ts +1 -0
  8. package/dist/components/index.js +1 -0
  9. package/dist/components/is-moving.svelte +3 -1
  10. package/dist/components/is-moving.svelte.d.ts +1 -0
  11. package/dist/components/mutation-section.svelte +19 -22
  12. package/dist/components/mutation-section.svelte.d.ts +2 -0
  13. package/dist/components/section-title.svelte +63 -0
  14. package/dist/components/section-title.svelte.d.ts +14 -0
  15. package/dist/components/widgets/arm/arm.svelte +17 -24
  16. package/dist/components/widgets/arm/joint-position-editor.svelte +89 -0
  17. package/dist/components/widgets/arm/joint-position-editor.svelte.d.ts +11 -0
  18. package/dist/components/widgets/arm/joint-position-quick-move.svelte +85 -0
  19. package/dist/components/widgets/arm/joint-position-quick-move.svelte.d.ts +9 -0
  20. package/dist/components/widgets/arm/joint-position-slider.svelte +36 -0
  21. package/dist/components/widgets/arm/joint-position-slider.svelte.d.ts +9 -0
  22. package/dist/components/widgets/arm/move-to-joint-positions.svelte +73 -127
  23. package/dist/components/widgets/arm/move-to-joint-positions.svelte.d.ts +1 -0
  24. package/dist/components/widgets/audio-input/audio-input.svelte +2 -0
  25. package/dist/components/widgets/audio-output/audio-output.svelte +2 -0
  26. package/dist/components/widgets/base/base.svelte +9 -1
  27. package/dist/components/widgets/camera/camera.svelte +156 -142
  28. package/dist/components/widgets/camera/live-or-polling-video.svelte +1 -0
  29. package/dist/components/widgets/discovery/discovery.svelte +1 -0
  30. package/dist/components/widgets/encoder/encoder.svelte +2 -0
  31. package/dist/components/widgets/gantry/gantry.svelte +28 -5
  32. package/dist/components/widgets/generic/generic.svelte +8 -1
  33. package/dist/components/widgets/gripper/gripper.svelte +11 -5
  34. package/dist/components/widgets/gripper/is-holding-something.svelte +1 -0
  35. package/dist/components/widgets/input-controller/input-controller.svelte +2 -1
  36. package/dist/components/widgets/motor/motor.svelte +11 -1
  37. package/dist/components/widgets/movement-sensor/movement-sensor.svelte +53 -26
  38. package/dist/components/widgets/power-sensor/power-sensor.svelte +4 -0
  39. package/dist/components/widgets/sensor/sensor.svelte +1 -0
  40. package/dist/components/widgets/servo/servo.svelte +10 -2
  41. package/dist/components/widgets/slam/move-on-map.svelte +4 -1
  42. package/dist/components/widgets/slam/slam.svelte +4 -1
  43. package/dist/components/widgets/vision-service/context.svelte.d.ts +1 -0
  44. package/dist/components/widgets/vision-service/context.svelte.js +7 -0
  45. package/dist/components/widgets/vision-service/detection-row.svelte +38 -0
  46. package/dist/components/widgets/vision-service/detection-row.svelte.d.ts +8 -0
  47. package/dist/components/widgets/vision-service/detection.svelte +7 -4
  48. package/dist/components/widgets/vision-service/legend.svelte +14 -11
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.js +1 -0
  51. package/package.json +5 -2
@@ -12,6 +12,7 @@
12
12
  createRefetchIntervalStore,
13
13
  RefetchIntervals,
14
14
  } from '../../refetch-interval-store.svelte'
15
+ import SectionTitle from '../../section-title.svelte'
15
16
 
16
17
  import PCDWidget from '../pcd/pcd-widget.svelte'
17
18
  import ExportScreenshot from './export-screenshot.svelte'
@@ -150,166 +151,179 @@
150
151
  }
151
152
 
152
153
  const headingID = $props.id()
154
+ const getImagesHeadingID = `${headingID}-images`
155
+ const getPointCloudHeadingID = `${headingID}-point-cloud`
153
156
  </script>
154
157
 
155
158
  <ConnectionStatus {partID}>
156
159
  {#snippet connected()}
157
- {#if isPlaying}
158
- <div class="flex gap-4 p-4 pb-3">
159
- <RefetchController
160
- {refetchInterval}
161
- allowLive
162
- queries={[imageQuery, pointcloudQuery]}
160
+ <section
161
+ class="flex flex-col"
162
+ aria-labelledby={getImagesHeadingID}
163
+ >
164
+ <div class="flex flex-col gap-0.5 p-4 pb-0">
165
+ <SectionTitle
166
+ title="GetImages"
167
+ api="rdk:component:camera"
168
+ headingId={getImagesHeadingID}
163
169
  />
164
- {#if sourceNames.length > 0 && refetchInterval.current !== RefetchIntervals.LIVE}
165
- <Label position="left">
166
- Source
167
- <Select
168
- value={selectedSource}
169
- on:change={onSourceSelect}
170
- slot="input"
171
- >
172
- {#each sourceNames as name (name)}
173
- <option value={name}>{name}</option>
174
- {/each}
175
- </Select>
176
- </Label>
177
- {/if}
178
- {#if panoCoverage}
179
- <Label>
180
- {panoToggleLabel}
181
- <ToggleButtons
182
- slot="input"
183
- options={['On', 'Off']}
184
- selected={displayAs360 ? 'On' : 'Off'}
185
- on:input={(event) => {
186
- displayAs360 = event.detail === 'On'
187
- }}
188
- />
189
- </Label>
190
- {/if}
191
170
  </div>
192
- {/if}
193
- <div class="flex h-full w-full gap-4 p-4">
194
- <div class="grow">
195
- {#if isPlaying}
196
- {#if displayAs360}
197
- {#if isLive}
198
- <!-- renderMode="always" keeps the live VideoTexture advancing each frame -->
199
- <Canvas renderMode="always">
200
- <LiveThreeSixtyCameraView
201
- {partID}
202
- {resourceName}
203
- coverage={panoCoverage}
204
- />
205
- </Canvas>
171
+ {#if isPlaying}
172
+ <div class="flex gap-4 p-4 pb-3">
173
+ <RefetchController
174
+ {refetchInterval}
175
+ allowLive
176
+ queries={[imageQuery, pointcloudQuery]}
177
+ />
178
+ {#if sourceNames.length > 0 && refetchInterval.current !== RefetchIntervals.LIVE}
179
+ <Label position="left">
180
+ Source
181
+ <Select
182
+ value={selectedSource}
183
+ on:change={onSourceSelect}
184
+ slot="input"
185
+ >
186
+ {#each sourceNames as name (name)}
187
+ <option value={name}>{name}</option>
188
+ {/each}
189
+ </Select>
190
+ </Label>
191
+ {/if}
192
+ {#if panoCoverage}
193
+ <Label>
194
+ {panoToggleLabel}
195
+ <ToggleButtons
196
+ slot="input"
197
+ options={['On', 'Off']}
198
+ selected={displayAs360 ? 'On' : 'Off'}
199
+ on:input={(event) => {
200
+ displayAs360 = event.detail === 'On'
201
+ }}
202
+ />
203
+ </Label>
204
+ {/if}
205
+ </div>
206
+ {/if}
207
+ <div class="flex h-full w-full gap-4 p-4">
208
+ <div class="grow">
209
+ {#if isPlaying}
210
+ {#if displayAs360}
211
+ {#if isLive}
212
+ <!-- renderMode="always" keeps the live VideoTexture advancing each frame -->
213
+ <Canvas renderMode="always">
214
+ <LiveThreeSixtyCameraView
215
+ {partID}
216
+ {resourceName}
217
+ coverage={panoCoverage}
218
+ />
219
+ </Canvas>
220
+ {:else}
221
+ <Canvas>
222
+ <ThreeSixtyCameraView
223
+ data={imageQuery.data}
224
+ coverage={panoCoverage}
225
+ />
226
+ </Canvas>
227
+ {/if}
206
228
  {:else}
207
- <Canvas>
208
- <ThreeSixtyCameraView
209
- data={imageQuery.data}
210
- coverage={panoCoverage}
211
- />
212
- </Canvas>
229
+ <LiveOrPollingVideo
230
+ {partID}
231
+ {resourceName}
232
+ showResolutionOptions
233
+ {isLive}
234
+ data={imageQuery.data}
235
+ error={imageQuery.error}
236
+ isLoading={imageQuery.isLoading}
237
+ refetch={imageQuery.refetch}
238
+ showMousePositionTooltip={mousePostionTooltip === 'On'}
239
+ sourceName={selectedSource}
240
+ />
213
241
  {/if}
214
242
  {:else}
215
- <LiveOrPollingVideo
216
- {partID}
217
- {resourceName}
218
- showResolutionOptions
219
- {isLive}
220
- data={imageQuery.data}
221
- error={imageQuery.error}
222
- isLoading={imageQuery.isLoading}
223
- refetch={imageQuery.refetch}
224
- showMousePositionTooltip={mousePostionTooltip === 'On'}
243
+ <div class="bg-medium flex h-64 w-80 items-center justify-center">
244
+ <Button
245
+ icon="play-circle-outline"
246
+ variant="dark"
247
+ onclick={() => {
248
+ isPlaying = true
249
+ }}
250
+ >
251
+ Start feed
252
+ </Button>
253
+ </div>
254
+ {/if}
255
+ </div>
256
+ {#if isPlaying}
257
+ <div class="flex flex-col items-start gap-2">
258
+ <ExportScreenshot
259
+ name={client.current?.name ?? ''}
225
260
  sourceName={selectedSource}
261
+ getImage={exportScreenshotQuery.refetch}
226
262
  />
227
- {/if}
228
- {:else}
229
- <div class="bg-medium flex h-64 w-80 items-center justify-center">
230
- <Button
231
- icon="play-circle-outline"
232
- variant="dark"
233
- onclick={() => {
234
- isPlaying = true
235
- }}
236
- >
237
- Start feed
238
- </Button>
239
- </div>
240
- {/if}
241
- </div>
242
- {#if isPlaying}
243
- <div class="flex flex-col items-start gap-2">
244
- <ExportScreenshot
245
- name={client.current?.name ?? ''}
246
- sourceName={selectedSource}
247
- getImage={exportScreenshotQuery.refetch}
248
- />
249
- {#if addImageToDataset}
250
- <Button
251
- icon="layers-triple-outline"
252
- onclick={async () => {
253
- let imgData
254
- if (refetchInterval.current === RefetchIntervals.LIVE) {
255
- const { isSuccess, data } = await imageQuery.refetch()
256
- if (!isSuccess) return
257
- imgData = data
258
- } else {
259
- imgData = imageQuery.data
260
- }
263
+ {#if addImageToDataset}
264
+ <Button
265
+ icon="layers-triple-outline"
266
+ onclick={async () => {
267
+ let imgData
268
+ if (refetchInterval.current === RefetchIntervals.LIVE) {
269
+ const { isSuccess, data } = await imageQuery.refetch()
270
+ if (!isSuccess) return
271
+ imgData = data
272
+ } else {
273
+ imgData = imageQuery.data
274
+ }
261
275
 
262
- const matchingImage = pickImageForSource(imgData?.images, selectedSource)
263
- const image = matchingImage?.image
264
- const mimeType = matchingImage?.mimeType
276
+ const matchingImage = pickImageForSource(imgData?.images, selectedSource)
277
+ const image = matchingImage?.image
278
+ const mimeType = matchingImage?.mimeType
265
279
 
266
- if (image) {
267
- addImageToDataset({
268
- binaryData: new Uint8Array(image),
269
- partID,
270
- componentType: 'camera',
271
- componentName: resourceName,
272
- methodName: 'captureAllFromCamera',
273
- mimeType: mimeType ?? 'image/png',
274
- dataRequestTimes: [new Date(), new Date()],
275
- })
276
- }
277
- }}
278
- >
279
- Add current image to dataset
280
- </Button>
281
- {/if}
282
- {#if !isFirefox}
283
- <PictureInPictureButton
284
- {resourceName}
285
- rate={refetchInterval.current}
286
- />
287
- {/if}
288
- <Label>
289
- Mouse Position Tooltip
280
+ if (image) {
281
+ addImageToDataset({
282
+ binaryData: new Uint8Array(image),
283
+ partID,
284
+ componentType: 'camera',
285
+ componentName: resourceName,
286
+ methodName: 'captureAllFromCamera',
287
+ mimeType: mimeType ?? 'image/png',
288
+ dataRequestTimes: [new Date(), new Date()],
289
+ })
290
+ }
291
+ }}
292
+ >
293
+ Add current image to dataset
294
+ </Button>
295
+ {/if}
296
+ {#if !isFirefox}
297
+ <PictureInPictureButton
298
+ {resourceName}
299
+ rate={refetchInterval.current}
300
+ />
301
+ {/if}
302
+ <Label>
303
+ Mouse Position Tooltip
290
304
 
291
- <ToggleButtons
292
- slot="input"
293
- options={['On', 'Off']}
294
- selected={mousePostionTooltip}
295
- on:input={setMousePostionTooltip}
296
- />
297
- </Label>
298
- </div>
299
- {/if}
300
- </div>
305
+ <ToggleButtons
306
+ slot="input"
307
+ options={['On', 'Off']}
308
+ selected={mousePostionTooltip}
309
+ on:input={setMousePostionTooltip}
310
+ />
311
+ </Label>
312
+ </div>
313
+ {/if}
314
+ </div>
315
+ </section>
301
316
 
302
317
  <section
303
318
  class="flex flex-col gap-4 p-4"
304
- aria-labelledby={headingID}
319
+ aria-labelledby={getPointCloudHeadingID}
305
320
  >
306
- <div>
307
- <h3
308
- id={headingID}
309
- class="pb-1.5 font-semibold"
310
- >
311
- GetPointCloud
312
- </h3>
321
+ <div class="flex flex-col gap-0.5">
322
+ <SectionTitle
323
+ title="GetPointCloud"
324
+ api="rdk:component:camera"
325
+ headingId={getPointCloudHeadingID}
326
+ />
313
327
  <p class="text-subtle-2 text-xs">
314
328
  Get depth measurement data from cameras with depth sensing support
315
329
  </p>
@@ -393,6 +393,7 @@
393
393
  {#if showResolutionOptions && isLive}
394
394
  <MutationSection
395
395
  title="SetStreamOptions"
396
+ api="rdk:component:camera"
396
397
  description="Change the resolution of the live stream video feed"
397
398
  lastError={resolutionMutation.error}
398
399
  class="-m-4 mt-4"
@@ -135,6 +135,7 @@
135
135
 
136
136
  <ApiSection
137
137
  title="DiscoverResources"
138
+ api="rdk:service:discovery"
138
139
  class="relative"
139
140
  >
140
141
  <Query
@@ -81,6 +81,7 @@
81
81
  <div class="grid w-full grid-cols-2 divide-x">
82
82
  <ApiSection
83
83
  title="GetPosition"
84
+ api="rdk:component:encoder"
84
85
  tooltip="Relative encoders return ticks since last zeroing. Absolute encoders return degrees."
85
86
  class="gap-3"
86
87
  >
@@ -99,6 +100,7 @@
99
100
 
100
101
  <ApiSection
101
102
  title="ResetPosition"
103
+ api="rdk:component:encoder"
102
104
  description="Set the current position as the new zero position"
103
105
  >
104
106
  <Button
@@ -6,6 +6,7 @@
6
6
  createResourceQuery,
7
7
  } from '@viamrobotics/svelte-sdk'
8
8
 
9
+ import { apiDocsHref } from '../../../api-docs-href'
9
10
  import ApiSection from '../../api-section.svelte'
10
11
  import ConnectionStatus from '../../connection-status.svelte'
11
12
  import IsMoving from '../../is-moving.svelte'
@@ -51,12 +52,26 @@
51
52
  aria-labelledby={positionHeadingID}
52
53
  >
53
54
  <h3
54
- class="flex flex-row items-center gap-1 text-sm"
55
+ class="text-subtle-2 flex flex-row items-center gap-1 text-sm"
55
56
  id={positionHeadingID}
56
57
  >
57
- <span class="font-semibold"> GetPosition </span>
58
+ <a
59
+ href={apiDocsHref('rdk:component:gantry', 'getPosition')}
60
+ target="_blank"
61
+ rel="noopener noreferrer external"
62
+ class="decoration-gray-5 hover:decoration-default text-default font-mono font-semibold underline underline-offset-3"
63
+ >
64
+ GetPosition
65
+ </a>
58
66
  and
59
- <span class="font-semibold"> GetLengths </span>
67
+ <a
68
+ href={apiDocsHref('rdk:component:gantry', 'getLengths')}
69
+ target="_blank"
70
+ rel="noopener noreferrer external"
71
+ class="decoration-gray-5 hover:decoration-default text-default font-mono font-semibold underline underline-offset-3"
72
+ >
73
+ GetLengths
74
+ </a>
60
75
  </h3>
61
76
  <Queries queries={[positionQuery, lengthsQuery]}>
62
77
  {@const positions = positionQuery.data}
@@ -69,7 +84,10 @@
69
84
  {/if}
70
85
  </Queries>
71
86
  </ApiSection>
72
- <ApiSection title="MoveToPosition">
87
+ <ApiSection
88
+ title="MoveToPosition"
89
+ api="rdk:component:gantry"
90
+ >
73
91
  <Query query={positionQuery}>
74
92
  {@const positions = positionQuery.data}
75
93
  {#if positions !== undefined}
@@ -106,6 +124,7 @@
106
124
  </ApiSection>
107
125
  <ApiSection
108
126
  title="Home"
127
+ api="rdk:component:gantry"
109
128
  description="Run the homing sequence"
110
129
  >
111
130
  <Home
@@ -116,7 +135,10 @@
116
135
  </div>
117
136
  </div>
118
137
  <div class="ml-auto flex w-full max-w-40 flex-col divide-y">
119
- <ApiSection title="Stop">
138
+ <ApiSection
139
+ title="Stop"
140
+ api="rdk:component:gantry"
141
+ >
120
142
  <StopButton
121
143
  error={stopMutation.error}
122
144
  onStop={() => {
@@ -126,6 +148,7 @@
126
148
  </ApiSection>
127
149
  <IsMoving
128
150
  client={GantryClient}
151
+ api="rdk:component:gantry"
129
152
  {partID}
130
153
  {resourceName}
131
154
  />
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { ResourceName } from '@viamrobotics/sdk'
3
3
 
4
+ import SectionTitle from '../../section-title.svelte'
5
+
4
6
  import DoCommand from '../do-command/do-command.svelte'
5
7
 
6
8
  interface Props {
@@ -11,6 +13,8 @@
11
13
 
12
14
  const { partID, resourceName, isComponent }: Props = $props()
13
15
 
16
+ const api = $derived(`rdk:${isComponent ? 'component' : 'service'}:generic`)
17
+
14
18
  const genericResourceName = $derived<ResourceName>({
15
19
  namespace: 'rdk',
16
20
  type: isComponent ? 'component' : 'service',
@@ -20,7 +24,10 @@
20
24
  </script>
21
25
 
22
26
  <div class="flex flex-col py-2 pl-4">
23
- <h3 class="font-semibold">DoCommand</h3>
27
+ <SectionTitle
28
+ title="DoCommand"
29
+ {api}
30
+ />
24
31
  </div>
25
32
 
26
33
  {#key genericResourceName}
@@ -30,10 +30,11 @@
30
30
  <ConnectionStatus {partID}>
31
31
  {#snippet connected()}
32
32
  <div class="flex flex-row divide-x">
33
- <span class="flex flex-row gap-4">
33
+ <span class="flex grow flex-row gap-4">
34
34
  <ApiSection
35
35
  title="Open"
36
- class="gap-3 pr-0"
36
+ api="rdk:component:gripper"
37
+ class="grow-0 gap-3 pr-0"
37
38
  >
38
39
  <Open
39
40
  {partID}
@@ -42,7 +43,8 @@
42
43
  </ApiSection>
43
44
  <ApiSection
44
45
  title="Grab"
45
- class="gap-3 pl-0"
46
+ api="rdk:component:gripper"
47
+ class="grow-0 gap-3 pl-0"
46
48
  >
47
49
  <Grab
48
50
  {partID}
@@ -50,8 +52,11 @@
50
52
  />
51
53
  </ApiSection>
52
54
  </span>
53
- <div class="ml-auto flex w-full max-w-40 flex-col divide-y">
54
- <ApiSection title="Stop">
55
+ <div class="flex shrink-0 flex-col divide-y">
56
+ <ApiSection
57
+ title="Stop"
58
+ api="rdk:component:gripper"
59
+ >
55
60
  <StopButton
56
61
  error={stopMutation.error}
57
62
  onStop={() => {
@@ -61,6 +66,7 @@
61
66
  </ApiSection>
62
67
  <IsMoving
63
68
  client={GripperClient}
69
+ api="rdk:component:gripper"
64
70
  {partID}
65
71
  {resourceName}
66
72
  />
@@ -30,6 +30,7 @@
30
30
 
31
31
  <ApiSection
32
32
  title="IsHoldingSomething"
33
+ api="rdk:component:gripper"
33
34
  bottomText="Updates automatically"
34
35
  class="grow"
35
36
  >
@@ -54,7 +54,8 @@
54
54
  </div>
55
55
 
56
56
  <ApiSection
57
- title="GetReadings"
57
+ title="GetEvents"
58
+ api="rdk:component:input_controller"
58
59
  class="relative"
59
60
  >
60
61
  <Query
@@ -86,6 +86,7 @@
86
86
  </MutationSection>
87
87
  <MutationSection
88
88
  title="SetPower"
89
+ api="rdk:component:motor"
89
90
  description="Move continuously"
90
91
  lastError={setPowerMutation.error}
91
92
  >
@@ -94,6 +95,7 @@
94
95
  {#if propertiesQuery.data?.positionReporting}
95
96
  <MutationSection
96
97
  title="SetRPM"
98
+ api="rdk:component:motor"
97
99
  description="Move indefinitely at a specified speed."
98
100
  lastError={setRPMMutation.error}
99
101
  >
@@ -101,6 +103,7 @@
101
103
  </MutationSection>
102
104
  <MutationSection
103
105
  title="GoFor"
106
+ api="rdk:component:motor"
104
107
  description="Move a specified number of revolutions"
105
108
  lastError={goForMutation.error}
106
109
  >
@@ -108,6 +111,7 @@
108
111
  </MutationSection>
109
112
  <MutationSection
110
113
  title="GoTo"
114
+ api="rdk:component:motor"
111
115
  description="Turn to a specified position"
112
116
  lastError={goToMutation.error}
113
117
  >
@@ -117,7 +121,10 @@
117
121
  </div>
118
122
 
119
123
  <div class="ml-auto flex w-full max-w-1/2 flex-col divide-y sm:max-w-1/3">
120
- <ApiSection title="Stop">
124
+ <ApiSection
125
+ title="Stop"
126
+ api="rdk:component:motor"
127
+ >
121
128
  <StopButton
122
129
  error={stopMutation.error}
123
130
  onStop={() => {
@@ -127,12 +134,14 @@
127
134
  </ApiSection>
128
135
  <IsMoving
129
136
  client={MotorClient}
137
+ api="rdk:component:motor"
130
138
  {partID}
131
139
  {resourceName}
132
140
  >
133
141
  <div class="flex flex-col gap-6 pt-2">
134
142
  <ApiSection
135
143
  title="IsPowered"
144
+ api="rdk:component:motor"
136
145
  class="gap-3 p-0"
137
146
  tooltip="Returns whether or not the motor is running and the current portion of max power.
138
147
 
@@ -158,6 +167,7 @@
158
167
  {#if propertiesQuery.data?.positionReporting === true}
159
168
  <ApiSection
160
169
  title="GetPosition"
170
+ api="rdk:component:motor"
161
171
  class="gap-3 p-0"
162
172
  tooltip="Reports the position of an encoded motor in revolutions from zero/home."
163
173
  >