Skip to content

feat!: built-in point cloud viewer (0.2.0) - #8

Merged
cmaranes merged 12 commits into
mainfrom
feat/builtin-viewer
Oct 22, 2026
Merged

cmaranes merged 12 commits into
mainfrom
feat/builtin-viewer

Conversation

@cmaranes

@cmaranes cmaranes commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Implements the 0.2.0 plan: the extension now shows point clouds in its own viewer and no longer depends on tatsy.vscode-3d-preview. That also makes it possible to publish to Open VSX.

What changes for users

  • Built-in viewer (webview + three.js): orbit controls, point size, RGB / height / solid coloring, Z-up or Y-up, axes and grid, and point count and bounding box. Viewing the same expression again updates its tab. The last toolbar choices become the defaults for new viewers.
  • pythonPlyPreview.maxPoints (default 1,000,000): larger clouds are randomly subsampled in Python before export, with a fixed seed so you get the same points every time. The viewer says when a cloud was subsampled.
  • Docker / remote debugging: if the debuggee can't write to the workspace path, the exporter writes to its temp folder, and the extension fetches the file through the debugger in 3 MB base64 chunks. It uses the DAP clipboard context, the only one where debugpy doesn't truncate strings (pydevd_comm.py). Every chunk's size is checked, so a truncated transfer fails instead of showing a partial cloud.
  • .ply custom editor with priority: "option": available via Reopen Editor With..., and it never replaces another default.
  • pythonPlyPreview.viewer: "default" restores the old behaviour, so people who prefer 3D Preview can keep it.

Implementation notes

  • src/webview/viewer.ts is bundled by esbuild.mjs into media/viewer.js, which is gitignored and built by npm run compile / vscode:prepublish. It's type-checked by its own src/webview/tsconfig.json, which uses DOM libs. The extension itself is still compiled by tsc.
  • src/viewerProtocol.ts types the messages exchanged in both directions. The webview reports loaded after parsing and before rendering, so the tests don't need WebGL.
  • activate() returns { onViewerMessage } so the integration tests can watch the viewer.
  • The vsix grows from 47 KB to 198 KB.

Commits

feat(export) subsampling → build esbuild/three → feat(viewer) viewer + settings + .ply editor → feat debugger transfer → test → feat! drop the dependency, docs, 0.2.0 → fix(viewer) point to the default setting when WebGL is unavailable

Test plan

  • python python/test_ply_export.py: new tests for subsampling, deterministic subsets, and the temp-folder fallback
  • npm test: 13/13 locally (Windows). New tests:
    • viewer loads lidar (500 points, colours) in a webview tab
    • panel reuse
    • maxPoints = 100 → 100 of 1000
    • forced debugger transfer of a 300k-point cloud (~4.5 MB, 2 chunks, ~1 s)
    • the default setting opens the .ply file instead
    • Reopen Editor With loads a .ply
  • npm run lint: 0 problems; the vsix contains only out/, media/, python/ply_export.py, the icon and the docs
  • CI green on Linux, Windows and macOS (no debugger retries). WebGL is blocklisted on the Linux runner: the viewer shows its fallback message and the tests still pass, because they check the parsed point count
  • Manual check (F5 → test-fixtures/workspace): rotate, pan and zoom, each colour mode, Z/Y up, point size, light and dark themes, a 1M-point array, and a 5M array capped by maxPoints
  • Not covered: attaching to a real Docker container. The test forces the debugger transfer; the exporter's temp-folder fallback is covered by the Python tests.

Before merging

  • images/viewer.gif recorded and added; the old demo GIFs and MP4s are removed

After merging

  • Open VSX: create the cmaranes namespace and add an OVSX_PAT secret to the marketplace environment; release.yml already publishes there when the secret exists.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y

cmaranes and others added 12 commits October 22, 2026 19:43
export() takes max_points (passed as __vp_max_points); larger clouds keep a
random subset with a fixed seed, so viewing the same cloud twice shows the
same points. The result string reports the original count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
Adds three.js and esbuild for the upcoming built-in viewer. The extension is
still compiled by tsc; esbuild.mjs bundles src/webview/viewer.ts (browser
code, type-checked by its own tsconfig) into media/viewer.js. The default
build task now runs both watchers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
Previews now open in a webview panel instead of handing the .ply file to the
3D Preview extension. The viewer has orbit controls, point size, RGB / height
/ solid coloring, a Z-up or Y-up toggle, axes and grid, and shows the point
count and bounding box. Viewing the same expression again reuses its panel.
Toolbar choices become the defaults for new panels.

- pythonPlyPreview.viewer: "default" restores the old behavior (open the
  file with whatever editor handles .ply)
- pythonPlyPreview.maxPoints (default 1,000,000) caps exported clouds; the
  viewer says when a cloud was subsampled
- .ply files can be opened in the viewer with Reopen Editor With... (the
  custom editor has priority "option", so it never replaces another default)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
When the debuggee runs in Docker or on another machine, the extension's
workspace path doesn't exist there. The exporter now writes to the debuggee's
temp folder instead and reports the path, and the extension reads the file
through the debugger in base64 chunks. It uses the DAP 'clipboard' context,
the only one in which debugpy doesn't truncate long strings, and checks the
size of every chunk. The temp file is removed afterwards.

The same transfer is used if the local read fails with ENOENT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
- the viewer reports the loaded point count (and the pre-subsampling count)
  back to the extension; activate() exposes these messages to the tests
- new tests: panel reuse, maxPoints, the debugger transfer of a 300k-point
  cloud (several chunks), the "default" viewer setting and the .ply editor
- the test instance no longer installs the 3D Preview extension

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
The built-in viewer replaces tatsy.vscode-3d-preview, so installing this
extension no longer pulls in a second one, and it can be published to Open
VSX. README and changelog describe the viewer, the settings and the debugger
transfer; version 0.2.0.

BREAKING CHANGE: point clouds open in the built-in viewer instead of the
default .ply editor. Set pythonPlyPreview.viewer to "default" to keep using
3D Preview, which is no longer installed automatically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
WebGL is blocklisted on the Linux CI runners, and can be on GPU-less VMs and
remote desktops too. The message now points to pythonPlyPreview.viewer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
- lead with what the extension does and a feature list
- a NumPy-only example script (plane, colored spiral, LiDAR-like ring, a
  2M-point cloud that gets subsampled); torch and Open3D are optional
- release notes keep the last two versions and link to the changelog
- the demo GIF slot points to images/viewer.gif, to be recorded

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
demo.py is the "Try it" script, so it can be run (and the demo GIF recorded)
in the Extension Development Host before 0.2.0 is released.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
images/viewer.gif shows viewing variables from the Variables panel, two
viewers side by side, RGB coloring and a LiDAR-like ring. The old demos
showed the 3D Preview flow and are no longer referenced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
The GitHub Release is now created right after the Marketplace publish, and an
Open VSX failure (e.g. a missing namespace or an expired token) is reported
without failing the run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JBDmV8VxTwVxxpuC8NMG8y
@cmaranes
cmaranes merged commit 415355f into main Oct 22, 2026
3 checks passed
@cmaranes
cmaranes deleted the feat/builtin-viewer branch October 22, 2026 19:36
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