@viamrobotics/motion-tools 0.14.0 → 0.14.1

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,15 @@
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
+ on:input={(event) => onChange((event.target as HTMLSelectElement).value)}
159
163
  >
160
164
  {#each options as option (option)}
161
165
  <option value={option}>{option}</option>
162
166
  {/each}
163
- </select>
167
+ </Select>
164
168
  {/snippet}
165
169
 
166
170
  {#if object}
@@ -259,7 +263,6 @@
259
263
  <strong class="font-semibold">parent frame</strong>
260
264
  <div class="flex gap-3">
261
265
  {@render ParentFrame({
262
- label: 'name',
263
266
  ariaLabel: 'parent frame name',
264
267
  value: referenceFrame,
265
268
  options: referenceFrameOptions,
@@ -494,16 +497,6 @@
494
497
 
495
498
  <h3 class="text-subtle-2 pt-3 pb-2">Actions</h3>
496
499
 
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
500
  {#if focused.current}
508
501
  <Button
509
502
  class="w-full"
@@ -522,5 +515,15 @@
522
515
  Enter object view
523
516
  </Button>
524
517
  {/if}
518
+
519
+ <WeblabActive experiment="MOTION_TOOLS_EDIT_FRAME">
520
+ {#if isFrameNode}
521
+ <Button
522
+ variant="danger"
523
+ class="mt-2 w-full"
524
+ onclick={() => detailConfigUpdater.deleteFrame()}>Delete Frame</Button
525
+ >
526
+ {/if}
527
+ </WeblabActive>
525
528
  </div>
526
529
  {/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.1",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",