Skip to content

editor: light WebGPU preview and /editor scene redirect - #570

Merged
Aymericr merged 6 commits into
pascalorg:mainfrom
ActArtech:feat/editor-light-preview
Aug 4, 2026
Merged

editor: light WebGPU preview and /editor scene redirect#570
Aymericr merged 6 commits into
pascalorg:mainfrom
ActArtech:feat/editor-light-preview

Conversation

@ActArtech

@ActArtech ActArtech commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Improves the standalone apps/editor local/MCP preview experience:

  1. Route compatibility — redirect /editor/:id to /scene/:id so MCP editorUrl links open saved scenes instead of 404.
  2. Light preview — scene list opens with ?disable=postFx,outline (existing viewer flags) and forces solid shading when that path is active; scene page exposes a Light preview control.
  3. Clearer home banner — home is a blank canvas; saved scenes live under Scenes (wording fix only).

No viewer package API changes; uses existing post-processing disable query flags.

How to test

  1. bun run --cwd apps/editor dev (or project equivalent) on port 3002 with a local scene store.
  2. Open /editor/<existing-scene-id> — expect redirect to /scene/<id> and the scene loads.
  3. Open /scenes — each card links with light-preview query; canvas should stay responsive (post-FX off).
  4. On a scene page, click Light preview — URL gains disable flags; shading goes solid.
  5. Home / still shows blank editor with link to saved scenes.

Screenshots / screen recording

N/A for core logic (routing + existing viewer flags). Optional: short clip of /editor/id redirect if desired.

Checklist

  • I've tested this locally with the editor dev server
  • My code follows the existing code style
  • I've updated relevant documentation (if applicable)
  • This PR targets the main branch

Note

Low Risk
Changes are limited to routing, copy, and an optional rendering flag wired through existing viewer disablePostFx support; no auth or persistence logic is touched.

Overview
Adds a non-permanent redirect from /editor/:id to /scene/:id so MCP/host editorUrl links open saved scenes in the OSS app instead of 404ing.

On saved-scene pages, Light preview reads ?disable=postFx (including after client-side navigation) and passes disablePostFx into @pascal-app/editor, which forwards it to the viewer so the post-processing pipeline is skipped for a lighter GPU path. A header control toggles that query param on/off.

The home local-editor banner copy is updated to clarify that / is a blank canvas and saved work lives under Scenes, with minor layout tweaks for wrapping on small screens.

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

Comment thread apps/editor/components/scene-loader.tsx Outdated
Comment thread apps/editor/components/scene-loader.tsx Outdated
@ActArtech

Copy link
Copy Markdown
Contributor Author

Addressed Bugbot finding L5 in the follow-up commit:

  • L5 - light preview now re-applies solid shading when search params change (useSearchParams dependency), so client navigation to ?disable=postFx on the same scene updates correctly.

Same pitfall is noted as L5 in packages/mcp/docs/layout-clearance-error-log.md (layout clearance PR) for future editor/MCP work.

@ActArtech

Copy link
Copy Markdown
Contributor Author

Addressed the latest Bugbot finding (N3 / L8):

  • When light-preview query flags are cleared, shading restores to rendered so the viewer is not stuck on the solid/light path after client navigation.

Previously we only set solid when flags were present.

Comment thread apps/editor/components/scene-loader.tsx Outdated
Comment thread apps/editor/components/scene-loader.tsx
@ActArtech

Copy link
Copy Markdown
Contributor Author

Addressed the latest Bugbot findings on light preview:

  • High - disablePostFx is now threaded SceneLoader → Editor → Viewer, so light preview actually disposes/skips the post-FX pipeline (not only setShading('solid'), which is a no-op when already solid).
  • Medium - Light preview is a button that always re-applies solid shading (lightApplyTick), even when the query is already ?disable=postFx,outline after the user changed toolbar shading.

Query flags still toggle disablePostFx; clearing them restores rendered + post-FX.

Comment thread packages/editor/src/components/editor/index.tsx
@ActArtech

Copy link
Copy Markdown
Contributor Author

Addressed the latest Bugbot finding:

  • Preview mode ignores disablePostFx - the version/toolbar preview Viewer now receives disablePostFx the same as the main editor canvas, so light-preview scenes do not re-enable post-FX when opening Preview.

@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 2 potential issues.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit a035819. Configure here.

Comment thread apps/editor/app/scenes/page.tsx Outdated
Comment thread apps/editor/components/scene-loader.tsx Outdated
alaa541 and others added 6 commits August 4, 2026 15:53
Redirect MCP-style /editor/:id URLs to /scene/:id, open scenes with a
lighter post-FX path for stable local WebGPU, and clarify the home
blank-canvas banner vs the saved scenes list.
SceneLoader stayed mounted on same-route Light preview navigation, so
shading never updated. Depend on useSearchParams so solid mode applies.
When ?disable=postFx is removed, set shading back to rendered so the
viewer is not stuck on the solid/light path after client navigation.
Pass disablePostFx from SceneLoader into Viewer so post-FX is actually
skipped (not only solid shading). Light preview button re-applies solid
shading even when the query is already active.
Toolbar/version preview mounts a second Viewer; without disablePostFx,
light-preview scenes re-enable post-FX and can reintroduce GPU load.
Every scene card linked to `?disable=postFx,outline`, so all saved scenes
opened with the post-FX pipeline off and no way back — the button only
pushed the query when it was absent. Links go back to `/scene/<id>` and the
button toggles the flag both ways.

