@viamrobotics/motion-tools 0.19.1 → 1.0.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.
Files changed (137) hide show
  1. package/README.md +56 -26
  2. package/dist/FrameConfigUpdater.svelte.d.ts +11 -17
  3. package/dist/FrameConfigUpdater.svelte.js +109 -109
  4. package/dist/WorldObject.svelte.js +2 -15
  5. package/dist/common/v1/common_pb.d.ts +950 -0
  6. package/dist/common/v1/common_pb.js +1399 -0
  7. package/dist/components/App.svelte +37 -21
  8. package/dist/components/App.svelte.d.ts +1 -0
  9. package/dist/components/BatchedArrows.svelte +102 -0
  10. package/dist/components/BatchedArrows.svelte.d.ts +3 -0
  11. package/dist/components/CameraControls.svelte +2 -3
  12. package/dist/components/Details.svelte +364 -365
  13. package/dist/components/Entities.svelte +73 -0
  14. package/dist/components/{WorldObjects.svelte.d.ts → Entities.svelte.d.ts} +3 -3
  15. package/dist/components/FileDrop.svelte +9 -23
  16. package/dist/components/Focus.svelte +2 -3
  17. package/dist/components/Frame.svelte +41 -22
  18. package/dist/components/Frame.svelte.d.ts +4 -6
  19. package/dist/components/GLTF.svelte +36 -0
  20. package/dist/components/GLTF.svelte.d.ts +11 -0
  21. package/dist/components/Geometry2.svelte +201 -0
  22. package/dist/components/Geometry2.svelte.d.ts +18 -0
  23. package/dist/components/KeyboardControls.svelte +3 -3
  24. package/dist/components/Line.svelte +10 -13
  25. package/dist/components/Line.svelte.d.ts +2 -2
  26. package/dist/components/LiveUpdatesBanner.svelte +51 -15
  27. package/dist/components/MeasureTool.svelte +4 -5
  28. package/dist/components/Pointcloud.svelte +27 -14
  29. package/dist/components/Pointcloud.svelte.d.ts +2 -2
  30. package/dist/components/PointerMissBox.svelte +3 -3
  31. package/dist/components/Pose.svelte +31 -6
  32. package/dist/components/Pose.svelte.d.ts +2 -2
  33. package/dist/components/Scene.svelte +7 -6
  34. package/dist/components/SceneProviders.svelte +0 -6
  35. package/dist/components/Selected.svelte +22 -16
  36. package/dist/components/StaticGeometries.svelte +51 -27
  37. package/dist/components/Tree/Tree.svelte +28 -22
  38. package/dist/components/Tree/Tree.svelte.d.ts +2 -3
  39. package/dist/components/Tree/TreeContainer.svelte +72 -40
  40. package/dist/components/Tree/Widgets.svelte +2 -5
  41. package/dist/components/Tree/buildTree.d.ts +3 -6
  42. package/dist/components/Tree/buildTree.js +19 -39
  43. package/dist/components/__tests__/__fixtures__/entity.d.ts +2 -0
  44. package/dist/components/__tests__/__fixtures__/entity.js +20 -0
  45. package/dist/components/__tests__/__fixtures__/resource.d.ts +17 -0
  46. package/dist/components/__tests__/__fixtures__/resource.js +13 -0
  47. package/dist/components/dashboard/Dashboard.svelte +5 -3
  48. package/dist/components/dashboard/Dashboard.svelte.d.ts +7 -2
  49. package/dist/components/widgets/ArmPositions.svelte +19 -7
  50. package/dist/draw/v1/drawing_pb.d.ts +341 -0
  51. package/dist/draw/v1/drawing_pb.js +417 -0
  52. package/dist/draw/v1/metadata_pb.d.ts +23 -0
  53. package/dist/draw/v1/metadata_pb.js +39 -0
  54. package/dist/draw/v1/scene_pb.d.ts +230 -0
  55. package/dist/draw/v1/scene_pb.js +298 -0
  56. package/dist/draw/v1/snapshot_pb.d.ts +42 -0
  57. package/dist/draw/v1/snapshot_pb.js +61 -0
  58. package/dist/draw/v1/transforms_pb.d.ts +23 -0
  59. package/dist/draw/v1/transforms_pb.js +39 -0
  60. package/dist/ecs/index.d.ts +4 -0
  61. package/dist/ecs/index.js +4 -0
  62. package/dist/ecs/traits.d.ts +128 -0
  63. package/dist/ecs/traits.js +81 -0
  64. package/dist/ecs/useQuery.svelte.d.ts +4 -0
  65. package/dist/ecs/useQuery.svelte.js +49 -0
  66. package/dist/ecs/useTrait.svelte.d.ts +19 -0
  67. package/dist/ecs/useTrait.svelte.js +40 -0
  68. package/dist/ecs/useWorld.d.ts +4 -0
  69. package/dist/ecs/useWorld.js +10 -0
  70. package/dist/frame.d.ts +2 -0
  71. package/dist/frame.js +14 -0
  72. package/dist/geometry.js +6 -6
  73. package/dist/hooks/__tests__/fixtures/ResizableTestWrapper.svelte +41 -0
  74. package/dist/hooks/__tests__/fixtures/ResizableTestWrapper.svelte.d.ts +6 -0
  75. package/dist/hooks/use3DModels.svelte.js +6 -4
  76. package/dist/hooks/useDrawAPI.svelte.d.ts +0 -10
  77. package/dist/hooks/useDrawAPI.svelte.js +144 -267
  78. package/dist/hooks/useFramelessComponents.svelte.js +1 -1
  79. package/dist/hooks/useFrames.svelte.d.ts +6 -2
  80. package/dist/hooks/useFrames.svelte.js +144 -68
  81. package/dist/hooks/useGeometries.svelte.d.ts +0 -2
  82. package/dist/hooks/useGeometries.svelte.js +49 -25
  83. package/dist/hooks/useObjectEvents.svelte.d.ts +3 -2
  84. package/dist/hooks/useObjectEvents.svelte.js +11 -7
  85. package/dist/hooks/usePartConfig.svelte.d.ts +1 -1
  86. package/dist/hooks/usePartConfig.svelte.js +2 -1
  87. package/dist/hooks/usePointclouds.svelte.d.ts +0 -2
  88. package/dist/hooks/usePointclouds.svelte.js +52 -21
  89. package/dist/hooks/usePose.svelte.js +15 -7
  90. package/dist/hooks/useResizable.svelte.d.ts +12 -0
  91. package/dist/hooks/useResizable.svelte.js +45 -0
  92. package/dist/hooks/useResourceByName.svelte.js +8 -5
  93. package/dist/hooks/useSelection.svelte.d.ts +13 -23
  94. package/dist/hooks/useSelection.svelte.js +45 -65
  95. package/dist/hooks/useVisibility.svelte.d.ts +2 -1
  96. package/dist/hooks/useWeblabs.svelte.d.ts +0 -1
  97. package/dist/hooks/useWeblabs.svelte.js +0 -1
  98. package/dist/hooks/useWorldState.svelte.d.ts +9 -0
  99. package/dist/hooks/useWorldState.svelte.js +158 -107
  100. package/dist/lib.d.ts +1 -0
  101. package/dist/lib.js +2 -0
  102. package/dist/three/BatchedArrow.d.ts +2 -3
  103. package/dist/three/BatchedArrow.js +3 -11
  104. package/dist/three/CapsuleGeometry.d.ts +1 -1
  105. package/dist/three/CapsuleGeometry.js +3 -1
  106. package/dist/transform.js +0 -15
  107. package/package.json +12 -7
  108. package/dist/components/WorldObject.svelte +0 -28
  109. package/dist/components/WorldObject.svelte.d.ts +0 -11
  110. package/dist/components/WorldObjects.svelte +0 -159
  111. package/dist/components/WorldState.svelte +0 -92
  112. package/dist/components/WorldState.svelte.d.ts +0 -7
  113. package/dist/components/__tests__/__fixtures__/worldObject.svelte.d.ts +0 -2
  114. package/dist/components/__tests__/__fixtures__/worldObject.svelte.js +0 -35
  115. package/dist/components/portal/Portal.svelte +0 -25
  116. package/dist/components/portal/Portal.svelte.d.ts +0 -8
  117. package/dist/components/portal/PortalTarget.svelte +0 -18
  118. package/dist/components/portal/PortalTarget.svelte.d.ts +0 -6
  119. package/dist/components/portal/index.d.ts +0 -2
  120. package/dist/components/portal/index.js +0 -2
  121. package/dist/components/portal/usePortalContext.svelte.d.ts +0 -5
  122. package/dist/components/portal/usePortalContext.svelte.js +0 -5
  123. package/dist/hooks/useArrows.svelte.d.ts +0 -3
  124. package/dist/hooks/useArrows.svelte.js +0 -9
  125. package/dist/hooks/useDraggable.svelte.d.ts +0 -10
  126. package/dist/hooks/useDraggable.svelte.js +0 -36
  127. package/dist/hooks/useObjects.svelte.d.ts +0 -7
  128. package/dist/hooks/useObjects.svelte.js +0 -35
  129. package/dist/hooks/usePersistentUUIDs.svelte.d.ts +0 -5
  130. package/dist/hooks/usePersistentUUIDs.svelte.js +0 -13
  131. package/dist/hooks/useResourceByName.svelte.d.ts +0 -7
  132. package/dist/hooks/useStaticGeometries.svelte.d.ts +0 -9
  133. package/dist/hooks/useStaticGeometries.svelte.js +0 -47
  134. package/dist/workers/worldStateWorker.d.ts +0 -1
  135. package/dist/workers/worldStateWorker.js +0 -114
  136. package/dist/world-state-messages.d.ts +0 -23
  137. package/dist/world-state-messages.js +0 -1
@@ -9,6 +9,7 @@
9
9
  import { VirtualList } from 'svelte-virtuallists'
10
10
  import { observe } from '@threlte/core'
11
11
  import { Icon } from '@viamrobotics/prime-core'
12
+ import { traits } from '../../ecs'
12
13
 
13
14
  const visibility = useVisibility()
14
15
  const expanded = useExpanded()
@@ -16,21 +17,26 @@
16
17
  interface Props {
17
18
  rootNode: TreeNode
18
19
  selections: string[]
20
+ dragElement?: HTMLElement
19
21
  onSelectionChange?: (event: tree.SelectionChangeDetails) => void
20
- onDragStart?: (event: MouseEvent) => void
21
- onDragEnd?: (event: MouseEvent) => void
22
22
  }
23
23
 
24
- let { rootNode, selections, onSelectionChange, onDragStart, onDragEnd }: Props = $props()
24
+ let { rootNode, selections, onSelectionChange, dragElement = $bindable() }: Props = $props()
25
25
 
26
- const collection = tree.collection<TreeNode>({
27
- nodeToValue: (node) => node.id,
28
- nodeToString: (node) => node.name,
29
- rootNode,
30
- })
26
+ const collection = $derived(
27
+ tree.collection<TreeNode>({
28
+ nodeToValue: (node) => `${node.entity}`,
29
+ nodeToString: (node) => node.entity.get(traits.Name) ?? '',
30
+ rootNode,
31
+ })
32
+ )
31
33
 
