From 11f8906e3ccad25751b837304e812f3a29b4432e Mon Sep 17 00:00:00 2001 From: Devin C Date: Tue, 26 May 2026 11:54:22 -0400 Subject: [PATCH 1/4] make devtools optional dep --- .changeset/honest-carrots-carry.md | 5 ++ docs/src/content/docs/guides/embedding.mdx | 14 ++++++ package.json | 8 +++- pnpm-lock.yaml | 6 +-- src/lib/components/App.svelte | 11 +++-- .../overlay/settings/Settings.svelte | 10 ++-- src/lib/hooks/useDevtools.svelte.ts | 46 +++++++++++++++++++ 7 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 .changeset/honest-carrots-carry.md create mode 100644 src/lib/hooks/useDevtools.svelte.ts diff --git a/.changeset/honest-carrots-carry.md b/.changeset/honest-carrots-carry.md new file mode 100644 index 000000000..40edcbe82 --- /dev/null +++ b/.changeset/honest-carrots-carry.md @@ -0,0 +1,5 @@ +--- +'@viamrobotics/motion-tools': minor +--- + +Make query dev tools an optional dependency diff --git a/docs/src/content/docs/guides/embedding.mdx b/docs/src/content/docs/guides/embedding.mdx index 57ac59628..a1b499585 100644 --- a/docs/src/content/docs/guides/embedding.mdx +++ b/docs/src/content/docs/guides/embedding.mdx @@ -83,6 +83,20 @@ That's enough to get an empty scene with the camera, grid, and overlay UI. From ``` +## Optional: TanStack Query devtools + +Motion-tools issues its Viam RPC calls through `@viamrobotics/svelte-sdk`, which is built on TanStack Query. The official [Svelte Query devtools](https://tanstack.com/query/latest/docs/framework/svelte/devtools) panel — inspect the query cache, view in-flight mutations, manually refetch — is supported, but the package is **not** bundled with motion-tools so it doesn't add weight for consumers that don't need it. + +To enable it, install the devtools package as a peer dependency: + +```bash +pnpm add -D @tanstack/svelte-query-devtools +``` + +No additional wiring is required: motion-tools attaches the devtools to the `QueryClient` your `` (from `@viamrobotics/svelte-sdk`) already sets up. + +Once installed, open Settings (the cog icon) → **Stats** tab → toggle **Query devtools**. The toggle only appears when the package resolves at runtime — if you uninstall the dev dep, the toggle and the floating button disappear with no other changes. + ## Rendering snapshots A **snapshot** is a serialized scene — every transform, drawing, and camera setting captured into one protobuf payload. Snapshots render purely client-side; no draw server, no live machine, no network calls. diff --git a/package.json b/package.json index 800625909..27c6d8c66 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@sveltejs/vite-plugin-svelte": "6.1.4", "@tailwindcss/forms": "0.5.10", "@tailwindcss/vite": "4.1.13", + "@tanstack/svelte-query-devtools": "6.0.2", "@testing-library/jest-dom": "6.8.0", "@testing-library/svelte": "5.2.8", "@testing-library/user-event": "^14.6.1", @@ -121,6 +122,7 @@ "@ag-grid-community/core": ">=32.3.0", "@ag-grid-community/styles": ">=32.3.0", "@dimforge/rapier3d-compat": ">=0.17", + "@tanstack/svelte-query-devtools": ">=6", "@threlte/core": ">=8", "@threlte/extras": ">=9", "@threlte/rapier": ">=3", @@ -144,6 +146,11 @@ "svelte-tweakpane-ui": ">=1.5", "svelte-virtuallists": ">=1" }, + "peerDependenciesMeta": { + "@tanstack/svelte-query-devtools": { + "optional": true + } + }, "pnpm": { "onlyBuiltDependencies": [ "@sentry/cli", @@ -189,7 +196,6 @@ "@connectrpc/connect": "1.7.0", "@connectrpc/connect-web": "1.7.0", "@neodrag/svelte": "^2.3.3", - "@tanstack/svelte-query-devtools": "^6.0.2", "earcut": "^3.0.2", "filtrex": "^3.1.0", "koota": "0.6.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db8c774f0..c1317fc9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,9 +24,6 @@ importers: '@neodrag/svelte': specifier: ^2.3.3 version: 2.3.3(svelte@5.55.7) - '@tanstack/svelte-query-devtools': - specifier: ^6.0.2 - version: 6.0.2(@tanstack/svelte-query@6.1.28(svelte@5.55.7))(svelte@5.55.7) '@zag-js/dialog': specifier: '>=1.31' version: 1.32.0 @@ -100,6 +97,9 @@ importers: '@tailwindcss/vite': specifier: 4.1.13 version: 4.1.13(vite@7.3.2(@types/node@25.6.0)(jiti@2.6.1)(lightningcss@1.30.1)(tsx@4.20.5)(yaml@2.8.1)) + '@tanstack/svelte-query-devtools': + specifier: 6.0.2 + version: 6.0.2(@tanstack/svelte-query@6.1.28(svelte@5.55.7))(svelte@5.55.7) '@testing-library/jest-dom': specifier: 6.8.0 version: 6.8.0 diff --git a/src/lib/components/App.svelte b/src/lib/components/App.svelte index daed2076b..732baf6c7 100644 --- a/src/lib/components/App.svelte +++ b/src/lib/components/App.svelte @@ -3,7 +3,6 @@ import type { Entity } from 'koota' import type { Snippet } from 'svelte' - import { SvelteQueryDevtools } from '@tanstack/svelte-query-devtools' import { Canvas } from '@threlte/core' import { PortalTarget } from '@threlte/extras' import { useXR } from '@threlte/xr' @@ -19,6 +18,7 @@ import XR from '$lib/components/xr/XR.svelte' import { provideWorld } from '$lib/ecs' import { type CameraPose, provideCameraControls } from '$lib/hooks/useControls.svelte' + import { provideDevtools } from '$lib/hooks/useDevtools.svelte' import { provideEnvironment } from '$lib/hooks/useEnvironment.svelte' import { providePartConfig } from '$lib/hooks/usePartConfig.svelte' import { createPartIDContext } from '$lib/hooks/usePartID.svelte' @@ -83,6 +83,7 @@ provideWorld() const settings = provideSettings() + const devtools = provideDevtools() const environment = provideEnvironment() const currentRobotCameraWidgets = $derived(settings.current.openCameraWidgets[partID] || []) const currentFramePovWidgets = $derived(settings.current.openFramePovWidgets[partID] || []) @@ -111,8 +112,12 @@ }) -{#if settings.current.enableQueryDevtools} - +{#if devtools.isAvailable && settings.current.enableQueryDevtools} + {@const Devtools = devtools.component} + {/if}
partID.current, 'camera') const visionServices = useResourceNames(() => partID.current, 'vision') const settings = useSettings() + const devtools = useDevtools() const { disabledCameras, disabledVisionServices } = $derived(settings.current) const geometries = useGeometries() const pointclouds = usePointClouds() @@ -278,9 +280,11 @@ {#snippet Stats()}
- + {#if devtools.isAvailable} + + {/if}
``` -## Optional: TanStack Query devtools - -Motion-tools issues its Viam RPC calls through `@viamrobotics/svelte-sdk`, which is built on TanStack Query. The official [Svelte Query devtools](https://tanstack.com/query/latest/docs/framework/svelte/devtools) panel — inspect the query cache, view in-flight mutations, manually refetch — is supported, but the package is **not** bundled with motion-tools so it doesn't add weight for consumers that don't need it. - -To enable it, install the devtools package as a peer dependency: - -```bash -pnpm add -D @tanstack/svelte-query-devtools -``` - -No additional wiring is required: motion-tools attaches the devtools to the `QueryClient` your `` (from `@viamrobotics/svelte-sdk`) already sets up. - -Once installed, open Settings (the cog icon) → **Stats** tab → toggle **Query devtools**. The toggle only appears when the package resolves at runtime — if you uninstall the dev dep, the toggle and the floating button disappear with no other changes. - ## Rendering snapshots A **snapshot** is a serialized scene — every transform, drawing, and camera setting captured into one protobuf payload. Snapshots render purely client-side; no draw server, no live machine, no network calls. diff --git a/docs/src/content/docs/plugins/debug.mdx b/docs/src/content/docs/plugins/debug.mdx new file mode 100644 index 000000000..a531dbc9e --- /dev/null +++ b/docs/src/content/docs/plugins/debug.mdx @@ -0,0 +1,33 @@ +--- +title: +description: Mount the TanStack Query devtools panel inside a motion-tools visualizer. +--- + +`` surfaces the [TanStack Query devtools](https://tanstack.com/query/latest/docs/framework/svelte/devtools) panel — inspect the query cache, view in-flight mutations, and refetch manually — for any motion-tools instance. Motion-tools issues its Viam RPC calls through `@viamrobotics/svelte-sdk` (built on TanStack Query), so the devtools attaches to the `QueryClient` your `` already sets up. No extra wiring required. + +The devtools package is an **optional peer dependency** — it isn't bundled with motion-tools, so consumers that don't mount `` pay nothing for it. + +## Install + +```bash +pnpm add -D @tanstack/svelte-query-devtools +``` + +## Usage + +Mount `` anywhere inside ``: + +```svelte + + +
+ + + +
+``` + +A floating TanStack logo appears in the bottom-left corner of the viewport — click it to open the devtools panel. Unmount the component (or remove it from the tree) to hide both the button and the panel. diff --git a/docs/src/content/docs/plugins/draw-service.mdx b/docs/src/content/docs/plugins/draw-service.mdx new file mode 100644 index 000000000..ccb3dbc64 --- /dev/null +++ b/docs/src/content/docs/plugins/draw-service.mdx @@ -0,0 +1,6 @@ +--- +title: +description: Stream draw-server output into an embedded motion-tools visualizer. +--- + +Documentation coming soon. diff --git a/docs/src/content/docs/plugins/selection.mdx b/docs/src/content/docs/plugins/selection.mdx new file mode 100644 index 000000000..5c2eccef7 --- /dev/null +++ b/docs/src/content/docs/plugins/selection.mdx @@ -0,0 +1,6 @@ +--- +title: +description: Lasso-pick points and geometries inside a motion-tools visualizer. +--- + +Documentation coming soon. diff --git a/docs/src/content/docs/plugins/skybox.mdx b/docs/src/content/docs/plugins/skybox.mdx new file mode 100644 index 000000000..dafdbc2ad --- /dev/null +++ b/docs/src/content/docs/plugins/skybox.mdx @@ -0,0 +1,6 @@ +--- +title: +description: Render an equirectangular skybox dome around the motion-tools scene. +--- + +Documentation coming soon. diff --git a/src/lib/components/App.svelte b/src/lib/components/App.svelte index 732baf6c7..766f3151e 100644 --- a/src/lib/components/App.svelte +++ b/src/lib/components/App.svelte @@ -18,7 +18,6 @@ import XR from '$lib/components/xr/XR.svelte' import { provideWorld } from '$lib/ecs' import { type CameraPose, provideCameraControls } from '$lib/hooks/useControls.svelte' - import { provideDevtools } from '$lib/hooks/useDevtools.svelte' import { provideEnvironment } from '$lib/hooks/useEnvironment.svelte' import { providePartConfig } from '$lib/hooks/usePartConfig.svelte' import { createPartIDContext } from '$lib/hooks/usePartID.svelte' @@ -83,7 +82,6 @@ provideWorld() const settings = provideSettings() - const devtools = provideDevtools() const environment = provideEnvironment() const currentRobotCameraWidgets = $derived(settings.current.openCameraWidgets[partID] || []) const currentFramePovWidgets = $derived(settings.current.openFramePovWidgets[partID] || []) @@ -112,14 +110,6 @@ }) -{#if devtools.isAvailable && settings.current.enableQueryDevtools} - {@const Devtools = devtools.component} - -{/if} -
partID.current, 'camera') const visionServices = useResourceNames(() => partID.current, 'vision') const settings = useSettings() - const devtools = useDevtools() const { disabledCameras, disabledVisionServices } = $derived(settings.current) const geometries = useGeometries() const pointclouds = usePointClouds() @@ -280,12 +278,6 @@ {#snippet Stats()}
- {#if devtools.isAvailable} - - {/if} - diff --git a/src/lib/hooks/useDevtools.svelte.ts b/src/lib/hooks/useDevtools.svelte.ts deleted file mode 100644 index d137c5440..000000000 --- a/src/lib/hooks/useDevtools.svelte.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { type Component, getContext, setContext } from 'svelte' - -type DevtoolsButtonPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' - -interface DevtoolsProps { - initialIsOpen?: boolean - buttonPosition?: DevtoolsButtonPosition -} - -type DevtoolsComponent = Component - -const key = Symbol('devtools-context') - -interface Context { - readonly isAvailable: boolean - readonly component: DevtoolsComponent | undefined -} - -export const provideDevtools = (): Context => { - let component = $state.raw() - - import('@tanstack/svelte-query-devtools') - .then((mod) => { - component = mod.SvelteQueryDevtools - }) - .catch(() => { - // Optional peer dependency not installed — devtools simply stay hidden. - }) - - const context: Context = { - get isAvailable() { - return component !== undefined - }, - get component() { - return component - }, - } - - setContext(key, context) - - return context -} - -export const useDevtools = () => { - return getContext(key) -} diff --git a/src/lib/hooks/useSettings.svelte.ts b/src/lib/hooks/useSettings.svelte.ts index ba5a384fd..fea0e6079 100644 --- a/src/lib/hooks/useSettings.svelte.ts +++ b/src/lib/hooks/useSettings.svelte.ts @@ -41,7 +41,6 @@ export interface Settings { enableMeasureAxisZ: boolean enableLabels: boolean - enableQueryDevtools: boolean // Widgets enableArmPositionsWidget: boolean @@ -116,7 +115,6 @@ const defaults = (): Settings => ({ enableMeasureAxisZ: true, enableLabels: false, - enableQueryDevtools: false, enableArmPositionsWidget: false, openCameraWidgets: {}, diff --git a/src/lib/plugins/Debug/Debug.svelte b/src/lib/plugins/Debug/Debug.svelte new file mode 100644 index 000000000..1c75d1e32 --- /dev/null +++ b/src/lib/plugins/Debug/Debug.svelte @@ -0,0 +1,5 @@ + + + diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index 7d7c137ca..ca7187d82 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -8,3 +8,6 @@ export { default as DrawService } from './DrawService/DrawService.svelte' // Skybox export { default as Skybox } from './Skybox/Skybox.svelte' + +// Debug +export { default as Debug } from './Debug/Debug.svelte' From 2b4f87635abe7cc2a5fd9ffb54a97756a999ffcc Mon Sep 17 00:00:00 2001 From: Devin C Date: Tue, 26 May 2026 17:55:25 -0400 Subject: [PATCH 3/4] add plugin docs --- .changeset/kind-hats-shop.md | 5 ++ .../src/content/docs/plugins/draw-service.mdx | 41 +++++++++++- docs/src/content/docs/plugins/selection.mdx | 64 ++++++++++++++++++- docs/src/content/docs/plugins/skybox.mdx | 31 ++++++++- package.json | 18 +++++- pnpm-lock.yaml | 18 +++--- 6 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 .changeset/kind-hats-shop.md diff --git a/.changeset/kind-hats-shop.md b/.changeset/kind-hats-shop.md new file mode 100644 index 000000000..ee115730f --- /dev/null +++ b/.changeset/kind-hats-shop.md @@ -0,0 +1,5 @@ +--- +'@viamrobotics/motion-tools': patch +--- + +Add plugin docs and make plugin dependencies optional diff --git a/docs/src/content/docs/plugins/draw-service.mdx b/docs/src/content/docs/plugins/draw-service.mdx index ccb3dbc64..23066fcc6 100644 --- a/docs/src/content/docs/plugins/draw-service.mdx +++ b/docs/src/content/docs/plugins/draw-service.mdx @@ -1,6 +1,43 @@ --- title: -description: Stream draw-server output into an embedded motion-tools visualizer. +description: Stream a running draw server's output into an embedded motion-tools visualizer. --- -Documentation coming soon. +`` connects an embedded `` to a running draw server over WebSocket so any `client/api` calls made elsewhere render in this instance live. Skip it if you only render snapshots — snapshots are entirely client-side and don't need the server. + +The plugin is **headless**: it provides the connection context and starts the stream; it doesn't render anything itself. + +The Connect RPC client is required for the ``. + +## Install + +```bash +pnpm add @connectrpc/connect @connectrpc/connect-web +``` + +## Usage + +```svelte + + +
+ + + +
+``` + +## Props + +| Prop | Type | Description | +| -------- | ---------------------------------------------- | ------------------------------------------------------------- | +| `config` | `{ backendIP: string; websocketPort: string }` | Host and port of the draw server. Default dev port is `3030`. | + +The `config` prop is reactive — change it and the plugin reconnects to the new endpoint. + +## Connecting from Go + +Once `` is mounted, point your Go code at the same backend and any `Draw*` call against a shared snapshot streams to the browser. See the [`client/api`](../../api/client-api/) reference for the producer side. diff --git a/docs/src/content/docs/plugins/selection.mdx b/docs/src/content/docs/plugins/selection.mdx index 5c2eccef7..d22e6ae4f 100644 --- a/docs/src/content/docs/plugins/selection.mdx +++ b/docs/src/content/docs/plugins/selection.mdx @@ -1,6 +1,66 @@ --- title: -description: Lasso-pick points and geometries inside a motion-tools visualizer. +description: Lasso- or ellipse-pick points and geometries inside a motion-tools visualizer. --- -Documentation coming soon. +`` adds a selection mode to the visualizer — a dashboard toggle plus an on-canvas lasso or ellipse tool that captures the points and geometries inside the drawn region. Selected entities are exposed via the `useSelectionPlugin` hook so you can react to them from your own UI. + +Selection mode automatically switches the camera to orthographic so 2D screen-space picking maps cleanly to the scene. + +The polygon-triangulation `earcut` library is required for the ``. + +## Install + +```bash +pnpm add earcut +``` + +## Usage + +```svelte + + +
+ + + +
+``` + +A new toggle appears in the top dashboard; clicking it enters selection mode. The popover next to it switches between **Lasso** and **Ellipse** tools. + +## Props + +| Prop | Type | Default | Description | +| ----------------------- | --------- | ------- | --------------------------------------------------------------------------------------------- | +| `enabled` | `boolean` | `false` | Enter selection mode automatically on mount. | +| `autoSelectNewEntities` | `boolean` | `false` | Auto-select the most recently created selection entity (sets it as the focused entity). | +| `children` | `Snippet` | — | Rendered only while selection mode is active — useful for overlays scoped to the active tool. | + +## Reading the selection + +Use `useSelectionPlugin` to read the current set of selected entities from anywhere inside ``: + +```svelte + + +

Selected {selection.current.length} entities

+ +``` + +The hook also exposes `clearSelections()` to remove all selection-marker entities from the world. + +## Traits + +For lower-level access to selection state (e.g. querying which points fall inside a selected region), the plugin exports its Koota traits under the `selectionTraits` namespace: + +```ts +import { selectionTraits } from '@viamrobotics/motion-tools/plugins' +``` diff --git a/docs/src/content/docs/plugins/skybox.mdx b/docs/src/content/docs/plugins/skybox.mdx index dafdbc2ad..6febf183b 100644 --- a/docs/src/content/docs/plugins/skybox.mdx +++ b/docs/src/content/docs/plugins/skybox.mdx @@ -3,4 +3,33 @@ title: description: Render an equirectangular skybox dome around the motion-tools scene. --- -Documentation coming soon. +`` wraps the scene in a [grounded skybox dome](https://threejs.org/examples/?q=skybox#webgl_materials_envmaps_groundprojected) loaded from an equirectangular image, anchoring the lower hemisphere to the world XY plane so geometry sits convincingly on the ground. + +Use it to add real-world context to a snapshot — a warehouse, a lab, or any 360° photo of the environment the robot operates in. + +## Usage + +```svelte + + +
+ + + +
+``` + +The image should be a 2:1 equirectangular projection (the standard format for 360° photos). Drop it in your app's `public/` directory or point `url` at any reachable HTTP endpoint. + +## Props + +| Prop | Type | Default | Description | +| ---------- | ----------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | +| `url` | `string` | — | URL of the equirectangular image. Required. | +| `position` | `[x: number, y: number, z: number]` | `[0, 0, height]` | World-space center of the dome. Default anchors the ground plane at world Z=0. | +| `rotation` | `[x: number, y: number, z: number]` | `[Math.PI / 2, 0, 0]` | Euler rotation in radians. Default aligns the image's vertical axis (+Y) with this scene's vertical axis (+Z); the Z component then acts as yaw. | +| `height` | `number` | `15` | Camera height above ground when the source photo was taken. Larger values magnify the lower portion of the image. | +| `radius` | `number` | `100` | Dome radius. Must exceed the scene camera's reach so the camera stays inside the dome. | diff --git a/package.json b/package.json index 27c6d8c66..db26adf0c 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,8 @@ "@ag-grid-community/core": "32.3.9", "@ag-grid-community/styles": "32.3.9", "@changesets/cli": "2.29.6", + "@connectrpc/connect": "1.7.0", + "@connectrpc/connect-web": "1.7.0", "@dimforge/rapier3d-compat": "0.18.2", "@eslint/compat": "2.0.2", "@eslint/js": "10.0.1", @@ -86,6 +88,7 @@ "@zag-js/tree-view": "1.22.1", "camera-controls": "3.1.0", "concurrently": "^9.2.1", + "earcut": "^3.0.2", "esbuild": "^0.27.3", "eslint": "10.0.2", "eslint-config-prettier": "10.1.8", @@ -121,6 +124,8 @@ "@ag-grid-community/client-side-row-model": ">=32.3.0", "@ag-grid-community/core": ">=32.3.0", "@ag-grid-community/styles": ">=32.3.0", + "@connectrpc/connect": ">=1", + "@connectrpc/connect-web": ">=1", "@dimforge/rapier3d-compat": ">=0.17", "@tanstack/svelte-query-devtools": ">=6", "@threlte/core": ">=8", @@ -139,6 +144,7 @@ "@zag-js/toggle-group": ">=1", "@zag-js/tree-view": ">=1", "camera-controls": ">=3", + "earcut": ">=3", "idb-keyval": ">=6", "lucide-svelte": ">=0.511", "runed": ">=0.28", @@ -147,8 +153,17 @@ "svelte-virtuallists": ">=1" }, "peerDependenciesMeta": { + "@connectrpc/connect": { + "optional": true + }, + "@connectrpc/connect-web": { + "optional": true + }, "@tanstack/svelte-query-devtools": { "optional": true + }, + "earcut": { + "optional": true } }, "pnpm": { @@ -193,10 +208,7 @@ ], "dependencies": { "@bufbuild/protobuf": "1.10.1", - "@connectrpc/connect": "1.7.0", - "@connectrpc/connect-web": "1.7.0", "@neodrag/svelte": "^2.3.3", - "earcut": "^3.0.2", "filtrex": "^3.1.0", "koota": "0.6.5", "lodash-es": "4.18.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1317fc9c..920c11f7a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,21 +15,12 @@ importers: '@bufbuild/protobuf': specifier: 1.10.1 version: 1.10.1 - '@connectrpc/connect': - specifier: 1.7.0 - version: 1.7.0(@bufbuild/protobuf@1.10.1) - '@connectrpc/connect-web': - specifier: 1.7.0 - version: 1.7.0(@bufbuild/protobuf@1.10.1)(@connectrpc/connect@1.7.0(@bufbuild/protobuf@1.10.1)) '@neodrag/svelte': specifier: ^2.3.3 version: 2.3.3(svelte@5.55.7) '@zag-js/dialog': specifier: '>=1.31' version: 1.32.0 - earcut: - specifier: ^3.0.2 - version: 3.0.2 filtrex: specifier: ^3.1.0 version: 3.1.0 @@ -58,6 +49,12 @@ importers: '@changesets/cli': specifier: 2.29.6 version: 2.29.6(@types/node@25.6.0) + '@connectrpc/connect': + specifier: 1.7.0 + version: 1.7.0(@bufbuild/protobuf@1.10.1) + '@connectrpc/connect-web': + specifier: 1.7.0 + version: 1.7.0(@bufbuild/protobuf@1.10.1)(@connectrpc/connect@1.7.0(@bufbuild/protobuf@1.10.1)) '@dimforge/rapier3d-compat': specifier: 0.18.2 version: 0.18.2 @@ -187,6 +184,9 @@ importers: concurrently: specifier: ^9.2.1 version: 9.2.1 + earcut: + specifier: ^3.0.2 + version: 3.0.2 esbuild: specifier: ^0.27.3 version: 0.27.3 From ccc350bae5112be00a684b3bcebf230ee4fe24b6 Mon Sep 17 00:00:00 2001 From: Devin C Date: Tue, 26 May 2026 18:04:46 -0400 Subject: [PATCH 4/4] expand draw service plugin docs --- .../src/content/docs/plugins/draw-service.mdx | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/docs/src/content/docs/plugins/draw-service.mdx b/docs/src/content/docs/plugins/draw-service.mdx index 23066fcc6..a7a5b93a8 100644 --- a/docs/src/content/docs/plugins/draw-service.mdx +++ b/docs/src/content/docs/plugins/draw-service.mdx @@ -38,6 +38,44 @@ pnpm add @connectrpc/connect @connectrpc/connect-web The `config` prop is reactive — change it and the plugin reconnects to the new endpoint. -## Connecting from Go +## Running a draw server -Once `` is mounted, point your Go code at the same backend and any `Draw*` call against a shared snapshot streams to the browser. See the [`client/api`](../../api/client-api/) reference for the producer side. +`` is a client — it needs a draw server to talk to. You have three options: + +### Use the motion-tools local app + +The fastest path: run motion-tools locally (`make up` or `pnpm dev`). The local app already hosts a draw server on port `3030`, and `client/api` calls to the same host land in it. + +### Host the Go `DrawService` yourself + +Motion-tools exports a ready-made `DrawService` from `github.com/viam-labs/motion-tools/draw` — the same Connect-RPC handler the local app uses. Wire it up to your own HTTP server: + +```go +package main + +import ( + "log" + "net/http" + + "connectrpc.com/connect" + "github.com/viam-labs/motion-tools/draw" + "github.com/viam-labs/motion-tools/draw/v1/drawv1connect" +) + +func main() { + svc := draw.NewDrawService("") // "" = use os.TempDir for chunked buffers + + mux := http.NewServeMux() + mux.Handle(drawv1connect.NewDrawServiceHandler(svc)) + + log.Fatal(http.ListenAndServe(":3030", mux)) +} +``` + +Your `client/api` producer code then points at this server, and `` in the browser subscribes to its stream. + +### Implement your own server + +If you need a custom backend (different language, different storage, extra side effects), the proto definitions are published under `github.com/viam-labs/motion-tools/draw/v1` (Go) and ship as `.proto` files in the source tree under `protos/draw/v1/`. Implement the `DrawService` RPCs (`AddEntity`, `UpdateEntity`, `RemoveEntity`, `StreamEntityChanges`, …) in whichever language you like; `` just needs a Connect-RPC server at the configured host:port. + +See the [`client/api`](../../api/client-api/) reference for the producer-side calls regardless of which server you use.