@viamrobotics/motion-tools 0.14.0 → 0.14.2

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.
@@ -13,7 +13,7 @@
13
13
  <script lang="ts">
14
14
  import { Check, Copy } from 'lucide-svelte'
15
15
  import { useTask } from '@threlte/core'
16
- import { Button, Icon } from '@viamrobotics/prime-core'
16
+ import { Button, Icon, Select, Input } from '@viamrobotics/prime-core'
17
17
  import {
18
18
  useSelectedObject,
19
19
  useFocusedObject,
@@ -106,15 +106,18 @@
106
106
  value,
107
107
  ariaLabel,
108
108
  }: {
109
- label: string
109
+ label?: string
110
110
  value: string
111
111
  ariaLabel: string
112
112
  })}
113
113
  <div>
114
114
  <span
115
115
  class="text-subtle-2"
116
- aria-label={`immutable ${ariaLabel}`}>{label}</span
116
+ aria-label={`immutable ${ariaLabel}`}
117
117
  >
118
+ {label}
119
+ </span>
120
+
118
121
  {value}
119
122
  </div>
120
123
  {/snippet}
@@ -130,14 +133,16 @@
130
133
  ariaLabel: string
131
134
  onInput: (value: string) => void
132
135
  })}
133
- <span class="text-subtle-2">{label}</span>
134
- <input
135
- type="number"
136
- aria-label={`mutable ${ariaLabel}`}
137
- class="max-w-24 min-w-0 flex-1 rounded border px-1 py-0.5 text-xs"
138
- {value}
139
- oninput={(e) => onInput((e.target as HTMLInputElement).value)}
140
- />
136
+ <div class="flex items-center gap-1">
137
+ <span class="text-subtle-2">{label}</span>
138
+ <Input
139
+ type="number"
140
+ aria-label={`mutable ${ariaLabel}`}
141
+ class="max-w-24 min-w-0 flex-1 rounded border px-1 py-0.5 text-xs"
142
+ {value}
143
+ on:input={(event) => onInput((event.target as HTMLInputElement).value)}
144
+ />
145
+ </div>
141
146
  {/snippet}
142
147
 
143
148
  {#snippet DropDownField({
@@ -151,16 +156,17 @@
151
156
  options: string[]
152
157
  onChange: (value: string) => void
153
158
  })}
154
- <select
159
+ <Select
155
160
  aria-label={`dropdown ${ariaLabel}`}
156
- class="w-full rounded border border-gray-300 px-2 py-1 text-sm"
157
161
  {value}
158
- onchange={(e) => onChange((e.target as HTMLSelectElement).value)}
162
+ onchange={(event: InputEvent) => {
163
+ onChange((event.target as HTMLSelectElement).value)
164
+ }}
159
165
  >
160
166
  {#each options as option (option)}
161
167
  <option value={option}>{option}</option>
162
168
  {/each}
163
- </select>
169
+ </Select>
164
170
  {/snippet}
165
171
 
166
172
  {#if object}
@@ -259,7 +265,6 @@
259
265
  <strong class="font-semibold">parent frame</strong>
260
266
  <div class="flex gap-3">
261
267
  {@render ParentFrame({
262
- label: 'name',
263
268
  ariaLabel: 'parent frame name',
264
269
  value: referenceFrame,
265
270
  options: referenceFrameOptions,
@@ -494,16 +499,6 @@
494
499
 
495
500
  <h3 class="text-subtle-2 pt-3 pb-2">Actions</h3>
496
501
 
497
- <WeblabActive experiment="MOTION_TOOLS_EDIT_FRAME">
498
- {#if isFrameNode}
499
- <Button
500
- variant="danger"
501
- class="mb-2 w-full"
502
- onclick={() => detailConfigUpdater.deleteFrame()}>Delete Frame</Button
503
- >
504
- {/if}
505
- </WeblabActive>
506
-
507
502
  {#if focused.current}
508
503
  <Button
509
504
  class="w-full"
@@ -522,5 +517,15 @@
522
517
  Enter object view
523
518
  </Button>
524
519
  {/if}
520
+
521
+ <WeblabActive experiment="MOTION_TOOLS_EDIT_FRAME">
522
+ {#if isFrameNode}
523
+ <Button
524
+ variant="danger"
525
+ class="mt-2 w-full"
526
+ onclick={() => detailConfigUpdater.deleteFrame()}>Delete Frame</Button
527
+ >
528
+ {/if}
529
+ </WeblabActive>
525
530
  </div>
526
531
  {/if}
@@ -17,6 +17,7 @@
17
17
  import { provideWorldStates } from '../hooks/useWorldState.svelte'
18
18
  import { provideArmClient } from '../hooks/useArmClient.svelte'
19
19
  import { provideArrows } from '../hooks/useArrows.svelte'
20
+ import { provideFramelessComponents } from '../hooks/useFramelessComponents.svelte'
20
21
  interface Props {
21
22
  children: Snippet<[{ focus: boolean }]>
22
23
  }
@@ -42,6 +43,7 @@
42
43
  provideObjects()
43
44
  provideWorldStates()
44
45
  provideArmClient(() => partID.current)
46
+ provideFramelessComponents()
45
47
 
46
48
  const { focus } = provideSelection()
47
49
  </script>
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { IconButton } from '@viamrobotics/prime-core'
2
3
  import Drawer from './Drawer.svelte'
3
4
  import { usePartConfig } from '../../hooks/usePartConfig.svelte'
4
5
  import { useFramelessComponents } from '../../hooks/useFramelessComponents.svelte'
@@ -12,12 +13,13 @@
12
13
  {#if framelessComponents.current.length > 0}
13
14
  <ul class="space-y-1">
14
15
  {#each framelessComponents.current as component (component)}
15
- <li class="text-sm text-gray-700">
16
+ <li class="flex items-center gap-2 text-xs text-gray-700">
16
17
  {component}
17
- <button
18
- class="focus:ring-opacity-50 ml-2 rounded bg-blue-500 px-2 py-1 text-xs text-white hover:bg-blue-600 focus:ring-2 focus:ring-blue-500 focus:outline-none"
19
- onclick={() => partConfig.createFrame(component)}>Add Frame</button
20
- >
18
+ <IconButton
19
+ label="Add frame"
20
+ icon="plus"
21
+ onclick={() => partConfig.createFrame(component)}
22
+ />
21
23
  </li>
22
24
  {/each}
23
25
  </ul>
@@ -156,10 +156,7 @@
156
156
  <h3 {...api.getLabelProps() as object}>{rootNode.name}</h3>
157
157
  </div>
158
158
 
159
- <div
160
- {...api.getTreeProps()}
161
- class="w-60"
162
- >
159
+ <div {...api.getTreeProps()}>
163
160
  {#if rootChildren.length === 0}
164
161
  <p class="text-subtle-2 px-2 py-4">No objects displayed</p>
165
162
  {:else if rootChildren.length > 200}
@@ -19,10 +19,8 @@ export const provideFrames = (partID) => {
19
19
  const partConfig = usePartConfig();
20
20
  const environment = useEnvironment();
21
21
  observe.pre(() => [revision], () => {
22
- if (!partConfig.isDirty) {
23
- untrack(() => query.current).refetch();
24
- logs.add('Fetching frames...');
25
- }
22
+ untrack(() => query.current).refetch();
23
+ logs.add('Fetching frames...');
26
24
  });
27
25
  observe.pre(() => [partConfig.isDirty], () => {
28
26
  if (partConfig.isDirty) {
@@ -254,30 +254,35 @@ export class StandalonePartConfig {
254
254
  this._localPartConfig = Struct.fromJson(configJson);
255
255
  this._partName = partResponse?.part?.name;
256
256
  const componentNameToFragmentId = {};
257
- const fragementRequests = [];
257
+ const fragmentRequests = [];
258
258
  if (configJson.fragments) {
259
259
  for (const fragmentId of configJson.fragments) {
260
- fragementRequests.push(standalonePartConfigProps.viamClient()?.appClient.getFragment(fragmentId));
260
+ fragmentRequests.push(standalonePartConfigProps.viamClient()?.appClient.getFragment(fragmentId));
261
261
  }
262
- const fragementResponses = await Promise.all(fragementRequests);
263
- for (const fragmentResponse of fragementResponses) {
264
- const fragmentId = fragmentResponse?.id;
265
- if (!fragmentId) {
266
- continue;
267
- }
268
- const components = fragmentResponse?.fragment?.fields['components'].kind;
269
- if (components?.case === 'listValue') {
270
- for (const component of components.value.values) {
271
- if (component.kind.case === 'structValue') {
272
- const componentName = component.kind.value.fields['name'].kind;
273
- if (componentName.case === 'stringValue') {
274
- componentNameToFragmentId[componentName.value] = fragmentId;
262
+ try {
263
+ const fragementResponses = await Promise.all(fragmentRequests);
264
+ for (const fragmentResponse of fragementResponses) {
265
+ const fragmentId = fragmentResponse?.id;
266
+ if (!fragmentId) {
267
+ continue;
268
+ }
269
+ const components = fragmentResponse?.fragment?.fields['components'].kind;
270
+ if (components?.case === 'listValue') {
271
+ for (const component of components.value.values) {
272
+ if (component.kind.case === 'structValue') {
273
+ const componentName = component.kind.value.fields['name'].kind;
274
+ if (componentName.case === 'stringValue') {
275
+ componentNameToFragmentId[componentName.value] = fragmentId;
276
+ }
275
277
  }
276
278
  }
277
279
  }
278
280
  }
281
+ this._componentNameToFragmentId = componentNameToFragmentId;
282
+ }
283
+ catch {
284
+ /* Do nothing */
279
285
  }
280
- this._componentNameToFragmentId = componentNameToFragmentId;
281
286
  }
282
287
  };
283
288
  initLocalConfig();
@@ -6,6 +6,8 @@ import { RefreshRates, useMachineSettings } from './useMachineSettings.svelte';
6
6
  import { fromStore, toStore } from 'svelte/store';
7
7
  import { useMotionClient } from './useMotionClient.svelte';
8
8
  import { useEnvironment } from './useEnvironment.svelte';
9
+ import { observe } from '@threlte/core';
10
+ import { untrack } from 'svelte';
9
11
  export const usePose = (name, parent) => {
10
12
  const { refreshRates } = useMachineSettings();
11
13
  const partID = usePartID();
@@ -40,6 +42,11 @@ export const usePose = (name, parent) => {
40
42
  },
41
43
  }));
42
44
  const query = fromStore(createQuery(toStore(() => options)));
45
+ observe.pre(() => [environment.current.viewerMode], () => {
46
+ if (environment.current.viewerMode === 'monitor') {
47
+ untrack(() => query.current).refetch();
48
+ }
49
+ });
43
50
  return {
44
51
  get current() {
45
52
  if (resource?.subtype === 'arm') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/motion-tools",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",