Skip to content

Refine timeline UX and fix production build for 1.4.3 - #81

Merged
Sportinger merged 35 commits into
masterfrom
Spalts
Apr 12, 2026
Merged

Refine timeline UX and fix production build for 1.4.3#81
Sportinger merged 35 commits into
masterfrom
Spalts

Conversation

@Sportinger

Copy link
Copy Markdown
Owner

Summary

  • replace preview thumbnails with large gray icons for splat, camera, and 3D object clips
  • keep clip meta visible while horizontally scrolling and improve playhead visibility/follow behavior
  • fix production build issues for deployment and bump the app version to 1.4.3

Verification

  • npm run build

MasterSelects and others added 30 commits March 24, 2026 14:08
Track page visits via Cloudflare KV in middleware, expose /api/visits
endpoint (protected by VISITOR_NOTIFY_SECRET), and add local polling
script with audio notification on new visitors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 6 AI debug tools for diagnosing the Gaussian Splat integration:
- getGaussianStatus: renderer singleton state snapshot
- getGaussianClips: all gaussian-avatar clips on timeline
- getGaussianLayers: active layers at current playhead
- testGaussianModule: fetch → blob → import() pipeline test
- testGaussianRenderer: full init → load → canvas → pixel check
- testGaussianImportPipeline: import → media store → timeline flow

Fix 'gaussian-avatar' missing from sourceType union in
ClipboardClipData and SerializedClipState (caused build errors).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root causes found and fixed:

1. Renderer module can't fetch blob: URLs — its internal axios
   silently fails, getInstance returns null. Fix: add blob-store
   middleware to Vite dev server that proxies blob data as HTTP
   URLs with .zip extension (module requires .zip in URL path).

2. loadAvatar set _avatarLoaded=true even when getInstance returned
   null — caused copyExternalImageToTexture to crash every frame on
   a canvas without a WebGL context (massive error spam). Fix: validate
   renderer instance and canvas WebGL context after getInstance.

3. processGaussianLayers had no try/catch around copyExternalImageToTexture
   — one error per frame with no throttle. Fix: try/catch with
   single-error logging.

4. loadGaussianAvatarMedia had no error handling — if blobUrlManager
   threw, clip stuck at isLoading=true forever. Fix: try/catch +
   null file guard.

5. is3D not serialized in SnapshotManager/ClipboardClipData — lost
   on undo/redo and copy/paste. Fix: add is3D to both types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add gaussian-avatar case in deserialization (serializationUtils.ts)
  — creates blob URL and sets gaussianAvatarUrl + blendshapes on restore
- Add gaussian-avatar case in updateTimelineClips (fileManageSlice.ts)
  — relink dialog now properly reconnects gaussian avatar files
- Serialize gaussianBlendshapes in project save
- Add gaussianBlendshapes to SerializableClip type

Without this, gaussian clips showed as "needs reload" (dashed outline)
after every page refresh with no way to reconnect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Same bug as gaussian-avatar — the relink handler fell through to the
generic else branch which only set the file reference but didn't
create a modelUrl blob URL. Three.js needs that URL to load the model.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e 1)

Register 'gaussian-splat' as a valid source type across all type unions
(LayerSource, TimelineClip.source, SerializableClip, MediaType, MediaFile,
SerializedClipState, ClipboardClipData). Add GaussianSplatSettings types
with render/temporal/particle sub-settings and defaults. Create clip
creation module (addGaussianSplatClip.ts), wire into clipSlice addClip
flow, serialization (save/load/relink), and media import pipeline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PLY and .splat parsers, header-only parse, LRU cache, normalize utils.
Cherry-picked from worktree-agent-ad13695f with index.ts fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GaussianSplatGpuRenderer, SplatRenderTargetPool, SplatCameraUtils,
gaussianSplat.wgsl shader. Cherry-picked from worktree-agent-ada90413.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Wave 2C)

