Ran command: `git diff -U0`
fix: resolve console warnings and add configurable debug logging
This commit addresses active console warnings and reduces logging noise in the SpeedGraph component.
### Changes:
- **index.html**: Fixed `[Deprecation]` warning by replacing the non-standard `-webkit-appearance: slider-vertical` with modern CSS properties (`writing-mode: vertical-lr; direction: rtl;`).
- **speedGraphSketch.js**:
- Resolved `pop() was called without matching push()` error by removing an orphaned `pop()` call in [drawStaticGraphToBuffer](cci//file:///d:/Work/Repo/refactor/steps/src/p5/speedGraphSketch.js:41:2-295:4).
- Muted "Density Info" messages by gating them behind a configurable debug flag.
- **debug.js**: Added a new [speedGraph](cci:1://file:///d:/Work/Repo/refactor/steps/src/p5/speedGraphSketch.js:7:0-538:2) flag (defaulted to `false`) to `debugFlags` to allow selective enabling of SpeedGraph logs.
IMPROVEMENTS:
- **utils.js**: Enhanced [extractTimestampInfo](cci//file:///d:/Work/Repo/refactor/steps/src/utils.js:36:0-70:1) with a robust, prefix-agnostic regular expression. It now identifies `YYYYMMDD_HHMMSS` and `DDMMYYYY_HHMMSS` patterns anywhere in a filename, enabling support for generic camera names like `cam_20260312_163310` or raw recordings.
- **dom.js**: Decoupled persistent overlays and debug diagnostics from mandatory video date-time metadata. The dashboard now remains fully functional even when loading generic video files (e.g., `output.mp4`).
- **Standardized Epoch Fallback**: Implemented a unified time-anchoring logic. If a filename lacks date-time information, the system now defaults to **January 1, 1970 (Unix Epoch)**, ensuring the absolute time clocks and sync diagnostics proceed without error or disappearance.
- **Bug Fixes**: Corrected syntax errors and structural scoping issues in the [updateDebugOverlay](cci:1://file:///d:/Work/Repo/refactor/steps/src/dom.js:200:0-291:1) function that were causing diagnostics to display inconsistently.
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.
Refactors the rendering logic for both main radar and zoom views to support
high-refresh-rate monitors and 4K displays without performance loss.
Key Changes:
1. Performance & Display:
- Uncapped Frame Rate: Set `p.frameRate(144)` in both sketches. This removes
artificial throttling on high-refresh monitors (75Hz+), eliminating
beat-frequency judder.
- 4K Quality: Removed `p.pixelDensity(1)` restrictions. High-end PCs will
now render at full device resolution (Retina/4K) instead of being downscaled.
2. Zoom Sketch Optimization:
- Viewport Culling: Implemented background slicing logic to only render the
visible portion of the static background image, significantly reducing GPU
bandwidth usage during zoom.
3. UX & Animation:
- Camera Smoothing: Applied a frame-rate independent Lerp (factor 0.5) to the
zoom camera. This mimics the weight and feel of the main radar cursor.
- Visual "Lead": Added a `leadFactor` (0.2) to the dashed hover circle. It
now interpolates between the camera and mouse, making controls feel
instant/elastic even while the view smooths out.
- Coordinate Fixes: Updated tooltip and connector logic to map correctly
from the smoothed camera space to screen space.
Addresses performance bottlenecks on high-end PCs and refines the
zoom interaction model to eliminate jitter while maintaining responsiveness.
Performance Optimizations:
1. High-DPI Scaling Fix:
- Forced `p.pixelDensity(1)` in zoomSketch.
- Issue: Retinal/4K screens were defaulting to pixelDensity 2.0+, causing
the GPU to render 4x the necessary pixels (e.g., 1000x1000 for a 500x500 canvas).
This bandwidth saturation caused FPS drops on powerful GPUs.
2. Refresh Rate Uncap:
- Set `p.frameRate(144)` explicitly.
- Issue: p5.js often throttles to 60fps. On 75Hz+ monitors, this caused a
"beat frequency" judder where update cycles missed display refresh cycles.
UX & Animation Logic:
1. Camera Smoothing (The "Cinematic" Feel):
- Decoupled the Zoom Camera position from the Raw Mouse position.
- Applied a Lerp smoothing factor of 0.5 (aligned with main radarSketch
cursor logic) to create fluid motion without feeling sluggish.
2. Visual "Lead" / Elasticity:
- Introduced `zoomLeadFactor` (0.2) for the dashed hover circle.
- The circle position is interpolated between the Smoothed Camera and
the Raw Mouse.
- Benefit: This creates a visual cue that "leads" the camera, making the
controls feel responsive/instant even while the view smoothly catches up.
3. Coordinate System Fixes:
- Reverted manual world-to-screen reprojection for tooltips.
- Adopted a relative screen-space transform:
(ItemScreenPos - CameraPos) * ZoomFactor.
- This ensures connector lines lock perfectly to visual elements regardless
of camera lag.
- Clamp delta time to minimum of 0 to prevent negative interpolation.
- Add protection against division by zero in SNR color mapping.
- Ensure trajectory points exist before fading alpha calculation.
- Standardize p.deltaTime usage for cross-browser stability.
Refactors animation and interpolation logic to ensure consistent behavior
across different monitor refresh rates (e.g., 60Hz vs 144Hz).
Changes:
- Implement time-delta adjusted smoothing for FPS counter and mouse tracking.
- Update IFT graph scaling in 'dom.js' to use performance-based delta time.
- Remove hardcoded 60FPS limit in zoom sketch to allow native refresh rates.
- Fix vertical tooltip jitter in 'drawUtils.js' by using smoothed mouse coordinates.
- Add 'lastOverlayUpdateTime' to global state for cross-module time tracking.
Adds linear interpolation (lerp) to mouse coordinates in the zoom view
to prevent jitter and provide a smoother user experience when inspecting
radar points.
Changes:
- Add `smoothedMouseX` and `smoothedMouseY` state to `radarSketch.js`.
- Apply smoothing factor (0.5) to mouse movement in the main draw loop.
- Update `handleCloseUpDisplay` and zoom sketch drawing to use smoothed coordinates.
- Ensure coordinates snap to mouse position on the first frame to prevent visual jumping.
- Implemented smooth dynamic scaling for the Inter-Frame Timing (IFT) graph that remains active even when playback is paused.
- Added a conditional `requestAnimationFrame` loop to ensure the graph scale converges smoothly to the target value during scrubbing or pause states.
- Tuned animation speeds: fast convergence (0.1 smoothing) during playback, slower (0.033) when paused for a more polished visual transition.
- Fixed a layout thrashing (forced reflow) performance issue in `updatePersistentOverlays` by reading `clientWidth` before writing text updates to the DOM.
radar view (src/drawUtils.js):
- Refactored drawTrackMarkers with a smart collision-resolution algorithm for floating labels.
- Added leader lines and themed tooltips for track IDs and speed data to prevent overlapping.
- Optimized velocity vectors with arrowheads and reduced thickness for better clarity.
- Updated drawRegionsOfInterest to use ROI_CLOSE_Y_MAX constant.
speed graph (src/p5/speedGraphSketch.js):
- Implemented a MATLAB-style spectral color scheme (Blue-Cyan-Green-Yellow-Red) to visualize track density on the CAN speed
line.
- Added a vertical smooth gradient legend bar for track density.
- Switched to robust 95th percentile normalization for density mapping to mitigate outlier noise.
- Synchronized density calculations with the "Confirmed Only" toggle.
- Updated the horizontal legend to reflect density-based coloring.
zoom view (src/p5/zoomSketch.js):
- Adjusted tooltip vertical offset for improved diagonal positioning.
- Disabled internal track detail boxes to prioritize the specialized zoom tooltip system.
- Implement robust fallback and safety logic to prevent division-by-zero or invalid state crashes.
- Update scale calculations and drawing utilities to use dynamic boundaries from appState.
- Refine radar-info-overlay to match plot width and center text content for better alignment.
- Add double-click reset functionality to the range slider to revert to default constants.
- Fix hover detection bug in God Mode during scroll-zoom interactions.
- Ensure Regions of Interest (ROI) scale proportionally with the dynamic plot range.
Vehicle Dimensions:
- Added "Show Dimensions" toggle to the display settings.
- Implemented `drawObjectDimensions` in `drawUtils.js` to render object extents as oriented rectangles based on `objectExtentRadii` and `objectExtentAngle`.
- Updated `radarSketch.js` to draw dimensions when enabled.
- Ensured dimensions and covariance ellipses are drawn at the `correctedPosition` for accurate alignment with track markers.
Track Visualization:
- Updated `drawTrajectories` to prioritize the `risk` property on track logs for coloring (0=Low/Blue, 1=Medium/Orange, 2=High/Red).
- Added fallback to `ttcCategoryTimeline` for backward compatibility.
- Fixed off-by-one error in trajectory history filtering to prevent drawing future points.
Frame Synchronization & UI:
- Refactored frame number display logic across Overlays, Data Explorer, and Timeline.
- UI now displays the actual `frameIdx` from the data source instead of the internal array index, resolving discrepancies between the Data Explorer and the main view.
This commit introduces several improvements to the radar visualization, focusing on responsiveness, error handling, and clearer data representation.
**Key Changes:**
* **Responsive Radar Overlay:** The persistent radar information overlay (`ift-dot-matrix` canvas) in `dom.js` is no longer a fixed size. It now dynamically adjusts its width to match the radar plot's size, ensuring a consistent visual experience across different screen resolutions and resize events. The internal drawing logic has been updated to dynamically calculate column counts based on the canvas's current dimensions.
* **Improved Error Handling in Drawing Utilities:** Comprehensive `try...catch` blocks have been added to all major drawing functions within `drawUtils.js`. This significantly enhances the application's robustness by gracefully handling potential errors during rendering, preventing crashes and providing better debugging insights.
* **TTC Category Fallback:** In `drawUtils.js`, the trajectory drawing logic for Time-to-Collision (TTC) now includes a fallback mechanism. If a track's `ttcCategory` is undefined or invalid, it will default to a neutral gray color, ensuring that trajectories are always rendered with a clear visual state.
- Implemented a "First Run" detection using `sessionStorage`.
- The User Guide modal now appears automatically when application is launched in a new tab or window.
- Logic added to skip the automatic file loading (cached session) on the first run to ensure a clean start for new sessions.
- Subsequent page reloads within the same session will hide the guide and resume the cached session as normal.
Create `shortcuts.html` as a standalone reference page.
Implement `#shortcuts-modal` in `index.html` to display key bindings.
Implement `#guide-modal` with an iframe to embed `User_Manual.html` directly in the app.
Refactor header navigation into color-coded buttons (Green/Blue/Amber-Indigo).
Update `src/dom.js` and `src/ui.js` to handle modal visibili and interactions (ESC key, 'k' key, click-to-close).
Improve visual consistency of header button heights and moda styling.
This commit refactors the monolithic `main.js` file to improve modularity, maintainability, and separation of concerns. The core application logic is now organized into more focused modules.
- **UI Logic (`ui.js`):** All general UI event listeners, including feature toggles, sliders, menu controls, and tooltips, have been moved from `main.js` into a new `src/ui.js` module. This centralizes UI-specific behavior.
- **Session Management (`session.js`):** The functionality for saving and loading application sessions has been extracted into a dedicated `src/session.js` module. This isolates all logic related to session state serialization and file handling.
- **`main.js` Simplification:** The `main.js` file now serves as a cleaner entry point, responsible for initializing all the different application modules in the correct order.
**Bug Fixes during Refactoring:**
- Corrected an issue where activating "God Mode" (`toggleCloseUp`) did not properly start the p5.js `loop()`, preventing the visualization from updating with mouse movement.
- Ensured the `toggleConfirmedOnly` state is now correctly saved and restored as part of the session file.
Adds a 'wheel' event listener to the speed graph container, binding it to the existing `handleTimelineWheel` function.
This change enhances user experience by allowing timeline scrubbing via the mouse scroll wheel when the cursor is over the speed graph. It creates consistent behavior across all major visualization components (radar canvas, timeline, and speed graph), improving the application's usability.
The logic is centralized within `initSyncUIHandlers` in `sync.js` to keep all synchronization-related event bindings in one place.
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.
The new loop iterates through the data and draws rectangles directly to the canvas. It changes ctx.fillStyle more often (once per column), but this cost is negligible to the cost of allocating and collecting memory 60 times a second. This should resolve the heap memory issue you observed.
theme redraw
2
3 - Implemented hover tooltips in `speedGraphSketch.js` to displ
CAN/Ego speeds and timestamps.
4 - Added drag-to-seek (scrubbing) and click-to-seek functionali
using native pointer events for smooth navigation.
5 - Integrated graph seeking with centralized `updateFrame` logi
to ensure consistent video synchronization and UI updates.
6 - Refactored input handling to use DOM listeners on the canvas
resolving conflicts with global p5 events (e.g., play button
issues).
7 - Optimized `theme.js` to redraw only the static graph buffer
theme changes instead of re-processing the entire dataset.
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.
" Here is the detailed breakdown of why that solution worked.
The Root Cause: Initialization Order
The error ellipse() ... received NaN occurred because the speedGraphSketch was trying to plot a point based on time
(x-axis), but the time value for the current frame was invalid (NaN or undefined).
Here is the chain of events that caused this:
1. The Dependency Chain:
* To draw the graph, the sketch needs frame.timestampMs.
* To calculate frame.timestampMs, the JSON Parser (parseVisualizationJson) needs appState.videoStartDate.
* To get appState.videoStartDate, the helper function calculateAndSetOffset() needs to know the Video Filename.
2. The Bug (Before Refactor):
When you dragged both files in, the code executed sequentially like this:
* Step A (JSON Block): The code detected the JSON file. It set appState.jsonFilename and immediately called
calculateAndSetOffset().
* CRITICAL FAILURE: At this exact moment, the code had not yet processed the Video file. It didn't know the
video filename.
* Result: calculateAndSetOffset couldn't find a video timestamp, so appState.videoStartDate remained null.
* Step B (Parsing): The JSON parser ran. Because videoStartDate was null, it skipped the math that calculates
frame.timestampMs. All your data frames were created with timestampMs: undefined.
* Step C (Video Block): The code moved to the Video file. It set appState.videoFilename and called
calculateAndSetOffset() again.
* Now it calculated the correct date, but it was too late. The JSON data had already been parsed and baked
without timestamps.
3. The Crash:
When the visualization started, the Speed Graph looked at the current frame, found timestampMs was missing, tried
to divide it by 1000, got NaN, and crashed the ellipse() drawing function.
How the Solution Fixed It
By restructuring processFilePipeline, we enforced the correct dependency order:
1. Pre-Processing (The Fix):
We moved the "registration" of both filenames to the very top of the function.
1 // 1. Register JSON file (if present)
2 // 2. Register Video file (if present)
2. Calculation:
We call calculateAndSetOffset() once, immediately after registration.
* Since both filenames are now known, it successfully extracts the timestamp from the video filename and sets
appState.videoStartDate.
3. Processing:
We then start the JSON parser.
* It receives the valid appState.videoStartDate.
* It successfully calculates frame.timestampMs for every frame.
In short: We ensured that the "ingredients" (filenames and dates) were fully prepared before we started "cooking"
(parsing) the data"
This commit extracts the keyboard shortcut handling logic from the main application entry point (`main.js`) into a new, dedicated module (`src/keyboard.js`).
Previously, all keydown event listeners were defined directly in `main.js`, mixing UI initialization with user input handling. This refactoring improves code organization and modularity by separating concerns, making the system easier to maintain and debug.
Key changes include:
- A new `src/keyboard.js` file was created to house all keyboard-related functionality.
- The `keydown` event listener and its handler function (`handleKeyDown`) were moved from `main.js` into the new module.
- An `initKeyboardShortcuts` function is now exported from `keyboard.js` and called from `main.js` during application startup.
- All dependencies required by the shortcut logic (from `dom.js`, `state.js`, `sync.js`, etc.) are now correctly imported into `keyboard.js`.
This is a pure refactoring effort and introduces no changes to application behavior.