From 1836e41024a2b7707b5db0792f85197abc0164f8 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:20:21 +0000 Subject: [PATCH 1/4] feat: add IsHoldingSomething status to gripper test widget (#64) Add a new IsHoldingSomething component that polls the gripper's isHoldingSomething API and displays a status pill ("Holding" / "Empty") in the gripper widget sidebar alongside IsMoving. Co-Authored-By: Claude Opus 4.6 --- .changeset/gripper-is-holding-something.md | 5 ++ .../__tests__/is-holding-something.spec.ts | 68 +++++++++++++++++++ .../components/widgets/gripper/gripper.svelte | 5 ++ .../gripper/is-holding-something.svelte | 42 ++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 .changeset/gripper-is-holding-something.md create mode 100644 src/lib/components/widgets/gripper/__tests__/is-holding-something.spec.ts create mode 100644 src/lib/components/widgets/gripper/is-holding-something.svelte diff --git a/.changeset/gripper-is-holding-something.md b/.changeset/gripper-is-holding-something.md new file mode 100644 index 0000000..5b4c0ae --- /dev/null +++ b/.changeset/gripper-is-holding-something.md @@ -0,0 +1,5 @@ +--- +'@viamrobotics/test-widgets': minor +--- + +Add `IsHoldingSomething` status display to gripper test widget diff --git a/src/lib/components/widgets/gripper/__tests__/is-holding-something.spec.ts b/src/lib/components/widgets/gripper/__tests__/is-holding-something.spec.ts new file mode 100644 index 0000000..a556f7d --- /dev/null +++ b/src/lib/components/widgets/gripper/__tests__/is-holding-something.spec.ts @@ -0,0 +1,68 @@ +import { render, screen } from '@testing-library/svelte' +import { createResourceQuery } from '@viamrobotics/svelte-sdk' +import { describe, expect, it, vi } from 'vitest' + +import Subject from '../is-holding-something.svelte' + +vi.mock('@viamrobotics/sdk', () => ({ + GripperClient: class {}, +})) + +vi.mock('@viamrobotics/svelte-sdk', () => ({ + createResourceClient: vi.fn(() => ({ current: {} })), + createResourceQuery: vi.fn(() => ({ + data: undefined, + isLoading: true, + isError: false, + error: null, + })), +})) + +describe('Gripper IsHoldingSomething', () => { + it('renders with "Empty" status when not holding', () => { + vi.mocked(createResourceQuery).mockReturnValue({ + data: false, + isLoading: false, + isError: false, + error: null, + } as never) + + render(Subject, { + props: { partID: 'test-part', resourceName: 'test-gripper' }, + }) + + expect(screen.getByText('Empty')).toBeInTheDocument() + }) + + it('renders with "Holding" status when holding something', () => { + vi.mocked(createResourceQuery).mockReturnValue({ + data: true, + isLoading: false, + isError: false, + error: null, + } as never) + + render(Subject, { + props: { partID: 'test-part', resourceName: 'test-gripper' }, + }) + + expect(screen.getByText('Holding')).toBeInTheDocument() + }) + + it('creates a resource query for isHoldingSomething with polling', () => { + vi.mocked(createResourceQuery).mockReturnValue({ + data: false, + isLoading: false, + isError: false, + error: null, + } as never) + + render(Subject, { + props: { partID: 'test-part', resourceName: 'test-gripper' }, + }) + + expect(createResourceQuery).toHaveBeenCalledWith(expect.anything(), 'isHoldingSomething', { + refetchInterval: 500, + }) + }) +}) diff --git a/src/lib/components/widgets/gripper/gripper.svelte b/src/lib/components/widgets/gripper/gripper.svelte index 876a4f0..33c7909 100644 --- a/src/lib/components/widgets/gripper/gripper.svelte +++ b/src/lib/components/widgets/gripper/gripper.svelte @@ -8,6 +8,7 @@ import StopButton from '$lib/components/stop-button.svelte' import Grab from './grab.svelte' + import IsHoldingSomething from './is-holding-something.svelte' import Open from './open.svelte' interface Props { @@ -63,6 +64,10 @@ {partID} {resourceName} /> + {/snippet} diff --git a/src/lib/components/widgets/gripper/is-holding-something.svelte b/src/lib/components/widgets/gripper/is-holding-something.svelte new file mode 100644 index 0000000..8556f30 --- /dev/null +++ b/src/lib/components/widgets/gripper/is-holding-something.svelte @@ -0,0 +1,42 @@ + + + + + + + From 3f8110cb25b7c1a36149b5ff987d8eee874e4e4e Mon Sep 17 00:00:00 2001 From: Devin C Date: Thu, 14 May 2026 15:56:18 -0400 Subject: [PATCH 2/4] cleanup and fixes --- .changeset/bumpy-sheep-sneeze.md | 5 +++++ .changeset/gripper-is-holding-something.md | 5 ----- .changeset/violet-pets-sleep.md | 5 +++++ src/lib/client-map.ts | 2 +- .../widgets/do-command/do-command.svelte | 3 ++- .../gripper/is-holding-something.svelte | 20 ++++++++++++++----- src/lib/get-resource-api.ts | 4 ++++ src/lib/get-resource-key.ts | 5 +++++ src/lib/index.ts | 3 ++- src/lib/resource.ts | 6 +----- src/routes/widgets/card-list-item.svelte | 4 ++-- src/routes/widgets/card-list.svelte | 3 ++- src/routes/widgets/resource-list.svelte | 3 ++- 13 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 .changeset/bumpy-sheep-sneeze.md delete mode 100644 .changeset/gripper-is-holding-something.md create mode 100644 .changeset/violet-pets-sleep.md create mode 100644 src/lib/get-resource-api.ts create mode 100644 src/lib/get-resource-key.ts diff --git a/.changeset/bumpy-sheep-sneeze.md b/.changeset/bumpy-sheep-sneeze.md new file mode 100644 index 0000000..0de4f5b --- /dev/null +++ b/.changeset/bumpy-sheep-sneeze.md @@ -0,0 +1,5 @@ +--- +'@viamrobotics/test-widgets': patch +--- + +Fix circular imports diff --git a/.changeset/gripper-is-holding-something.md b/.changeset/gripper-is-holding-something.md deleted file mode 100644 index 5b4c0ae..0000000 --- a/.changeset/gripper-is-holding-something.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@viamrobotics/test-widgets': minor ---- - -Add `IsHoldingSomething` status display to gripper test widget diff --git a/.changeset/violet-pets-sleep.md b/.changeset/violet-pets-sleep.md new file mode 100644 index 0000000..ec7e1f5 --- /dev/null +++ b/.changeset/violet-pets-sleep.md @@ -0,0 +1,5 @@ +--- +'@viamrobotics/test-widgets': minor +--- + +Add `IsHoldingSomething` widget diff --git a/src/lib/client-map.ts b/src/lib/client-map.ts index e8c0f57..770be08 100644 --- a/src/lib/client-map.ts +++ b/src/lib/client-map.ts @@ -31,7 +31,7 @@ import { WorldStateStoreClient, } from '@viamrobotics/sdk' -import { getResourceAPI } from './resource.ts' +import { getResourceAPI } from './get-resource-api.ts' export const clientMap = { 'rdk:component:arm': ArmClient, diff --git a/src/lib/components/widgets/do-command/do-command.svelte b/src/lib/components/widgets/do-command/do-command.svelte index e9f1ddc..91840b6 100644 --- a/src/lib/components/widgets/do-command/do-command.svelte +++ b/src/lib/components/widgets/do-command/do-command.svelte @@ -6,7 +6,8 @@ import { PersistedState } from 'runed' import { supportsDoCommand } from '$lib/client-map' - import { getResourceAPI, getResourceKey } from '$lib/resource' + import { getResourceAPI } from '$lib/get-resource-api' + import { getResourceKey } from '$lib/get-resource-key' import ErrorDisplay from '../../error.svelte' import { createDoCommandClient } from './create-do-command-client.svelte' diff --git a/src/lib/components/widgets/gripper/is-holding-something.svelte b/src/lib/components/widgets/gripper/is-holding-something.svelte index 8556f30..c167b44 100644 --- a/src/lib/components/widgets/gripper/is-holding-something.svelte +++ b/src/lib/components/widgets/gripper/is-holding-something.svelte @@ -6,6 +6,9 @@ import Query from '$lib/components/query.svelte' import StatusPill from '$lib/components/status-pill.svelte' + import ClosedGripperSvg from './closed-gripper-svg.svelte' + import OpenGripperSvg from './open-gripper-svg.svelte' + interface Props { partID: string resourceName: string @@ -33,10 +36,17 @@ {query} contentCx="h-5" > - +
+ {#if query.data} + + {:else} + + {/if} + +
diff --git a/src/lib/get-resource-api.ts b/src/lib/get-resource-api.ts new file mode 100644 index 0000000..0bf0547 --- /dev/null +++ b/src/lib/get-resource-api.ts @@ -0,0 +1,4 @@ +import type { ResourceName } from '@viamrobotics/sdk' + +export const getResourceAPI = ({ namespace, type, subtype }: ResourceName) => + `${namespace}:${type}:${subtype}` diff --git a/src/lib/get-resource-key.ts b/src/lib/get-resource-key.ts new file mode 100644 index 0000000..8d221a9 --- /dev/null +++ b/src/lib/get-resource-key.ts @@ -0,0 +1,5 @@ +import type { ResourceName } from '@viamrobotics/sdk' + +import { getResourceAPI } from './get-resource-api.ts' + +export const getResourceKey = (name: ResourceName) => `${getResourceAPI(name)}/${name.name}` diff --git a/src/lib/index.ts b/src/lib/index.ts index b364307..68f40e3 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -6,6 +6,7 @@ export { export { clientForResource } from './client-map' export * from './components' +export { getResourceAPI } from './get-resource-api' export { providePip, usePip } from './pip/context.svelte' -export { getResourceAPI, hasWidget, showResourceWidget, widgetForResource } from './resource' +export { hasWidget, showResourceWidget, widgetForResource } from './resource' diff --git a/src/lib/resource.ts b/src/lib/resource.ts index 3839b42..7c9ef68 100644 --- a/src/lib/resource.ts +++ b/src/lib/resource.ts @@ -28,6 +28,7 @@ import { SwitchWidget, VisionServiceWidget, } from './components' +import { getResourceAPI } from './get-resource-api.ts' export type NamedResourceStatus = ResourceStatus & { name: ResourceName @@ -102,11 +103,6 @@ const resourceMap = 'rdk:service:video': [clientMap['rdk:service:video'], undefined, true], } as const -export const getResourceAPI = ({ namespace, type, subtype }: ResourceName) => - `${namespace}:${type}:${subtype}` - -export const getResourceKey = (name: ResourceName) => `${getResourceAPI(name)}/${name.name}` - // sorts resource names by local/remote -> type -> name (alphabetical) to produce a list like // component a // component z diff --git a/src/routes/widgets/card-list-item.svelte b/src/routes/widgets/card-list-item.svelte index 4fd57a1..1aec5ac 100644 --- a/src/routes/widgets/card-list-item.svelte +++ b/src/routes/widgets/card-list-item.svelte @@ -35,9 +35,9 @@ import ResourceIcon from '$lib/components/resource-icon.svelte' import SectionGroup from '$lib/components/section-group.svelte' import DoCommandWidget from '$lib/components/widgets/do-command/do-command.svelte' + import { getResourceAPI } from '$lib/get-resource-api' + import { getResourceKey } from '$lib/get-resource-key' import { - getResourceAPI, - getResourceKey, type NamedResourceStatus, ResourceStatusText, widgetForResource, diff --git a/src/routes/widgets/card-list.svelte b/src/routes/widgets/card-list.svelte index e47374e..c0deb03 100644 --- a/src/routes/widgets/card-list.svelte +++ b/src/routes/widgets/card-list.svelte @@ -1,6 +1,7 @@