Skip to content

Conversation

@ameer2468
Copy link
Collaborator

@ameer2468 ameer2468 commented Oct 16, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Reset hover state when zoom segments are deleted or none remain
    • Prevent accidental segment creation while hovering over existing segments
  • Enhancements

    • Live drag-to-create and drag-to-resize zoom segments with immediate visual feedback
    • Improved segment insertion, ordering and clamping to neighbors/total duration
    • Enforced minimum segment duration and visual width safeguards for consistent behavior
  • UI

    • Track-level hover tracking added for clip/scene/zoom to drive preview and interactions

@ameer2468 ameer2468 self-assigned this Oct 16, 2025
@ameer2468 ameer2468 added the Desktop Things affecting desktop app label Oct 16, 2025
@ameer2468 ameer2468 removed their assignment Oct 16, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 16, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds drag-to-create and live-drag editing for zoom segments on the Timeline, tracks which sub-track is hovered, and moves hover state into the shared editor context. Creation enforces minimum width/duration, clamps to neighbors/totalDuration, and finalizes or defaults a 1s segment on mouseup.

Changes

Cohort / File(s) Summary
Zoom track — create/edit via drag
apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
Replaces hover-based preview with drag-driven creation: new constants for min pixel/sec widths, creatingSegmentViaDrag state, helpers newSegmentMinDuration() and newSegmentDetails(), mouse down → move → up flow that creates (first move) and updates segments (subsequent moves) via createSegment/updateSegment, computes insertion index, clamps to neighbors/totalDuration, and finalizes or creates a default 1s segment on mouseup. Also simplifies zoom segment access with optional chaining and switches label rendering to a unified Switch/Match path.
Clip track — hover tracking
apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx
Adds onMouseEnter/onMouseLeave to set/clear timeline.hoveredTrack to "clip" and attaches handlers to the TrackRoot wrapper. Removed metaQuery from context destructure.
Scene track — hover tracking
apps/desktop/src/routes/editor/Timeline/SceneTrack.tsx
Adds onMouseEnter/onMouseLeave to set/clear timeline.hoveredTrack to "scene" in editor state (in addition to existing hover/reset behavior).
Timeline container — hover initialization
apps/desktop/src/routes/editor/Timeline/index.tsx
Adds onMouseEnter to set timeline.hoveredTrack to null when entering the timeline area to centralize hover tracking.
Editor context — new hoveredTrack state
apps/desktop/src/routes/editor/context.ts
Adds timeline.hoveredTrack to the editor context state: `null

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ZoomTrack as ZoomTrack Component
    participant Editor as Editor Context / State
    participant Store as ZoomSegments Store

    User->>ZoomTrack: mousedown (record start pos/time)
    ZoomTrack->>Editor: read timeline.hoveredTrack
    alt hoveredTrack === "zoom" (hovering existing segment)
        ZoomTrack-->>User: ignore create (early return)
    else not hovering
        ZoomTrack->>ZoomTrack: set creatingSegmentViaDrag, store start
    end

    User->>ZoomTrack: mousemove (first significant move)
    alt first move: create
        ZoomTrack->>ZoomTrack: compute newSegmentDetails() (start,end,index,min)
        ZoomTrack->>Store: createSegment(start,end,index)
    else subsequent moves: update
        ZoomTrack->>ZoomTrack: compute clamped end via neighbors & totalDuration
        ZoomTrack->>Store: updateSegment(segmentId, newEnd)
    end

    User->>ZoomTrack: mouseup
    alt movement occurred
        ZoomTrack->>Store: finalize segment (keep created/updated)
    else no movement
        ZoomTrack->>Store: createSegment(default 1s)
    end

    Note over ZoomTrack,Editor: On segment deletion or zero segments\nreset/adjust hover state as needed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hopped along the timeline track,

I pressed, I dragged — a segment cracked.
Clamped by neighbors, tiny and neat,
I stitched a frame with nimble feet.
A hop, a click — a new little beat.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "desktop: zoom delete bug fix + mouse and drag" directly corresponds to the actual changes in the codebase. The "zoom delete bug fix" refers to the added reset logic for hover state when segments are deleted (handling currentCount < previousCount scenarios), while "mouse and drag" accurately describes the new segment creation functionality implemented through mouse down and drag interactions. The title is specific, concise, and clearly communicates the primary changes without being vague or misleading. Although it doesn't enumerate every technical detail like bounds clamping or minimum duration enforcement, the title appropriately captures the main aspects of the change at a high level suitable for scanning PR history.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1746fa0 and 000ee2c.

📒 Files selected for processing (5)
  • apps/desktop/src/routes/editor/Timeline/ClipTrack.tsx (1 hunks)
  • apps/desktop/src/routes/editor/Timeline/SceneTrack.tsx (2 hunks)
  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (9 hunks)
  • apps/desktop/src/routes/editor/Timeline/index.tsx (1 hunks)
  • apps/desktop/src/routes/editor/context.ts (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (1)

166-206: Clamp end-time to both min and max; prevent overlap with next segment or duration

createSegment/updateSegment enforce only min width, so end can exceed the next segment or track end (especially when gap < min width). This causes overlaps during creation and drag. Clamp to [minEnd, maxEnd] and no-op creation if there isn’t enough space.

Apply this diff:

@@
-          const minDuration = minPixelWidth * secsPerPixel();
-          const minEndTime = startTime + minDuration;
-
-          zoomSegments.splice(index, 0, {
-            start: startTime,
-            end: Math.max(minEndTime, endTime),
-            amount: 1.5,
-            mode: {
-              manual: {
-                x: 0.5,
-                y: 0.5,
-              },
-            },
-          });
-
-          createdSegmentIndex = index;
+          const minDuration = minPixelWidth * spp;
+          const minEndTime = startTime + minDuration;
+          const next = zoomSegments.find((s) => s.start > startTime);
+          const maxEndTime = next ? next.start - 0.1 : trackDuration - 0.1;
+          if (minEndTime > maxEndTime) {
+            // Not enough space to honor min width; skip insertion.
+            return;
+          }
+          const boundedEnd = Math.min(Math.max(endTime, minEndTime), maxEndTime);
+          zoomSegments.splice(index, 0, {
+            start: startTime,
+            end: boundedEnd,
+            amount: 1.5,
+            mode: { manual: { x: 0.5, y: 0.5 } },
+          });
+          createdSegmentIndex = index;
+          segmentCreated = true;
@@
-      });
-    });
-  });
-  segmentCreated = true;
+      });
+    });
+  });
@@
-  const updateSegment = (endTime: number) => {
+  const updateSegment = (endTime: number) => {
     if (!segmentCreated || createdSegmentIndex === -1) return;
-
-    const minDuration = minPixelWidth * secsPerPixel();
-    const minEndTime = startTime + minDuration;
-
-    setProject(
-      "timeline",
-      "zoomSegments",
-      createdSegmentIndex,
-      "end",
-      Math.max(minEndTime, endTime),
-    );
+    const minDuration = minPixelWidth * spp;
+    const minEndTime = startTime + minDuration;
+    const next = project.timeline?.zoomSegments?.find(
+      (s, idx) => idx !== createdSegmentIndex && s.start > startTime,
+    );
+    const maxEndTime = next ? next.start - 0.1 : trackDuration - 0.1;
+    const boundedEnd = Math.min(Math.max(endTime, minEndTime), maxEndTime);
+    setProject("timeline", "zoomSegments", createdSegmentIndex, "end", boundedEnd);
   };

Also applies to: 207-221

🧹 Nitpick comments (4)
apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (4)

160-165: Snapshot secsPerPixel/duration at mousedown to keep math stable while dragging

Avoid drift if zoom/timeline changes mid-drag.

-          const minPixelWidth = 80;
-          let segmentCreated = false;
+          const minPixelWidth = 80;
+          const spp = secsPerPixel();
+          const trackDuration = duration();
+          let segmentCreated = false;
           let createdSegmentIndex = -1;
           const initialMouseX = e.clientX;
           const initialEndTime = startTime + 1;

222-251: Use snapshots during drag; keep bounds consistent with creation logic

Use spp/trackDuration captured at mousedown.

-          const deltaX = moveEvent.clientX - initialMouseX;
-          const deltaTime = deltaX * secsPerPixel();
+          const deltaX = moveEvent.clientX - initialMouseX;
+          const deltaTime = deltaX * spp;
           const newEndTime = initialEndTime + deltaTime;
@@
-          const maxEndTime = nextSegment
-            ? nextSegment.start - 0.1
-            : duration() - 0.1;
+          const maxEndTime = nextSegment ? nextSegment.start - 0.1 : trackDuration - 0.1;
@@
-          const minDuration = minPixelWidth * secsPerPixel();
+          const minDuration = minPixelWidth * spp;

116-144: Use findIndex !== -1 (not undefined); remove dead check

findIndex returns -1 on miss. Cleans up control flow; avoids confusing undefined checks.

-          if (nextSegmentIndex !== undefined) {
+          if (nextSegmentIndex !== -1) {
             const prevSegmentIndex = nextSegmentIndex - 1;
-
-            if (prevSegmentIndex === undefined) return;

43-69: Hover reset on deletion: LGTM; small simplification possible

Solid fix. Consider combining conditions to a single branch.

-    // If segments were deleted (count decreased), reset hover state
-    if (currentCount < prevCount) {
+    // If segments were deleted or none exist, reset hover state
+    if (currentCount < prevCount || currentCount === 0) {
       setHoveringSegment(false);
       setHoveredTime(undefined);
-    }
-
-    // If no segments exist, also reset
-    if (currentCount === 0) {
-      setHoveringSegment(false);
-      setHoveredTime(undefined);
-    }
+    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 548d5ba and 77215df.

📒 Files selected for processing (1)
  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
apps/desktop/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/src/**/*.{ts,tsx}: Desktop icons are auto-imported (unplugin-icons); do not import icons manually
Desktop IPC: Call generated tauri_specta commands/events; listen to generated events and use typed interfaces
Use @tanstack/solid-query for server state in the desktop app

Files:

  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (1)

156-269: LGTM! Well-structured segment creation with live dragging.

The implementation correctly handles:

  • Boundary detection and clamping to prevent overlaps
  • Minimum duration enforcement (80px minimum width)
  • Sorted insertion via dynamic index calculation
  • Memory-safe event listener disposal
  • Default 1-second segment when no drag occurs

Optional: Consider extracting repeated calculations.

The minimum duration/end time logic is calculated in three places (lines 187-188, 212-213, 237-238). While not a functional issue, extracting to a helper or calculating once could improve maintainability:

const getMinEndTime = () => {
  const minDuration = minPixelWidth * secsPerPixel();
  return startTime + minDuration;
};

Additionally, magic numbers (80 for minPixelWidth, 0.1 for boundaries, 1 for default duration) could be named constants at the top of the component for easier tuning.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77215df and 1746fa0.

📒 Files selected for processing (1)
  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
apps/desktop/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/desktop/src/**/*.{ts,tsx}: Desktop icons are auto-imported (unplugin-icons); do not import icons manually
Desktop IPC: Call generated tauri_specta commands/events; listen to generated events and use typed interfaces
Use @tanstack/solid-query for server state in the desktop app

Files:

  • apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (3)
apps/desktop/src/routes/editor/Timeline/ZoomTrack.tsx (3)

43-44: LGTM! Clean state tracking for deletion detection.

The signal provides the foundation for detecting when segments are removed, enabling proper hover state cleanup.


50-69: LGTM! Robust hover state management.

The effect correctly handles both deletion scenarios (count decrease and empty state) and prevents stale hover states when segments are removed from the DOM.


147-153: LGTM! Proper guards prevent unwanted segment creation.

Both guards are correctly implemented:

  • Prevents creation while hovering existing segments
  • Restricts creation to left-click only (addresses previous review feedback)

@ameer2468 ameer2468 merged commit 302ffe1 into main Oct 17, 2025
13 of 14 checks passed
@ameer2468 ameer2468 deleted the zoom-mouse-drag-and-bug-fix branch October 17, 2025 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Desktop Things affecting desktop app

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants