summaryrefslogtreecommitdiff
path: root/apps/web/src/timeline/components
diff options
context:
space:
mode:
authorcashmere <cashmere@autistici.org>2026-07-26 21:06:33 +0200
committercashmere <cashmere@autistici.org>2026-07-26 21:06:33 +0200
commitfc0a801e8829b623d62137a3f7cbdec38c6d555a (patch)
treeb4c9a0458e92eaf44d3331ce3b0ea3ff906a9d15 /apps/web/src/timeline/components
parentcf5e79e919144200294fb9fed22a222592a0aeea (diff)
downloadopencut-fc0a801e8829b623d62137a3f7cbdec38c6d555a.tar.gz
feat: freeze frame at playhead
Enable the timeline toolbar freeze-frame button: capture the composited canvas as PNG, add it as an image media asset, split the video element at the playhead, shift the right part by the freeze duration, and insert a 3-second image element in the gap. Adds RendererManager.captureFrame(), reused by createSnapshot(). Also adds the Nix devshell (flake.nix/flake.lock: bun + nodejs) and AGENTS.md repository guidelines.
Diffstat (limited to 'apps/web/src/timeline/components')
-rw-r--r--apps/web/src/timeline/components/timeline-toolbar.tsx18
1 files changed, 15 insertions, 3 deletions
diff --git a/apps/web/src/timeline/components/timeline-toolbar.tsx b/apps/web/src/timeline/components/timeline-toolbar.tsx
index 22acd447..6208ef27 100644
--- a/apps/web/src/timeline/components/timeline-toolbar.tsx
+++ b/apps/web/src/timeline/components/timeline-toolbar.tsx
@@ -95,6 +95,18 @@ function ToolbarLeftSection() {
const isCurrentlyBookmarked = useEditor((e) =>
e.scenes.isBookmarked({ time: e.playback.getCurrentTime() }),
);
+ const canFreezeFrame = useEditor((e) => {
+ const time = e.playback.getCurrentTime();
+ const { overlay, main } = e.scenes.getActiveScene().tracks;
+ return [...overlay, main].some((track) =>
+ track.elements.some(
+ (element) =>
+ element.type === "video" &&
+ time > element.startTime &&
+ time < element.startTime + element.duration,
+ ),
+ );
+ });
const selectedElement =
selectedElements.length === 1
? (editor.timeline.getElementsWithTracks({
@@ -185,9 +197,9 @@ function ToolbarLeftSection() {
<ToolbarButton
icon={<HugeiconsIcon icon={SnowIcon} />}
- tooltip="Freeze frame (coming soon)"
- disabled={true}
- onClick={({ event: _event }) => {}}
+ tooltip="Freeze frame"
+ disabled={!canFreezeFrame}
+ onClick={({ event }) => handleAction({ action: "freeze-frame", event })}
/>
<ToolbarButton