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

192 lines
7.3 KiB
Markdown

# Konverter
A macOS menu bar file converter in the spirit of [Tangerine](https://tangerineformac.com):
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.
<p align="center">
<em>Shift = convert · Command (+Shift) = tools · Control = actions</em>
</p>
## 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:
```bash
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+.
```bash
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):
```bash
./build-app.sh 0.1.0 # → Konverter.app, signed with Developer ID
open Konverter.app
```
## Releasing
```bash
./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:
```bash
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)
```bash
brew tap TZGyn/tap https://gitea.tzgyn.dev/TZGyn/homebrew-tap.git
brew trust TZGyn/tap
brew install --cask konverter
```
Upgrade script:
```bash
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.