Skip to content

feat(viewer): add a plugin extension API for add-ons - #37

Open
ericgozzi wants to merge 9 commits into
feature/toolbar-schemafrom
feature/extension-api
Open

ericgozzi wants to merge 9 commits into
feature/toolbar-schemafrom
feature/extension-api

Conversation

@ericgozzi

Copy link
Copy Markdown
Collaborator

Summary

Adds a plugin extension API so authoring tools can live in separate packages instead of the core viewer ("compas_threejs should not be a CAD library"). The first add-on built on it is compas_threejs_draw (drawing tools, snapping, drawing planes, material editing), which uses only this public API.

Stacked on #26 (feature/toolbar-schema): plugins and extraToolbarModules work together, so an add-on can put its tools in the toolbar.

API

createViewer(container, { plugins: [plugin] }), where a ViewerPlugin is { id, install(context) } and install may return a cleanup run on dispose. The ViewerExtensionContext it gets is deliberately narrow - the scene, camera, renderer and controls stay internal:

  • addOverlay(object) - plugin-owned objects, rendered but never picked or reset
  • pointerRay, pointerOnPlane, pickObjects, objectBounds, objectVertices - raycasting and reading visible backend objects
  • beginInteraction(handlers) - takes over pointer/keyboard input for a session (picking, gizmo and built-in shortcuts stand down; orbiting keeps working); focuses the canvas
  • send(message) - messages to the backend over the viewer's transport
  • selection / onSelectionChange, getMaterial / setMaterial
  • setTransformSnap({ grid, angle }) - snaps gizmo edits; on release, faces that moved land exactly on the grid (the correction from feature/bidirectional-sync's grid snap)
  • viewportSize, onResize, requestRender, onDispose

Also: examples/embedded_extension_plugin.html, a README section, and the package smoke test typechecks a consumer plugin.

Included from other PRs

This branch also carries two changes that are opened on their own against main, because the draw add-on needs them - they drop out of this diff once those merge:

Test plan

  • tests/viewer_extensions.test.ts covers the API (install order, duplicate ids, cleanup order and errors, overlays, rays, picking, bounds, vertices, interaction handoff and interruption, focus, send, selection, materials); tests/viewer_lifecycle.test.ts covers setTransformSnap and the move/scale correction.
  • prettier, lint, typecheck, test (58), build:app, build:library pass; test:package passes with fix(ci): pass shell: true so test:package can spawn npm.cmd on Windows #31's Windows fix applied locally.
  • Exercised end to end with compas_threejs_draw and a running compas_threejs App (drawing, snapping, gizmo snap, material edits).

🤖 Generated with Claude Code

ericgozzi and others added 9 commits September 23, 2026 10:18
Adds CompasViewerOptions.plugins and a narrow ViewerExtensionContext so
authoring tools can live in separate packages: an overlay layer that is
never picked or reset, pointer rays and plane/object raycasts, object
bounds, resize/dispose hooks, and beginInteraction() to take over pointer
and keyboard input for a session. The scene, camera, renderer and
controls stay internal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A session started from a button outside the viewer left focus on that
button, so keystrokes such as Escape never reached the session's
onKeyDown until the user clicked the canvas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd edit materials

Adds send(), selection(), onSelectionChange(), getMaterial() and
setMaterial() to ViewerExtensionContext, so add-ons can create backend
objects (create_geometry) and host the material editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A picked object wears the pick highlight, with its own material parked
until deselection. A material update for it replaced the highlight but
left the stale parked material, so deselecting put the old color back
until the object was rebuilt (e.g. by moving it). Updates now replace the
parked material, and a local edit shows the real material at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds setTransformSnap({ grid, angle }) to ViewerExtensionContext. Translate
and scale snap while dragging, and on release the object's bounding-box
faces that moved land exactly on the grid (the correction from
feature/bidirectional-sync's grid snap). Rotate snaps to the angle step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Polygon renders as a filled face triangulated in its own plane (so
concave shapes fill correctly), facing up when horizontal, with its
outline. Arc renders as a line sampled between its start and end angles.
Both move from "deferred" to "included" in the support matrix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…jects

Adds objectVertices() to ViewerExtensionContext: each visible backend
object's vertices with its kind - points, line (in drawing order) or mesh
(deduplicated, at most 2000) - so add-ons can snap to real endpoints and
corners rather than bounding boxes. Also clarifies that keyboard shortcuts
on the canvas are fine; only pointer takeover goes through
beginInteraction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant