@viamrobotics/test-widgets 0.6.1 → 0.8.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 (104) 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/client-map.d.ts +30 -30
  5. package/dist/client-map.js +32 -29
  6. package/dist/components/angle-unit-toggle.svelte +1 -4
  7. package/dist/components/api-section.svelte +21 -23
  8. package/dist/components/api-section.svelte.d.ts +2 -0
  9. package/dist/components/index.d.ts +21 -0
  10. package/dist/components/index.js +21 -0
  11. package/dist/components/is-moving.svelte +4 -5
  12. package/dist/components/is-moving.svelte.d.ts +1 -0
  13. package/dist/components/mutation-section.svelte +20 -23
  14. package/dist/components/mutation-section.svelte.d.ts +2 -0
  15. package/dist/components/section-title.svelte +63 -0
  16. package/dist/components/section-title.svelte.d.ts +14 -0
  17. package/dist/components/widgets/arm/arm.svelte +25 -29
  18. package/dist/components/widgets/arm/get-joint-positions-widget.svelte +36 -0
  19. package/dist/components/widgets/arm/get-joint-positions-widget.svelte.d.ts +7 -0
  20. package/dist/components/widgets/arm/is-moving-widget.svelte +20 -0
  21. package/dist/components/widgets/arm/is-moving-widget.svelte.d.ts +7 -0
  22. package/dist/components/widgets/arm/joint-position-editor.svelte +89 -0
  23. package/dist/components/widgets/arm/joint-position-editor.svelte.d.ts +11 -0
  24. package/dist/components/widgets/arm/joint-position-quick-move.svelte +85 -0
  25. package/dist/components/widgets/arm/joint-position-quick-move.svelte.d.ts +9 -0
  26. package/dist/components/widgets/arm/joint-position-slider.svelte +36 -0
  27. package/dist/components/widgets/arm/joint-position-slider.svelte.d.ts +9 -0
  28. package/dist/components/widgets/arm/move-to-joint-positions-widget.svelte +9 -5
  29. package/dist/components/widgets/arm/move-to-joint-positions.svelte +73 -127
  30. package/dist/components/widgets/arm/move-to-joint-positions.svelte.d.ts +1 -0
  31. package/dist/components/widgets/arm/move-to-position.svelte +31 -3
  32. package/dist/components/widgets/audio-input/audio-input.svelte +2 -0
  33. package/dist/components/widgets/audio-input/get-properties-widget.svelte +43 -0
  34. package/dist/components/widgets/audio-input/get-properties-widget.svelte.d.ts +7 -0
  35. package/dist/components/widgets/audio-output/audio-output.svelte +2 -0
  36. package/dist/components/widgets/audio-output/get-properties-widget.svelte +43 -0
  37. package/dist/components/widgets/audio-output/get-properties-widget.svelte.d.ts +7 -0
  38. package/dist/components/widgets/base/base.svelte +9 -1
  39. package/dist/components/widgets/base/is-moving-widget.svelte +20 -0
  40. package/dist/components/widgets/base/is-moving-widget.svelte.d.ts +7 -0
  41. package/dist/components/widgets/base/quick-move.svelte +12 -5
  42. package/dist/components/widgets/camera/camera.svelte +156 -142
  43. package/dist/components/widgets/camera/live-or-polling-video.svelte +9 -1
  44. package/dist/components/widgets/discovery/discovery.svelte +1 -0
  45. package/dist/components/widgets/encoder/encoder-position-type.d.ts +8 -0
  46. package/dist/components/widgets/encoder/encoder-position-type.js +20 -0
  47. package/dist/components/widgets/encoder/encoder.svelte +5 -20
  48. package/dist/components/widgets/encoder/get-position-widget.svelte +54 -0
  49. package/dist/components/widgets/encoder/get-position-widget.svelte.d.ts +7 -0
  50. package/dist/components/widgets/gantry/gantry.svelte +28 -5
  51. package/dist/components/widgets/gantry/get-position-widget.svelte +43 -0
  52. package/dist/components/widgets/gantry/get-position-widget.svelte.d.ts +7 -0
  53. package/dist/components/widgets/gantry/is-moving-widget.svelte +20 -0
  54. package/dist/components/widgets/gantry/is-moving-widget.svelte.d.ts +7 -0
  55. package/dist/components/widgets/generic/generic.svelte +8 -1
  56. package/dist/components/widgets/gripper/gripper.svelte +11 -5
  57. package/dist/components/widgets/gripper/is-holding-something.svelte +2 -4
  58. package/dist/components/widgets/gripper/is-moving-widget.svelte +20 -0
  59. package/dist/components/widgets/gripper/is-moving-widget.svelte.d.ts +7 -0
  60. package/dist/components/widgets/input-controller/input-controller.svelte +2 -1
  61. package/dist/components/widgets/motor/is-moving-widget.svelte +20 -0
  62. package/dist/components/widgets/motor/is-moving-widget.svelte.d.ts +7 -0
  63. package/dist/components/widgets/motor/motor.svelte +13 -9
  64. package/dist/components/widgets/movement-sensor/get-accuracy-widget.svelte +38 -0
  65. package/dist/components/widgets/movement-sensor/get-accuracy-widget.svelte.d.ts +7 -0
  66. package/dist/components/widgets/movement-sensor/get-compass-heading-widget.svelte +38 -0
  67. package/dist/components/widgets/movement-sensor/get-compass-heading-widget.svelte.d.ts +7 -0
  68. package/dist/components/widgets/movement-sensor/get-orientation-widget.svelte +38 -0
  69. package/dist/components/widgets/movement-sensor/get-orientation-widget.svelte.d.ts +7 -0
  70. package/dist/components/widgets/movement-sensor/get-position-widget.svelte +38 -0
  71. package/dist/components/widgets/movement-sensor/get-position-widget.svelte.d.ts +7 -0
  72. package/dist/components/widgets/movement-sensor/movement-sensor.svelte +53 -26
  73. package/dist/components/widgets/power-sensor/get-current-widget.svelte +38 -0
  74. package/dist/components/widgets/power-sensor/get-current-widget.svelte.d.ts +7 -0
  75. package/dist/components/widgets/power-sensor/get-power-widget.svelte +38 -0
  76. package/dist/components/widgets/power-sensor/get-power-widget.svelte.d.ts +7 -0
  77. package/dist/components/widgets/power-sensor/get-voltage-widget.svelte +38 -0
  78. package/dist/components/widgets/power-sensor/get-voltage-widget.svelte.d.ts +7 -0
  79. package/dist/components/widgets/power-sensor/power-sensor.svelte +4 -0
  80. package/dist/components/widgets/sensor/sensor.svelte +1 -0
  81. package/dist/components/widgets/servo/is-moving-widget.svelte +20 -0
  82. package/dist/components/widgets/servo/is-moving-widget.svelte.d.ts +7 -0
  83. package/dist/components/widgets/servo/servo.svelte +10 -2
  84. package/dist/components/widgets/slam/get-position-widget.svelte +38 -0
  85. package/dist/components/widgets/slam/get-position-widget.svelte.d.ts +7 -0
  86. package/dist/components/widgets/slam/move-on-map.svelte +4 -1
  87. package/dist/components/widgets/slam/slam.svelte +4 -1
  88. package/dist/components/widgets/vision-service/context.svelte.d.ts +1 -0
  89. package/dist/components/widgets/vision-service/context.svelte.js +7 -0
  90. package/dist/components/widgets/vision-service/detection-row.svelte +38 -0
  91. package/dist/components/widgets/vision-service/detection-row.svelte.d.ts +8 -0
  92. package/dist/components/widgets/vision-service/detection.svelte +7 -4
  93. package/dist/components/widgets/vision-service/legend.svelte +14 -11
  94. package/dist/components/widgets/vision-service/vision-service.svelte +9 -10
  95. package/dist/index.d.ts +5 -1
  96. package/dist/index.js +6 -1
  97. package/dist/pip/context.svelte.d.ts +2 -0
  98. package/dist/pip/context.svelte.js +48 -20
  99. package/dist/resource-triplet.d.ts +37 -0
  100. package/dist/resource-triplet.js +37 -0
  101. package/dist/resource-widget.js +187 -0
  102. package/dist/resource.d.ts +0 -9
  103. package/dist/resource.js +0 -72
  104. package/package.json +7 -4
