Two unrelated noise sources after wiring the editor into the webapp:
1. FontLoader was fetching /resources/fonts/CarterOneRegular.json,
which exists in the Tauri public/ but not in the webapp's. Vite's
SPA fallback returns index.html (200, HTML body), the FontLoader's
progress callback fires "Font Loaded: 1" three times as it streams,
then JSON.parse chokes on the leading `<` — surfaces as an
unhandled promise rejection from LoadingPlaceHolderManager.
Copy the same font asset into the webapp's public/ tree (same
pattern Tauri and editor3d already use).
2. Editor.initialize() reads sceneContainerEl.offsetWidth/offsetHeight
to size the WebGLRenderer + post-processing pipeline. On the
webapp the flex chain (SidebarInset → wrapper → SceneContainer)
isn't always laid out by first call, so the dimensions are
transiently 0×0. setSize(0,0) then configureMain(0,0) allocates
six post-process framebuffer attachments at 0 size, each emitting
INVALID_FRAMEBUFFER_OPERATION until the next window.resize event
cascades through and resizes them properly. Clamp the initial
container offset to ≥1 (Math.max(1, ...)) and replace the
`?? 0` fallbacks on the canvas dimensions with `|| 1`. First
frame renders into a harmless 1×1, next resize fixes everything.
The 3D editor lib already exposes cacheJsonString + onSceneSerialized
props (Tauri wires them through useTabStore so per-tab scene state
survives tab switches). The webapp wasn't using them, so navigating
to /gallery, /create-image, etc. and back blew away whatever the user
had been building in the playground.
Add a small zustand store (mirrors enter-to-generate-store.ts) backed
by sessionStorage so the serialized JSON survives both SPA navigation
and tab reloads. Read cacheJsonString once via getState() on mount so
re-renders don't restart the engine; write back via the lib's
onSceneSerialized callback on unmount.
Scoped to the no-token playground: when sceneToken is in the URL the
server-loaded scene is the source of truth, so we neither restore
from cache (would override a shared scene) nor write the session back
into it (would later resurrect a stranger's scene as the playground).
- styles.css: move .glass into @layer components so Tailwind's
.absolute utility wins the cascade. OnboardingHelper was rendering
position: relative and pushing the editor down on first paint until
clicking dismissed it.
- SceneContainer + Stage3DBody: drop hardcoded calc(100vh - 68px) and
height - 56 topbar offsets; the lib now fills its parent container.
- Webapp adapter: drop the +56 getViewportSize compensation now that
the lib doesn't subtract a topbar from the supplied height.
- Tauri PageScene: wrap Stage3D in a calc(100vh - 56px) container
(matching .topbar-spacer) so its shape is preserved.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds /pagescene (and /pagescene/:sceneToken) under the webapp's public
chrome (inside ProtectedLayout, outside RequireAuth) so anonymous users
can view scenes too. Adapter implementation mirrors the website's, with
two webapp-specific tweaks: imports the local useSession / showToast /
invalidateSession helpers, and getViewportSize returns the wrapper's
live ResizeObserver-measured rect so DnD hit-testing matches the visible
canvas (not window.innerWidth).
Sidebar handling: the lib's position-fixed UI is scoped to a wrapper
div with transform: translateZ(0), which creates a new containing block.
The wrapper renders inside SidebarInset's flex flow, so the editor sits
naturally to the right of the sidebar and below the topbar; expanded-
sidebar behavior is deferred (currently shrinks the page further per the
flex layout).
Sidebar nav gets an "Edit 3D" entry between Image and Video. Tailwind
config gets brand-primary/secondary/tertiary, action color, red/green/
blue DEFAULTs; styles.css adds .bg-ui-panel-gradient, .prevent-select,
.dot-typing — same gap-fills landed on the website.
No new endpoints. tsconfig churn from nx sync.
useTauriPageSceneAdapter, GlobalFileDropHandler, and UploadImagesButton
now pull UploadModal3D/Image/Splat from the shared lib. Removes the
host-local UploadModal3D/UploadModalImage/UploadModalSplat directories
since they're no longer referenced anywhere.
UploadModalMedia/ stays — it backs the host-only image-plane upload
adapter helpers, not the modal slots.
- added make video button to the create image page gallery card action
button row and the lightbox modal
- clicking it will redirect to the create video page and insert the
image as a reference automatically
- Lightbox now shows full media metadata: Aspect Ratio, Resolution,
Size, Duration, Audio, and Created (with timestamp).
- Added Author header at the top so the close button no longer overlaps
the Prompt copy button.
- Extracted aspect ratio / resolution / duration formatters into
`@storyteller/common` so the website and desktop app share them.
Customers occasionally saw "0 Credits" with no signal that the fetch had
failed silently. Add a status badge on the coin (slow/failed/ recovered,
hidden in the happy path), a 60s background refresh, and gate the
initial fetch on AUTH_STATUS.LOGGED_IN to remove the 1s setTimeout
band-aid that was racing intermediate auth states.
Only the submit button triggers generation by default. The "Enter to
generate" toggle (available in both app and website settings top right
nav) still allows opting into Enter-to-submit.
- Pressing **Enter** in any prompt box now adds a new line instead of
submitting - fixes complaints about accidental generations
- **Shift+Enter** is the new submit shortcut
- Desktop app gets an "Enter to generate" toggle in Settings → General
(off by default) for users who prefer the old behavior
- Applies to all prompt boxes in the Artcraft desktop app and the
website