@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
@@ -0,0 +1,36 @@
1
+ <script lang="ts">
2
+ import { Slider, ThemeUtils } from 'svelte-tweakpane-ui'
3
+
4
+ import { degreesToRadians, formatNumeric, radiansToDegrees } from '../../../format'
5
+
6
+ interface Props {
7
+ value: number
8
+ minDegrees: number
9
+ maxDegrees: number
10
+ useRadians: boolean
11
+ }
12
+
13
+ let { value = $bindable(), minDegrees, maxDegrees, useRadians }: Props = $props()
14
+
15
+ const min = $derived(useRadians ? degreesToRadians(minDegrees) : minDegrees)
16
+ const max = $derived(useRadians ? degreesToRadians(maxDegrees) : maxDegrees)
17
+ const step = $derived(useRadians ? 0.01 : 0.1)
18
+
19
+ const sliderFormat = (v: number) => formatNumeric(v, useRadians ? 3 : 1)
20
+ const toDisplay = (degrees: number) => (useRadians ? degreesToRadians(degrees) : degrees)
21
+ const toDegrees = (display: number) => (useRadians ? radiansToDegrees(display) : display)
22
+ </script>
23
+
24
+ <div class="joint-slider min-w-0">
25
+ <Slider
26
+ value={toDisplay(value)}
27
+ on:change={(event) => {
28
+ value = toDegrees(event.detail.value)
29
+ }}
30
+ {min}
31
+ {max}
32
+ {step}
33
+ format={sliderFormat}
34
+ theme={ThemeUtils.presets.light}
35
+ />
36
+ </div>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ value: number;
3
+ minDegrees: number;
4
+ maxDegrees: number;
5
+ useRadians: boolean;
6
+ }
7
+ declare const JointPositionSlider: import("svelte").Component<Props, {}, "value">;
8
+ type JointPositionSlider = ReturnType<typeof JointPositionSlider>;
9
+ export default JointPositionSlider;
@@ -1,93 +1,68 @@
1
1
  <script lang="ts">
2
- import { Button, Icon, NumericInput, Tooltip } from '@viamrobotics/prime-core'
2
+ import { Icon, Tooltip } from '@viamrobotics/prime-core'
3
3
 
4
4
  import AngleUnitToggle from '../../angle-unit-toggle.svelte'
5
5
  import CopyButton from '../../copy-button.svelte'
6
6
  import ErrorDisplay from '../../error.svelte'
7
7
  import PasteButton from '../../paste-button.svelte'
8
- import Table from '../../table.svelte'
9
- import { numberValueFromEvent } from '../../../event-handlers'
10
8
  import { degreesToRadians, formatNumeric, radiansToDegrees } from '../../../format'
11
9
 
12
- import { getOutOfBoundsSide, isOutsideJointLimit, type JointLimit } from './joint-position-limits'
10
+ import JointPositionEditor from './joint-position-editor.svelte'
11
+ import { type JointLimit } from './joint-position-limits'
12
+ import JointPositionQuickMove from './joint-position-quick-move.svelte'
13
+
14
+ type ControlMode = 'jointPositions' | 'quickMove'
13
15
 
14
16
  interface Props {
15
17
  positions: number[]
16
18
  moveToJointPositions: (jointPositions: number[]) => void
17
19
  lastError: Error | null
18
20
  jointLimitsDegrees: JointLimit[]
21
+ isMoving?: boolean
19
22
  }
20
23
 
21
- const { positions, moveToJointPositions, lastError, jointLimitsDegrees }: Props = $props()
24
+ const {
25
+ positions,
26
+ moveToJointPositions,
27
+ lastError,
28
+ jointLimitsDegrees,
29
+ isMoving = false,
30
+ }: Props = $props()
22
31
 
23
32
  // svelte-ignore state_referenced_locally
24
- let desiredPositions = $state([...positions])
33
+ let desiredPositions = $state([...positions]) // in degrees
25
34
  let useRadians = $state(false)
35
+ let controlMode = $state<ControlMode>('jointPositions')
26
36
 
