Skip to content

fix(editor): store scans locally without host uploads - #726

Open
anisayakmitra-in wants to merge 1 commit into
pascalorg:mainfrom
anisayakmitra-in:fix/local-scan-upload
Open

fix(editor): store scans locally without host uploads#726
anisayakmitra-in wants to merge 1 commit into
pascalorg:mainfrom
anisayakmitra-in:fix/local-scan-upload

Conversation

@anisayakmitra-in

@anisayakmitra-in anisayakmitra-in commented Aug 28, 2026

Copy link
Copy Markdown

Summary

Fixes the self-hosted scan upload path, which currently accepts a file and then silently does nothing when a host upload callback is absent.

Details

  • Stores local GLB and glTF uploads through the existing IndexedDB asset storage.
  • Creates and selects the corresponding ScanNode on the active level.
  • Retains the existing host upload flow when a callback is supplied.
  • Adds coverage for local scan asset creation.

Fixes #645

Validation

  • bun test packages/editor/src/lib/local-guide-image.test.ts
  • bun run --filter @pascal-app/editor check-types
  • bun run --filter @pascal-app/editor test (760 pass)
  • biome check on the changed implementation files

Note

Low Risk
Scoped editor upload branching and local asset creation; host upload path is preserved when a callback is supplied.

Overview
Fixes scan uploads doing nothing in setups that do not provide a host onUploadAsset callback (e.g. self-hosted editor). GLB/glTF files now follow the same local path as guide images: persist via saveAsset (IndexedDB), create a ScanNode on the level, select it, and drive the existing upload UI through success/error states.

Adds createLocalScan (plus shared filename helpers) in local-guide-image.ts, with a unit test. When a host callback is present, behavior is unchanged except the scan handoff calls onUploadAsset directly after the project-id guard.

Reviewed by Cursor Bugbot for commit 0a6549d. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0a6549d. Configure here.

setSelectedReferenceId(scan.id)
setSelection({ selectedIds: [], zoneId: null })
useUploadStore.getState().setResult(levelId, url)
window.setTimeout(() => useUploadStore.getState().clearUpload(levelId), 600)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uploaded scan can stay hidden

Low Severity

The new local scan success path selects the created ScanNode but never calls setShowScans(true). The guide-image path in the same handler does call setShowGuides(true). If showScans is off (a persisted per-project preference), the scan appears in the tree while ScanRenderer keeps the mesh unmounted, so the upload can look like it failed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0a6549d. Configure here.

@Aymericr Aymericr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — it's exactly the shape I sketched in #645: mirror local-guide-image.ts, persist through saveAsset, and let the existing node side do the rendering. I verified the whole chain: asset:// is on the AssetUrl allowlist, ScanRenderer resolves it through useAssetUrl/loadAssetUrl so it survives reload, the 200 MB cap applies before your branch, and the hosted app always passes onUploadAsset so the host path is untouched (tightening onUploadAsset?.() to onUploadAsset() after the guard is right). The test is a faithful mirror of the lib's shape too.

One change before merge, and Bugbot is right about it: the success path needs setShowScans(true). The guide branch right above yours calls setShowGuides(true) before selecting; showScans is a persisted per-project preference (use-viewer.ts), and packages/nodes/src/scan/renderer.tsx computes visible = showScans && node.visible — so with scans toggled off your upload succeeds invisibly, which is the exact failure mode this PR exists to kill. Grab setShowScans next to the setShowGuides subscription in site-panel/index.tsx and call it in the success path.

Two things I noticed and am explicitly not asking you to fix here:

  • Deleting an asset:// scan orphans the File in IndexedDB (handleDelete only routes http(s) URLs to onDeleteAsset). Guide images have the same gap today, so this stays parity — but scans are up to 200 MB, so I'll file a follow-up for local asset cleanup.
  • mock.module('@pascal-app/core', …) is process-global in bun test; the spread keeps the blast radius to saveAsset and nothing else in the package calls it under test, so it's fine as is.

Push the setShowScans fix and this is good to go. Appreciate you carrying this from the issue write-up through to the PR — the workaround you documented there has a proper home now. (I've approved the CI run.)

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.

Self-hosted editor: scan upload button is a silent no-op (onUploadAsset never wired locally)

2 participants