@@ -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>;
@@ -76,13 +76,36 @@
76
76
  oZ: 'OZ',
77
77
  theta: 'θ',
78
78
  } as const
79
+ const positionUnits = $derived({
80
+ x: 'mm',
81
+ y: 'mm',
82
+ z: 'mm',
83
+ oX: '',
84
+ oY: '',
85
+ oZ: '',
86
+ theta: useRadians ? 'rad' : 'deg',
87
+ } as const satisfies Record<keyof Pose, string>)
88
+
79
89
  const positionLabelsList = Object.entries(positionLabels) as [keyof Pose, string][]
80
90
  </script>
81
91
 
82
92
  <div class="flex min-w-0 flex-col gap-4">
83
93
  <!-- Controls Header -->
84
94
  <div class="flex items-center justify-between">
85
- <span class="text-sm">Pose Values</span>
95
+ <span class="flex flex-row items-center gap-1 text-sm">
96
+ Pose Values
97
+ <Tooltip>
98
+ <Icon
99
+ name="information-outline"
100
+ cx="text-gray-6"
101
+ />
102
+
103
+ <span slot="description">
104
+ Pose is with respect to the arm origin and does not take into account the motion service
105
+ or frame system.
106
+ </span>
107
+ </Tooltip>
108
+ </span>
86
109
  <div class="flex gap-1">
