@viamrobotics/motion-tools 1.34.1 → 1.34.3

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.
@@ -203,7 +203,7 @@ export const provideFrames = (partID) => {
203
203
  // values would shift entities whose parents the user is portaling
204
204
  // into — the gizmo's drag target moves underneath it. Once we're
205
205
  // back in monitor mode, the next sync resumes the overwrite.
206
- if (!isEditMode) {
206
+ if (!isEditMode || !editSession.current) {
207
207
  const edited = existing.get(traits.EditedMatrix);
208
208
  if (edited) {
209
209
  poseToMatrix(pose, edited);
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { Portal } from '@threlte/extras'
3
+ import { Icon } from '@viamrobotics/prime-core'
3
4
 
4
5
  import DashboardButton from '../../components/overlay/dashboard/Button.svelte'
5
6
  import FloatingPanel from '../../components/overlay/FloatingPanel.svelte'
@@ -37,7 +38,7 @@
37
38
  <div class="flex gap-2">
38
39
  <textarea
39
40
  class="flex-1 resize-none rounded border border-gray-300 p-2 text-xs focus:ring-1 focus:ring-gray-400 focus:outline-none disabled:bg-gray-50 disabled:text-gray-400"
40
- placeholder="Describe the frame change, e.g. 'Move the arm 200mm forward along X'"
41
+ placeholder="Describe a frame change — translation, rotation, or parent. e.g. 'Move arm 200mm forward and rotate 90° left'"
41
42
  rows={3}
42
43
  disabled={sceneBuilder.uiState === 'loading' || sceneBuilder.uiState === 'diff'}
43
44
  bind:value={prompt}
@@ -69,10 +70,6 @@
69
70
 
70
71
  <!-- diff ready -->
71
72
  {#if sceneBuilder.uiState === 'diff'}
72
- {#if sceneBuilder.explanation}
73
- <p class="text-gray-6 italic">{sceneBuilder.explanation}</p>
74
- {/if}
75
-
76
73
  {#if sceneBuilder.diffGroups.length > 0}
77
74
  <div class="flex flex-col gap-2 overflow-auto">
78
75
  {#each sceneBuilder.diffGroups as group (group.componentName)}
@@ -111,11 +108,13 @@
111
108
  {/if}
112
109
 
113
110
  {#if sceneBuilder.updateErrors.length > 0}
114
- <ul class="text-red-6 space-y-0.5">
115
- {#each sceneBuilder.updateErrors as err (err.componentName)}
116
- <li><span class="font-mono">{err.componentName}</span>: {err.reason}</li>
117
- {/each}
118
- </ul>
111
+ <div class="border-danger-medium bg-danger-light rounded border p-2">
112
+ <ul class="text-danger-dark space-y-0.5">
113
+ {#each sceneBuilder.updateErrors as err (err.componentName)}
114
+ <li><span class="font-mono">{err.componentName}</span>: {err.reason}</li>
115
+ {/each}
116
+ </ul>
117
+ </div>
119
118
  {/if}
120
119
 
121
120
  <div class="mt-auto flex gap-2">
@@ -136,13 +135,22 @@
136
135
 
137
136
  <!-- error -->
138
137
  {#if sceneBuilder.uiState === 'error'}
139
- <p class="text-red-6">{sceneBuilder.errorMessage}</p>
140
- <button
141
- class="self-start rounded border border-gray-300 px-3 py-1.5 hover:bg-gray-50"
142
- onclick={sceneBuilder.resetError}
143
- >
144
- Try again
145
- </button>
138
+ <div class="border-danger-medium bg-danger-light flex flex-col gap-2 rounded border p-2.5">
139
+ <div class="text-danger-dark flex items-center gap-1.5 font-medium">
140
+ <Icon
141
+ name="alert-circle-outline"
142
+ aria-hidden="true"
143
+ />
144
+ Error
145
+ </div>
146
+ <p class="text-danger-dark">{sceneBuilder.errorMessage}</p>
147
+ <button
148
+ class="border-danger-medium text-danger-dark self-start rounded border px-3 py-1.5 hover:bg-[#F8E1DF]"
149
+ onclick={sceneBuilder.resetError}
150
+ >
151
+ Try again
152
+ </button>
153
+ </div>
146
154
  {/if}
147
155
  </div>
148
156
  </FloatingPanel>
@@ -1,4 +1,12 @@
1
1
  import { applyEulerDeltaToPose, createPoseFromFrame } from '../../transform';
2
+ function mergeTranslation(a, b) {
3
+ return a || b ? { x: b?.x ?? a?.x, y: b?.y ?? a?.y, z: b?.z ?? a?.z } : undefined;
4
+ }
5
+ function mergeOrientation(a, b) {
6
+ return a || b
7
+ ? { roll: b?.roll ?? a?.roll, pitch: b?.pitch ?? a?.pitch, yaw: b?.yaw ?? a?.yaw }
8
+ : undefined;
9
+ }
2
10
  /**
3
11
  * Validates LLM-proposed frame deltas and computes the resulting changes without
4
12
  * applying them. Each PreparedUpdate carries old and new values so the caller
@@ -8,7 +16,25 @@ export function validateProposedFrameDeltas(deltas, config) {
8
16
  const errors = [];
9
17
  const prepared = [];
10
18
  const knownNames = new Set(config.components.map((c) => c.name));
19
+ // Merge multiple deltas for the same component — the LLM sometimes splits
20
+ // translation and orientation into separate entries despite the schema saying one per component.
21
+ const mergedDeltas = new Map();
11
22
  for (const delta of deltas) {
23
+ const existing = mergedDeltas.get(delta.componentName);
24
+ if (existing) {
25
+ mergedDeltas.set(delta.componentName, {
26
+ componentName: delta.componentName,
27
+ translation: mergeTranslation(existing.translation, delta.translation),
28
+ orientation: mergeOrientation(existing.orientation, delta.orientation),
29
+ parent: delta.parent ?? existing.parent,
30
+ explanation: [existing.explanation, delta.explanation].filter(Boolean).join(', ') || undefined,
31
+ });
32
+ }
33
+ else {
34
+ mergedDeltas.set(delta.componentName, delta);
35
+ }
36
+ }
37
+ for (const delta of mergedDeltas.values()) {
12
38
  const component = config.components.find((c) => c.name === delta.componentName);
13
39
  if (!component) {
14
40
  errors.push({ componentName: delta.componentName, reason: 'Component not found in config' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/motion-tools",
3
- "version": "1.34.1",
3
+ "version": "1.34.3",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -11,6 +11,7 @@
11
11
  "@changesets/cli": "2.29.6",
12
12
  "@connectrpc/connect": "1.7.0",
13
13
  "@connectrpc/connect-web": "1.7.0",
14
+ "@langchain/anthropic": "^1.4.0",
14
15
  "@dimforge/rapier3d-compat": "0.18.2",
15
16
  "@eslint/compat": "2.0.2",
16
17
  "@eslint/js": "10.0.1",
@@ -85,7 +86,8 @@
85
86
  "vite-plugin-devtools-json": "1.0.0",
86
87
  "vite-plugin-glsl": "^1.5.5",
87
88
  "vite-plugin-mkcert": "1.17.9",
88
- "vitest": "3.2.6"
89
+ "vitest": "3.2.6",
90
+ "zod": "^4.4.3"
89
91
  },
90
92
  "peerDependencies": {
91
93
  "@ag-grid-community/client-side-row-model": ">=32.3.0",
@@ -163,17 +165,13 @@
163
165
  ],
164
166
  "dependencies": {
165
167
  "@bufbuild/protobuf": "1.10.1",
166
- "@connectrpc/connect": "1.7.0",
167
- "@connectrpc/connect-web": "1.7.0",
168
- "@langchain/anthropic": "^1.4.0",
169
168
  "@neodrag/svelte": "^2.3.3",
170
169
  "d3-force": "^3.0.0",
171
170
  "filtrex": "^3.1.0",
172
171
  "koota": "0.6.5",
173
172
  "lodash-es": "4.18.1",
174
173
  "three-mesh-bvh": "^0.9.8",
175
- "uuid-tool": "^2.0.3",
176
- "zod": "^4.4.3"
174
+ "uuid-tool": "^2.0.3"
177
175
  },
178
176
  "scripts": {
179
177
  "dev": "concurrently \"pnpm dev:bun\" \"go run cmd/draw-server/main.go -port 3030\"",