34
+ const id = $props.id()
32
35
  const service = useMachine(tree.machine, {
33
- collection,
36
+ id,
37
+ get collection() {
38
+ return collection
39
+ },
34
40
  onSelectionChange(details) {
35
41
  onSelectionChange?.(details)
36
42
  },
@@ -46,7 +52,10 @@
46
52
 
47
53
  observe(
48
54
  () => [selections],
49
- () => untrack(() => api.setSelectedValue(selections))
55
+ () =>
56
+ untrack(() => {
57
+ api.setSelectedValue(selections)
58
+ })
50
59
  )
51
60
 
52
61
  observe(
@@ -68,7 +77,7 @@
68
77
  })}
69
78
  {@const nodeProps = { indexPath, node }}
70
79
  {@const nodeState = api.getNodeState(nodeProps)}
71
- {@const isVisible = visibility.get(node.id) ?? true}
80
+ {@const isVisible = visibility.get(node.entity) ?? true}
72
81
  {@const { selected } = nodeState}
73
82
 
74
83
  {#if nodeState.isBranch}
@@ -93,14 +102,14 @@
93
102
  class="flex items-center"
94
103
  {...api.getBranchTextProps(nodeProps)}
95
104
  >
96
- {node.name}
105
+ {node.entity.get(traits.Name)}
97
106
  </span>
98
107
 
99
108
  <button
100
109
  class="text-gray-6"
101
110
  onclick={(event) => {
102
111
  event.stopPropagation()
103
- visibility.set(node.id, !isVisible)
112
+ visibility.set(node.entity, !isVisible)
104
113
  }}
105
114
  >
106
115
  {#if isVisible}
@@ -113,7 +122,7 @@
113
122
  <div {...api.getBranchContentProps(nodeProps)}>
114
123
  <div {...api.getBranchIndentGuideProps(nodeProps)}></div>
115
124
 
116
- {#each children as node, index (node.id)}
125
+ {#each children as node, index (node.entity)}
117
126
  {@render treeNode({ node, indexPath: [...indexPath, index], api })}
118
127
  {/each}
119
128
  </div>
@@ -124,14 +133,14 @@
124
133
  {...api.getItemProps(nodeProps)}
125
134
  >
126
135
  <span class="flex items-center gap-1.5">
127
- {node.name}
136
+ {node.entity.get(traits.Name)}
128
137
  </span>
129
138
 
130
139
  <button
131
140
  class="text-gray-6"
132
141
  onclick={(event) => {
133
142
  event.stopPropagation()
134
- visibility.set(node.id, !isVisible)
143
+ visibility.set(node.entity, !isVisible)
135
144
  }}
136
145
  >
137
146
  {#if isVisible}
@@ -147,13 +156,10 @@
147
156
  <div class="root-node">
148
157
  <div {...api.getRootProps() as object}>
149
158
  <div class="border-medium flex items-center gap-1 border-b p-2">
150
- <button
151
- onmousedown={onDragStart}
152
- onmouseup={onDragEnd}
153
- >
159
+ <button bind:this={dragElement}>
154
160
  <Icon name="drag" />
155
161
  </button>
156
- <h3 {...api.getLabelProps() as object}>{rootNode.name}</h3>
162
+ <h3 {...api.getLabelProps() as object}>{rootNode.entity.get(traits.Name)}</h3>
157
163
  </div>
158
164
 
159
165
  <div {...api.getTreeProps()}>
@@ -174,7 +180,7 @@
174
180
  style="height:{Math.min(8, Math.max(rootChildren.length, 5)) * 32}px;"
175
181
  class="overflow-auto"
176
182
  >
177
- {#each rootChildren as node, index (node.id)}
183
+ {#each rootChildren as node, index (node.entity)}
178
184
  {@render treeNode({ node, indexPath: [Number(index)], api })}
179
185
  {/each}
180
186
  </div>
@@ -3,10 +3,9 @@ import type { TreeNode } from './buildTree';
3
3
  interface Props {
4
4
  rootNode: TreeNode;
5
5
  selections: string[];
6
+ dragElement?: HTMLElement;
6
7
  onSelectionChange?: (event: tree.SelectionChangeDetails) => void;
7
- onDragStart?: (event: MouseEvent) => void;
8
- onDragEnd?: (event: MouseEvent) => void;
9
8
  }
10
- declare const Tree: import("svelte").Component<Props, {}, "">;
9
+ declare const Tree: import("svelte").Component<Props, {}, "dragElement">;
11
10
  type Tree = ReturnType<typeof Tree>;
12
11
  export default Tree;
@@ -1,74 +1,106 @@
1
1
  <script lang="ts">
2
+ import type { Vector2Like } from 'three'
3
+ import { draggable } from '@neodrag/svelte'
2
4
  import Tree from './Tree.svelte'
3
-
4
- import { buildTreeNodes, type TreeNode } from './buildTree'
5
- import { useSelected } from '../../hooks/useSelection.svelte'
5
+ import { useSelectedEntity } from '../../hooks/useSelection.svelte'
6
6
  import { provideTreeExpandedContext } from './useExpanded.svelte'
7
- import { isEqual } from 'lodash-es'
8
- import { useObjects } from '../../hooks/useObjects.svelte'
9
7
  import Settings from './Settings.svelte'
10
8
  import Logs from './Logs.svelte'
11
- import { useDraggable } from '../../hooks/useDraggable.svelte'
12
- import { useWorldStates } from '../../hooks/useWorldState.svelte'
13
9
  import Widgets from './Widgets.svelte'
14
10
  import AddFrames from './AddFrames.svelte'
15
11
  import { useEnvironment } from '../../hooks/useEnvironment.svelte'
16
12
  import { usePartID } from '../../hooks/usePartID.svelte'
17
13
  import { usePartConfig } from '../../hooks/usePartConfig.svelte'
18
- import WeblabActive from '../weblab/WeblabActive.svelte'
19
- import { WEBLABS_EXPERIMENTS } from '../../hooks/useWeblabs.svelte'
14
+ import { traits, useWorld } from '../../ecs'
15
+ import { IsExcluded, type Entity } from 'koota'
16
+ import { buildTreeNodes, type TreeNode } from './buildTree'
17
+ import { MIN_DIMENSIONS, useResizable } from '../../hooks/useResizable.svelte'
18
+ import { PersistedState } from 'runed'
19
+
20
20
  const { ...rest } = $props()
21
21
 
22
+ const dragPosition = new PersistedState<Vector2Like | undefined>('tree-drag-position', undefined)
23
+
22
24
  provideTreeExpandedContext()
23
25
 
26
+ let container = $state.raw<HTMLDivElement>()
27
+ let dragElement = $state.raw<HTMLElement>()
28
+
24
29
  const partID = usePartID()
25
- const selected = useSelected()
26
- const objects = useObjects()
27
- const draggable = useDraggable('treeview')
28
- const worldStates = useWorldStates()
30
+ const selectedEntity = useSelectedEntity()
31
+ const resizable = useResizable(() => 'treeview')
29
32
  const environment = useEnvironment()
30
33
  const partConfig = usePartConfig()
34
+ const world = useWorld()
31
35
 
32
- let rootNode = $state<TreeNode>({
33
- id: 'world',
34
- name: 'World',
35
- children: [],
36
- href: '/',
37
- })
36
+ const worldEntity = world.spawn(IsExcluded, traits.Name('World'))
37
+
38
+ let children = $state<TreeNode[]>([])
39
+
40
+ let pending = false
41
+ const flush = () => {
42
+ if (pending) return
43
+ pending = true
44
+ window.setTimeout(() => {
45
+ children = buildTreeNodes(world.query(traits.Name))
46
+ pending = false
47
+ })
48
+ }
38
49
 
39
- const nodes = $derived(buildTreeNodes(objects.current, worldStates.current))
50
+ world.onAdd(traits.Name, flush)
51
+ world.onAdd(traits.Parent, flush)
52
+ world.onRemove(traits.Name, flush)
53
+ world.onRemove(traits.Parent, flush)
54
+ world.onChange(traits.Name, flush)
55
+ world.onChange(traits.Parent, flush)
40
56
 
41
- $effect.pre(() => {
42
- if (!isEqual(rootNode.children, nodes)) {
43
- rootNode.children = nodes
57
+ const rootNode = $derived<TreeNode>({
58
+ entity: worldEntity,
59
+ children,
60
+ })
61
+
62
+ $effect(() => {
63
+ if (container) {
64
+ resizable.observe(container)
44
65
  }
45
66
  })
46
67
  </script>
47
68
 
48
- <div
49
- class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 w-60 overflow-y-auto border text-xs"
50
- style:transform="translate({draggable.current.x}px, {draggable.current.y}px)"
51
- {...rest}
52
- >
53
- {#key rootNode}
69
+ {#if resizable.isLoaded}
70
+ <div
71
+ bind:this={container}
72
+ class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 resize overflow-y-auto border text-xs"
73
+ style:min-width="{MIN_DIMENSIONS.width}px"
74
+ style:min-height="{MIN_DIMENSIONS.height}px"
75
+ style:width={resizable.current ? `${resizable.current.width}px` : undefined}
76
+ style:height={resizable.current ? `${resizable.current.height}px` : undefined}
77
+ use:draggable={{
78
+ bounds: 'body',
79
+ handle: dragElement,
80
+ defaultPosition: dragPosition.current,
81
+ onDragEnd(data) {
82
+ dragPosition.current = { x: data.offsetX, y: data.offsetY }
83
+ },
84
+ }}
85
+ {...rest}
86
+ >
54
87
  <Tree
55
88
  {rootNode}
56
- selections={selected.current ? [selected.current] : []}
89
+ bind:dragElement
90
+ selections={selectedEntity.current ? [`${selectedEntity.current}`] : []}
57
91
  onSelectionChange={(event) => {
58
- selected.set(event.selectedValue[0])
92
+ const value = event.selectedValue[0]
93
+
94
+ selectedEntity.set(value ? (Number(value) as Entity) : undefined)
59
95
  }}
60
- onDragStart={draggable.onDragStart}
61
- onDragEnd={draggable.onDragEnd}
62
96
  />
63
- {/key}
64
97
 
65
- <WeblabActive experiment={WEBLABS_EXPERIMENTS.MOTION_TOOLS_EDIT_FRAME}>
66
98
  {#if environment.current.isStandalone && partID.current && partConfig.hasEditPermissions}
67
99
  <AddFrames />
68
100
  {/if}
69
- </WeblabActive>
70
101
 
71
- <Logs />
72
- <Settings />
73
- <Widgets />
74
- </div>
102
+ <Logs />
103
+ <Settings />
104
+ <Widgets />
105
+ </div>
106
+ {/if}
@@ -6,11 +6,8 @@
6
6
  const settings = useSettings()
7
7
  </script>
8
8
 
9
- <Drawer
10
- name="Widgets"
11
- defaultOpen
12
- >
13
- <div class="flex h-24 flex-col gap-2 overflow-scroll p-3">
9
+ <Drawer name="Widgets">
10
+ <div class="flex flex-col gap-2 overflow-scroll p-3">
14
11
  <div class="flex items-center justify-between gap-4 py-2">
15
12
  Arm positions
16
13
  <Switch
@@ -1,12 +1,9 @@
1
- import type { useWorldStates } from '../../hooks/useWorldState.svelte';
2
- import type { WorldObject } from '../../WorldObject.svelte';
1
+ import type { Entity, QueryResult, Trait } from 'koota';
3
2
  export interface TreeNode {
4
- id: string;
5
- name: string;
3
+ entity: Entity;
6
4
  children?: TreeNode[];
7
- href: string;
8
5
  }
9
6
  /**
10
7
  * Creates a tree representing parent child / relationships from a set of frames.
11
8
  */
12
- export declare const buildTreeNodes: (objects: WorldObject[], worldStates: ReturnType<typeof useWorldStates>["current"]) => TreeNode[];
9
+ export declare const buildTreeNodes: (entities: QueryResult<[Trait]>) => TreeNode[];
@@ -1,52 +1,32 @@
1
+ import { traits } from '../../ecs';
1
2
  /**
2
3
  * Creates a tree representing parent child / relationships from a set of frames.
3
4
  */
4
- export const buildTreeNodes = (objects, worldStates) => {
5
+ export const buildTreeNodes = (entities) => {
5
6
  const nodeMap = new Map();
6
7
  const rootNodes = [];
7
- for (const object of objects) {
8
- const node = {
9
- name: object.name,
10
- id: object.uuid,
11
- children: [],
12
- href: `/selection/${object.name}`,
13
- };
14
- nodeMap.set(object.name, node);
15
- if (object.referenceFrame === 'world') {
8
+ const childNodes = [];
9
+ for (const entity of entities) {
10
+ const parent = entity.get(traits.Parent);
11
+ const name = entity.get(traits.Name) ?? '';
12
+ const node = { entity };
13
+ nodeMap.set(name, node);
14
+ if (!parent || parent === 'world') {
16
15
  rootNodes.push(node);
17
16
  }
18
- }
19
- for (const object of objects) {
20
- if (object.referenceFrame && object.referenceFrame !== 'world') {
21
- const parentNode = nodeMap.get(object.referenceFrame);
22
- const child = nodeMap.get(object.name);
23
- if (parentNode && child) {
24
- parentNode.children?.push(child);
25
- }
17
+ else {
18
+ childNodes.push(node);
26
19
  }
27
20
  }
28
- for (const worldState of Object.values(worldStates)) {
29
- const node = {
30
- name: worldState.name,
31
- id: worldState.name,
32
- children: [],
33
- href: `/world-state/${worldState.name}`,
34
- };
35
- for (const object of worldState.worldObjects) {
36
- const child = {
37
- name: object.name,
38
- id: object.uuid,
39
- children: [],
40
- href: `/world-state/${worldState.name}/${object.name}`,
41
- };
42
- const parentNode = object.referenceFrame && nodeMap.has(object.referenceFrame)
43
- ? nodeMap.get(object.referenceFrame)
44
- : node;
45
- nodeMap.set(object.name, child);
46
- parentNode.children?.push(child);
21
+ for (const node of childNodes) {
22
+ const parent = node.entity.get(traits.Parent);
23
+ if (parent) {
24
+ const parentNode = nodeMap.get(parent);
25
+ if (parentNode) {
26
+ parentNode.children ??= [];
27
+ parentNode.children?.push(node);
28
+ }
47
29
  }
48
- nodeMap.set(worldState.name, node);
49
- rootNodes.push(node);
50
30
  }
51
31
  return rootNodes;
52
32
  };
@@ -0,0 +1,2 @@
1
+ import type { Entity, World } from 'koota';
2
+ export declare const createEntityFixture: (world: World) => Entity;
@@ -0,0 +1,20 @@
1
+ import { traits } from '../../../ecs';
2
+ export const createEntityFixture = (world) => {
3
+ return world.spawn(traits.Parent('parent_frame'), traits.Name('Test Object'), traits.Pose({
4
+ x: 10,
5
+ y: 20,
6
+ z: 30,
7
+ oX: 0.1,
8
+ oY: 0.2,
9
+ oZ: 0.3,
10
+ theta: 0.4,
11
+ }), traits.EditedPose({
12
+ x: 10,
13
+ y: 20,
14
+ z: 30,
15
+ oX: 0.1,
16
+ oY: 0.2,
17
+ oZ: 0.3,
18
+ theta: 0.4,
19
+ }), traits.Box({ x: 0.01, y: 0.02, z: 0.03 }));
20
+ };
@@ -0,0 +1,17 @@
1
+ export declare const resource: {
2
+ name: string;
3
+ frame: {
4
+ parent: string;
5
+ translation: {
6
+ x: number;
7
+ y: number;
8
+ z: number;
9
+ };
10
+ geometry: {
11
+ type: string;
12
+ x: number;
13
+ y: number;
14
+ z: number;
15
+ };
16
+ };
17
+ };
@@ -0,0 +1,13 @@
1
+ export const resource = {
2
+ name: 'Test Object',
3
+ frame: {
4
+ parent: 'parent_frame',
5
+ translation: { x: 10, y: 20, z: 30 },
6
+ geometry: {
7
+ type: 'box',
8
+ x: 10,
9
+ y: 20,
10
+ z: 30,
11
+ },
12
+ },
13
+ };
@@ -1,15 +1,15 @@
1
1
  <script>
2
2
  import { useSettings } from '../../hooks/useSettings.svelte'
3
- import PortalTarget from '../portal/PortalTarget.svelte'
3
+ import { PortalTarget } from '@threlte/extras'
4
4
  import Button from './Button.svelte'
5
5
 
6
- let { ...rest } = $props()
6
+ let { dashboard, ...rest } = $props()
7
7
 
8
8
  const settings = useSettings()
9
9
  </script>
10
10
 
11
11
  <div
12
- class="absolute top-2 z-1000 flex w-full justify-center gap-2"
12
+ class="absolute top-2 z-10 flex w-full justify-center gap-2"
13
13
  {...rest}
14
14
  >
15
15
  <!-- camera view -->
@@ -83,4 +83,6 @@
83
83
  {/if}
84
84
 
85
85
  <PortalTarget id="dashboard" />
86
+
87
+ {@render dashboard?.()}
86
88
  </div>
@@ -1,6 +1,11 @@
1
1
  export default Dashboard;
2
2
  type Dashboard = {
3
3
  $on?(type: string, callback: (e: any) => void): () => void;
4
- $set?(props: Partial<Record<string, any>>): void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
5
  };
6
- declare const Dashboard: import("svelte").Component<Record<string, any>, {}, "">;
6
+ declare const Dashboard: import("svelte").Component<{
7
+ dashboard: any;
8
+ } & Record<string, any>, {}, "">;
9
+ type $$ComponentProps = {
10
+ dashboard: any;
11
+ } & Record<string, any>;
@@ -1,13 +1,21 @@
1
1
  <script lang="ts">
2
+ import type { Vector2Like } from 'three'
3
+ import { draggable } from '@neodrag/svelte'
2
4
  import { formatNumeric } from '../../format'
3
5
  import Table from '../shared/Table.svelte'
4
- import { useDraggable } from '../../hooks/useDraggable.svelte'
5
6
  import { useArmClient } from '../../hooks/useArmClient.svelte'
6
7
  import { Icon, Label, Select } from '@viamrobotics/prime-core'
8
+ import { PersistedState } from 'runed'
7
9
 
8
10
  const { ...rest } = $props()
9
11
 
10
- const draggable = useDraggable('arm-current-positions')
12
+ const dragPosition = new PersistedState<Vector2Like | undefined>(
13
+ 'details-drag-position',
14
+ undefined
15
+ )
16
+
17
+ let dragElement = $state.raw<HTMLElement>()
18
+
11
19
  const armClient = useArmClient()
12
20
 
13
21
  let selectedArm = $state(armClient.names[0])
@@ -17,16 +25,20 @@
17
25
 
18
26
  <div
19
27
  class="bg-extralight border-medium absolute top-0 left-0 z-1000 m-2 overflow-y-auto border text-xs"
20
- style:transform="translate({draggable.current.x}px, {draggable.current.y}px)"
28
+ use:draggable={{
29
+ bounds: 'body',
30
+ handle: dragElement,
31
+ defaultPosition: dragPosition.current,
32
+ onDragEnd(data) {
33
+ dragPosition.current = { x: data.offsetX, y: data.offsetY }
34
+ },
35
+ }}
21
36
  {...rest}
22
37
  >
23
38
  <div class="flex min-w-0 flex-col">
24
39
  <div class="flex w-full items-center justify-between">
25
40
  <div class="border-medium flex w-full items-center gap-1 border-b p-2">
26
- <button
27
- onmousedown={draggable.onDragStart}
28
- onmouseup={draggable.onDragEnd}
29
- >
41
+ <button bind:this={dragElement}>
30
42
  <Icon name="drag" />
31
43
  </button>
32
44
  <h3>Arm positions</h3>