87
110
  <AngleUnitToggle
88
111
  {useRadians}
@@ -99,7 +122,7 @@
99
122
  <thead>
100
123
  <tr>
101
124
  <th>Pose</th>
102
- <th>Value ({useRadians ? 'radians' : 'degrees'})</th>
125
+ <th>Value</th>
103
126
  </tr>
104
127
  </thead>
105
128
  <tbody>
@@ -107,7 +130,12 @@
107
130
  {@const [key, label] = labelList}
108
131
  {@const value = Number.parseFloat(formatNumeric(displayPosition[key]))}
109
132
  <tr>
110
- <th>{label}</th>
133
+ <th>
134
+ <span class="relative inline-flex justify-center">
135
+ {label}
136
+ <abbr class="text-subtle-2 absolute left-full ml-1">{positionUnits[key]}</abbr>
137
+ </span>
138
+ </th>
111
139
  <th>
112
140
  <NumericInput
113
141
  cx="max-w-[76px]"
@@ -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
  >
@@ -0,0 +1,43 @@
1
+ <script lang="ts">
2
+ import { AudioInClient } from '@viamrobotics/sdk'
3
+ import { createResourceClient, createResourceQuery } from '@viamrobotics/svelte-sdk'
4
+
5
+ import ApiSection from '../../api-section.svelte'
6
+ import Query from '../../query.svelte'
7
+
8
+ import Properties from './properties.svelte'
9
+
10
+ interface Props {
11
+ partID: string
12
+ resourceName: string
13
+ }
14
+
15
+ const { partID, resourceName }: Props = $props()
16
+
17
+ const client = createResourceClient(
18
+ AudioInClient,
19
+ () => partID,
20
+ () => resourceName
21
+ )
22
+
23
+ const query = createResourceQuery(client, 'getProperties', { refetchInterval: 500 })
24
+ </script>
25
+
26
+ <ApiSection
27
+ title="GetProperties"
28
+ description="Audio input properties"
29
+ class="grow"
30
+ >
31
+ <Query
32
+ {query}
33
+ contentCx="h-6"
34
+ >
35
+ {#if query.data !== undefined}
36
+ <Properties
37
+ supportedCodecs={query.data.supportedCodecs}
38
+ sampleRateHz={query.data.sampleRateHz}
39
+ numChannels={query.data.numChannels}
40
+ />
41
+ {/if}
42
+ </Query>
43
+ </ApiSection>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ partID: string;
3
+ resourceName: string;
4
+ }
5
+ declare const GetPropertiesWidget: import("svelte").Component<Props, {}, "">;
6
+ type GetPropertiesWidget = ReturnType<typeof GetPropertiesWidget>;
7
+ export default GetPropertiesWidget;
@@ -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
  >
