Milimo Frontend State Manager Skill
As the Milimo Frontend State Manager, your primary responsibility is maintaining the integrity, performance, and synchronization of the React 18 UI.
1. The Zustand "God Store" (timelineStore.ts)
Milimo uses a single timelineStore.ts built from 7 distinct slices to prevent context re-render thrashing:
projectSlice(Project metadata, save/load)shotSlice(Storyboard elements, generation status)trackSlice(Timeline lanes, clip positioning)playbackSlice(currentTime, playing status, maximum duration cache)uiSlice(Tooltips, panel visibility)elementSlice(Project elements gallery)serverSlice(SSE handling, active jobs dictionary)
RULES:
- No independent React Contexts for global state.
- Always use the
<Toggle>component for switches. - Components subscribing to frequent updates (like
currentTimefromPlaybackEngine) MUST use selectiveuseStore(useShallow(state => ...))bounds or access.getState()directly viauseRefto avoid React reconciliation loops.
2. Magnetic V1 & Free Placement (Timeline Layout)
The timeline UI is entirely DOM/CSS-based absolute positioning (no HTML5 <canvas>).
- Centralized in
computeTimelineLayout()insidetimelineUtils.ts. - Track 0 (V1) behaves magically: It ignores user-specified
startFrame. It calculates layout by iteratively appending thedurationof ShotN-1to ShotN. Dragging a clip on V1 simply reorders the array. - Track 1 (V2) & Track 2 (A1) are "free placement": Clips are absolutely positioned based on their explicitly defined
startFrame, allowing overlapping overlays and audio.
3. Browser Quirks & Engine Workarounds
Safari is notoriously difficult for media web apps. The frontend must strictly adhere to these workarounds:
A. Safari Audio Fix (GlobalAudioManager.ts)
Safari blocks <audio> autoplay and suffers from aggressive media desync when syncing HTML5 video to separate audio tags.
- The Playback Loop:
PlaybackEngine.tsxuses a headlessrequestAnimationFrameto advancecurrentTimeviaDate.now()deltas. - Audio Decoding:
GlobalAudioManager.tspre-fetches.mp3files via HTTP Range requests and usesdecodeAudioDatainto an in-memoryAudioBufferSourceNode. - Mute Control: Audio nodes remain playing but are muted (
gain.value = 0) instead of being stopped, circumventing Safari user-gesture policies on re-start. - Drift Tolerance: The frontend hardcodes a 250ms drift tolerance. If the video
<video>tag current time deviates from thePlaybackEnginetime by more than 250ms, it forces a.fastSeek().
B. Safari Draggable Hover Bug
Elements with draggable={true} in Safari WebKit silently swallow CSS pointer events, completely neutralizing Tailwind's group-hover and standard :hover pseudo-classes intended for child overlay UI.
- DO NOT use CSS hover states for interactive buttons inside draggable elements.
- INSTEAD, use explicit React state (e.g.,
const [isHovered, setIsHovered] = useState(false)) bound toonMouseEnterandonMouseLeavesynthetic events (which Safari does reliably fire) to toggle UI visibility.
4. SSE & React Hooks
Backend task updates flow exclusively through Server-Sent Events.
SSEProvider.tsxmanages a single robustEventSourcewith exponential backoff.- The Zustand
serverSlice.tsexposeshandleServerEvent()which patches theshotSliceimplicitly on"progress","cancelled", and"complete"messages. - Job Poller: SSE dropouts happen. On initial page mount,
jobPoller.tsgrabs all shots markedisGenerating: trueand fires explicitGET /status/{job_id}calls to hydrate the UI.
5. UI Layout Components
- The layout revolves around
Layout.tsx, wrapping a top toolbar,CinematicPlayer.tsxcenter stage,VisualTimeline.tsxsticky at the bottom, and a right-side inspector (InspectorPanel.tsx,MediaLibrary.tsx,StoryboardView.tsxmutually exclusive visible tabs). - Drag-and-drop between Library and the Inspector is supported via HTML5 drag APIs.

