Add screen recording, crop, and a Cap-style result window

Recording: Ctrl+Shift+Cmd+R (or the menu) opens the same selection
flow — a marquee records that region, clicking a window records it
(following moves). Video is H.264 .mov at 60fps via SCStream ->
AVAssetWriter with the cursor included, auto-saved to ~/Movies and
revealed in Finder on stop. A red border and a floating timer/stop bar
show during recording, both excluded from the video. Audio is wired
behind a flag for later.

The result window gains a Crop button (crop to the selection;
annotations translate and stay editable) and is restyled after Cap:
sliding-pill Screenshot/Text switcher, capsule buttons in Cap blue,
circular icon buttons, and the content on a rounded card — light and
dark.

Generated with OpenCode

Assisted-by: Kimi:k3
This commit is contained in:
2026-10-24 21:48:59 +08:00
parent 04b84e188a
commit 6a2afef1d2
8 changed files with 706 additions and 140 deletions
+22
View File
@@ -34,19 +34,41 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
}
HotkeyManager.shared.onTrigger = { CaptureController.shared.begin() }
HotkeyManager.shared.onFrozenTrigger = { CaptureController.shared.begin(frozen: true) }
HotkeyManager.shared.onRecordTrigger = {
if RecordingController.shared.state.isRecording {
RecordingController.shared.stop()
} else {
CaptureController.shared.begin(recording: true)
}
}
HotkeyManager.shared.register()
}
func applicationWillTerminate(_ notification: Notification) {
RecordingController.shared.finalizeForTermination()
}
}
struct MenuView: View {
@State private var granted = PermissionManager.isGranted
@ObservedObject private var recordingState = RecordingController.shared.state
var body: some View {
Button("Capture & OCR") { CaptureController.shared.begin() }
.keyboardShortcut("s", modifiers: [.command, .shift])
.disabled(recordingState.isRecording)
Button("Capture Frozen Screen") { CaptureController.shared.begin(frozen: true) }
.keyboardShortcut("s", modifiers: [.command, .shift, .control])
.disabled(recordingState.isRecording)
if recordingState.isRecording {
Button("Stop Recording") { RecordingController.shared.stop() }
.keyboardShortcut("r", modifiers: [.command, .shift, .control])
} else {
Button("Record Screen…") { CaptureController.shared.begin(recording: true) }
.keyboardShortcut("r", modifiers: [.command, .shift, .control])
}
Button("Extract from Image File…") { ResultWindow.shared.showOpenPanel() }