@@ -0,0 +1,43 @@
1
+ <script lang="ts">
2
+ import { AudioOutClient } from '@viamrobotics/sdk'
3
+ import { createResourceClient, createResourceQuery } from '@viamrobotics/svelte-sdk'
4
+
5
+ import ApiSection from '../../api-section.svelte'
6
+ import Query from '../../query.svelte'
7
+
8
+ import Properties from './properties.svelte'
9
+
10
+ interface Props {
11
+ partID: string
12
+ resourceName: string
13
+ }
14
+
15
+ const { partID, resourceName }: Props = $props()
16
+
17
+ const client = createResourceClient(
18
+ AudioOutClient,
19
+ () => partID,
20
+ () => resourceName
21
+ )
22
+
23
+ const query = createResourceQuery(client, 'getProperties', { refetchInterval: 500 })
24
+ </script>
25
+
26
+ <ApiSection
27
+ title="GetProperties"
28
+ description="Audio output properties"
29
+ class="grow"
30
+ >
31
+ <Query
32
+ {query}
33
+ contentCx="h-6"
34
+ >
35
+ {#if query.data !== undefined}
36
+ <Properties
37
+ supportedCodecs={query.data.supportedCodecs}
38
+ sampleRateHz={query.data.sampleRateHz}
39
+ numChannels={query.data.numChannels}
40
+ />
41
+ {/if}
42
+ </Query>
43
+ </ApiSection>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ partID: string;
3
+ resourceName: string;
4
+ }
5
+ declare const GetPropertiesWidget: import("svelte").Component<Props, {}, "">;
6
+ type GetPropertiesWidget = ReturnType<typeof GetPropertiesWidget>;
7
+ export default GetPropertiesWidget;
@@ -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
  >
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { BaseClient } from '@viamrobotics/sdk'
3
+
4
+ import IsMoving from '../../is-moving.svelte'
5
+ import { ResourceTriplets } from '../../../resource-triplet'
6
+
7
+ interface Props {
8
+ partID: string
9
+ resourceName: string
10
+ }
11
+
12
+ const { partID, resourceName }: Props = $props()
13
+ </script>
14
+
15
+ <IsMoving
16
+ client={BaseClient}
17
+ api={ResourceTriplets.Base}
18
+ {partID}
19
+ {resourceName}
20
+ />
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ partID: string;
3
+ resourceName: string;
4
+ }
5
+ declare const IsMovingWidget: import("svelte").Component<Props, {}, "">;
6
+ type IsMovingWidget = ReturnType<typeof IsMovingWidget>;
7
+ export default IsMovingWidget;
@@ -90,8 +90,11 @@
90
90
  }
91
91
  }
92
92
 
93
+ // stopImmediatePropagation (not stopPropagation) in the capture phase so movement
94
+ // keys never reach sibling window keydown listeners on the same target — e.g. the
95
+ // config builder's "A" add-resource hotkey. stopPropagation leaves siblings running.
93
96
  const onKeydown = (event: KeyboardEvent) => {
94
- if (!isKeyboardEnabled || event.repeat) {
97
+ if (!isKeyboardEnabled) {
95
98
  return
96
99
  }
97
100
 
@@ -100,9 +103,13 @@
100
103
  return
101
104
  }
102
105
 
103
- event.stopPropagation()
106
+ event.stopImmediatePropagation()
104
107
  event.preventDefault()
105
108
 
109
+ if (event.repeat) {
110
+ return
111
+ }
112
+
106
113
  pressedKeys[key] = true
107
114
  onKeyChange()
108
115
  }
@@ -117,7 +124,7 @@
117
124
  return
118
125
  }
119
126
 
120
- event.stopPropagation()
127
+ event.stopImmediatePropagation()
121
128
  event.preventDefault()
122
129
 
123
130
  pressedKeys[key] = false
@@ -132,8 +139,8 @@
132
139
  </script>
133
140
 
134
141
  <svelte:window
135
- onkeydown={onKeydown}
136
- onkeyup={onKeyup}
142
+ onkeydowncapture={onKeydown}
143
+ onkeyupcapture={onKeyup}
137
144
  />
138
145
 
139
146
  <div class="flex grow flex-col gap-6">