@viamrobotics/test-widgets 0.7.0 → 0.9.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 (98) hide show
  1. package/README.md +33 -0
  2. package/dist/client-map.d.ts +30 -30
  3. package/dist/client-map.js +32 -29
  4. package/dist/components/index.d.ts +22 -0
  5. package/dist/components/index.js +22 -0
  6. package/dist/components/is-moving.svelte +1 -4
  7. package/dist/components/mutation-section.svelte +1 -1
  8. package/dist/components/navigation-map/components/obstacle.svelte +2 -1
  9. package/dist/components/three/capsule-geometry.d.ts +10 -0
  10. package/dist/components/three/capsule-geometry.js +19 -0
  11. package/dist/components/three/index.d.ts +1 -0
  12. package/dist/components/three/index.js +1 -0
  13. package/dist/components/widgets/arm/arm.svelte +8 -5
  14. package/dist/components/widgets/arm/get-joint-positions-widget.svelte +36 -0
  15. package/dist/components/widgets/arm/get-joint-positions-widget.svelte.d.ts +7 -0
  16. package/dist/components/widgets/arm/is-moving-widget.svelte +20 -0
  17. package/dist/components/widgets/arm/is-moving-widget.svelte.d.ts +7 -0
  18. package/dist/components/widgets/arm/move-to-joint-positions-widget.svelte +9 -5
  19. package/dist/components/widgets/arm/move-to-position.svelte +31 -3
  20. package/dist/components/widgets/audio-input/get-properties-widget.svelte +43 -0
  21. package/dist/components/widgets/audio-input/get-properties-widget.svelte.d.ts +7 -0
  22. package/dist/components/widgets/audio-output/get-properties-widget.svelte +43 -0
  23. package/dist/components/widgets/audio-output/get-properties-widget.svelte.d.ts +7 -0
  24. package/dist/components/widgets/base/is-moving-widget.svelte +20 -0
  25. package/dist/components/widgets/base/is-moving-widget.svelte.d.ts +7 -0
  26. package/dist/components/widgets/base/quick-move.svelte +12 -5
  27. package/dist/components/widgets/camera/live-or-polling-video.svelte +8 -1
  28. package/dist/components/widgets/do-command/do-command.svelte +21 -7
  29. package/dist/components/widgets/do-command/do-command.svelte.d.ts +8 -1
  30. package/dist/components/widgets/encoder/encoder-position-type.d.ts +8 -0
  31. package/dist/components/widgets/encoder/encoder-position-type.js +20 -0
  32. package/dist/components/widgets/encoder/encoder.svelte +3 -20
  33. package/dist/components/widgets/encoder/get-position-widget.svelte +54 -0
  34. package/dist/components/widgets/encoder/get-position-widget.svelte.d.ts +7 -0
  35. package/dist/components/widgets/gantry/get-position-widget.svelte +43 -0
  36. package/dist/components/widgets/gantry/get-position-widget.svelte.d.ts +7 -0
  37. package/dist/components/widgets/gantry/is-moving-widget.svelte +20 -0
  38. package/dist/components/widgets/gantry/is-moving-widget.svelte.d.ts +7 -0
  39. package/dist/components/widgets/gripper/gripper.svelte +1 -1
  40. package/dist/components/widgets/gripper/is-holding-something.svelte +1 -4
  41. package/dist/components/widgets/gripper/is-moving-widget.svelte +20 -0
  42. package/dist/components/widgets/gripper/is-moving-widget.svelte.d.ts +7 -0
  43. package/dist/components/widgets/motion/component-name-select.svelte +29 -0
  44. package/dist/components/widgets/motion/component-name-select.svelte.d.ts +9 -0
  45. package/dist/components/widgets/motion/frame-system-config.d.ts +28 -0
  46. package/dist/components/widgets/motion/frame-system-config.js +30 -0
  47. package/dist/components/widgets/motion/motion.svelte +27 -0
  48. package/dist/components/widgets/motion/motion.svelte.d.ts +7 -0
  49. package/dist/components/widgets/motion/move-widget.svelte +84 -0
  50. package/dist/components/widgets/motion/move-widget.svelte.d.ts +7 -0
  51. package/dist/components/widgets/motion/move.svelte +127 -0
  52. package/dist/components/widgets/motion/move.svelte.d.ts +14 -0
  53. package/dist/components/widgets/motion/parse-move-args.d.ts +24 -0
  54. package/dist/components/widgets/motion/parse-move-args.js +23 -0
  55. package/dist/components/widgets/motion/parse-pasted-pose.d.ts +10 -0
  56. package/dist/components/widgets/motion/parse-pasted-pose.js +30 -0
  57. package/dist/components/widgets/motion/pose-in-frame-input.svelte +168 -0
  58. package/dist/components/widgets/motion/pose-in-frame-input.svelte.d.ts +10 -0
  59. package/dist/components/widgets/motor/is-moving-widget.svelte +20 -0
  60. package/dist/components/widgets/motor/is-moving-widget.svelte.d.ts +7 -0
  61. package/dist/components/widgets/motor/motor.svelte +2 -8
  62. package/dist/components/widgets/movement-sensor/get-accuracy-widget.svelte +38 -0
  63. package/dist/components/widgets/movement-sensor/get-accuracy-widget.svelte.d.ts +7 -0
  64. package/dist/components/widgets/movement-sensor/get-compass-heading-widget.svelte +38 -0
  65. package/dist/components/widgets/movement-sensor/get-compass-heading-widget.svelte.d.ts +7 -0
  66. package/dist/components/widgets/movement-sensor/get-orientation-widget.svelte +38 -0
  67. package/dist/components/widgets/movement-sensor/get-orientation-widget.svelte.d.ts +7 -0
  68. package/dist/components/widgets/movement-sensor/get-position-widget.svelte +38 -0
  69. package/dist/components/widgets/movement-sensor/get-position-widget.svelte.d.ts +7 -0
  70. package/dist/components/widgets/navigation/obstacles/meshes.svelte +1 -2
  71. package/dist/components/widgets/power-sensor/get-current-widget.svelte +38 -0
  72. package/dist/components/widgets/power-sensor/get-current-widget.svelte.d.ts +7 -0
  73. package/dist/components/widgets/power-sensor/get-power-widget.svelte +38 -0
  74. package/dist/components/widgets/power-sensor/get-power-widget.svelte.d.ts +7 -0
  75. package/dist/components/widgets/power-sensor/get-voltage-widget.svelte +38 -0
  76. package/dist/components/widgets/power-sensor/get-voltage-widget.svelte.d.ts +7 -0
  77. package/dist/components/widgets/servo/is-moving-widget.svelte +20 -0
  78. package/dist/components/widgets/servo/is-moving-widget.svelte.d.ts +7 -0
  79. package/dist/components/widgets/slam/get-position-widget.svelte +38 -0
  80. package/dist/components/widgets/slam/get-position-widget.svelte.d.ts +7 -0
  81. package/dist/components/widgets/vision-service/vision-service.svelte +9 -10
  82. package/dist/index.d.ts +6 -1
  83. package/dist/index.js +6 -1
  84. package/dist/is-known-resource.d.ts +3 -0
  85. package/dist/is-known-resource.js +5 -0
  86. package/dist/pip/context.svelte.d.ts +2 -0
  87. package/dist/pip/context.svelte.js +48 -20
  88. package/dist/registry.d.ts +28 -0
  89. package/dist/registry.js +176 -0
  90. package/dist/resource-triplet.d.ts +37 -0
  91. package/dist/resource-triplet.js +37 -0
  92. package/dist/resource-widget-types.d.ts +21 -0
  93. package/dist/resource-widget-types.js +1 -0
  94. package/dist/resource.d.ts +0 -9
  95. package/dist/resource.js +0 -72
  96. package/dist/show-resource-widget.d.ts +3 -0
  97. package/dist/show-resource-widget.js +10 -0
  98. package/package.json +23 -6
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The components the motion service can `Move` are exactly the frames in the
3
+ * machine's frame system config. `Move` builds a kinematic chain to a frame, so
4
+ * anything with a configured frame is a valid target.
5
+ *
6
+ * @param config - The machine's frame system config, or `undefined` while loading.
7
+ * @returns The non-empty reference frame names, sorted alphabetically.
8
+ */
9
+ export const movableFrameNames = (config) => {
10
+ if (!config) {
11
+ return [];
12
+ }
13
+ return config
14
+ .map((entry) => entry.frame?.referenceFrame ?? '')
15
+ .filter((name) => name !== '')
16
+ .toSorted((a, b) => a.localeCompare(b));
17
+ };
18
+ /**
19
+ * The parent frame a component is attached to in the machine's frame system.
20
+ * A frame's parent is the reference frame of the pose that positions it (its
21
+ * observer frame), so `getPose` and `Move` can be expressed relative to it.
22
+ *
23
+ * @param config - The machine's frame system config, or `undefined` while loading.
24
+ * @param componentName - The component (frame) whose parent to look up.
25
+ * @returns The parent frame name, or `'world'` when there is no configured parent.
26
+ */
27
+ export const parentFrame = (config, componentName) => {
28
+ const entry = config?.find((item) => item.frame?.referenceFrame === componentName);
29
+ return entry?.frame?.poseInObserverFrame?.referenceFrame || 'world';
30
+ };
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ import ApiSection from '../../api-section.svelte'
3
+ import ConnectionStatus from '../../connection-status.svelte'
4
+
5
+ import MoveWidget from './move-widget.svelte'
6
+
7
+ interface Props {
8
+ partID: string
9
+ resourceName: string
10
+ }
11
+
12
+ const { partID, resourceName }: Props = $props()
13
+ </script>
14
+
15
+ <ConnectionStatus {partID}>
16
+ {#snippet connected()}
17
+ <ApiSection
18
+ title="Move"
19
+ api="rdk:service:motion"
20
+ >
21
+ <MoveWidget
22
+ {partID}
23
+ {resourceName}
24
+ />
25
+ </ApiSection>
26
+ {/snippet}
27
+ </ConnectionStatus>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ partID: string;
3
+ resourceName: string;
4
+ }
5
+ declare const Motion: import("svelte").Component<Props, {}, "">;
6
+ type Motion = ReturnType<typeof Motion>;
7
+ export default Motion;
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import { MotionClient, type RobotClient } from '@viamrobotics/sdk'
3
+ import {
4
+ createResourceClient,
5
+ createResourceMutation,
6
+ createRobotQuery,
7
+ useRobotClient,
8
+ } from '@viamrobotics/svelte-sdk'
9
+
10
+ import ComponentNameSelect from './component-name-select.svelte'
11
+ import { movableFrameNames, parentFrame } from './frame-system-config'
12
+ import Move from './move.svelte'
13
+ import { type MoveInput, parseMoveArgs } from './parse-move-args'
14
+
15
+ interface Props {
16
+ partID: string
17
+ resourceName: string
18
+ }
19
+
20
+ const { partID, resourceName }: Props = $props()
21
+
22
+ const robotClient = useRobotClient(() => partID)
23
+ const client = createResourceClient(
24
+ MotionClient,
25
+ () => partID,
26
+ () => resourceName
27
+ )
28
+
29
+ const move = createResourceMutation(client, 'move')
30
+ const frameSystem = createRobotQuery(robotClient, 'frameSystemConfig', () => ({
31
+ refetchInterval: 5000,
32
+ }))
33
+ const frameNames = $derived(movableFrameNames(frameSystem.data))
34
+
35
+ let selectedName = $state<string>()
36
+ const componentName = $derived(selectedName ?? frameNames[0] ?? '')
37
+
38
+ const destinationFrame = $derived(parentFrame(frameSystem.data, componentName))
39
+ const poseArgs = $derived<Parameters<RobotClient['getPose']>>([
40
+ componentName,
41
+ destinationFrame,
42
+ [],
43
+ ])
44
+
45
+ const poseQuery = createRobotQuery(
46
+ robotClient,
47
+ 'getPose',
48
+ () => poseArgs,
49
+ () => ({ enabled: componentName !== '' })
50
+ )
51
+
52
+ const currentPose = $derived(poseQuery.data?.pose)
53
+ const poseError = $derived(poseQuery.error instanceof Error ? poseQuery.error : null)
54
+
55
+ let parseError = $state<Error>()
56
+
57
+ const executeMove = (input: MoveInput) => {
58
+ try {
59
+ parseError = undefined
60
+ move.mutate(parseMoveArgs(componentName, input), {})
61
+ } catch (error) {
62
+ parseError = error instanceof Error ? error : new Error(String(error))
63
+ }
64
+ }
65
+ </script>
66
+
67
+ <div class="flex flex-col gap-4">
68
+ <ComponentNameSelect
69
+ value={componentName}
70
+ options={frameNames}
71
+ onChange={(value) => {
72
+ selectedName = value
73
+ }}
74
+ />
75
+ <Move
76
+ {componentName}
77
+ {currentPose}
78
+ currentReferenceFrame={destinationFrame}
79
+ isPending={move.isPending}
80
+ lastError={parseError ?? move.error ?? poseError}
81
+ storageKey={`${partID}/${resourceName}/motion-move`}
82
+ onExecute={executeMove}
83
+ />
84
+ </div>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ partID: string;
3
+ resourceName: string;
4
+ }
5
+ declare const MoveWidget: import("svelte").Component<Props, {}, "">;
6
+ type MoveWidget = ReturnType<typeof MoveWidget>;
7
+ export default MoveWidget;
@@ -0,0 +1,127 @@
1
+ <script lang="ts">
2
+ import type { Pose } from '@viamrobotics/sdk'
3
+
4
+ import { Button, Progress } from '@viamrobotics/prime-core'
5
+ import { CodeEditor } from '@viamrobotics/prime-core/code-editor'
6
+ import { PersistedState } from 'runed'
7
+
8
+ import ErrorDisplay from '../../error.svelte'
9
+
10
+ import type { MoveInput } from './parse-move-args'
11
+
12
+ import PoseInFrameInput from './pose-in-frame-input.svelte'
13
+
14
+ interface Props {
15
+ componentName: string
16
+ currentPose?: Pose
17
+ currentReferenceFrame?: string
18
+ isPending: boolean
19
+ lastError: Error | null
20
+ storageKey: string
21
+ onExecute: (input: MoveInput) => void
22
+ }
23
+
24
+ const {
25
+ componentName,
26
+ currentPose,
27
+ currentReferenceFrame,
28
+ isPending,
29
+ lastError,
30
+ storageKey,
31
+ onExecute,
32
+ }: Props = $props()
33
+
34
+ const zeroPose: Pose = { x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 }
35
+
36
+ let edit = $state<{ component: string; referenceFrame: string; pose: Pose }>()
37
+ const isEdited = $derived(edit?.component === componentName)
38
+ const referenceFrame = $derived(
39
+ isEdited && edit ? edit.referenceFrame : (currentReferenceFrame ?? 'world')
40
+ )
41
+
42
+ const pose = $derived(isEdited && edit ? edit.pose : (currentPose ?? zeroPose))
43
+
44
+ // PersistedState is created inside $derived so Svelte can track the storageKey
45
+ // dependency. The localStorage read on construction is benign and idempotent.
46
+ // storageKey is stable for the lifetime of this component.
47
+ const worldState = $derived(new PersistedState(`${storageKey}/world-state`, ''))
48
+ const constraints = $derived(new PersistedState(`${storageKey}/constraints`, ''))
49
+
50
+ const disabled = $derived(componentName === '' || isPending)
51
+
52
+ const execute = () => {
53
+ onExecute({
54
+ referenceFrame,
55
+ pose,
56
+ worldStateJson: worldState.current,
57
+ constraintsJson: constraints.current,
58
+ })
59
+ }
60
+ </script>
61
+
62
+ <div class="flex min-w-0 flex-col gap-4">
63
+ <PoseInFrameInput
64
+ {referenceFrame}
65
+ {pose}
66
+ onReferenceFrameChange={(frame) => {
67
+ edit = { component: componentName, referenceFrame: frame, pose }
68
+ }}
69
+ onPoseChange={(next) => {
70
+ edit = { component: componentName, referenceFrame, pose: next }
71
+ }}
72
+ />
73
+
74
+ <div class="flex flex-col gap-1">
75
+ <span class="text-xs font-medium">
76
+ World state <abbr class="text-subtle-2">(optional JSON)</abbr>
77
+ </span>
78
+ <CodeEditor
79
+ label="World state"
80
+ language="json"
81
+ value={worldState.current}
82
+ onChange={(next: string) => {
83
+ worldState.current = next
84
+ }}
85
+ class="h-32 overflow-y-auto"
86
+ />
87
+ </div>
88
+
89
+ <div class="flex flex-col gap-1">
90
+ <span class="text-xs font-medium">
91
+ Constraints <abbr class="text-subtle-2">(optional JSON)</abbr>
92
+ </span>
93
+ <CodeEditor
94
+ label="Constraints"
95
+ language="json"
96
+ value={constraints.current}
97
+ onChange={(next: string) => {
98
+ constraints.current = next
99
+ }}
100
+ class="h-32 overflow-y-auto"
101
+ />
102
+ </div>
103
+
104
+ <div class="flex items-center gap-2">
105
+ <Button
106
+ class="w-fit"
107
+ icon="play-circle-outline"
108
+ variant="dark"
109
+ {disabled}
110
+ onclick={execute}
111
+ >
112
+ Execute
113
+ </Button>
114
+ {#if isPending}
115
+ <Progress
116
+ size="medium"
117
+ variant="dark"
118
+ />
119
+ {/if}
120
+ </div>
121
+
122
+ <p class="text-subtle-2 text-xs">
123
+ Move blocks until the motion completes. Stop the component itself to interrupt.
124
+ </p>
125
+
126
+ <ErrorDisplay {lastError} />
127
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { Pose } from '@viamrobotics/sdk';
2
+ import type { MoveInput } from './parse-move-args';
3
+ interface Props {
4
+ componentName: string;
5
+ currentPose?: Pose;
6
+ currentReferenceFrame?: string;
7
+ isPending: boolean;
8
+ lastError: Error | null;
9
+ storageKey: string;
10
+ onExecute: (input: MoveInput) => void;
11
+ }
12
+ declare const Move: import("svelte").Component<Props, {}, "">;
13
+ type Move = ReturnType<typeof Move>;
14
+ export default Move;
@@ -0,0 +1,24 @@
1
+ import { Constraints, type Pose, type PoseInFrame, WorldState } from '@viamrobotics/sdk';
2
+ /** User-entered inputs for a motion `Move` call. Pose is always mm + degrees. */
3
+ export interface MoveInput {
4
+ referenceFrame: string;
5
+ pose: Pose;
6
+ /** Optional `WorldState` as proto JSON. Empty/whitespace means "omit". */
7
+ worldStateJson: string;
8
+ /** Optional `Constraints` as proto JSON. Empty/whitespace means "omit". */
9
+ constraintsJson: string;
10
+ }
11
+ /** Positional arguments for `MotionClient.move`, ready to spread into `mutate`. */
12
+ export type MoveArgs = [PoseInFrame, string, WorldState | undefined, Constraints | undefined];
13
+ /**
14
+ * Builds the positional arguments for `MotionClient.move` from form inputs.
15
+ *
16
+ * Empty or whitespace-only JSON fields are omitted (passed as `undefined`).
17
+ * Non-empty JSON is parsed with the generated message classes and throws on
18
+ * invalid input — callers should catch and surface the error.
19
+ *
20
+ * @param componentName - Name of the component to move.
21
+ * @param input - The pose, reference frame, and optional world-state/constraints JSON.
22
+ * @returns The `[destination, componentName, worldState?, constraints?]` tuple.
23
+ */
24
+ export declare const parseMoveArgs: (componentName: string, input: MoveInput) => MoveArgs;
@@ -0,0 +1,23 @@
1
+ import { Constraints, WorldState } from '@viamrobotics/sdk';
2
+ /**
3
+ * Builds the positional arguments for `MotionClient.move` from form inputs.
4
+ *
5
+ * Empty or whitespace-only JSON fields are omitted (passed as `undefined`).
6
+ * Non-empty JSON is parsed with the generated message classes and throws on
7
+ * invalid input — callers should catch and surface the error.
8
+ *
9
+ * @param componentName - Name of the component to move.
10
+ * @param input - The pose, reference frame, and optional world-state/constraints JSON.
11
+ * @returns The `[destination, componentName, worldState?, constraints?]` tuple.
12
+ */
13
+ export const parseMoveArgs = (componentName, input) => {
14
+ const destination = {
15
+ referenceFrame: input.referenceFrame,
16
+ pose: input.pose,
17
+ };
18
+ const worldState = input.worldStateJson.trim() === '' ? undefined : WorldState.fromJsonString(input.worldStateJson);
19
+ const constraints = input.constraintsJson.trim() === ''
20
+ ? undefined
21
+ : Constraints.fromJsonString(input.constraintsJson);
22
+ return [destination, componentName, worldState, constraints];
23
+ };
@@ -0,0 +1,10 @@
1
+ import type { Pose } from '@viamrobotics/sdk';
2
+ /**
3
+ * Parses a clipboard string as a pose. Accepts a JSON object with numeric
4
+ * `x`, `y`, `z`, `oX`, `oY`, `oZ`, and `theta` fields — the same shape the arm
5
+ * `MoveToPosition` widget copies, so poses can be pasted between the two.
6
+ *
7
+ * @param data - The pasted string.
8
+ * @returns The parsed pose, or `undefined` if it is not a valid pose.
9
+ */
10
+ export declare const parsePastedPose: (data: string) => Pose | undefined;
@@ -0,0 +1,30 @@
1
+ const poseKeys = ['x', 'y', 'z', 'oX', 'oY', 'oZ', 'theta'];
2
+ const isPose = (value) => {
3
+ if (typeof value !== 'object' || value === null) {
4
+ return false;
5
+ }
6
+ const record = value;
7
+ return poseKeys.every((key) => typeof record[key] === 'number');
8
+ };
9
+ /**
10
+ * Parses a clipboard string as a pose. Accepts a JSON object with numeric
11
+ * `x`, `y`, `z`, `oX`, `oY`, `oZ`, and `theta` fields — the same shape the arm
12
+ * `MoveToPosition` widget copies, so poses can be pasted between the two.
13
+ *
14
+ * @param data - The pasted string.
15
+ * @returns The parsed pose, or `undefined` if it is not a valid pose.
16
+ */
17
+ export const parsePastedPose = (data) => {
18
+ let parsed;
19
+ try {
20
+ parsed = JSON.parse(data);
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ if (!isPose(parsed)) {
26
+ return undefined;
27
+ }
28
+ const { x, y, z, oX, oY, oZ, theta } = parsed;
29
+ return { x, y, z, oX, oY, oZ, theta };
30
+ };
@@ -0,0 +1,168 @@
1
+ <script
2
+ lang="ts"
3
+ module
4
+ >
5
+ import type { Pose } from '@viamrobotics/sdk'
6
+
7
+ const poseLabelsList = Object.entries({
8
+ x: 'X',
9
+ y: 'Y',
10
+ z: 'Z',
11
+ oX: 'OX',
12
+ oY: 'OY',
13
+ oZ: 'OZ',
14
+ theta: 'θ',
15
+ }) as [keyof Pose, string][]
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ import { Button, Icon, Input, Label, NumericInput, Tooltip } from '@viamrobotics/prime-core'
20
+
21
+ import AngleUnitToggle from '../../angle-unit-toggle.svelte'
22
+ import CopyButton from '../../copy-button.svelte'
23
+ import PasteButton from '../../paste-button.svelte'
24
+ import Table from '../../table.svelte'
25
+ import { numberValueFromEvent } from '../../../event-handlers'
26
+ import { degreesToRadians, formatNumeric, radiansToDegrees } from '../../../format'
27
+
28
+ import { parsePastedPose } from './parse-pasted-pose'
29
+
30
+ interface Props {
31
+ referenceFrame: string
32
+ pose: Pose
33
+ onReferenceFrameChange: (frame: string) => void
34
+ onPoseChange: (pose: Pose) => void
35
+ }
36
+
37
+ const { referenceFrame, pose, onReferenceFrameChange, onPoseChange }: Props = $props()
38
+
39
+ let useRadians = $state(false)
40
+
41
+ const displayPose = $derived({
42
+ ...pose,
43
+ theta: useRadians ? degreesToRadians(pose.theta) : pose.theta,
44
+ })
45
+
46
+ const copyData = $derived(JSON.stringify(pose))
47
+
48
+ const handlePaste = (data: string): boolean => {
49
+ const parsed = parsePastedPose(data)
50
+ if (!parsed) {
51
+ return false
52
+ }
53
+ onPoseChange(parsed)
54
+ return true
55
+ }
56
+
57
+ const handleValueChange = (key: keyof Pose, inputValue: number) => {
58
+ const nextValue = key === 'theta' && useRadians ? radiansToDegrees(inputValue) : inputValue
59
+ onPoseChange({ ...pose, [key]: nextValue })
60
+ }
61
+
62
+ const zero = () => {
63
+ onPoseChange({ x: 0, y: 0, z: 0, oX: 0, oY: 0, oZ: 1, theta: 0 })
64
+ }
65
+
66
+ const poseUnits = $derived({
67
+ x: 'mm',
68
+ y: 'mm',
69
+ z: 'mm',
70
+ oX: '',
71
+ oY: '',
72
+ oZ: '',
73
+ theta: useRadians ? 'rad' : 'deg',
74
+ })
75
+ </script>
76
+
77
+ <div class="flex min-w-0 flex-col gap-4">
78
+ <div class="flex items-center justify-between">
79
+ <span class="flex flex-row items-center gap-1 text-sm">
80
+ Destination pose
81
+ <Tooltip>
82
+ <Icon
83
+ name="information-outline"
84
+ cx="text-gray-6"
85
+ />
86
+
87
+ <span slot="description">
88
+ The target pose expressed in the given reference frame. Translations are in millimeters.
89
+ </span>
90
+ </Tooltip>
91
+ </span>
92
+ <div class="flex gap-1">
93
+ <AngleUnitToggle
94
+ {useRadians}
95
+ onToggle={() => {
96
+ useRadians = !useRadians
97
+ }}
98
+ />
99
+ <CopyButton data={copyData} />
100
+ <PasteButton onPaste={handlePaste} />
101
+ </div>
102
+ </div>
103
+
104
+ <Label>
105
+ Reference frame
106
+
107
+ <Input
108
+ slot="input"
109
+ value={referenceFrame}
110
+ placeholder="world"
111
+ on:input={(event) => {
112
+ onReferenceFrameChange((event.target as HTMLInputElement).value)
113
+ }}
114
+ />
115
+ </Label>
116
+
117
+ <Table>
118
+ <thead>
119
+ <tr>
120
+ <th>Pose</th>
121
+ <th>Value</th>
122
+ </tr>
123
+ </thead>
124
+ <tbody>
125
+ {#each poseLabelsList as labelList (labelList)}
126
+ {@const [key, label] = labelList}
127
+ {@const value = Number.parseFloat(formatNumeric(displayPose[key]))}
128
+ <tr>
129
+ <th>
130
+ <span class="relative inline-flex justify-center">
131
+ {label}
132
+ <abbr class="text-subtle-2 absolute left-full ml-1">{poseUnits[key]}</abbr>
133
+ </span>
134
+ </th>
135
+ <th>
136
+ <NumericInput
137
+ cx="max-w-[76px]"
138
+ {value}
139
+ on:change={(event) => {
140
+ handleValueChange(key, numberValueFromEvent(event) ?? 0)
141
+ }}
142
+ />
143
+ </th>
144
+ </tr>
145
+ {/each}
146
+ </tbody>
147
+ </Table>
148
+
149
+ <div class="flex flex-col gap-2">
150
+ <span class="flex flex-row gap-2">
151
+ <h4 class="text-xs font-semibold">Quick set</h4>
152
+ <Tooltip>
153
+ <Icon
154
+ name="information-outline"
155
+ cx="text-gray-6"
156
+ />
157
+
158
+ <span slot="description"> Will update the pose values but will not execute </span>
159
+ </Tooltip>
160
+ </span>
161
+ <Button
162
+ class="w-fit"
163
+ onclick={zero}
164
+ >
165
+ Zero
166
+ </Button>
167
+ </div>
168
+ </div>
@@ -0,0 +1,10 @@
1
+ import type { Pose } from '@viamrobotics/sdk';
2
+ interface Props {
3
+ referenceFrame: string;
4
+ pose: Pose;
5
+ onReferenceFrameChange: (frame: string) => void;
6
+ onPoseChange: (pose: Pose) => void;
7
+ }
8
+ declare const PoseInFrameInput: import("svelte").Component<Props, {}, "">;
9
+ type PoseInFrameInput = ReturnType<typeof PoseInFrameInput>;
10
+ export default PoseInFrameInput;
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { MotorClient } 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={MotorClient}
17
+ api={ResourceTriplets.Motor}
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;
@@ -148,10 +148,7 @@
148
148
  Stepper motors will report ”true” if they are being powered while holding
149
149
  a position and while they are turning."
150
150
  >
151
- <Query
152
- contentCx="h-4"
153
- query={isPoweredQuery}
154
- >
151
+ <Query query={isPoweredQuery}>
155
152
  {#if isPoweredQuery.data !== undefined}
156
153
  {@const [isPowered, powerPct] = isPoweredQuery.data}
157
154
  <span class="font-roboto-mono flex flex-row gap-1 text-xs">
@@ -171,10 +168,7 @@
171
168
  class="gap-3 p-0"
172
169
  tooltip="Reports the position of an encoded motor in revolutions from zero/home."
173
170
  >
174
- <Query
175
- contentCx=""
176
- query={positionQuery}
177
- >
171
+ <Query query={positionQuery}>
178
172
  <p class="font-roboto-mono text-default text-xs">
179
173
  {formatNumeric(positionQuery.data, 4)}
180
174
  </p>
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ import { MovementSensorClient } 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 Accuracy from './accuracy.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
+ MovementSensorClient,
19
+ () => partID,
20
+ () => resourceName
21
+ )
22
+
23
+ const query = createResourceQuery(client, 'getAccuracy', { refetchInterval: 500 })
24
+ </script>
25
+
26
+ <ApiSection
27
+ title="GetAccuracy"
28
+ class="grow"
29
+ >
30
+ <Query
31
+ {query}
32
+ contentCx="h-6"
33
+ >
34
+ {#if query.data !== undefined}
35
+ <Accuracy data={query.data} />
36
+ {/if}
37
+ </Query>
38
+ </ApiSection>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ partID: string;
3
+ resourceName: string;
4
+ }
5
+ declare const GetAccuracyWidget: import("svelte").Component<Props, {}, "">;
6
+ type GetAccuracyWidget = ReturnType<typeof GetAccuracyWidget>;
7
+ export default GetAccuracyWidget;