Skip to content

Edit video events in the timeline#1677

Merged
LeonardoRosaa merged 44 commits into
mainfrom
horatiu-lig-10189-edit-events
Jul 21, 2026
Merged

Edit video events in the timeline#1677
LeonardoRosaa merged 44 commits into
mainfrom
horatiu-lig-10189-edit-events

Conversation

@horatiualmasan

@horatiualmasan horatiualmasan commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

What has changed and why?

Update for editing the video events.

How has it been tested?

new tests, manual test

edit_events.mp4

Did you update CHANGELOG.md?

  • Yes
  • Not needed (internal change)

Summary by CodeRabbit

  • New Features
    • Added editable, resizable video event timelines with draggable start/end handles (including updated VideoPlayer/VideoDetails wiring).
    • Annotation updates can now persist optional event time ranges (start/end).
  • Bug Fixes
    • Improved temporal-span validation: rejects inverted ranges and requires both start and end when updating time ranges.
  • Tests
    • Added backend tests covering temporal-span update success and rejection cases.
    • Extended UI tests to verify editable handle rendering and resize/seek behavior.

horatiualmasan and others added 30 commits July 7, 2026 17:54
…ions' into horatiu-lig-9805-import-activitynet-style-event-annotations-3
Previously, filtering and counting video annotations only considered
annotations attached to a video's frames. Videos can also carry
annotations directly (e.g. ActivityNet-style event/classification labels
on the whole video). This makes both the video filter and the annotation
counter consider these direct video annotations in addition to frame
annotations.
…ions-3' into horatiu-lig-9806-filter-videos-by-event-metadata
Mirrors a <video> element's playback state into reactive fields and
exposes intent callbacks (seek, play/pause, mute, fullscreen) for a
custom control bar. Listening via native events keeps it composable
with handlers on the element. Covered by a harness-driven unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compose useVideoPlayback + VideoControls, forcing native <video controls>
off so the full-width scrubber can host aligned timeline overlays. Adds a
startTimeS prop (null waits for a deep-link timestamp) and a region ref
for fullscreen. Updates tests and stories accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hand the deep-linked frame's timestamp to VideoPlayer via startTimeS
instead of seeking after load, and remount on frame-number change with
a composite #key. Align the annotation overlay to the <video> box and
fix min-h-0 layout so the player fills its card. Adds a VideoDetails
test covering the frame-load branch and deep-link handoff.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full-width scrubber + transport buttons that own no playback state and
call back on user intent. Includes helpers (formatTime, clampPercent,
timeFromClientX) and unit tests. Exported from the components barrel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Interactive Playground plus paused / near-end states, on a dark backdrop
matching how the bar overlays a video.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirrors a <video> element's playback state into reactive fields and
exposes intent callbacks (seek, play/pause, mute, fullscreen) for a
custom control bar. Listening via native events keeps it composable
with handlers on the element. Covered by a harness-driven unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Compose useVideoPlayback + VideoControls, forcing native <video controls>
off so the full-width scrubber can host aligned timeline overlays. Adds a
startTimeS prop (null waits for a deep-link timestamp) and a region ref
for fullscreen. Updates tests and stories accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Hand the deep-linked frame's timestamp to VideoPlayer via startTimeS
instead of seeking after load, and remount on frame-number change with
a composite #key. Align the annotation overlay to the <video> box and
fix min-h-0 layout so the player fills its card. Adds a VideoDetails
test covering the frame-load branch and deep-link handoff.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…github.com:lightly-ai/lightly-studio into horatiu-lig-9807-show-event-bars-on-the-timeline.d
horatiualmasan and others added 9 commits July 16, 2026 23:49
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ts-model' into horatiu-lig-9807-show-event-bars-on-the-timeline-3-events-timeline
…ts-timeline' into horatiu-lig-9807-show-event-bars-on-the-timeline-3-events-wiring
…ts-wiring' of github.com:lightly-ai/lightly-studio into horatiu-lig-9807-show-event-bars-on-the-timeline-3-events-wiring
@horatiualmasan
horatiualmasan requested a review from a team as a code owner July 17, 2026 13:04
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c68dcaa1-76ad-416f-b59d-e851383103f9

📥 Commits

Reviewing files that changed from the base of the PR and between 6a7bdfa and 3ed9e89.

📒 Files selected for processing (1)
  • lightly_studio/tests/services/annotations_service/test_update_annotation.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • lightly_studio/tests/services/annotations_service/test_update_annotation.py

📝 Walkthrough

Walkthrough

Annotation updates now carry temporal spans through the API, service, and resolver layers. Video event timeline edges can be resized interactively, with optimistic rendering and persistence through the annotation mutation flow.

Changes

Temporal span annotation editing

Layer / File(s) Summary
Temporal update contracts and dispatch
lightly_studio/src/lightly_studio/api/routes/api/annotation.py, lightly_studio/src/lightly_studio/services/annotations_service/update_annotation.py, lightly_studio/tests/services/annotations_service/test_update_annotation.py
Annotation update inputs and service models accept optional start/end timestamps, validate paired fields, dispatch temporal-span updates, and test the resulting behavior.
Temporal span persistence and validation
lightly_studio/src/lightly_studio/resolvers/annotation_resolver/..., lightly_studio/src/lightly_studio/services/annotations_service/..., lightly_studio/tests/resolvers/annotations/test_update_temporal_span.py
Temporal spans are validated, persisted, refreshed, exported, and tested for successful updates and rejected inputs.
Interactive timeline resizing
lightly_studio_view/src/lib/components/VideoEventTimeline/...
Editable timelines render draggable edge handles, preview pending spans, clamp resize durations, seek during dragging, and emit finalized resize callbacks.
Video editing mutation wiring
lightly_studio_view/src/lib/components/VideoPlayer/VideoPlayer.svelte, lightly_studio_view/src/lib/components/VideoDetails/...
VideoPlayer forwards resize behavior to the timeline, while VideoDetails persists resized spans and refreshes video data. Tests mock the annotation mutation hook.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant VideoEventTimeline
  participant VideoPlayer
  participant VideoDetails
  participant AnnotationAPI
  User->>VideoEventTimeline: Drag event edge
  VideoEventTimeline->>VideoPlayer: Emit resized event span
  VideoPlayer->>VideoDetails: Forward resize callback
  VideoDetails->>AnnotationAPI: Submit start/end timestamps
  AnnotationAPI-->>VideoDetails: Return update result
  VideoDetails->>VideoEventTimeline: Refresh timeline data
Loading

Possibly related PRs

Suggested reviewers: malteebner

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: editable video events in the timeline.
Description check ✅ Passed The description matches the required sections and gives a change summary plus testing notes, though it is sparse on context and reproduction steps.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch horatiu-lig-10189-edit-events

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.

Base automatically changed from horatiu-lig-9807-show-event-bars-on-the-timeline-3-events-wiring to main July 21, 2026 16:44
@lightly-fast-track-bot

lightly-fast-track-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

❌ Fast Track: checks did not pass

Failed guardrails: frontend/complexity (Guardrail threw: Cannot find module 'eslint'
Require stack:

  • /home/runner/work/lightly-studio/lightly-studio/lightly_studio_view/package.json), backend/complexity (Guardrail threw: spawn uv ENOENT), backend/coverage (Guardrail threw: spawn uv ENOENT), diff-size (PR adds 503 line(s), which exceeds the limit of 215.), frontend/coverage ( [FAIL] lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.svelte: coverage data not found
    [FAIL] lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.svelte: coverage data not found
    [FAIL] lightly_studio_view/src/lib/components/VideoPlayer/VideoPlayer.svelte: coverage data not found)
Guardrail Result Message
dummy Always passes.
frontend/complexity Guardrail threw: Cannot find module 'eslint' Require stack: - /home/runner/work/lightly-studio/lightly-studio/lightly_studio_view/package.json
backend/complexity Guardrail threw: spawn uv ENOENT
backend/coverage Guardrail threw: spawn uv ENOENT
diff-size PR adds 503 line(s), which exceeds the limit of 215.
frontend/coverage [FAIL] lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.svelte: coverage data not found [FAIL] lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.svelte: coverage data not found [FAIL] lightly_studio_view/src/lib/components/VideoPlayer/VideoPlayer.svelte: coverage data not found

Reflects 3ed9e89.

@LeonardoRosaa
LeonardoRosaa enabled auto-merge July 21, 2026 17:28

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.test.ts (1)

46-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test exercises handleEventResize itself.

This diff only adds the mock needed for the component to mount; there's no test asserting updateAnnotations is called with the expected AnnotationUpdateInput payload, or that onVideoUpdate fires after resize. Worth a follow-up test, especially once error handling is added to handleEventResize.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.test.ts`
around lines 46 - 50, Extend the VideoDetails tests to exercise
handleEventResize, verifying updateAnnotations receives the expected
AnnotationUpdateInput payload and onVideoUpdate is called after a successful
resize. Reuse the existing useUpdateAnnotationsMutation mock and cover the
success path without changing unrelated component behavior.
lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.test.ts (1)

94-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding coverage for the 'end' edge and the min-duration clamp.

clampSpan has a symmetric 'end' branch and a MIN_EVENT_DURATION_S clamp that aren't exercised by any test here — worth a follow-up test given how easy it is to regress clamping math.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.test.ts`
around lines 94 - 128, Add tests alongside the existing edge-drag test for
dragging the end handle and for enforcing the MIN_EVENT_DURATION_S minimum when
an edge crosses the opposite boundary. Reuse the existing VideoEventTimeline
setup and geometry mocks, and assert both onResize’s resulting start/end values
and onSeek’s final playback position.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.svelte`:
- Around line 46-61: Update handleEventResize to catch rejected
updateAnnotations calls and surface the persistence error through the
component’s existing user-facing error notification mechanism, while retaining
the finally block’s onVideoUpdate refresh. Ensure the fire-and-forget
VideoEventTimeline onResize callback does not produce an unhandled rejection.

In
`@lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.svelte`:
- Around line 207-225: Make the edge handles in the editable branch
keyboard-operable by adding a keydown handler alongside the existing pointer
handlers and implementing arrow-key adjustments for the corresponding start or
end edge. Reuse the existing resize/update logic where possible, preserve
aria-valuenow updates and duration/span bounds, and ignore unrelated keys.

In
`@lightly_studio/src/lightly_studio/services/annotations_service/update_annotation.py`:
- Around line 60-66: Update the temporal-span handling in the annotation update
flow to raise a ValueError when exactly one of annotation_update.start_time_s or
annotation_update.end_time_s is provided. Continue calling
annotations_service.update_temporal_span only when both bounds are present, and
preserve existing handling for updates without either bound.

---

Nitpick comments:
In `@lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.test.ts`:
- Around line 46-50: Extend the VideoDetails tests to exercise
handleEventResize, verifying updateAnnotations receives the expected
AnnotationUpdateInput payload and onVideoUpdate is called after a successful
resize. Reuse the existing useUpdateAnnotationsMutation mock and cover the
success path without changing unrelated component behavior.

In
`@lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.test.ts`:
- Around line 94-128: Add tests alongside the existing edge-drag test for
dragging the end handle and for enforcing the MIN_EVENT_DURATION_S minimum when
an edge crosses the opposite boundary. Reuse the existing VideoEventTimeline
setup and geometry mocks, and assert both onResize’s resulting start/end values
and onSeek’s final playback position.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4e1ddc7e-be7c-4c8f-848a-6ab14d13ca28

📥 Commits

Reviewing files that changed from the base of the PR and between 6b59d02 and d80d259.

📒 Files selected for processing (12)
  • lightly_studio/src/lightly_studio/api/routes/api/annotation.py
  • lightly_studio/src/lightly_studio/resolvers/annotation_resolver/__init__.py
  • lightly_studio/src/lightly_studio/resolvers/annotation_resolver/update_temporal_span.py
  • lightly_studio/src/lightly_studio/services/annotations_service/__init__.py
  • lightly_studio/src/lightly_studio/services/annotations_service/update_annotation.py
  • lightly_studio/src/lightly_studio/services/annotations_service/update_temporal_span.py
  • lightly_studio/tests/resolvers/annotations/test_update_temporal_span.py
  • lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.svelte
  • lightly_studio_view/src/lib/components/VideoDetails/VideoDetails.test.ts
  • lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.svelte
  • lightly_studio_view/src/lib/components/VideoEventTimeline/VideoEventTimeline.test.ts
  • lightly_studio_view/src/lib/components/VideoPlayer/VideoPlayer.svelte

@LeonardoRosaa
LeonardoRosaa added this pull request to the merge queue Jul 21, 2026
Merged via the queue into main with commit 4f9be71 Jul 21, 2026
24 checks passed
@LeonardoRosaa
LeonardoRosaa deleted the horatiu-lig-10189-edit-events branch July 21, 2026 18:19
@coderabbitai coderabbitai Bot mentioned this pull request Jul 21, 2026
2 tasks
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.

2 participants