@viamrobotics/motion-tools 0.5.3 → 0.5.5

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.
@@ -1,5 +1,5 @@
1
1
  import type { Geometry, Pose } from '@viamrobotics/sdk';
2
- import { BatchedMesh, Box3, Object3D, Vector3 } from 'three';
2
+ import { BatchedMesh, Box3, Object3D, Vector3, type ColorRepresentation } from 'three';
3
3
  export type PointsGeometry = {
4
4
  case: 'points';
5
5
  value: Float32Array<ArrayBuffer>;
@@ -11,11 +11,12 @@ export type LinesGeometry = {
11
11
  export type Geometries = Geometry['geometryType'] | PointsGeometry | LinesGeometry;
12
12
  export type Metadata = {
13
13
  colors?: Float32Array;
14
- color?: string;
14
+ color?: ColorRepresentation;
15
15
  gltf?: {
16
16
  scene: Object3D;
17
17
  };
18
18
  points?: Vector3[];
19
+ pointSize?: number;
19
20
  batched?: {
20
21
  id: number;
21
22
  object: BatchedMesh;
@@ -20,7 +20,7 @@
20
20
 
21
21
  createPartIDContext(() => partID)
22
22
 
23
- let root = $state<HTMLElement>()
23
+ let root = $state.raw<HTMLElement>()
24
24
  </script>
25
25
 
26
26
  <div
@@ -21,7 +21,6 @@
21
21
 
22
22
  const object = $derived(focusedObject.current ?? selectedObject.current)
23
23
  const object3d = $derived(focusedObject3d.current ?? selectedObject3d.current)
24
-
25
24
  const worldPosition = $derived(object3d?.getWorldPosition(new Vector3()))
26
25
  const worldQuaternion = $derived(object3d?.getWorldQuaternion(new Quaternion()))
27
26
  const worldOrientation = $derived(
@@ -12,16 +12,18 @@
12
12
  const vec = new Vector3()
13
13
 
14
14
  let center = $state.raw<[number, number, number]>([0, 0, 0])
15
+ let size = $state.raw<[number, number, number]>([0, 0, 0])
15
16
 
16
17
  $effect(() => {
17
18
  if (object3d) {
18
19
  box.setFromObject(object3d)
20
+ size = box.getSize(vec).toArray()
19
21
  center = box.getCenter(vec).toArray()
20
22
  }
21
23
  })
22
24
  </script>
23
25
 
24
- <Camera position={[2, 0, 0]}>
26
+ <Camera position={[size[0], 0, 0]}>
25
27
  <TrackballControls target={center}>
26
28
  <Gizmo />
27
29
  </TrackballControls>
@@ -20,11 +20,13 @@
20
20
  const s = $derived(keys.has('s'))
21
21
  const a = $derived(keys.has('a'))
22
22
  const d = $derived(keys.has('d'))
23
+ const r = $derived(keys.has('r'))
24
+ const f = $derived(keys.has('f'))
23
25
  const up = $derived(keys.has('arrowup'))
24
26
  const left = $derived(keys.has('arrowleft'))
25
27
  const down = $derived(keys.has('arrowdown'))
26
28
  const right = $derived(keys.has('arrowright'))
27
- const any = $derived(w || s || a || d || up || left || down || right)
29
+ const any = $derived(w || s || a || d || r || f || up || left || down || right)
28
30
 
29
31
  const { start, stop } = useTask(
30
32
  (delta) => {
@@ -46,6 +48,14 @@
46
48
  cameraControls.forward(-0.01 * dt, true)
47
49
  }
48
50
 
51
+ if (r) {
52
+ cameraControls.dolly(0.01 * dt, true)
53
+ }
54
+
55
+ if (f) {
56
+ cameraControls.dolly(-0.01 * dt, true)
57
+ }
58
+
49
59
  if (left) {
50
60
  cameraControls.rotate(-0.1 * MathUtils.DEG2RAD * dt, 0, true)
51
61
  }
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
+ import { Points, BufferAttribute, BufferGeometry, PointsMaterial } from 'three'
3
+
2
4
  import { T } from '@threlte/core'
3
- import { Points, BufferAttribute, BufferGeometry, Color, PointsMaterial } from 'three'
4
5
  import type { WorldObject } from '../WorldObject'
5
6
  import { useObjectEvents } from '../hooks/useObjectEvents.svelte'
6
7
  import { meshBounds } from '@threlte/extras'
@@ -12,18 +13,15 @@
12
13
 
13
14
  let { object }: Props = $props()
14
15
 
15
- const points = new Points()
16
- const geometry = new BufferGeometry()
17
- const material = new PointsMaterial({
18
- size: 0.01,
19
- color: new Color('#888888'),
20
- })
21
-
22
16
  const colors = $derived(object.metadata.colors)
23
17
  const positions = $derived(object.geometry?.value ?? new Float32Array())
24
18
 
25
- $effect(() => {
26
- material.vertexColors = colors !== undefined
19
+ const points = new Points()
20
+ const geometry = new BufferGeometry()
21
+ const material = new PointsMaterial()
22
+
23
+ $effect.pre(() => {
24
+ material.size = object.metadata.pointSize ?? 0.01
27
25
  })
28
26
 
29
27
  $effect.pre(() => {
@@ -31,8 +29,13 @@
31
29
  })
32
30
 
33
31
  $effect.pre(() => {
32
+ material.vertexColors = colors !== undefined
33
+ material.color.set(colors ? 0xffffff : (object.metadata.color ?? '#888888'))
34
+
35
+ material.toneMapped = false
34
36
  if (colors) {
35
37
  geometry.setAttribute('color', new BufferAttribute(colors, 3))
38
+ geometry.attributes.color.needsUpdate = true
36
39
  }
37
40
  })
38
41
 
@@ -82,6 +82,8 @@
82
82
  plane="xy"
83
83
  sectionColor="#333"
84
84
  infiniteGrid
85
+ cellSize={0.5}
86
+ sectionSize={10}
85
87
  fadeOrigin={new Vector3()}
86
88
  fadeDistance={25}
87
89
  />
@@ -39,7 +39,6 @@
39
39
  providePointclouds(() => partID.current)
40
40
  provideMotionClient(() => partID.current)
41
41
  provideObjects()
42
-
43
42
  const { focus } = provideSelection()
44
43
  </script>
45
44
 
@@ -18,9 +18,6 @@
18
18
  class: className = '',
19
19
  onclick,
20
20
  }: Props = $props()
21
-
22
- const activeClasses = 'z-10 border-gray-5 bg-white text-gray-8'
23
- const inactiveClasses = 'bg-light border-medium text-disabled'
24
21
  </script>
25
22
 
26
23
  <Tooltip
@@ -28,7 +25,11 @@
28
25
  location="bottom"
29
26
  >
30
27
  <label
31
- class={[className, 'relative block border', active ? activeClasses : inactiveClasses]}
28
+ class={[
29
+ className,
30
+ 'relative block border',
31
+ active ? 'border-gray-5 text-gray-8 z-10 bg-white' : 'bg-light border-medium text-disabled',
32
+ ]}
32
33
  aria-describedby={tooltipID}
33
34
  >
34
35
  <button
@@ -6,14 +6,14 @@ import { observe } from '@threlte/core';
6
6
  import { useLogs } from './useLogs.svelte';
7
7
  const key = Symbol('frames-context');
8
8
  export const provideFrames = (partID) => {
9
+ const client = useRobotClient(partID);
10
+ const machineStatus = useMachineStatus(partID);
11
+ const logs = useLogs();
9
12
  const refreshRates = useRefreshRates();
10
13
  if (!refreshRates.has('Frames')) {
11
14
  refreshRates.set('Frames', 1);
12
15
  }
13
- const logs = useLogs();
14
- const client = useRobotClient(partID);
15
16
  const query = createRobotQuery(client, 'frameSystemConfig');
16
- const machineStatus = useMachineStatus(partID);
17
17
  const revision = $derived(machineStatus.current?.config.revision);
18
18
  const shouldFetch = $derived(refreshRates.get('Frames') === 1);
19
19
  observe.pre(() => [revision], () => {
@@ -1,26 +1,25 @@
1
1
  import { Object3D } from 'three';
2
2
  import type { WorldObject } from '../WorldObject';
3
- type UUID = string;
4
3
  interface SelectionContext {
5
- readonly current: UUID | undefined;
6
- set(value?: UUID): void;
4
+ readonly current: string | undefined;
5
+ set(value?: string): void;
7
6
  }
8
7
  interface FocusContext {
9
- readonly current: UUID | undefined;
10
- set(value?: UUID): void;
8
+ readonly current: string | undefined;
9
+ set(value?: string): void;
11
10
  }
12
11
  export declare const provideSelection: () => {
13
12
  selection: {
14
13
  readonly current: string | undefined;
15
- set(value?: UUID): void;
14
+ set(value?: string): void;
16
15
  };
17
16
  focus: {
18
17
  readonly current: string | undefined;
19
- set(value?: UUID): void;
18
+ set(value?: string): void;
20
19
  };
21
20
  hover: {
22
21
  readonly current: string | undefined;
23
- set(value?: UUID): void;
22
+ set(value?: string): void;
24
23
  };
25
24
  };
26
25
  export declare const useSelected: () => SelectionContext;
@@ -1,5 +1,5 @@
1
1
  import { getContext, setContext } from 'svelte';
2
- import { Vector3, Vector4 } from 'three';
2
+ import { Color, Vector3, Vector4 } from 'three';
3
3
  import { NURBSCurve } from 'three/addons/curves/NURBSCurve.js';
4
4
  import { parsePcdInWorker } from '../loaders/pcd';
5
5
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
@@ -15,6 +15,22 @@ const tryParse = (json) => {
15
15
  return;
16
16
  }
17
17
  };
18
+ class Float32Reader {
19
+ littleEndian = true;
20
+ offset = 0;
21
+ buffer = new ArrayBuffer();
22
+ view = new DataView(this.buffer);
23
+ async init(data) {
24
+ this.buffer = await data.arrayBuffer();
25
+ this.view = new DataView(this.buffer);
26
+ return this;
27
+ }
28
+ read() {
29
+ const result = this.view.getFloat32(this.offset, this.littleEndian);
30
+ this.offset += 4;
31
+ return result;
32
+ }
33
+ }
18
34
  export const provideShapes = () => {
19
35
  let pointsIndex = 0;
20
36
  let geometryIndex = 0;
@@ -28,9 +44,12 @@ export const provideShapes = () => {
28
44
  const nurbs = $state([]);
29
45
  const models = $state([]);
30
46
  let connectionStatus = $state('connecting');
47
+ const color = new Color();
48
+ const direction = new Vector3();
49
+ const origin = new Vector3();
50
+ const vec3 = new Vector3();
31
51
  const loader = new GLTFLoader();
32
- const addPcd = async (data) => {
33
- const buffer = await data.arrayBuffer();
52
+ const addPCD = async (buffer) => {
34
53
  const { positions, colors } = await parsePcdInWorker(new Uint8Array(buffer));
35
54
  points.push(new WorldObject(`points ${++pointsIndex}`, undefined, undefined, {
36
55
  case: 'points',
@@ -70,23 +89,33 @@ export const provideShapes = () => {
70
89
  const object = new WorldObject(data.name, data.pose, data.parent, { case: 'line', value: new Float32Array() }, { color, points: curve.getPoints(200) });
71
90
  nurbs.push(object);
72
91
  };
73
- const direction = new Vector3();
74
- const origin = new Vector3();
75
- const vec3 = new Vector3();
76
92
  const batchedArrow = new BatchedArrow();
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- const addPoses = (nextPoses, colors, arrowHeadAtPose) => {
79
- for (let i = 0, l = nextPoses.length; i < l; i += 1) {
80
- const pose = nextPoses[i];
81
- const length = 0.1;
82
- direction.set(pose.oX ?? 0, pose.oY ?? 0, pose.oZ ?? 0);
83
- origin.set((pose.x ?? 0) / 1000, (pose.y ?? 0) / 1000, (pose.z ?? 0) / 1000);
84
- if (arrowHeadAtPose) {
93
+ const addPoses = async (reader) => {
94
+ // Read counts
95
+ const nPoints = reader.read();
96
+ const nColors = reader.read();
97
+ const arrowHeadAtPose = reader.read();
98
+ // Read positions
99
+ const nextPoses = new Float32Array(nPoints * 6);
100
+ for (let i = 0; i < nPoints * 6; i++) {
101
+ nextPoses[i] = reader.read();
102
+ }
103
+ // Read raw colors
104
+ const colors = new Float32Array(nColors * 3);
105
+ for (let i = 0; i < nColors * 3; i++) {
106
+ colors[i] = reader.read();
107
+ }
108
+ const length = 0.1;
109
+ for (let i = 0, j = 0, l = nextPoses.length; i < l; i += 6, j += 3) {
110
+ origin.set(nextPoses[i], nextPoses[i + 1], nextPoses[i + 2]).multiplyScalar(0.001);
111
+ direction.set(nextPoses[i + 3], nextPoses[i + 4], nextPoses[i + 5]);
112
+ if (arrowHeadAtPose === 1) {
85
113
  // Compute the base position so the arrow ends at the origin
86
114
  origin.sub(vec3.copy(direction).multiplyScalar(length));
87
115
  }
88
- const arrowId = batchedArrow.addArrow(direction, origin, length, colors[i]);
89
- poses.push(new WorldObject(`pose ${++poseIndex}`, pose, undefined, undefined, {
116
+ color.set(colors[j], colors[j + 1], colors[j + 2]);
117
+ const arrowId = batchedArrow.addArrow(direction, origin, length, color);
118
+ poses.push(new WorldObject(`pose ${++poseIndex}`, undefined, undefined, undefined, {
90
119
  getBoundingBoxAt(box3) {
91
120
  return batchedArrow.getBoundingBoxAt(arrowId, box3);
92
121
  },
@@ -97,6 +126,58 @@ export const provideShapes = () => {
97
126
  }));
98
127
  }
99
128
  };
129
+ const addPoints = async (reader) => {
130
+ // Read label length
131
+ const labelLen = reader.read();
132
+ let label = '';
133
+ for (let i = 0; i < labelLen; i++) {
134
+ label += String.fromCharCode(reader.read());
135
+ }
136
+ // Read counts
137
+ const nPoints = reader.read();
138
+ const nColors = reader.read();
139
+ // Read default color
140
+ const r = reader.read();
141
+ const g = reader.read();
142
+ const b = reader.read();
143
+ // Read positions
144
+ const positions = new Float32Array(nPoints * 3);
145
+ for (let i = 0; i < nPoints * 3; i++) {
146
+ positions[i] = reader.read();
147
+ }
148
+ const getColors = () => {
149
+ // Read raw colors
150
+ const rawColors = new Float32Array(nColors * 3);
151
+ for (let i = 0; i < nColors * 3; i++) {
152
+ rawColors[i] = reader.read();
153
+ }
154
+ const colors = new Float32Array(nPoints * 3);
155
+ colors.set(rawColors);
156
+ // Cover the gap for any points not colored
157
+ for (let i = nColors; i < nPoints; i++) {
158
+ const offset = i * 3;
159
+ colors[offset] = r;
160
+ colors[offset + 1] = g;
161
+ colors[offset + 2] = b;
162
+ }
163
+ return colors;
164
+ };
165
+ const pointSize = 0.01;
166
+ const metadata = nColors > 0
167
+ ? {
168
+ colors: getColors(),
169
+ color: new Color(r, g, b).convertLinearToSRGB(),
170
+ pointSize,
171
+ }
172
+ : {
173
+ color: new Color(r, g, b).convertLinearToSRGB(),
174
+ pointSize,
175
+ };
176
+ points.push(new WorldObject(label ?? `points ${++pointsIndex}`, undefined, undefined, {
177
+ case: 'points',
178
+ value: positions,
179
+ }, metadata));
180
+ };
100
181
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
182
  const addGeometries = (geometries, colors, parent) => {
102
183
  let i = 0;
@@ -105,6 +186,13 @@ export const provideShapes = () => {
105
186
  i += 1;
106
187
  }
107
188
  };
189
+ const addGLTF = async (buffer) => {
190
+ const blob = new Blob([buffer], { type: 'model/gltf-binary' });
191
+ const url = URL.createObjectURL(blob);
192
+ const gltf = await loader.loadAsync(url);
193
+ models.push(new WorldObject(gltf.scene.name, undefined, undefined, undefined, { gltf }));
194
+ URL.revokeObjectURL(url);
195
+ };
108
196
  const remove = (names) => {
109
197
  let index = -1;
110
198
  for (const name of names) {
@@ -150,23 +238,6 @@ export const provideShapes = () => {
150
238
  geometryIndex = 0;
151
239
  poseIndex = 0;
152
240
  };
153
- let metadata = undefined;
154
- const handleMetadata = (data) => {
155
- const json = tryParse(data);
156
- if ('ext' in json) {
157
- metadata = json;
158
- return true;
159
- }
160
- return false;
161
- };
162
- const loadGLTF = async (data) => {
163
- const buffer = await data.arrayBuffer();
164
- const blob = new Blob([buffer], { type: 'model/gltf-binary' });
165
- const url = URL.createObjectURL(blob);
166
- const gltf = await loader.loadAsync(url);
167
- models.push(new WorldObject(gltf.scene.name, undefined, undefined, undefined, { gltf }));
168
- URL.revokeObjectURL(url);
169
- };
170
241
  const { BACKEND_IP, BUN_SERVER_PORT } = globalThis;
171
242
  const scheduleReconnect = () => {
172
243
  setTimeout(() => {
@@ -189,20 +260,22 @@ export const provideShapes = () => {
189
260
  console.log('Websocket error', JSON.stringify(event));
190
261
  ws.close();
191
262
  };
192
- const onMessage = (event) => {
193
- if (typeof event.data === 'string') {
194
- if (handleMetadata(event.data)) {
195
- return;
196
- }
197
- }
263
+ const onMessage = async (event) => {
198
264
  if (typeof event.data === 'object' && 'arrayBuffer' in event.data) {
199
- if (metadata?.ext === 'glb') {
200
- loadGLTF(event.data);
265
+ const reader = await new Float32Reader().init(event.data);
266
+ const type = reader.read();
267
+ if (type === 0) {
268
+ return addPoints(reader);
201
269
  }
202
- else if (metadata?.ext === 'pcd') {
203
- addPcd(event.data);
270
+ else if (type === 1) {
271
+ return addPoses(reader);
272
+ }
273
+ else if (type === 2) {
274
+ return addPCD(reader.buffer);
275
+ }
276
+ else {
277
+ return addGLTF(reader.buffer);
204
278
  }
205
- return;
206
279
  }
207
280
  const data = tryParse(event.data);
208
281
  if (!data)
@@ -219,9 +292,6 @@ export const provideShapes = () => {
219
292
  if ('Knots' in data) {
220
293
  return addNurbs(data, data.Color);
221
294
  }
222
- if ('poses' in data) {
223
- return addPoses(data.poses, data.colors, data.arrowHeadAtPose);
224
- }
225
295
  if ('geometry' in data) {
226
296
  addGeometry(data.geometry, data.color);
227
297
  }
@@ -1,4 +1,4 @@
1
- import { BatchedMesh, MeshBasicMaterial, Object3D, Vector3, Box3, Matrix4 } from 'three';
1
+ import { BatchedMesh, MeshBasicMaterial, Object3D, Vector3, Box3, Matrix4, type ColorRepresentation } from 'three';
2
2
  interface Arrow {
3
3
  shaftId: number;
4
4
  headId: number;
@@ -17,7 +17,7 @@ export declare class BatchedArrow {
17
17
  shaftWidth?: number | undefined;
18
18
  material?: MeshBasicMaterial | undefined;
19
19
  });
20
- addArrow(dir: Vector3, origin: Vector3, length?: number, color?: string): number;
20
+ addArrow(dir: Vector3, origin: Vector3, length?: number, color?: ColorRepresentation): number;
21
21
  getArrowId(instanceId: number): number | undefined;
22
22
  getBoundingBoxAt(arrowId: number, target: Box3): Box3 | undefined;
23
23
  removeArrow(arrowId: number): void;
@@ -6,6 +6,7 @@ const box1 = new Box3();
6
6
  const box2 = new Box3();
7
7
  const mat4_1 = new Matrix4();
8
8
  const mat4_2 = new Matrix4();
9
+ const col = new Color();
9
10
  let index = 0;
10
11
  export class BatchedArrow {
11
12
  batchedMesh;
@@ -57,7 +58,7 @@ export class BatchedArrow {
57
58
  const coneMatrix = this._computeTransform(coneOrigin, dir, headLength, headWidth * 4);
58
59
  this.batchedMesh.setMatrixAt(headId, coneMatrix);
59
60
  if (color) {
60
- const col = new Color(color);
61
+ col.set(color);
61
62
  this.batchedMesh.setColorAt(shaftId, col);
62
63
  this.batchedMesh.setColorAt(headId, col);
63
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viamrobotics/motion-tools",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "Motion visualization with Viam",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -8,65 +8,66 @@
8
8
  "@ag-grid-community/client-side-row-model": "32.3.5",
9
9
  "@ag-grid-community/core": "32.3.5",
10
10
  "@ag-grid-community/styles": "32.3.5",
11
- "@changesets/cli": "2.29.4",
11
+ "@changesets/cli": "2.29.5",
12
12
  "@dimforge/rapier3d-compat": "0.17.3",
13
- "@eslint/compat": "1.3.0",
14
- "@eslint/js": "9.28.0",
15
- "@playwright/test": "1.53.0",
13
+ "@eslint/compat": "1.3.1",
14
+ "@eslint/js": "9.30.1",
15
+ "@playwright/test": "1.53.2",
16
+ "@sentry/sveltekit": "9.34.0",
16
17
  "@skeletonlabs/skeleton": "3.1.3",
17
18
  "@skeletonlabs/skeleton-svelte": "1.2.3",
18
19
  "@sveltejs/adapter-static": "3.0.8",
19
- "@sveltejs/kit": "2.21.4",
20
- "@sveltejs/package": "2.3.11",
20
+ "@sveltejs/kit": "2.22.2",
21
+ "@sveltejs/package": "2.3.12",
21
22
  "@sveltejs/vite-plugin-svelte": "5.1.0",
22
23
  "@tailwindcss/forms": "0.5.10",
23
- "@tailwindcss/vite": "4.1.10",
24
- "@tanstack/svelte-query": "5.80.6",
25
- "@tanstack/svelte-query-devtools": "5.80.6",
24
+ "@tailwindcss/vite": "4.1.11",
25
+ "@tanstack/svelte-query": "5.81.5",
26
+ "@tanstack/svelte-query-devtools": "5.81.5",
26
27
  "@testing-library/jest-dom": "6.6.3",
27
28
  "@testing-library/svelte": "5.2.8",
28
- "@threlte/core": "8.0.4",
29
+ "@threlte/core": "8.0.5",
29
30
  "@threlte/extras": "9.4.0",
30
31
  "@threlte/rapier": "3.1.4",
31
- "@threlte/xr": "1.0.6",
32
- "@types/bun": "1.2.15",
32
+ "@threlte/xr": "1.0.8",
33
+ "@types/bun": "1.2.17",
33
34
  "@types/lodash-es": "4.17.12",
34
- "@types/three": "0.177.0",
35
- "@typescript-eslint/eslint-plugin": "8.34.0",
36
- "@typescript-eslint/parser": "8.34.0",
35
+ "@types/three": "0.178.0",
36
+ "@typescript-eslint/eslint-plugin": "8.35.1",
37
+ "@typescript-eslint/parser": "8.35.1",
37
38
  "@viamrobotics/prime-core": "0.1.5",
38
- "@viamrobotics/sdk": "0.43.0",
39
- "@viamrobotics/svelte-sdk": "0.3.3",
40
- "@vitejs/plugin-basic-ssl": "2.0.0",
41
- "@zag-js/svelte": "1.15.2",
42
- "@zag-js/tree-view": "1.15.2",
39
+ "@viamrobotics/sdk": "0.45.0",
40
+ "@viamrobotics/svelte-sdk": "0.4.1",
41
+ "@vitejs/plugin-basic-ssl": "2.1.0",
42
+ "@zag-js/svelte": "1.18.1",
43
+ "@zag-js/tree-view": "1.18.1",
43
44
  "camera-controls": "2.10.1",
44
- "eslint": "9.28.0",
45
+ "eslint": "9.30.1",
45
46
  "eslint-config-prettier": "10.1.5",
46
- "eslint-plugin-svelte": "3.9.2",
47
- "globals": "16.2.0",
47
+ "eslint-plugin-svelte": "3.10.1",
48
+ "globals": "16.3.0",
48
49
  "idb-keyval": "6.2.2",
49
50
  "jsdom": "26.1.0",
50
51
  "lodash-es": "4.17.21",
51
- "lucide-svelte": "0.514.0",
52
- "prettier": "3.5.3",
52
+ "lucide-svelte": "0.525.0",
53
+ "prettier": "3.6.2",
53
54
  "prettier-plugin-svelte": "3.4.0",
54
- "prettier-plugin-tailwindcss": "0.6.12",
55
+ "prettier-plugin-tailwindcss": "0.6.13",
55
56
  "publint": "0.3.12",
56
- "runed": "0.28.0",
57
- "svelte": "5.33.19",
58
- "svelte-check": "4.2.1",
57
+ "runed": "0.29.1",
58
+ "svelte": "5.34.8",
59
+ "svelte-check": "4.2.2",
59
60
  "svelte-virtuallists": "1.4.2",
60
- "tailwindcss": "4.1.10",
61
- "three": "0.177.0",
62
- "threlte-uikit": "1.1.0",
63
- "tsx": "4.20.1",
61
+ "tailwindcss": "4.1.11",
62
+ "three": "0.178.0",
63
+ "threlte-uikit": "1.2.0",
64
+ "tsx": "4.20.3",
64
65
  "typescript": "5.8.3",
65
- "typescript-eslint": "8.34.0",
66
+ "typescript-eslint": "8.35.1",
66
67
  "vite": "6.3.5",
67
- "vite-plugin-devtools-json": "0.2.0",
68
+ "vite-plugin-devtools-json": "0.2.1",
68
69
  "vite-plugin-mkcert": "1.17.8",
69
- "vitest": "3.2.3"
70
+ "vitest": "3.2.4"
70
71
  },
71
72
  "peerDependencies": {
72
73
  "@dimforge/rapier3d-compat": ">=0.17",