27
- const resetToZero = () => {
28
- desiredPositions = [...desiredPositions].fill(0)
29
- }
37
+ const isQuickMoveMode = $derived(controlMode === 'quickMove')
30
38
 
31
- const resetToCurrent = () => {
32
- desiredPositions = [...positions]
33
- }
39
+ const getJointMin = (index: number): number => jointLimitsDegrees[index]?.minDegrees ?? -180
40
+ const getJointMax = (index: number): number => jointLimitsDegrees[index]?.maxDegrees ?? 180
41
+
42
+ const toDisplayAngle = (degrees: number) => (useRadians ? degreesToRadians(degrees) : degrees)
34
43
 
35
- const handleJointInputChange = (index: number, inputValue: number) => {
36
- // default is degrees, so if user has toggle to radians, convert back to degrees before setting
37
- // (we only convert to radians when displaying)
38
- desiredPositions[index] = useRadians ? radiansToDegrees(inputValue) : inputValue
44
+ const displayPositions = $derived(desiredPositions.map((degrees) => toDisplayAngle(degrees)))
45
+ const copyData = $derived(`[${displayPositions.map((v) => formatNumeric(v)).join(', ')}]`)
46
+
47
+ const toggleMode = () => {
48
+ controlMode = isQuickMoveMode ? 'jointPositions' : 'quickMove'
39
49
  }
40
50
 
41
51
  const handlePaste = (data: string): boolean => {
42
52
  try {
43
- desiredPositions = JSON.parse(data) as number[]
53
+ const parsed = JSON.parse(data) as number[]
54
+ desiredPositions = parsed.map((pos, i) => {
55
+ const degrees = useRadians ? radiansToDegrees(pos) : pos
56
+ return Math.min(Math.max(degrees, getJointMin(i)), getJointMax(i))
57
+ })
44
58
  } catch {
45
59
  return false
46
60
  }
47
61
  return true
48
62
  }
49
- const degreesToDisplayAngle = (degrees: number) => {
50
- return useRadians ? degreesToRadians(degrees) : degrees
51
- }
52
-
53
- const outOfBoundsMessage = (displayValue: number, limit: JointLimit) => {
54
- const min = degreesToDisplayAngle(limit.minDegrees)
55
- const max = degreesToDisplayAngle(limit.maxDegrees)
56
- const unit = useRadians ? 'rad' : 'deg'
57
- const side = getOutOfBoundsSide(displayValue, min, max)
58
-
59
- if (side === 'below-min') {
60
- return `Min value is ${formatNumeric(min)} ${unit}`
61
- }
62
-
63
- if (side === 'above-max') {
64
- return `Max value is ${formatNumeric(max)} ${unit}`
65
- }
66
- }
67
-
68
- const displayPositions = $derived(desiredPositions.map((pos) => degreesToDisplayAngle(pos)))
69
-
70
- const copyData = $derived(`[${displayPositions.join(', ')}]`)
71
-
72
- const outOfBoundsByIndex = $derived(
73
- displayPositions.map((displayValue, index) => {
74
- const limit = jointLimitsDegrees[index]
75
- if (!limit) {
76
- return false
77
- }
78
- return isOutsideJointLimit(
79
- displayValue,
80
- degreesToDisplayAngle(limit.minDegrees),
81
- degreesToDisplayAngle(limit.maxDegrees)
82
- )
83
- })
84
- )
85
-
86
- const hasOutOfBoundsPositions = $derived(outOfBoundsByIndex.some(Boolean))
87
63
  </script>
88
64
 
89
65
  <div class="flex min-w-0 flex-col gap-4">
90
- <!-- Controls Header -->
91
66
  <div class="flex items-center justify-between">
92
67
  <span class="flex flex-row items-center gap-1 text-sm">
93
68
  Joint Positions
@@ -96,88 +71,59 @@
96
71
  name="information-outline"
97
72
  cx="text-gray-6"
98
73
  />
99
-
100
74
  <span slot="description">
101
75
  Joint position limits are based solely on the arm kinematics and do not take into account
102
76
  motion service limit overrides.
103
77
  </span>
104
78
  </Tooltip>
105
79
  </span>
106
- <div class="flex gap-1">
107
- <AngleUnitToggle
108
- {useRadians}
109
- onToggle={() => {
110
- useRadians = !useRadians
111
- }}
112
- />
113
- <CopyButton data={copyData} />
114
- <PasteButton onPaste={handlePaste} />
115
- </div>
116
- </div>
117
-
118
- <Table>
119
- <thead>
120
- <tr>
121
- <th> Joint </th>
122
- <th>Move ({useRadians ? 'radians' : 'degrees'})</th>
123
- </tr>
124
- </thead>
125
- <tbody>
126
- {#each { length: positions.length }, index}
127
- {@const limit = jointLimitsDegrees[index]}
128
- {@const value = Number.parseFloat(formatNumeric(displayPositions[index]))}
129
- {@const outOfBounds = outOfBoundsByIndex[index]}
130
- <tr>
131
- <th>{index}</th>
132
- <th>
133
- <div class="flex flex-col items-center gap-0.5 py-0.5">
134
- <NumericInput
135
- cx="max-w-[76px]"
136
- {value}
137
- on:change={(event) => {
138
- const inputValue = numberValueFromEvent(event) ?? 0
139
- handleJointInputChange(index, inputValue)
140
- }}
141
- />
142
- {#if limit && outOfBounds}
143
- <p class="text-danger-dark text-center text-[10px] leading-snug whitespace-normal">
144
- {outOfBoundsMessage(displayPositions[index], limit)}
145
- </p>
146
- {/if}
147
- </div>
148
- </th>
149
- </tr>
150
- {/each}
151
- </tbody>
152
- </Table>
153
-
154
- <div class="mb-2 flex flex-col gap-2">
155
- <span class="flex flex-row gap-2">
156
- <h4 class="text-xs font-semibold">Quick set</h4>
157
- <Tooltip>
158
- <Icon
159
- name="information-outline"
160
- cx="text-gray-6"
80
+ <div class="flex items-center gap-1">
81
+ <div class="flex gap-1">
82
+ {#if !isQuickMoveMode}
83
+ <CopyButton data={copyData} />
84
+ <PasteButton onPaste={handlePaste} />
85
+ {/if}
86
+ <AngleUnitToggle
87
+ {useRadians}
88
+ onToggle={() => {
89
+ useRadians = !useRadians
90
+ }}
161
91
  />
162
-
92
+ </div>
93
+ <Tooltip>
94
+ <button
95
+ onclick={toggleMode}
96
+ aria-label={isQuickMoveMode ? 'Exit quick move mode' : 'Enter quick move mode'}
97
+ class={[
98
+ 'hover:border-medium hover:bg-medium active:bg-gray-2 rounded p-0.5',
99
+ isQuickMoveMode ? 'text-amber-600' : 'text-gray-6',
100
+ ]}
101
+ >
102
+ <Icon name="lightning-bolt-outline" />
103
+ </button>
163
104
  <span slot="description">
164
- Will update the "Move to" input values but will not execute
105
+ {isQuickMoveMode ? 'Exit quick move mode' : 'Enter quick move mode'}
165
106
  </span>
166
107
  </Tooltip>
167
- </span>
168
- <div class="flex flex-col gap-2 sm:flex-row">
169
- <Button onclick={resetToZero}>Zero</Button>
170
- <Button onclick={resetToCurrent}>Current position</Button>
171
108
  </div>
172
109
  </div>
173
- <Button
174
- class="mt-auto w-fit"
175
- disabled={hasOutOfBoundsPositions}
176
- icon="play-circle-outline"
177
- variant="dark"
178
- onclick={() => moveToJointPositions(desiredPositions)}
179
- >
180
- Execute
181
- </Button>
110
+
111
+ {#if isQuickMoveMode}
112
+ <JointPositionQuickMove
113
+ {positions}
114
+ {moveToJointPositions}
115
+ {useRadians}
116
+ {isMoving}
117
+ />
118
+ {:else}
119
+ <JointPositionEditor
120
+ bind:desiredPositions
121
+ {positions}
122
+ {moveToJointPositions}
123
+ {useRadians}
124
+ {jointLimitsDegrees}
125
+ />
126
+ {/if}
127
+
182
128
  <ErrorDisplay {lastError} />
183
129
  </div>
@@ -4,6 +4,7 @@ interface Props {
4
4
  moveToJointPositions: (jointPositions: number[]) => void;
5
5
  lastError: Error | null;
6
6
  jointLimitsDegrees: JointLimit[];
7
+ isMoving?: boolean;
7
8
  }
8
9
  declare const MoveToJointPositions: import("svelte").Component<Props, {}, "">;
9
10
  type MoveToJointPositions = ReturnType<typeof MoveToJointPositions>;
@@ -62,6 +62,7 @@
62
62
  <div class="flex w-full flex-col divide-y">
63
63
  <MutationSection
64
64
  title="GetAudio"
65
+ api="rdk:component:audio_input"
65
66
  description="Capture audio from the device"
66
67
  lastError={capture.error}
67
68
  >
@@ -131,6 +132,7 @@
131
132
  <div class="ml-auto flex w-full max-w-1/2 flex-col divide-y sm:max-w-1/3">
132
133
  <ApiSection
133
134
  title="GetProperties"
135
+ api="rdk:component:audio_input"
134
136
  description="Audio input properties"
135
137
  class="relative"
136
138
  >
@@ -107,6 +107,7 @@
107
107
  <div class="flex w-full flex-col divide-y">
108
108
  <MutationSection
109
109
  title="Play"
110
+ api="rdk:component:audio_output"
110
111
  description="Send audio data to the device"
111
112
  lastError={playContext.error}
112
113
  >
@@ -178,6 +179,7 @@
178
179
  <div class="ml-auto flex w-full max-w-1/2 flex-col divide-y sm:max-w-1/3">
179
180
  <ApiSection
180
181
  title="GetProperties"
182
+ api="rdk:component:audio_output"
181
183
  description="Audio output properties"
182
184
  class="relative"
183
185
  >
@@ -83,7 +83,10 @@
83
83
  />
84
84
  </MutationSection>
85
85
  <div class="ml-auto flex w-full max-w-40 grow flex-col divide-y">
86
- <ApiSection title="Stop">
86
+ <ApiSection
87
+ title="Stop"
88
+ api="rdk:component:base"
89
+ >
87
90
  <StopButton
88
91
  error={stopMutation.error}
89
92
  onStop={() => {
@@ -93,6 +96,7 @@
93
96
  </ApiSection>
94
97
  <IsMoving
95
98
  client={BaseClient}
99
+ api="rdk:component:base"
96
100
  {partID}
97
101
  {resourceName}
98
102
  />
@@ -100,6 +104,7 @@
100
104
  </div>
101
105
  <MutationSection
102
106
  title="MoveStraight"
107
+ api="rdk:component:base"
103
108
  description="Move across a given distance at a given velocity"
104
109
  lastError={moveStraightMutation.error}
105
110
  >
@@ -107,6 +112,7 @@
107
112
  </MutationSection>
108
113
  <MutationSection
109
114
  title="Spin"
115
+ api="rdk:component:base"
110
116
  description="Turn to a given angle at a given velocity"
111
117
  lastError={spinMutation.error}
112
118
  >
@@ -114,6 +120,7 @@
114
120
  </MutationSection>
115
121
  <MutationSection
116
122
  title="SetPower"
123
+ api="rdk:component:base"
117
124
  description="Move continuously at a given amount of power"
118
125
  lastError={setPowerMutation.error}
119
126
  >
@@ -121,6 +128,7 @@
121
128
  </MutationSection>
122
129
  <MutationSection
123
130
  title="SetVelocity"
131
+ api="rdk:component:base"
124
132
  description="Move continually at a given velocity"
125
133
  lastError={setVelocityMutation.error}
126
134
  >