This commit refactors the initialization phase of the Visualizer to act as a Single Page Application (SPA). A persistent workspace shell is now initially hidden behind a "Data Synchronizer" Start Screen, which handles file ingestion before seamlessly revealing the canvas.
Part 1: Implement SPA Start Screen Modal and Data Routing
* steps/index.html: Added the full-screen `start-screen-modal` overlay to intercept the user on load. Removed text placeholders inside the video and canvas containers since the workspace is now exclusively visible when populated.
* steps/src/dom.js: Exported references for the new modal, drop zone, and primary load buttons.
* steps/src/main.js: Routed standard drag-and-drop and click events to target the new Start Screen elements rather than the legacy footer buttons, initiating the existing file pipeline.
Part 2: Integrate Loading Progress Natively
* steps/index.html: Added a hidden `start-progress-container` directly inside the Start Screen card to prevent overlapping popups.
* steps/src/dom.js: Exported references to the native progress bar and text elements.
* steps/src/modal.js: Refactored `showLoadingModal`, `updateLoadingModal`, and `hideModal` to dynamically route progress updates. If the Start Screen is active, it renders the inline progress bar and disables background buttons rather than spawning the generic popup modal.
* steps/src/fileLoader.js: Updated `processFilePipeline` to fully dismiss the `start-screen-modal` upon successful data ingestion and precomputation, seamlessly transitioning the user.
Part 3: Replicate Accessibility and Theme Controls
* steps/index.html: Duplicated the Quick Start Guide, Codebase Overview, What's New, and Theme Toggle buttons, positioning them in the top-right corner of the Start Screen. Also fixed a `-webkit-appearance` CSS warning on the range slider.
* steps/src/dom.js: Exported references for the new accessibility buttons and theme toggle SVGs.
* steps/src/ui.js: Attached existing `toggleGuideModal`, `toggleCodebaseModal`, and `toggleChangelogModal` event listeners to the new replicated buttons, granting users access to documentation before data load.
* steps/src/theme.js: Updated `initializeTheme()` so switching the theme from either the workspace or the Start Screen bidirectionally syncs the Light/Dark SVGs on both buttons simultaneously.
Improves the time offset feature by making manual offsets persistent and enhancing the user workflow.
- **Persistent Manual Offsets**: Manual offsets are now saved to IndexedDB, keyed by filename. They are automatically applied when the same file is loaded in a future session.
- **Click to Revert & Forget**: The "Manual" status indicator is now clickable. Clicking it reverts to the auto-calculated offset and, crucially, deletes the saved manual offset from the database. This ensures future sessions will correctly default to the automatic calculation.
- **Consistent UI**: The "Manual" indicator now appears instantly with a consistent gray style, whether set manually or loaded from cache, removing UI ambiguity.
- **Refactored Sync Logic**: The `revertToAutoOffset` function has been refactored to reuse the core `forceResyncWithOffset` logic. This reduces code duplication and centralizes the synchronization process, making it more robust and easier to maintain.
Resolve infinite p5.js auto-draw loop during file processing by ensuring comprehensive UI cleanup and correct p5 instance lifecycle management (`redraw` instead of `lo`).
Introduce conditional video retention in `resetUIForNewLoad`, allowing the loaded video to persist when only new JSON data is supplied.
This commit introduces a series of significant improvements to the file processing pipeline and UI update logic, resolving several critical bugs related to state management, UI freezes, and component rendering. The primary goal was to decouple UI components from strict file dependencies and ensure the application remains responsive and predictable under various file loading scenarios.
### Key Changes and Fixes:
1. **Graceful Handling of File Swapping (Fixes UI Freeze):**
- Resolved a critical bug where loading a new JSON file while a video was already present would cause the application to freeze at "Parsing JSON (100%)".
- The root cause was an incomplete state reset. The fix involves calling `resetVisualization()` at the beginning of `finalizeSetup()`, which ensures the playback timeline and animation loops are reset to a clean slate before redrawing with new data.
- This change allows users to seamlessly load and compare different JSON logs against the same video without unloading the video or freezing the application.
2. **Decoupled and Robust Speed Graph:**
- The speed graph is no longer dependent on a video file to render. It will now draw correctly as long as valid JSON data is available.
- The graph's X-axis is now consistently scaled based on the JSON data's internal duration (`finalDuration = jsonDuration`). This makes the graph's behavior predictable and ensures all data within the JSON file is always visible.
- Corrected the data source for plotting. The graph now uses the `frame.timestamp` property, which is the reliable relative time from the start of the log. This fixes a bug where the graph would appear to start late (e.g., at 220s) because it was incorrectly using `timestampMs`, a property modified by the video synchronization offset.
3. **Object URL Lifecycle Management (Memory Leak Fix):**
- Implemented proper lifecycle management for video object URLs created with `URL.createObjectURL()`.
- Before creating a new video URL, the application now checks for and revokes any existing `appState.videoObjectUrl` to prevent memory leaks from accumulating unused blob URLs.
4. **Verified Synchronization Failsafe:**
- Confirmed that the failsafe logic in `calculateAndSetOffset` is working as intended. If the automatically calculated offset between a video and JSON file exceeds a 30-second threshold, it correctly defaults to `0`, preventing extreme and incorrect synchronization when mismatched files are loaded.
This commit introduces significant improvements to the file loading pipeline to enhance stability and user experience.
The application startup is no longer blocked by file caching, and it now gracefully handles corrupted or slow-loading video files instead of hanging.
Key Changes:
- **Non-Blocking File Caching:**
- Caching files to IndexedDB is now a non-blocking, "fire-and-forget" operation.
- `saveFileWithMetadata` calls are no longer awaited, allowing the UI and parsing to proceed immediately.
- `Promise.allSettled` is used to log cache results in the background.
- **Robust Video Loading:**
- Implemented a 10-second timeout for the video `canplaythrough` event to prevent indefinite hangs.
- If the timeout occurs but `loadedmetadata` has fired, the video is treated as playable, allowing the application to continue.
- If the video fails to load entirely (due to a timeout or an `error` event), the user is presented with a modal to either "Retry" or "Continue without Video".
- **Bug Fixes:**
- Fixed a critical state bug where `appState.videoMissing` was not reset on a new file load, which caused issues when re-uploading a valid video after a failure.
- Resolved multiple race conditions in the modal system where error and choice modals would close prematurely after being displayed.
- Replaced the blocking `alert()` on storage quota errors with a non-blocking modal, ensuring the app remains responsive.
Make file caching fire-and-forget so IndexedDB writes do not block parsing and video loading.
*Actions*
Update the file pipeline code where saveFileWithMetadata(...) is called:
Replace await saveFileWithMetadata(...) with a non-blocking call that stores the promise in an array, e.g. cachePromises.push(saveFileWithMetadata(...).catch(e => logWarning(e))).
At the end of the pipeline kick off Promise.allSettled(cachePromises) but do not await it before parsing starts; instead log results or show a non-blocking notification on failure.
Add a configurable CACHE_BLOCKING boolean flag in config. Default false; if true (special mode) allow blocking for debugging.
Add logging and UI notification for cache failures without blocking the pipeline. Convert any alert() on quota errors into a non-blocking modal message or toast.
Add unit tests / smoke tests:
Simulate slow saveFileWithMetadata() (wrap with artificial delay) and confirm parsing and video loading start immediately without waiting for caching to finish.
Simulate a QuotaExceededError and confirm the pipeline continues.
*Check list*
Replace await saveFileWithMetadata(...) with non-blocking logic.
Pipeline starts parsing immediately even when cache save is delayed.
Promise.allSettled(cachePromises) is triggered and results logged.
UI shows non-blocking notification for cache failures (no alert() popups).
Tests simulate slow save and quota errors and pass.
2
3 - **Modularization:** Extracted monolithic file handling from `main.js` into a new dedicated `src/fileLoader.js`
module.
4 - **Incremental Loading:** Implemented logic to handle JSON and Video files independently, allowing users to replac
individual files without breaking the application state.
5 - **Race Condition Fixes:** Updated `src/db.js` to return Promises and await database initialization, resolving
startup crashes.
6 - **Sync Logic Correction:** Reordered the loading pipeline to ensure file metadata (and thus timestamps) are
extracted *before* parsing visualization data. This fixes `NaN` errors in the speed graph and ensures correct video
synchronization.
7 - **Optimization:** Added a `fromCache` flag to skip redundant database writes when reloading a previous session.
8 - **Testing:** Added `tests/fileLoader.test.js` to verify the new loading pipeline.