Skip to content

app-16292: small component tweaks to support per-entity pose mutators#737

Open
DTCurrie wants to merge 74 commits into
mainfrom
app-16292/3-component-tweaks
Open

app-16292: small component tweaks to support per-entity pose mutators#737
DTCurrie wants to merge 74 commits into
mainfrom
app-16292/3-component-tweaks

Conversation

@DTCurrie
Copy link
Copy Markdown
Member

@DTCurrie DTCurrie commented Jun 1, 2026

Small entity-rendering, raycasting, and UI primitive changes that unblock the Gizmos plugin in #738.

Note

Use this PR preview to view all of the changes from this stack: https://viamrobotics.github.io/visualization/pr-preview/pr-739/snapshot

Stack

  1. Shared ECS traits and helpers (app-16292: shared ECS traits and helpers for entity pose plugins #735)
  2. Details refactor (app-16292: refactor Details panel into focused subcomponents #736)
  3. This PR: Component tweaks
  4. Gizmos plugin (app-16292: add Gizmos plugin #738)
  5. Gizmos plugin docs (app-16292: add docs page for the Gizmos plugin #739)

Frontend

  • src/lib/components/Entities/LineDots.svelte: tracks the instance IDs returned by addInstance per effect run instead of assuming a sequential 0..N-1 range. Fixes Invalid instanceId errors when positions changes between runs.
  • src/lib/components/Scene.svelte: enables BVH raycasting when settings.interactionMode === 'gizmo', matching the existing 'measure' and 'select' branches.
  • src/lib/components/SelectedTransformControls.svelte: reads ref.position and ref.quaternion directly instead of getWorldPosition / getWorldQuaternion. Frame-style renderers set matrixAutoUpdate = false and re-compose their local matrix from worldMatrix on every flush, so the world getters were returning the pre-drag transform and the entity Matrix trait never updated.
  • src/lib/components/overlay/Popover.svelte: extends the trigger snippet signature with { isOpen } and the children snippet with { close } so consumers can reflect open state in their trigger and dismiss the popover programmatically.
  • src/lib/components/overlay/dashboard/Button.svelte: adds the shapes (lucide) icon and a disableTooltip prop for buttons whose persistent active state already conveys what the tool is.

Testing

Ran pnpm check and pnpm test. Drag-rotated a frame entity with TransformControls in the dev app to confirm the SelectedTransformControls change actually mutates the entity Matrix after the fix.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 1, 2026

🦋 Changeset detected

Latest commit: f376b4d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@viamrobotics/motion-tools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

DTCurrie added 2 commits June 1, 2026 17:11
Foundation for the upcoming Gizmos plugin. No callers yet; these
symbols are consumed by follow-up PRs in the stack.

* writeMatrix(entity, patch): patch a Pose onto an entity's Matrix
  trait via createPose/matrixToPose/poseToMatrix, then dirty the
  trait. Used by the refactored Details panel and the Gizmos plugin.
* CustomDetails trait: marker so the Details panel suppresses its
  default frame/pose UI for entities that render their own.
* useMouseRaycaster firstHitOnly option (forwarded to three-mesh-bvh).
* 'gizmo' value added to Settings.interactionMode.
* Re-exports of DEFAULT_LINE_WIDTH and ARROW_LENGTH for downstream
  consumers.
DTCurrie added 2 commits June 1, 2026 17:13
The Details overlay component had grown to handle four entirely
different entity shapes (color, geometry, line, matrix) inline.
Split each into its own component under details/ so callers and
reviewers can reason about one entity type at a time.

* ColorDetails.svelte, GeometryDetails.svelte, MatrixDetails.svelte
  and LineDetails/LineDetails.svelte are now individual focused
  components.
* linePositions.ts extracts the line-position derivation with its
  own spec.
* MatrixDetails uses writeMatrix() added in the prior PR.
* vitest-setup-client.ts stubs @threlte/extras Portal / PortalTarget
  / HTML so the refactored panel can render in unit tests.

Behavior should be unchanged; existing Details.svelte.spec.ts
continues to pass with the new mocks.
DTCurrie added 2 commits June 1, 2026 17:14
Forward-looking changes to keep the next PR (Gizmos plugin) small.

* Entities/Line.svelte, Entities/Mesh.svelte: default opacity 1
  instead of 0.7. The 0.7 default predated transparent materials
  being opt-in; entities that want transparency now set
  `opacity.current` explicitly.
* Entities/LineDots.svelte: track the IDs returned by `addInstance`
  on each effect run rather than assuming sequential 0..N-1. Fixes
  "Invalid instanceId" when positions change.
* Scene.svelte: enable BVH raycasting when `interactionMode` is
  'gizmo', matching the existing 'measure' / 'select' branches.
* SelectedTransformControls.svelte: read `ref.position` /
  `ref.quaternion` directly instead of `getWorldPosition` /
  `getWorldQuaternion`. Frame-style renderers with
  `matrixAutoUpdate = false` had stale world matrices, so the gizmo
  visuals moved but the entity Matrix never updated.
* Popover.svelte: extend the trigger / children snippets with
  `{ isOpen }` and `{ close }` props so consumers can react to open
  state and close programmatically.
* dashboard/Button.svelte: add the `shapes` icon (lucide) and a
  `disableTooltip` prop so tools with persistent active state can
  suppress the noisy hover tooltip.
@DTCurrie DTCurrie force-pushed the app-16292/3-component-tweaks branch from cddf5ac to 030b969 Compare June 1, 2026 21:14
@DTCurrie

This comment was marked as outdated.

@claude

This comment was marked as outdated.

Comment thread src/lib/components/SelectedTransformControls.svelte Outdated
Comment thread src/lib/components/overlay/Popover.svelte
Comment thread src/lib/components/overlay/dashboard/Button.svelte
github-actions Bot and others added 2 commits June 1, 2026 21:27
- Move writeMatrix below updateGeometryTrait for consistent file layout
- Add JSDoc to writeMatrix documenting round-trip semantics and no-op behavior
- Filter undefined values from patch before Object.assign to prevent NaN in matrix
- Add comment clarifying firstHitOnly affects both hover and click handlers
- Strengthen orientation test with oX/oY/oZ assertions

Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
@DTCurrie

This comment was marked as outdated.

@claude

This comment was marked as outdated.

github-actions Bot and others added 8 commits June 2, 2026 14:14
- SelectedTransformControls: expand onChange comment to document the
  flat-scene-hierarchy contract (non-FramesAPI entities must mount under
  the scene root for local ≈ world; points to stageFrameTransform as the
  parent-inverse reference if that ever changes)
- Button.svelte.spec.ts: add test verifying aria/role contract is
  preserved when disableTooltip=true and active=true

Popover children type: Snippet<[]> (implicit children) is assignable to
Snippet<[{ close: () => void }]> under TypeScript callback-parameter
compatibility — pnpm check is clean, no change needed.

Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
- linePositions.ts: add bounds check to writeLinePosition; return copy unchanged on out-of-bounds index
- Details.svelte: await navigator.clipboard.writeText() and wrap in try/catch to handle permission denial
- MatrixDetails.svelte: add comments explaining useQuery vs configFrames and unconditional rendering
- linePositions.spec.ts: add edge cases for out-of-bounds write and single-point removal guard
- Add component tests for ColorDetails, GeometryDetails, MatrixDetails, and LineDetails

Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
…stHitOnly effect split

- Add early-exit guard in writeMatrix when filtered patch is empty to avoid
  spurious entity.changed(Matrix) notifications
- Add tests for undefined field filtering and empty/all-undefined patch paths
- Split firstHitOnly assignment into its own $effect so toggling it doesn't
  tear down and re-attach event listeners

Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- Details.svelte handleParentChange: add invalidate() after setParent() so
  reparenting non-frame entities triggers a Three.js re-render
- ColorDetails, GeometryDetails, LineDetails: add useThrelte() and call
  invalidate() after every ECS trait mutation that affects rendering
- Details.svelte: revert panel width w-80 → w-70 (accidental experiment change)
- LineDetails.svelte.spec.ts: fix pre-existing prettier formatting

Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
@DTCurrie DTCurrie force-pushed the app-16292/3-component-tweaks branch from c73bc1c to 70a5b6f Compare June 2, 2026 21:59
@DTCurrie
Copy link
Copy Markdown
Member Author

DTCurrie commented Jun 2, 2026

@claude final review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jun 2, 2026

Claude finished @DTCurrie's task in 2m 45s —— View job


Final Review

  • Gather context — reading changed files and diff
  • Review each changed file
  • Check Popover snippet compatibility with existing callers
  • Verify interactionMode type includes 'gizmo'
  • Summarize findings

Overall: this is ready to merge. All five logic changes are correct, the comments are accurate and appropriately detailed for non-obvious invariants, and the tests adequately cover the new disableTooltip prop. No blocking issues found.

Per-file notes

LineDots.svelte — fix is clean and the approach is correct. The instances array is only populated (and the cleanup closure only registered) when positions is truthy, so the early-return guard on cleanup is no longer needed. The closure captures instances by reference so it sees the fully-populated array at cleanup time. No issues.

Scene.svelte'gizmo' is already a valid interactionMode literal in useSettings.svelte.ts:10, so the addition is type-safe. Simple, correct.

SelectedTransformControls.svelte — fix is correct. The matrixToPose → vector3ToPose/quaternionToPose two-step correctly preserves the unchanged component (rotation when translating, position when rotating). The removed ref.quaternion.copy(quaternion) was a redundant workaround artifact. The NOTE: block directly in the comment addresses the prior review concern about the flat-hierarchy assumption. The unused Quaternion and Vector3 imports are correctly pruned.

Popover.svelte — the API extension is backwards-compatible. Existing callers (MeasureTool.svelte, SelectionTool.svelte) destructure only triggerProps from the trigger snippet and pass implicit children content — TypeScript allows a function with fewer declared parameters to satisfy a function type with more (parameter list is covariant in cardinality), so svelte-check will not flag these callers. Confirmed: pnpm check output shows no new errors in those files, only the pre-existing missing proto-generated-file errors unrelated to this PR.

Button.sveltestate={disableTooltip ? 'invisible' : undefined} is the idiomatic prime-core Tooltip API for suppressing the tooltip. Shapes import and icon branch are straightforward. The JSDoc is clear.

Button.svelte.spec.ts — both new cases correctly assert that disableTooltip and active are orthogonal. Tests follow the existing pattern exactly.

Minor nit (non-blocking)

In Popover.svelte, close could optionally be $derived so it stays referentially stable if api ever changes identity:

const close = $derived(() => api.setOpen(false))

In practice this doesn't matter since the close callback only reads api at call time (not at closure time), but it would match the reactive pattern used for api itself. Not worth a round-trip change.

DTCurrie and others added 10 commits June 3, 2026 10:05
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
- vitest-setup-client.ts: `invalidate` was nested inside `scene` in the
  useThrelte mock instead of at the top level; `const { invalidate } =
  useThrelte()` returned undefined in all tests.
- linePositions.ts: `removeLinePosition` had a duplicate
  `if (source.length <= 6) return source` guard (dead code after the
  bounds-check fix in a prior commit).

Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Devin T. Currie <DTCurrie@users.noreply.github.com>
…otion-tools into app-16292/3-component-tweaks
…otion-tools into app-16292/3-component-tweaks
Base automatically changed from app-16292/2-details-refactor to main June 3, 2026 21:07
@DTCurrie DTCurrie force-pushed the app-16292/3-component-tweaks branch from ee8ba77 to fec6d11 Compare June 4, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants