Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions apps/editor/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,12 @@ export default function Home() {
<div className="relative h-screen w-screen">
{PROJECT_ID === 'local-editor' && (
<div className="pointer-events-none absolute top-3 left-1/2 z-40 -translate-x-1/2">
<div className="pointer-events-auto flex items-center gap-3 rounded-full border border-border/60 bg-background/90 px-4 py-1.5 text-xs shadow-sm backdrop-blur">
<span className="text-muted-foreground">Local editor — scenes are not saved.</span>
<Link className="font-medium text-foreground hover:underline" href="/scenes">
Open recent scenes
</Link>
<span aria-hidden className="text-muted-foreground">
·
<div className="pointer-events-auto flex max-w-[min(92vw,42rem)] flex-wrap items-center justify-center gap-x-3 gap-y-1 rounded-full border border-border/60 bg-background/90 px-4 py-1.5 text-xs shadow-sm backdrop-blur">
<span className="text-muted-foreground">
Blank canvas — saved scenes are under Scenes (not this page).
</span>
<Link className="font-medium text-foreground hover:underline" href="/scenes">
Create new
Open saved scenes
</Link>
</div>
</div>
Expand Down
31 changes: 30 additions & 1 deletion apps/editor/components/scene-loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import {
import { Hammer, Layers } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useRouter, useSearchParams } from 'next/navigation'
import { useCallback, useEffect, useRef, useState } from 'react'
import { cn } from '@/lib/utils'
import { BuildTab } from './build-tab'
import { CommunityViewerToolbarLeft, CommunityViewerToolbarRight } from './viewer-toolbar'

Expand Down Expand Up @@ -92,14 +93,27 @@ function sceneGraphSignature(graph: SceneGraphWithCollections): string {
})
}

/**
* `?disable=postFx` is read at post-processing module load, so it only takes
* effect on a full page load. Reading it here as well lets the flag survive a
* client-side navigation, since `disablePostFx` is a live prop.
*/
function isLightPreviewQuery(searchParams: URLSearchParams): boolean {
const disable = searchParams.get('disable') ?? ''
return disable.split(',').some((p) => p.trim() === 'postFx')
}

export function SceneLoader({ initialScene, meta }: SceneLoaderProps) {
const router = useRouter()
const searchParams = useSearchParams()
const versionRef = useRef(meta.version)
const lastRemoteGraphJsonRef = useRef<string | null>(null)
const suppressRemoteSaveUntilRef = useRef(0)
const [conflict, setConflict] = useState(false)
const [saveError, setSaveError] = useState<string | null>(null)

const lightPreview = isLightPreviewQuery(searchParams)

const handleLoad = useCallback(async () => initialScene, [initialScene])

const handleSave = useCallback(
Expand Down Expand Up @@ -224,6 +238,20 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) {
</div>
)}
<div className="pointer-events-none absolute top-4 right-4 z-40 flex items-center gap-2">
<button
aria-pressed={lightPreview}
className={cn(
'pointer-events-auto rounded-md border border-border px-3 py-1.5 font-medium text-xs shadow-sm backdrop-blur',
lightPreview ? 'bg-accent' : 'bg-background/90 hover:bg-accent/40',
)}
onClick={() =>
router.push(lightPreview ? `/scene/${meta.id}` : `/scene/${meta.id}?disable=postFx`)
}
title="Skip the post-processing pipeline — lighter on the GPU, no ambient occlusion or selection outlines"
type="button"
>
Light preview
Comment thread
cursor[bot] marked this conversation as resolved.
</button>
<Link
className="pointer-events-auto rounded-md border border-border bg-background/90 px-3 py-1.5 font-medium text-xs shadow-sm backdrop-blur hover:bg-accent/40"
href="/scenes"
Expand All @@ -232,6 +260,7 @@ export function SceneLoader({ initialScene, meta }: SceneLoaderProps) {
</Link>
</div>
<Editor
disablePostFx={lightPreview}
layoutVersion="v2"
onLoad={handleLoad}
onSave={handleSave}
Expand Down
11 changes: 11 additions & 0 deletions apps/editor/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ const nextConfig: NextConfig = {
typescript: {
ignoreBuildErrors: true,
},
// MCP / package metadata returns `/editor/<id>` (hosted route). This open-source
// app serves saved scenes at `/scene/<id>` — redirect so links and bookmarks work.
async redirects() {
return [
{
source: '/editor/:id',
destination: '/scene/:id',
permanent: false,
},
]
},
transpilePackages: [
'three',
'@pascal-app/viewer',
Expand Down
13 changes: 13 additions & 0 deletions packages/editor/src/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ export interface EditorProps {
// Thumbnail
onThumbnailCapture?: (blob: Blob, cameraData: SnapshotCameraData) => void

/**
* When true, skip the viewer post-FX pipeline (same as `?disable=postFx`).
* Hosts use this for a stable local "light preview" without relying only on
* module-load URL flags or shading toggles.
*/
disablePostFx?: boolean

// Version preview overlays (rendered by host app)
sidebarOverlay?: ReactNode
viewerBanner?: ReactNode
Expand Down Expand Up @@ -961,6 +968,7 @@ const ViewerCanvas = memo(function ViewerCanvas({
onThumbnailCapture,
viewerSceneSlot,
floorplanSceneSlot,
disablePostFx = false,
}: {
isVersionPreviewMode: boolean
isLoading: boolean
Expand All @@ -973,6 +981,7 @@ const ViewerCanvas = memo(function ViewerCanvas({
onThumbnailCapture?: (blob: Blob, cameraData: SnapshotCameraData) => void
viewerSceneSlot?: ReactNode
floorplanSceneSlot?: ReactNode
disablePostFx?: boolean
}) {
const viewMode = useEditor((s) => s.viewMode)
const floorplanPaneRatio = useEditor((s) => s.floorplanPaneRatio)
Expand Down Expand Up @@ -1086,6 +1095,7 @@ const ViewerCanvas = memo(function ViewerCanvas({
<SelectionPersistenceManager enabled={hasLoadedInitialScene && !showLoader} />
<Viewer
defaultRender={EDITOR_DEFAULT_RENDER}
disablePostFx={disablePostFx}
hoverStyles={EDITOR_HOVER_STYLES}
onSceneReadyChange={onSceneReadyChange}
renderContext="editor"
Expand Down Expand Up @@ -1131,6 +1141,7 @@ export default function Editor({
isLoading = false,
onLoaderChange,
onThumbnailCapture,
disablePostFx = false,
sidebarOverlay,
viewerBanner,
settingsPanelProps,
Expand Down Expand Up @@ -1313,6 +1324,7 @@ export default function Editor({
const previewViewerContent = (
<Viewer
defaultRender={EDITOR_DEFAULT_RENDER}
disablePostFx={disablePostFx}
hoverStyles={EDITOR_HOVER_STYLES}
renderContext="editor"
selectionManager="default"
Expand All @@ -1331,6 +1343,7 @@ export default function Editor({

const viewerCanvas = (
<ViewerCanvas
disablePostFx={disablePostFx}
Comment thread
cursor[bot] marked this conversation as resolved.
hasLoadedInitialScene={hasLoadedInitialScene}
isFirstPersonMode={isFirstPersonMode}
isLoading={isLoading}
Expand Down
Loading