Files
konverter/README.md
T
TZGyn a01d5def89 Give mixed selections something to do
A Drag of different kinds showed an empty wheel, because the catalog only
answers for a single category. Filee's answer is a mixed profile, so add
Zip (one archive, flat names) and Merge (non-PDFs are converted to PDF
first); a multi-file drag of one kind gets Zip too.
2026-10-20 01:30:42 +08:00

7.3 KiB

Konverter

A macOS menu bar file converter in the spirit of Tangerine: drag a file in Finder, hold a modifier key, get a wheel of outputs around the cursor, drop on one, and the converted file appears next to the original. Everything runs locally — no uploads, no accounts, no analytics.

Shift = convert · Command (+Shift) = tools · Control = actions

Using it

  1. Launch Konverter. It lives in the menu bar (LSUIElement, no Dock icon).
  2. In Finder, drag one or more files (folders work for archives).
  3. Hold a trigger key while dragging — the wheel fades in under the pointer:
    • Shift ⇧ — conversion targets (PNG, JPG, PDF, MP4, ZIP, …).
    • Command ⌘ + Shift — file tools (Compress, Resize, Trim, Watermark, Merge, Extract, …), each with its own small form.
    • Control ⌃ — actions (AirDrop, Float, Copy, Move, Copy Path, Terminal, Reveal).
  4. Move the pointer onto a segment (or use ←/→ and Return) and drop. Dropping on the dead zone in the middle, pressing Escape, or dropping the file anywhere else cancels and lets Finder do its normal thing.

Mixed selections still work: Zip packs the selection into one archive and Merge turns it into one PDF (non-PDFs are converted first).

The wheel also works without dragging: the menu bar item and the Finder Quick Action Convert with Konverter… open the same options as a menu.

Keyboard/pointer alone is enough to use it. The optional keys (Escape, arrows, Return) come from a global key monitor, so they need Accessibility permission — without it the wheel still works by pointer, it just cannot read those keys.

Preferences

Trigger keys (Shift/Command/Control/Option/fn), wheel style (radial/list), radius, labels, dimming, output folder (beside the original or a chosen folder), conflict policy (keep both / replace), progress window, reveal when finished, sound, use of external tools, and open at login. Stored in UserDefaults under the konverter. prefix.

What it converts

94 input extensions are recognised. Outputs offered per kind of file:

Input kind Outputs
Image PNG, JPG, HEIC, WebP, TIFF, BMP, GIF, AVIF, PDF
Video MP4, MOV, MKV, WebM, AVI, WMV, GIF, MP3, M4A, WAV
Audio MP3, M4A, WAV, FLAC, OGG, Opus, AIFF, WMA
PDF DOCX, PPTX, XLSX, JPG, PNG, EPUB, TXT, MD, HTML
Document DOCX, MD, TXT, RTF, HTML, EPUB, PDF
Slides PDF, JPG, PNG
Spreadsheet XLSX, ODS, CSV, TSV, PDF
Ebook TXT, HTML, MD, PDF
Subtitle SRT, VTT, TXT
Archive / folder ZIP, TAR, GZIP

Plus 39 tools:

Group Tools
Image Compress, Resize, Crop, Rotate, Flip, Grayscale, Strip Metadata, Watermark, Read QR
Video Compress, Trim, Mute, Speed, Snapshot, To GIF, Resize
Audio Trim, Normalize, Channels, Speed, Bleep, Compress
PDF Merge, Split, Rotate, Compress, Encrypt, Decrypt, Images to PDF, Collage, Extract Text
Document To PDF, Statistics
Spreadsheet To CSV
Subtitle Shift Time
Archive Extract, List Contents, Zip
Any Info

Mixed selections

Dragging files of different kinds still shows a wheel instead of nothing: Zip packs the whole selection into one archive (flat names, like Finder's Compress), and Merge turns everything into one PDF — photos, documents and existing PDFs are converted to PDF first, then combined. A multi-file drag of a single kind gets Zip in its tools wheel too.

Engines and fallbacks

Layer Used for Ships with macOS
ImageIO / Core Graphics raster image decode/encode yes
PDFKit / Core Text PDF read/write, text extraction yes
AVFoundation MP4/MOV/M4A/WAV/AIFF/GIF yes
textutil, afconvert, ditto, tar, gzip, zip documents, audio, archives yes
Hand-written XLSX / EPUB / Markdown / CSV / TSV / SRT-VTT office and ebook plumbing yes
ffmpeg / ffprobe MKV, WebM, AVI, WMV, MP3, FLAC, OGG, Opus, WMA, bleep optional
LibreOffice PAGES, KEY, NUMBERS, PPT, PPTX, XLS, ODS optional
cwebp WebP encoding optional

Optional helpers are detected once at launch and are only ever additive: a format whose engine is missing shows as unavailable in the wheel (with the reason) instead of failing after you drop. Install them with:

brew install ffmpeg webp          # cwebp comes from the webp formula
brew install --cask libreoffice

Notes on specific formats:

  • WebP out needs cwebp or an ffmpeg built with libwebp. ImageIO on macOS has no WebP encoder, and sips cannot write it either.
  • SVG / PSD / RAW in are rasterised through Quick Look (qlmanage) when ImageIO refuses them.
  • PAGES / KEY / NUMBERS out only exist through LibreOffice. In Safari-style document conversion, the Apple formats keep their layout via LibreOffice's filter, not a hand-rolled writer.
  • Compressing a PNG has no lossy quality knob, so the slider drives colour depth (posterisation) instead — a real size drop, at the cost of banding at low settings.

Building

Requires Xcode (Swift 6 toolchain) and macOS 13+.

swift build                       # debug binary
swift build -c release

./.build/debug/Konverter --self-test   # 46 checks over every engine
./.build/debug/Konverter --convert photo.heic --to png
./.build/debug/Konverter --convert clip.mov --tool trimVideo \
    --param start=0:02 --param end=0:05
./.build/debug/Konverter --convert image.png --tool compressImage --param quality=50

--self-test builds its own fixtures in a temp directory and asserts that each engine produces a non-empty, plausible file. Skips (printed as SKIP) mean an optional engine is absent on this machine.

Build the app bundle (ad-hoc signed when no Developer ID identity is present):

./build-app.sh 0.1.0            # → Konverter.app, signed with Developer ID
open Konverter.app

Releasing

./release.sh 0.1.0              # build → sign → notarize → staple → zip + sha256

release.sh refuses to hand back a build that is not notarized and stapled; it also verifies spctl -a -vv Konverter.app reports source=Notarized Developer ID before printing the sha256. Notary credentials default to the shared App Store Connect key and can be overridden with KONVERTER_NOTARY_API_KEY_PATH, KONVERTER_NOTARY_API_KEY_ID, and KONVERTER_NOTARY_API_ISSUER.

Then tag, publish, and attach the zip:

git tag v0.1.0 && git push origin v0.1.0
tea releases create --tag v0.1.0 --title "Konverter 0.1.0" \
  --note "<what changed>" --asset dist/Konverter.zip \
  --repo TZGyn/konverter --login gitea.tzgyn.dev

Install (Homebrew)

brew tap TZGyn/tap https://gitea.tzgyn.dev/TZGyn/homebrew-tap.git
brew trust TZGyn/tap
brew install --cask konverter

Upgrade script:

brew update
brew upgrade --cask konverter

Privacy

Nothing leaves the machine. There is no networking code in the app, no telemetry, and no third-party dependencies in Package.swift. Conversions are performed by Apple frameworks, the bundled CLI tools, and (optionally) local ffmpeg / LibreOffice.

Development docs

AGENTS.md covers the architecture, the drag/wheel mechanics, and the sharp edges worth knowing before changing the code.