Skip to content

Conversation

@naoNao89
Copy link
Contributor

@naoNao89 naoNao89 commented Jul 30, 2025

Summary

This PR addresses two critical issues in the OpenCut editor:

🎯 Issue #481: Timeline vertical scroll synchronization

  • Problem: Timeline vertical scroll synchronization issues causing race conditions between left panel and main timeline
  • Solution: Separate horizontal and vertical scroll sync mechanisms with robust error handling

🎯 Issue #490: Audio cursor not stopping at end of file

  • Problem: Audio files shorter than 10 seconds continue playing until the 10-second timeline minimum instead of stopping at actual audio end
  • Solution: Use actual content duration instead of timeline duration for playback stopping logic

Changes Made

Timeline Scroll Fix (#481)

  • Modified: apps/web/src/components/editor/timeline/index.tsx
    • Separated horizontal and vertical scroll sync mechanisms using independent refs
    • Fixed inconsistent cleanup logic for event listeners
    • Added robust error handling with try-catch blocks
    • Improved code documentation and maintainability

Audio Cursor Fix (#490)

  • Modified: apps/web/src/stores/playback-store.ts

    • Added getEffectivePlaybackDuration() method with comprehensive error handling
    • Changed playback stopping logic to use actual content duration
    • Added TypeScript interfaces for timeline store integration
  • Modified: apps/web/src/stores/timeline-store.ts

    • Exposed timeline store globally to avoid circular dependencies
    • Added documentation for the global store pattern

Quality Assurance

Code Quality

  • Full TypeScript compliance with proper type safety
  • Comprehensive error handling and graceful fallbacks
  • Extensive inline documentation and comments
  • Follows project patterns and conventions

Functionality Testing

  • Timeline vertical scroll synchronization fixed ✅
  • Audio < 10s stops at actual duration ✅
  • Audio ≥ 10s works as before ✅
  • Video playback completely unaffected ✅
  • No breaking changes or regressions ✅

Performance & Reliability

  • Minimal overhead (single function call per animation frame for audio fix)
  • No memory leaks or object creation issues
  • Graceful degradation with fallback mechanisms
  • Cross-browser compatibility using standard APIs

Root Cause Analysis

Timeline Scroll Issue (#481)

  • Shared state between horizontal and vertical scroll sync caused race conditions
  • Inconsistent cleanup logic led to event listener accumulation
  • Nested conditional logic made vertical sync dependent on horizontal sync

Audio Cursor Issue (#490)

  • Playback timer used timeline duration (10s minimum) instead of actual content duration
  • No mechanism to access actual audio/video length for stopping logic
  • Timeline store circular dependency prevented direct access to content duration

Testing

Manual Testing Completed

  • Timeline vertical scroll synchronization working correctly
  • Audio files < 10 seconds stop at actual duration
  • Audio files ≥ 10 seconds work as before
  • Video playback unaffected
  • Mixed audio/video content works correctly
  • Edge cases (empty timelines, invalid data) handled gracefully

Browser Testing

  • Chrome/Chromium-based browsers
  • Firefox
  • Safari (WebKit)

Risk Assessment

  • Risk Level: Low
  • Breaking Changes: None
  • Performance Impact: Minimal positive (more accurate playback, better scroll sync)
  • Rollback Complexity: Simple (revert commits)

Implementation Highlights

Timeline Scroll Fix

  • Independent isUpdatingHorizontalRef and isUpdatingVerticalRef prevent race conditions
  • Consistent event listener cleanup prevents memory leaks
  • Robust error handling with console warnings for debugging

Audio Cursor Fix

  • getEffectivePlaybackDuration() safely accesses timeline store via global reference
  • Comprehensive validation handles all edge cases (NaN, infinite, negative values)
  • Maintains full backward compatibility with existing playback behavior

Closes

Additional Notes

  • Both fixes maintain full backward compatibility
  • No external dependencies added
  • Ready for immediate deployment
  • Comprehensive documentation and error handling included
  • Fixes have been validated by automated code review (coderabbitai)

…app#481)

- Separate horizontal and vertical scroll sync mechanisms to prevent race conditions
- Fix inconsistent cleanup logic for event listeners
- Add robust error handling with try-catch blocks
- Improve code documentation and maintainability

Fixes OpenCut-app#481
@vercel
Copy link

vercel bot commented Jul 30, 2025

@naoNao89 is attempting to deploy a commit to the OpenCut OSS Team on Vercel.

A member of the Team first needs to authorize it.

@netlify
Copy link

netlify bot commented Jul 30, 2025

👷 Deploy request for appcut pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit a273f42

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 30, 2025

Walkthrough

The Timeline component's scroll synchronization logic was refactored to separate horizontal and vertical sync into two independent effects. Each direction now uses its own throttling ref and event listeners, with error handling and cleanup. This change isolates horizontal and vertical syncing, addressing race conditions and improving reliability.

Changes

Cohort / File(s) Change Summary
Timeline Scroll Sync Refactor
apps/web/src/components/editor/timeline/index.tsx
Refactored scroll synchronization logic: split horizontal and vertical syncing into separate useEffect hooks, introduced independent throttling refs, added error handling, and ensured cleanup of event listeners. No changes to exported/public API.
Playback Duration Fix
apps/web/src/stores/playback-store.ts
Added getEffectivePlaybackDuration method to dynamically determine playback duration from timeline store, updated playback timer logic to use effective duration instead of fixed minimum. Added type declarations and detailed comments.
Global Timeline Store Exposure
apps/web/src/stores/timeline-store.ts
Exposed useTimelineStore hook globally via globalThis.__timelineStore to allow playback store to access timeline duration without circular dependencies. No changes to exports or control flow otherwise.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TimelineComponent
    participant RulerViewport
    participant TracksViewport
    participant TrackLabelsViewport

    User->>RulerViewport: Scroll horizontally
    RulerViewport->>TimelineComponent: onHorizontalScroll event
    TimelineComponent->>TracksViewport: Sync horizontal scrollLeft

    User->>TracksViewport: Scroll horizontally
    TracksViewport->>TimelineComponent: onHorizontalScroll event
    TimelineComponent->>RulerViewport: Sync horizontal scrollLeft

    User->>TracksViewport: Scroll vertically
    TracksViewport->>TimelineComponent: onVerticalScroll event
    TimelineComponent->>TrackLabelsViewport: Sync vertical scrollTop

    User->>TrackLabelsViewport: Scroll vertically
    TrackLabelsViewport->>TimelineComponent: onVerticalScroll event
    TimelineComponent->>TracksViewport: Sync vertical scrollTop
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Vertical timeline and main timeline scroll y position should sync (#481)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Added getEffectivePlaybackDuration and playback timer update (apps/web/src/stores/playback-store.ts) Not related to scroll synchronization or vertical timeline sync issue #481; addresses audio playback duration correctness.
Global exposure of timeline store via globalThis.__timelineStore (apps/web/src/stores/timeline-store.ts) Supports playback duration fix, unrelated to scroll sync or vertical timeline scroll bug.

Possibly related PRs

Poem

In the Timeline’s gentle scroll,
Horizontal, vertical—now in control!
Two refs keep sync in line,
No more race conditions to malign.
With every hop and every track,
The rabbit cheers: “Scrolls are back!”
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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/web/src/components/editor/timeline/index.tsx (1)

482-538: Excellent implementation of independent vertical scroll synchronization.

This new effect correctly addresses the core issue #481 by providing dedicated vertical scroll sync with its own throttling state. The bidirectional synchronization between track labels and tracks content is well-implemented.

Consider using separate timing refs for each scroll handler to avoid potential timing conflicts:

+  const lastTrackLabelsSync = useRef(0);
+  const lastTracksVerticalSync = useRef(0);
   
   const handleTrackLabelsScroll = () => {
     const now = Date.now();
-    if (isUpdatingVerticalRef.current || now - lastVerticalSync.current < 16)
+    if (isUpdatingVerticalRef.current || now - lastTrackLabelsSync.current < 16)
       return;
-    lastVerticalSync.current = now;
+    lastTrackLabelsSync.current = now;
     // ... rest of handler
   };

   const handleTracksVerticalScroll = () => {
     const now = Date.now();
-    if (isUpdatingVerticalRef.current || now - lastVerticalSync.current < 16)
+    if (isUpdatingVerticalRef.current || now - lastTracksVerticalSync.current < 16)
       return;
-    lastVerticalSync.current = now;
+    lastTracksVerticalSync.current = now;
     // ... rest of handler
   };

This would provide even more precise throttling control for each direction.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between deda0e3 and 0fe7b66.

📒 Files selected for processing (1)
  • apps/web/src/components/editor/timeline/index.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{jsx,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{jsx,tsx}: Don't use accessKey attribute on any HTML element.
Don't set aria-hidden="true" on focusable elements.
Don't add ARIA roles, states, and properties to elements that don't support them.
Don't use distracting elements like <marquee> or <blink>.
Only use the scope prop on <th> elements.
Don't assign non-interactive ARIA roles to interactive HTML elements.
Make sure label elements have text content and are associated with an input.
Don't assign interactive ARIA roles to non-interactive HTML elements.
Don't assign tabIndex to non-interactive HTML elements.
Don't use positive integers for tabIndex property.
Don't include "image", "picture", or "photo" in img alt prop.
Don't use explicit role property that's the same as the implicit/default role.
Make static elements with click handlers use a valid role attribute.
Always include a title element for SVG elements.
Give all elements requiring alt text meaningful information for screen readers.
Make sure anchors have content that's accessible to screen readers.
Assign tabIndex to non-interactive HTML elements with aria-activedescendant.
Include all required ARIA attributes for elements with ARIA roles.
Make sure ARIA properties are valid for the element's supported roles.
Always include a type attribute for button elements.
Make elements with interactive roles and handlers focusable.
Give heading elements content that's accessible to screen readers (not hidden with aria-hidden).
Always include a lang attribute on the html element.
Always include a title attribute for iframe elements.
Accompany onClick with at least one of: onKeyUp, onKeyDown, or onKeyPress.
Accompany onMouseOver/onMouseOut with onFocus/onBlur.
Include caption tracks for audio and video elements.
Use semantic elements instead of role attributes in JSX.
Make sure all anchors are valid and navigable.
Ensure all ARIA properties (aria-*) are valid.
Use valid, non-abstract ARIA roles for elements with...

Files:

  • apps/web/src/components/editor/timeline/index.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals.
Don't use the arguments object.
Don't use the comma operator.
Don't write functions that exceed a given Cognitive Complexity score.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names that aren't base 10 or use underscore separators.
Remove redundant terms from logical expressions.
Use while loops instead of for loops when you don't need initializer and update expressions.
Don't reassign const variables....

Files:

  • apps/web/src/components/editor/timeline/index.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't return a value from a function with the return type 'void'.
Don't use the TypeScript directive @ts-ignore.
Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use either T[] or Array<T> consistently.
Initialize each enum member value explicitly.
Use export type for types.
Use import type for types.
Make sure all enum members are literal values.
Don't use TypeScript const enum.
Don't declare empty interfaces.
Don't let variables evolve into any type through reassignments.
Don't use the any type.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use implicit any type on variable declarations.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Use consistent accessibility modifiers on class properties and methods.
Use function types instead of object types with call signatures.
Don't use void type outside of generic or return types.

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and...

Files:

  • apps/web/src/components/editor/timeline/index.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: simonorzel26
PR: OpenCut-app/OpenCut#324
File: apps/web/src/components/editor/snap-indicator.tsx:43-43
Timestamp: 2025-07-17T08:26:10.961Z
Learning: In the timeline refactor PR #324, the snap indicator component in apps/web/src/components/editor/snap-indicator.tsx requires the hard-coded `ml-48` class in addition to the calculated `leftPosition` for proper alignment. This is intentional and needed for the new timeline layout.
apps/web/src/components/editor/timeline/index.tsx (3)

Learnt from: simonorzel26
PR: #324
File: apps/web/src/components/editor/snap-indicator.tsx:43-43
Timestamp: 2025-07-17T08:26:10.961Z
Learning: In the timeline refactor PR #324, the snap indicator component in apps/web/src/components/editor/snap-indicator.tsx requires the hard-coded ml-48 class in addition to the calculated leftPosition for proper alignment. This is intentional and needed for the new timeline layout.

Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .cursor/rules/ultracite.mdc:0-0
Timestamp: 2025-07-27T22:15:27.748Z
Learning: Applies to **/*.{jsx,tsx} : Only use the scope prop on <th> elements.

Learnt from: CR
PR: OpenCut-app/OpenCut#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-07-27T22:14:46.402Z
Learning: Applies to **/*.{jsx,tsx} : Only use the scope prop on <th> elements.

⏰ 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). (2)
  • GitHub Check: build (windows-latest)
  • GitHub Check: build (macos-latest)
🔇 Additional comments (2)
apps/web/src/components/editor/timeline/index.tsx (2)

117-121: LGTM! Excellent separation of concerns.

The introduction of separate refs for horizontal and vertical scroll synchronization correctly addresses the race condition issue described in #481. Using independent throttling state prevents the conflicts that occurred with the shared isUpdatingRef approach.


427-480: Horizontal Scroll Synchronization Is Well-Implemented

The useEffect cleanly sets up bidirectional scroll syncing between the ruler and tracks with:

  • A 16 ms throttle (≈60 fps) using isUpdatingHorizontalRef and timestamp checks
  • Try-catch blocks that guard against runtime errors
  • Proper teardown of event listeners on unmount

A quick search didn’t reveal any other throttling or debounce constants in the repo, so this appears to be the sole timing control. Please manually verify that the 16 ms interval provides smooth scrolling on lower-end devices.

- Use actual content duration instead of timeline duration for playback stopping
- Add getEffectivePlaybackDuration() method with comprehensive error handling
- Expose timeline store globally to avoid circular dependencies
- Maintain full backward compatibility for all existing functionality

Fixes OpenCut-app#490: Audio files shorter than 10 seconds now stop at actual duration
instead of continuing until the 10-second timeline minimum.
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/web/src/stores/playback-store.ts (1)

186-186: Consider using consistent console method for warnings.

The coding guidelines specify "Don't use console" but these console.warn statements appear to be intentional debugging aids. However, consider whether these warnings should use a proper logging system in production or be conditionally enabled based on environment.

If you want to maintain these warnings, consider using a consistent logging approach:

-        console.warn("Timeline store getState is not a function, using fallback duration");
+        if (process.env.NODE_ENV === 'development') {
+          console.warn("Timeline store getState is not a function, using fallback duration");
+        }

Also applies to: 197-197

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fe7b66 and a273f42.

📒 Files selected for processing (2)
  • apps/web/src/stores/playback-store.ts (3 hunks)
  • apps/web/src/stores/timeline-store.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/stores/timeline-store.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Don't use consecutive spaces in regular expression literals.
Don't use the arguments object.
Don't use the comma operator.
Don't write functions that exceed a given Cognitive Complexity score.
Don't use unnecessary boolean casts.
Don't use unnecessary callbacks with flatMap.
Use for...of statements instead of Array.forEach.
Don't create classes that only have static members (like a static namespace).
Don't use this and super in static contexts.
Don't use unnecessary catch clauses.
Don't use unnecessary constructors.
Don't use unnecessary continue statements.
Don't export empty modules that don't change anything.
Don't use unnecessary escape sequences in regular expression literals.
Don't use unnecessary labels.
Don't use unnecessary nested block statements.
Don't rename imports, exports, and destructured assignments to the same name.
Don't use unnecessary string or template literal concatenation.
Don't use String.raw in template literals when there are no escape sequences.
Don't use useless case statements in switch statements.
Don't use ternary operators when simpler alternatives exist.
Don't use useless this aliasing.
Don't initialize variables to undefined.
Don't use the void operators (they're not familiar).
Use arrow functions instead of function expressions.
Use Date.now() to get milliseconds since the Unix Epoch.
Use .flatMap() instead of map().flat() when possible.
Use literal property access instead of computed property access.
Don't use parseInt() or Number.parseInt() when binary, octal, or hexadecimal literals work.
Use concise optional chaining instead of chained logical expressions.
Use regular expression literals instead of the RegExp constructor when possible.
Don't use number literal object member names that aren't base 10 or use underscore separators.
Remove redundant terms from logical expressions.
Use while loops instead of for loops when you don't need initializer and update expressions.
Don't reassign const variables....

Files:

  • apps/web/src/stores/playback-store.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Don't use primitive type aliases or misleading types.
Don't use empty type parameters in type aliases and interfaces.
Don't use any or unknown as type constraints.
Don't return a value from a function with the return type 'void'.
Don't use the TypeScript directive @ts-ignore.
Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and type annotations.
Use either T[] or Array<T> consistently.
Initialize each enum member value explicitly.
Use export type for types.
Use import type for types.
Make sure all enum members are literal values.
Don't use TypeScript const enum.
Don't declare empty interfaces.
Don't let variables evolve into any type through reassignments.
Don't use the any type.
Don't misuse the non-null assertion operator (!) in TypeScript files.
Don't use implicit any type on variable declarations.
Don't merge interfaces and classes unsafely.
Don't use overload signatures that aren't next to each other.
Use the namespace keyword instead of the module keyword to declare TypeScript namespaces.
Use consistent accessibility modifiers on class properties and methods.
Use function types instead of object types with call signatures.
Don't use void type outside of generic or return types.

**/*.{ts,tsx}: Don't use TypeScript enums.
Don't export imported variables.
Don't add type annotations to variables, parameters, and class properties that are initialized with literal expressions.
Don't use TypeScript namespaces.
Don't use non-null assertions with the ! postfix operator.
Don't use parameter properties in class constructors.
Don't use user-defined types.
Use as const instead of literal types and...

Files:

  • apps/web/src/stores/playback-store.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: simonorzel26
PR: OpenCut-app/OpenCut#324
File: apps/web/src/components/editor/snap-indicator.tsx:43-43
Timestamp: 2025-07-17T08:26:10.961Z
Learning: In the timeline refactor PR #324, the snap indicator component in apps/web/src/components/editor/snap-indicator.tsx requires the hard-coded `ml-48` class in addition to the calculated `leftPosition` for proper alignment. This is intentional and needed for the new timeline layout.
⏰ 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). (1)
  • GitHub Check: build (windows-latest)
🔇 Additional comments (4)
apps/web/src/stores/playback-store.ts (4)

4-16: LGTM! Well-structured type definitions for avoiding circular dependencies.

The approach of using global type declarations to avoid circular imports between stores is appropriate. The interface definitions are clear and properly typed.


21-21: LGTM! Clean interface extension.

The addition of getEffectivePlaybackDuration method to the interface is straightforward and follows TypeScript conventions.


38-46: LGTM! Improved playback stopping logic with clear documentation.

The change from using state.duration to state.getEffectivePlaybackDuration() directly addresses the issue where audio shorter than 10 seconds would continue playing. The detailed comment explains the fix well.


158-231: Excellent implementation with comprehensive error handling and validation.

The getEffectivePlaybackDuration method is well-implemented with:

  • Proper error handling using try-catch
  • Multiple validation checks for type safety
  • Clear fallback behavior
  • Detailed JSDoc documentation
  • Appropriate console warnings for debugging

The method correctly handles edge cases like:

  • Missing timeline store
  • Invalid function types
  • Non-numeric, NaN, or infinite duration values
  • Zero or negative durations

The implementation follows defensive programming principles and provides excellent observability through console warnings.

@naoNao89 naoNao89 changed the title Fix timeline vertical scroll synchronization issue (#481) fix: resolve timeline scroll sync and audio cursor issues (#481, #490) Jul 30, 2025
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.

[BUG] Editor cursor does not stop at the end of an audio file [BUG] UI: vertical timeline does not sync scroll y position

1 participant