Snapshot pages (for [snap]): player-empty, player-loaded, player-segment, sidebar-folders, export-progress, settings-folders, settings-saves.
As the user, I press Command-O, choose a video, and it appears in the player paused on frame 1. Screens: player-empty, player-loaded Acceptance:
- [snap] Empty window shows "Open or drop a video" and the supported formats.
- [snap] Loaded window shows the frame, file name in the title, and the transport bar.
- [unit] Frame list for a sample MP4 has the right count and rising timestamps. Status: done
As the user, I press Space to play or pause. Depends: S-001 Acceptance:
- [self] Space toggles playback; the play button icon matches the state.
- [self] Volume slider changes volume; mute works. Status: todo
As the user, I press Left or Right to step one frame back or forward. Depends: S-001 Screens: player-loaded Acceptance:
- [unit] Stepping uses the frame list: Right at the last frame stays; Left at frame 0 stays.
- [self] After a step the displayed frame index and timecode update and match the player time.
- [snap] Frame counter and timecode (mm:ss.mmm) show beside the playhead. Status: done
As the user, I drag the playhead and the picture follows frame-accurately. Buttons jump 10 seconds back and forward. Depends: S-001 Acceptance:
- [unit] Time to frame lookup snaps to the frame at or before the time.
- [self] Dragging seeks with zero tolerance and lands on exact frames. Status: done
As the user, I press I and O (or drag the two handles) to set the segment start and end; Reset clears it. Depends: S-004 Screens: player-segment Acceptance:
- [snap] The timeline shows the segment as a highlighted range with two handles.
- [unit] End before start wraps around the end of the clip (frame ranges as in the old app).
- [self] Reset removes the segment. Status: done
As the user, I press L and the segment plays on repeat. Depends: S-005 Acceptance:
- [self] Playback jumps from the end frame to the start frame with no visible stall.
- [self] Without a segment, L loops the whole clip. Status: done
As the user, I turn on "Reverse loop" and the segment plays forward, then backward, then forward. Depends: S-006 Acceptance:
- [unit] Reverse frame order is 0..n-1 then n-2..1 (same as the old
reverseFrameOrder). - [self] The loop alternates direction at the segment ends without building a temporary file.
- [manual] Reversed playback is smooth on H.264 and HEVC clips. Status: done
As the user, I pick 2×, 1.5×, 1.25×, 1×, 0.75×, 0.5× or 0.25× and playback, loops, and exports use it. Depends: S-002 Acceptance:
- [unit] Speed list is exactly 2, 1.5, 1.25, 1, 0.75, 0.5, 0.25.
- [self] At 2× playback covers about twice the frames, and a 2× export is half as long.
- [self] Pitch stays natural at reduced speed. Status: done
As the user, I flip the video horizontally for viewing, screenshots, and exports. Depends: S-001 Acceptance:
- [self] The mirror toggle flips the displayed picture; screenshots and exports match it. Status: done
As the user, I see five folder shortcuts (Movies, Downloads, Desktop, Documents, Pictures) and the videos in the chosen one. Screens: sidebar-folders Acceptance:
- [unit] Folder listing keeps only supported extensions and sorts case-insensitively.
- [snap] Sidebar shows the folders and the video list; the open file is highlighted. Status: done
As the user, I click a video in the list and it opens in the current window. Depends: S-010 Acceptance:
- [self] Clicking replaces the open video and resets the segment. Status: todo
As the user, I drag a video onto the window or the Dock icon and it opens. Depends: S-001 Acceptance:
- [self] Drop on the window opens the file; unsupported files show an error banner.
- [manual] Dock icon drop and Finder "Open With" open a new window. Status: todo
As the user, I press Command-N and get a second window with its own video. Acceptance:
- [self] Two windows play different files independently. Status: todo
As the user, I refresh the list and hide or show the sidebar. Depends: S-010 Acceptance:
- [self] Refresh picks up a file added to the folder. Status: todo
As the user, I press S and the exact frame is saved as PNG in the screenshot folder as ssNNNNNNNN.png.
Depends: S-003, S-009
Acceptance:
- [unit] Next file counter is the max of saved counter and existing files plus 1.
- [self] The PNG matches the displayed frame size, mirrored if mirror is on.
- [snap] A toast confirms the saved file name. Status: done
As the user, I choose "Export MP4 H.264, 24 FPS, 768x512 (letterboxed)" from the export menu and get loopNNNNNNNN.mp4 of the segment, honoring speed, mirror, and ping-pong.
Depends: S-007, S-008, S-009
Screens: export-progress
Acceptance:
- [unit] Letterbox rect fits inside 768x512 with black bars and even sizes.
- [self] Output has the right duration and frame rate.
- [snap] A progress sheet shows percent and a Cancel button. Status: done
As the user, I choose "Export animated GIF, 15 FPS, max 640 px wide" and get a .gif in the export folder.
Depends: S-016
Acceptance:
- [self] GIF is 15 fps and at most 640 px wide, loops forever. Status: done
As the user, I press Cancel and the export stops and leaves no partial file. Depends: S-016 Acceptance:
- [self] Cancel removes the unfinished output. Status: done
As the user, I press Delete and the segment is cut out of the clip. Depends: S-005 Acceptance:
- [unit] Remaining frame ranges for a middle, edge, and wrapped selection are right.
- [self] The player shows the shortened clip; the original file is unchanged. Status: done
As the user, I choose Append and add another video to the end. Depends: S-001 Acceptance:
- [self] The playhead timeline covers both clips; frame stepping crosses the join. Status: done
As the user, I press Command-Z to undo the last append or delete. Depends: S-019 Acceptance:
- [self] One level of undo restores the previous clip and segment. Status: done
As the user, I export a segment and the MP4 has the clip's sound, cut to the same frames, reversed on the ping-pong return, and slowed with the picture. Depends: S-016 Acceptance:
- [unit] Frame order splits into forward and backward audio runs; a wrapped segment splits at the jump.
- [self] The MP4 has an audio track as long as the picture.
- [self] Ping-pong sound rises, then falls back.
- [self] At half speed the sound lasts twice as long and keeps its pitch. Status: done
As the user, I open a file AVFoundation cannot read and it still plays, after a short "Preparing media" step. Depends: S-001 Acceptance:
- [unit] Playback plan picks direct, remux, or transcode from stream info.
- [self] An MKV with H.264 remuxes in seconds; an AVI transcodes with progress.
- [self] Without ffmpeg installed, an error banner names what is missing. Status: done
As the user, I open an HLG or PQ clip and it shows with sensible color. Acceptance:
- [manual] HDR clips are not black. Status: todo
As the user, I open an animated GIF and step through its frames. Depends: S-022 Acceptance:
- [self] GIF opens and steps frame by frame. Status: done
As the user, I edit the five folder shortcuts and the screenshot and export folders in Settings. Screens: settings-folders, settings-saves Acceptance:
- [snap] Settings tabs "Folders" and "Saves".
- [unit] Settings survive a restart; counters never go backward. Status: done
As the user, I can do every action from the menu bar with its shortcut shown. Acceptance:
- [self] Each menu item is enabled only when it can run. Status: todo
As the user, I see a proper icon, an empty-folder message, and clear error banners. Screens: player-empty, sidebar-folders Acceptance:
- [snap] Folder with no videos shows "No supported videos". Status: todo
As the user, I pick 1.25×, 1.5×, or 2× from the Speed menu (or Control-1 to 7) and the clip plays and exports faster. Depends: S-008 Acceptance:
- [unit] Speeds list has 2, 1.5, 1.25 above 1; a 2× plan shows every other frame.
- [self] 2× playback covers about twice the frames; a 2× export is half as long. Status: done
As the user, I open a video and it loops without my turning Loop on. Depends: S-006 Acceptance:
- [self] A new player has looping on. Status: done
As the user, I click Export, or press Command-E, and get an MP4 at the clip's own size and frame rate. The arrow next to Export offers the 768×512 MP4 and the GIF. Depends: S-016 Acceptance:
- [unit] The size is the source size rounded to even; the bit rate grows with the pixel count, within 8 to 55 Mbit/s.
- [self] A 640×360 30 fps clip exports as 640×360 at 30 fps, with the speed and mirror applied. Status: done
As the user, I turn on Mirror (M) and the picture turns around its vertical centre line: left becomes right and it stays the right way up, in the player, screenshots, and exports. Depends: S-009 Acceptance:
- [self] The player's frame has the square on the right when mirrored, same size; turning Mirror off clears it. Status: done
As the user, I open the Adjustments panel from the toolbar (Option-Command-A), move the sliders, and see the picture change. Screenshots and exports match. Reset All puts every slider back. Depends: S-001 Screens: adjust Acceptance:
- [self] Saturation 0 removes the colour in the player's frame; brightness changes screenshots.
- [self] With the panel open, at the default and a narrow window, its sliders and the transport buttons stay inside the window.
- [snap] The panel opens beside the video with five sliders, their values, and Reset All. Status: done
As the user, I turn on Video Info (Option-Command-I) and the size and frame rate show over the corner of the video. It stays off until I turn it on, and the choice is kept. Depends: S-001 Screens: info Acceptance:
- [snap] A dark capsule at the top left reads "640×360 · 30 fps". Status: done
As the user, I hover over any icon button and see what it does and its shortcut. Acceptance:
- [unit] Every toolbar item, transport button, and the volume control has a tooltip view; resting on one shows its label after half a second, and leaving hides it.
- [manual] The label appears below the icon on screen. Status: done
- Electron shell, asar packing, single-instance lock: a native app does not need them.
- Bundled GPL ffmpeg build: ffmpeg is optional and read from Homebrew; no redistribution.
- Backup of app.asar versions: git replaces it.
As the user, I pick Light, Dark, or System in Settings > Appearance, and every window follows at once. Screens: settings-appearance-light.png, settings-appearance-dark.png Acceptance:
- [self] Dark, Light, and System set the app-wide appearance; System follows the Mac.
- [snap] The Appearance tab shows a three-way control above the accent preview.
- [manual] The choice is kept after relaunch. Status: done
As the user, I choose "Export MP4 (Upscaled 2×)" and get a video twice the size, enlarged by Apple's machine-learning upscaler. Acceptance:
- [self] A 640×360 clip exports as 1280×720 at the same frame rate, with the mirror and adjustments kept.
- [self] A "Faster but Softer" variant gives the same size, working from half-size pictures (about three times quicker on Full HD, with visibly less detail than Best Quality).
- [self] A clip whose last frames start past the end of its video track still exports; those frames show the last decodable picture.
- [self] The item is disabled with an explanation for clips over 1920×1080, and on Macs without the upscaler.
- [manual] The progress sheet shows an estimate of the time left.
- [manual] The first use shows "Downloading the upscaling model…" with progress, then "Upscaling video…"; Cancel stops it and leaves no file.
- [manual] The result looks sharper than a plain enlargement. Status: done
As the user, I press the Full Screen button (or F) and the video fills the screen with no sidebar or toolbar; moving the pointer shows the controls, and Escape or F leaves. Acceptance:
- [self] The window enters and leaves macOS full screen, and an open Adjustments panel is put away and brought back.
- [manual] Entering full screen by any route (green button, Control-Command-F) also hides the sidebar.
- [manual] The controls fade in over the bottom of the video when the pointer moves and fade out, with the pointer, after 2.5 seconds at rest.
- [manual] Loop, speed, and frame stepping keep working while full screen. Status: done
As the user, I open a video and the list of videos closes so the picture has the whole window; I can bring it back with the sidebar button. Acceptance:
- [self] Loading the first video closes the sidebar; closing the video shows it again; loading another video later leaves it as I set it. Status: done
As the user, I click anywhere on the video to play it, and click again to pause. Acceptance:
- [self] A click on the picture toggles playback.
- [manual] Clicks on the controls and toolbar do not toggle playback. Status: done
As the user, I open a video that starts with an empty edit or skips its first packets, and every export works and stays in step with the picture and sound. Acceptance:
- [unit] Packet times are mapped through the track's edit list; packets outside it are dropped.
- [self] A file that starts 1/30 s late opens with all its frames, the first at 1/30 s, and exports at original size. Status: done
As the user, I open videos from Finder, with FrameLoop running or not, and get one window per video and no blank extra window. Acceptance:
- [manual]
open -a FrameLoop a.mp4cold gives 1 window; again while running gives 1 more; three files at once give three windows (checked with System Events window counts). Status: done
- S-023 HDR: AVFoundation plays HDR natively; not checked with a real HDR clip.
- S-029..S-033: the player's picture was checked through AVFoundation frames; the live window was not viewed (no Screen Recording permission).
- S-002, S-011..S-014, S-026, S-027 not verified or only partly built (Open With, Dock drop, icon).
- Per-folder recents, frame-by-frame annotation.