Drop the `setShading` effect. `disablePostFx` already skips the whole
pipeline, and `shading` is a persisted user preference keyed by render
context: forcing it wrote 'solid' (or 'rendered') into `shadingByContext`,
so opening one scene changed the shading of the blank canvas at `/`.

`?disable=outline` is redundant once postFx is off (the outline pass only
exists inside the pipeline), and it costs the selection/hover outlines that
are the only 3D feedback for a single selected node. `safe=1` was an
undocumented second spelling with no other reference in the repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Aymericr
Aymericr force-pushed the feat/editor-light-preview branch from a035819 to 7931a33 Compare August 4, 2026 19:59
@Aymericr

Aymericr commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Both problems here are real, and I verified each from source before touching anything.

The 404 is genuine. editorUrlForScene returns `/editor/${id}` (packages/mcp/src/storage/sqlite-scene-store.ts:150-152), and it feeds every SceneMeta/ProjectStatus the local store emits — save_scene, create_project, project_status, list_scenes. apps/editor/app/ has scene/[id] and scenes/, and no editor/ directory. So every editorUrl an MCP client is handed by the local store 404s. packages/mcp/src/resources/agent-guide.ts:16 even tells agents "Return the final editorUrl from tool output. Do not infer routes" — the one route they're told to trust is the broken one.

The reason the path is /editor/<id> at all is that it matches the hosted app (apps/community/app/editor/[projectId]/, which does exist), and the MCP package is shared. Your redirect is the right call for that reason: it keeps one URL shape across both surfaces instead of forking the store's output per host. Rewrite-vs-redirect came up on #551, which touches the same route — I checked the objection there and it doesn't apply. view-toggles.tsx:136 parses window.location.pathname.split('/editor/')[1], but it's gated on useUploadStore.getState().uploadHandler, and apps/editor never calls registerUploadHandler, so that branch is unreachable here. permanent: false is also the right choice while the OSS route is /scene.

Light preview is worth having, and disablePostFx was the correct mechanism to reach for — I'd add the reason it's necessary rather than just tidier, because it's the strongest argument in this PR and the description doesn't make it: PERF_POST_FX_DISABLED (post-processing.tsx:96) is evaluated once at module load. ?disable=postFx therefore only works on a full page load; after any client-side navigation the flag in the URL is inert. Threading the prop is what makes the flag survive router.push. Worth stating, since it's what separates this from "the URL flag already did this."

I've rebased onto main (Next 16.3 landed in #579) and pushed three fixes, since all of them are small and I'd rather unblock this than round-trip:

1. Light preview was one-way and applied to everything. scenes/page.tsx appended ?disable=postFx,outline to every card, and the button only pushed the query if (!lightPreview) — nothing ever removed it. Net effect: every saved scene opened degraded, permanently, with no path back to full rendering. A diagnostic flag became the default rendering mode for all saved scenes. Cards now link to plain /scene/<id>, and the button toggles both directions with aria-pressed for state.

2. The setShading effect had to go. shading is a persisted user preference, keyed by render context — setShading writes shadingByContext[renderContext] into the viewer-preferences localStorage entry (use-viewer.ts:381-384, :548-551). I ran the store directly: after light preview, persisted shadingByContext is {editor: 'solid'}; after a no-query visit, {editor: 'rendered'}. Since Viewer's init reads shadingByContext[ctx] ?? defaultShading (viewer/index.tsx:492-493) and EDITOR_DEFAULT_RENDER is {shading: 'solid'}, opening one scene silently changes the shading of the blank canvas at /, in a way nothing in the app undoes. It's also unnecessary: disablePostFx already skips the pipeline entirely. The try/catch around it was a hint worth following up — useViewer is a plain zustand store with no mount requirement, so getState() can't throw there; the catch was guarding a condition that doesn't occur.

3. Dropped outline from the query, and safe=1. outlineEnabled is only read inside the pipeline-build effect (post-processing.tsx:335, :527), so once postFx is off the outline pass doesn't exist anyway — disable=outline is a no-op in combination and misleading on its own. It's not free, either: the merged outline pass is what draws selected (white/yellow) and hovered (blue) edges, and for a single selected node it's the only 3D feedback there is — GroupSelectionBox3D needs selectedIds.length > 1, and no renderer varies material by selection. safe=1 was a second undocumented spelling with no other occurrence in the repo.

Gates after the rebase: bun run check clean (it was failing — useExhaustiveDependencies on the lightApplyTick dep, since the effect never read the tick), check-types 9/9, bun run test 2869 pass / 0 fail.

One thing I deliberately did not change, as a note for whoever picks up the perf thread: the underlying complaint is that the pipeline is heavy on local WebGPU, and a manual toggle is a workaround rather than a fix. There is already an auto-fallback — MAX_PIPELINE_RETRIES at post-processing.tsx:115 drops to direct rendering after three failed builds — so the gap is scenes that are slow but not failing. That's #492's territory, and it needs a measurement, not a flag. The toggle is still worth having for exactly the case that motivated you: an escape hatch when the machine can't keep up.

Merging once CI is green. Thanks for chasing the Bugbot findings down as far as you did — the disablePostFx threading in particular was the right response to the "setShading is a no-op when already solid" note.

@Aymericr
Aymericr merged commit 4905255 into pascalorg:main Aug 4, 2026
2 checks passed
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.

3 participants