Repository navigation
Releases: impressiver/snitt
Release list
Snitt 0.8.0
What's Changed
- fix(release): narrow the one unreviewed push to the two files it needs by @impressiver in #191
- chore(scripts): resolve bash through env, not /bin by @impressiver in #192
- fix(build): stop a test deleting the developer's built app by @impressiver in #193
- fix(tests): do not mistake the checkout path for a linked library by @impressiver in #194
- deps: bump github.com/sparkle-project/sparkle from 2.9.6 to 2.10.0 by @dependabot[bot] in #170
- fix(build): survive a dependency bump by @impressiver in #195
- fix(build): compare each product against the sources it actually compiles by @impressiver in #198
- docs(spec): one writer for a recording, and an agent that drives the editor by @impressiver in #197
- fix(agent): refuse an edit to a recording open in the editor (W4) by @impressiver in #200
- fix(test): assert the presentation span, not a frame count that stood in for it by @impressiver in #204
- feat(agent): let an agent drive Snitt's own editor (D109 + W7) by @impressiver in #201
- feat(editor): a lane only for an audio source that was actually captured (D110) by @impressiver in #206
- feat(editor): the titlebar is a real NSToolbar (D97, half of #161) by @impressiver in #209
- fix(editor): the scroll indicator follows the timeline it describes by @impressiver in #211
- release: 0.8.0 by @impressiver in #212
Full Changelog: v0.7.0...v0.8.0
Snitt 0.7.0
What's Changed
- docs(plans): audit the agent API, and the work it implies by @impressiver in #178
- fix(mcp): refuse string booleans everywhere, and file agent recordings with git context by @impressiver in #179
- feat(mcp): give tool results a machine-readable half (D103) by @impressiver in #180
- feat(mcp): let an agent actually see the frame it recorded by @impressiver in #181
- fix(gate): give each checkout its own test logs by @impressiver in #186
- feat(agent-api): three answers instead of one internal_error (D106) by @impressiver in #182
- feat(agent-api): report dropped vocabulary, and grants before the call (D104) by @impressiver in #183
- feat(agents): give pixels, tidy in one call, one verb per job (D105) by @impressiver in #184
- feat(mcp): caption a demo, and write the narration it captions (D107) by @impressiver in #185
- feat(mcp): let an agent find the recordings it left behind (D108) by @impressiver in #188
- fix(permissions): spend the explanation only once it is answered by @impressiver in #189
- release: 0.7.0 by @impressiver in #190
Full Changelog: v0.6.3...v0.7.0
Snitt 0.6.3
What's Changed
- feat(release): verify what was published, not that files exist by @impressiver in #176
- release: 0.6.3 by @impressiver in #177
Full Changelog: v0.6.2...v0.6.3
Snitt 0.6.2
What's Changed
- docs(runbook): a locked screen reports the notary credential as missing by @impressiver in #168
- ci(dependabot): watch Sparkle, which alerts never covered by @impressiver in #169
- fix(build): releases shipped a stale app binary from a dead product path by @impressiver in #173
- fix(release): build releases optimised, and bump by the smallest increment by @impressiver in #174
- release: 0.6.2 by @impressiver in #175
Full Changelog: v0.6.1...v0.6.2
Snitt 0.6.1
What's Changed
- docs(spec): queued work moves to the issue tracker by @impressiver in #165
- fix(signing): declare the microphone entitlement the Hardened Runtime requires by @impressiver in #166
- release: 0.6.1 by @impressiver in #167
Full Changelog: v0.6.0...v0.6.1
Snitt 0.6.0
What's Changed
- fix(appcast): emit the build number Sparkle compares, not the marketing version by @impressiver in #126
- chore(oss): prepare the repository to go public by @impressiver in #127
- fix(settings): even the divider margins; move "Export for" into the sheet by @impressiver in #128
- feat(editor): record a voiceover after the fact, anchored to the footage (D93) by @impressiver in #129
- feat(import): open plain video as a Snitt document, and save it somewhere by @impressiver in #130
- test(capture): stop asserting an equivalence the platform never promised by @impressiver in #131
- feat(menu): split New, gate it on the clipboard, and take Preview's icon by @impressiver in #132
- fix(editor): show that a voiceover is recording, and let the fader reach silence by @impressiver in #133
- fix(voiceover): reach the export mix, and show the lane on older documents by @impressiver in #134
- docs(spec): record the CLA → DCO change as D98, and what M8 actually costs by @impressiver in #135
- feat(transcript): narrate into the transcript, in its own colour, hidden when muted by @impressiver in #136
- fix(timeline): colour the voiceover lane like its words, and sync the zoom control by @impressiver in #137
- fix(timeline): one colour per track, and lanes in the order the file has them by @impressiver in #138
- fix(subtitles): one voice per caption, two lines when they overlap by @impressiver in #139
- fix(transcript): one voice per row, in the pane and on the phrase lane by @impressiver in #140
- docs(licensing): retire the CLA for a DCO plus an Apache-2.0 grant (D98) by @impressiver in #141
- fix(transcript): stop rows overlapping, and put the timestamp back on line one by @impressiver in #143
- feat(transcript): write narration with +, and delete by keystroke (D100) by @impressiver in #144
- feat(editor): open the transcript section when the recording has words by @impressiver in #145
- fix(transcript): make the + actually open the narration field by @impressiver in #146
- feat(audio): a take over-dubs the microphone; the third track is for synthesis (D102) by @impressiver in #147
- feat(transport): a record button, a count-in, and a pause that keeps the take by @impressiver in #148
- fix: restore the test gate on Xcode 27, and pre-public sweep by @impressiver in #150
- fix(build): make the sources compile on both toolchains by @impressiver in #151
- chore(gate): build with warnings-as-errors, the way CI does by @impressiver in #152
- docs(ci): count the test-target warnings instead of remembering them by @impressiver in #153
- fix(tests): declare the actor isolation, and gate the whole package by @impressiver in #154
- release: 0.6.0 by @impressiver in #155
- fix(release): the appcast/app build-number guard could never run by @impressiver in #156
Full Changelog: v0.5.0...v0.6.0
Snitt 0.5.0
What's Changed
- fix(release): split the version keys, and leave main on the next version by @impressiver in #101
- fix(automation): identify the caller on the socket, and gate reading recordings by @impressiver in #102
- feat(export): copy the samples when nothing would change a pixel by @impressiver in #103
- feat(editor): say why a transcript is empty, instead of showing nothing by @impressiver in #104
- feat(transcription): move to SpeechAnalyzer, and delete the workaround it replaces by @impressiver in #105
- feat(export): Export for a destination, and Share by @impressiver in #106
- docs(spec): record on-device auto-titling as D94, deferred after measurement by @impressiver in #107
- feat(overlay): the timing model for captions and marker banners, and a shared brand package by @impressiver in #109
- feat(editor): preview captions and marker banners, on Playback ▸ Show by @impressiver in #111
- feat(export): burn captions and marker banners into mp4 and GIF by @impressiver in #112
- fix(timeline): make folds and cut lines selectable, so Delete can remove them by @impressiver in #113
- fix(export): bound GIF dimensions, and give the sheet an Overlays section by @impressiver in #114
- docs(spec): record unattended agent recording (D95) and GIF size estimation (D96) by @impressiver in #115
- feat(consent): let an agent record unattended, on a grant that expires (D95) by @impressiver in #116
- refactor(settings): group the Settings window, and name three settings honestly by @impressiver in #125
- fix(markers): make a marker-pane click show the marker, and show it at once by @impressiver in #118
- fix(transport): navigate marks from where a seek is going, not a stale clock by @impressiver in #119
- fix(timeline): let a phrase click select the utterance, and stop it re-seeking by @impressiver in #120
- feat(transcript): put the transcript in the left rail, as a timed phrase list by @impressiver in #121
- fix(share): make File ▸ Share a submenu that opens instantly, like QuickTime's by @impressiver in #122
- feat(editor): make the side panel a right-hand accordion, and un-lag word clicks by @impressiver in #123
- test(diagnostics): wait for the log entry instead of racing it by @impressiver in #124
Full Changelog: v0.4.0...v0.5.0
Snitt 0.4.0
What's Changed
- Prove the notary credential; untrack appcast.xml by @impressiver in #87
- Filmstrip test: assert luma spread, not first-versus-last by @impressiver in #88
- docs(spec): the rev 5 UI spec — brand tokens, style sheet, 14 fleet work items by @impressiver in #89
- feat(ui): one brand palette, and every surface that owned a colour forwards to it by @impressiver in #90
- feat(ui): the recolour batch — transport, export sheet, settings, rail, menu bar by @impressiver in #91
- feat(timeline): the geometry cluster — cuts as seams, segmented waveform, branded ladder, words lane by @impressiver in #92
- feat(editor): the toolbar is the titlebar by @impressiver in #93
- feat(icon): the mark, re-rendered for macOS 26+ by @impressiver in #94
- feat(ui): the polish sweep, and the item the PR table lost by @impressiver in #95
- fix: ten defects reported from use, plus open-on-launch and the finished HUD by @impressiver in #96
- fix(tests): make the test gates safe under parallel execution by @impressiver in #97
- feat: make clicks actually show — capture their positions (D64), and a Playback ▸ Show Clicks toggle by @impressiver in #98
- fix(menu): trim the status menu, restore its divider, and bind Show Clicks to ⇧⌘C by @impressiver in #99
- release: 0.4.0 by @impressiver in #100
Full Changelog: v0.3.0...v0.4.0
Snitt 0.3.0
What's Changed
- feat(release): ship a DMG installer alongside the update zip by @impressiver in #64
- feat(export): choose a resolution, with its size ceiling, before exporting by @impressiver in #65
- feat(hud): a floating recording HUD, and a human pause to go with it by @impressiver in #66
- feat(editor): the timeline gets a budget, so the picture stops paying for it by @impressiver in #67
- feat(editor): one transport, one shortcut registry, and marks you can step to by @impressiver in #68
- fix(timeline): folds get a lane and a y-gate, markers get their 24pt back by @impressiver in #69
- feat(a11y): make the timeline reachable without a pointer by @impressiver in #70
- feat(editor): chrome follows the appearance, media surfaces do not by @impressiver in #71
- feat(editor): transcript behind a toggle, and the word lane's tiers measured by @impressiver in #72
- feat(timeline): a phrase-grouped transcript lane (D89) by @impressiver in #73
- fix(timeline): halve the filmstrip's share, grow the transcript lane, drop its fill by @impressiver in #74
- feat(editor): build the chrome the design asked for by @impressiver in #75
- fix(editor): halve the filmstrip again, legible centred phrases, an adaptive well by @impressiver in #76
- feat(editor): a digital VU ladder beside each audio lane by @impressiver in #77
- fix(timeline): the filmstrip was never actually shrinking, and three UI fixes by @impressiver in #78
- feat(timeline): marker double-click, fold expand-and-select, "Markers" everywhere by @impressiver in #79
- fix(timeline): lanes take what they need, and fixture names stop colliding by @impressiver in #80
- fix(timeline): restore right-click Remove Cut, and make Delete mean both edits by @impressiver in #81
- test: fix the coordinate bug behind three test failures, add a gesture matrix by @impressiver in #82
- feat(editor): resizable remembered panes, and a scrollable zoomed timeline by @impressiver in #83
- Replace the export save panel with a designed sheet by @impressiver in #84
- Release script: publish, then verify the assets landed by @impressiver in #85
- Preflight: check credentials by asking the scripts that own them by @impressiver in #86
Full Changelog: v0.2.0...v0.3.0
Snitt 0.2.0
Snitt 0.2.0
v0.1.0 could record a window and trim it. 0.2.0 is where Snitt became an editor —
123 commits, 304 files, +28,013 lines — plus on-device transcription, a real agent
surface, and an open-source licence.
Universal (arm64 + x86_64), signed with Developer ID, notarized and stapled.
The editor
The timeline stopped being a scrub bar.
- Waveforms and a filmstrip in every track — log-scale, with clipping marks, one
band per audio source. Dark surface, orange waveform. - Per-track gain and mute, applied live in the preview and honoured by the export
mix. - Crop as a bounding box you drag and adjust before committing — nothing reaches
the edit until you press Apply. - Auto-deep-trim: cut the spans where nothing happens, with presets and the
criteria behind them stated. An automatic fold says what it removed. - Expanded folds insert space, so the playhead skips them instead of walking
through footage that isn't there. - A chapter panel for markers, with typed times and drag-to-move.
- Transport: rewind, scrub-stops-playback, zoom and snapping with on-screen
controls. - Opens centred at 75% of the screen.
Transcription — on device, and editable
Runs on your Mac with Apple's recognizer. The audio never leaves the machine.
- The transcript is an editing surface: click a word to jump there, select a phrase
and delete it to cut those seconds. - Correct a word in place — double-click, type, done.
- Tell it what to expect. A recording carries a vocabulary of the symbol names,
file names and product names a general speech model has never heard, and
re-transcribes against them. - The spoken word highlights as playback reaches it, and the transcript breaks into
lines at the speaker's own pauses. - Confidence is rendered, not hidden — a word the recognizer doubted looks doubted.
- Marker transcripts export as WebVTT subtitles.
Agents
snitt_auto_deep_trim,snitt crop, pause/resume, and screenshot-the-recording over
both MCP and the CLI.snitt setupregisters the MCP server with agent hosts.- Snitt starts on demand when a client finds it closed.
- A client can report input the OS never saw — an agent's synthetic keystrokes and
clicks land in the event log as content-free beats, and clicks can be drawn on the
exported video. - Agent-initiated recordings disclose themselves in the UI.
Capture and export
- Pause and resume a recording, from the menu bar or over the agent surface; a
paused recording looks distinctly paused. - Microphone recording from the GUI, with a warning when your speakers will bleed
into the mic. - A configurable save destination.
- Customizable hotkeys for record and marker.
- Export gained a resolution knob and a pre-flight menu that prices every option
before you commit to one. .snittbundles get a Finder icon drawn from the recording itself.
The project
- Licensed MPL-2.0, with a contributor agreement — file-level copyleft, so a
commercial competitor cannot take the code closed, and GPL-compatible. - A README, a spec conformance pass, and CI.
Notable fixes
- Clicking a transcript word highlighted the word before it — intermittently. The
seek target quantizes to 1/600s, landing up to 0.8ms before the word's start, so
the previous word won the lookup whenever a word's start time rounded down. - The Settings window was released out from under itself — a use-after-free that
crashed shipped v0.1.0 when you toggled "Log input events". - Timeline gestures were interpreted on the wrong axis after the view moved to
output time. - Transcription only covered the tail of a recording; it now runs per utterance.
- Silence detection measures against the 90th percentile, not the max, so one loud
moment no longer makes a quiet recording read as busy. snitt trimdiscarded existing cuts instead of preserving them.- Adjusting gain no longer resets the playhead to the start.
events.jsonnow guards itsschemaVersion, likeedit.json.
Upgrading
edit.json and events.json from v0.1.0 open unchanged — there is a pinned test
against a real captured v0.1.0 bundle, including the read-then-save path.
Existing installs with automatic checks enabled will be offered this build by Sparkle.
Updates are verified three ways before they install: TLS to github.com, the Developer ID
signature, and an EdDSA signature over the archive.