- FileTypeIcon: green/teal point cloud icon for gaussian-splat
- PropertiesPanel: GaussianSplatTab with render settings (scale, planes, max splats)
- MediaPanel: accept .ply/.splat file imports
- useExternalDrop: recognize gaussian splat files for drag-drop
- Fix: restore useGaussianSplat feature flag for old avatar path
- Fix: add typeOverride to ImportParams for gaussian-splat/avatar imports
Cherry-picked from worktree-agent-a995b351 with conflict resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restores stashed changes for gaussian-avatar support in LayerBuilder,
LayerCollector, and ExportLayerBuilder that were in progress before
the gaussian-splat orchestrator work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- LayerBuilderService: add buildGaussianSplatLayer() with clip-local time
- LayerCollector: add gaussian-splat stub handler (textureView filled by RenderDispatcher)
- RenderDispatcher: add processGaussianSplatLayers() — per-layer WebGPU rendering,
  async scene loading, in-place LayerRenderData replacement preserving compositor properties
- ExportLayerBuilder: add gaussian-splat branch for export path

Each gaussian-splat clip renders into its own texture (no merging).
Compositor receives per-layer opacity, blend mode, masks, and effects intact.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… (Wave 5)

- Add TemporalSampler: deterministic, stateless frame selection for 4D gaussian
  splat sequences with loop/clamp/pingpong modes and configurable frame blending
- Add FrameBufferSwapper: double-buffered GPU storage for smooth frame transitions
  without pipeline stalls, only re-uploads when frames change
- Add ParticleCompute: GPU compute pass applying deterministic particle offsets
  (explode, drift, swirl, dissolve) to splat positions/scales/opacities
- Add temporalBlend.wgsl: compute shader for frame interpolation with nlerp
  quaternion blending
- Add particleCompute.wgsl: compute shader with deterministic hash-based
  particle effects (no Math.random, no accumulated state)
- Integrate particle pipeline into GaussianSplatGpuRenderer.renderToTexture()
  with per-clip output buffer caching and bind group management
- Wire clip-local time and particle/temporal settings from layer source through
  RenderDispatcher into the GPU renderer
- All effects are fully deterministic: scrub to frame N == play-through to frame N

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…s (Wave 4)

Adds large-asset performance optimizations for scenes with 100K+ gaussians:

- SplatVisibilityPass: GPU compute frustum culling that tests each splat against
  6 camera frustum planes with bounding sphere margin. Outputs visible indices
  via atomic counter. Async readback updates draw count for next frame.

- SplatSortPass: GPU bitonic merge sort that orders visible splats back-to-front
  by view-space depth. Uses float-to-sortable-u32 encoding for correct ordering.
  O(n log²n) parallel sort, works well up to ~1M elements.

- gaussianSplat.wgsl: Added sortedIndices storage buffer (binding 1) for index
  indirection. Vertex shader now reads splat[sortedIndices[instance_index]].

- GaussianSplatGpuRenderer: Integrated cull→sort→render pipeline with thresholds
  (cull > 50K splats, sort > 1K splats), sort frequency throttling, maxSplats
  budget cap, and graceful fallback to unsorted path on failure.

- Identity index buffers created per-scene for the unsorted default path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uncommitted changes

Includes stashed workspace changes from staging (2026-04-04): interactive tutorial
overlay stubs, gaussian splat export preloading, preview enhancements, and radix sort fix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace cached useMemo selectedGaussianSplatTransform with getFreshGaussianTransform()
that reads the interpolated transform directly from the store at each mousedown/wheel
event. Fixes race condition where second camera drag at same playhead position would
jump back to pre-edit values because the memoized transform wasn't invalidated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…panels

Mesh, Move to Folder, and Show in Explorer submenus were clipped by
dock panel overflow:hidden containers. Now uses position:fixed with
dynamic viewport boundary detection to always stay visible.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ectional media sync

Right-click any clip in the timeline to set its label color via a color grid submenu.
Colors are synced bidirectionally with the media panel — setting a color on a clip updates
the media item, and vice versa. All clip types supported (video, audio, image, text, solid,
mesh, camera, compositions) with fallback matching for legacy clips without mediaFileId.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…a panel

They were incorrectly classified as media files requiring a File object.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
labelColor was never included in the project file format, so colors set on
media files, folders and compositions were lost on reload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Conflicts:
#	tools/visitor-tray/.env.example
#	tools/visitor-tray/Install-Startup.ps1
#	tools/visitor-tray/README.md
#	tools/visitor-tray/VisitorTray.ps1
#	tools/visitor-tray/start.cmd
@Sportinger
Sportinger merged commit ac66f5f into master Apr 12, 2026
2 of 3 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.

1 participant