From 9c2b4e540fc6f8139304d71737f3a036197d9b9f Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 11:53:33 +0200 Subject: [PATCH 01/43] docs: add design tokens (draft) with generated reference and preview Co-Authored-By: Claude Sonnet 5.5 --- Design/build_tokens.py | 139 ++++++++++++ Design/preview.template.html | 380 ++++++++++++++++++++++++++++++++ Design/tokens.json | 204 +++++++++++++++++ docs/design/tokens-preview.html | 380 ++++++++++++++++++++++++++++++++ docs/design/tokens.md | 283 ++++++++++++++++++++++++ 5 files changed, 1386 insertions(+) create mode 100644 Design/build_tokens.py create mode 100644 Design/preview.template.html create mode 100644 Design/tokens.json create mode 100644 docs/design/tokens-preview.html create mode 100644 docs/design/tokens.md diff --git a/Design/build_tokens.py b/Design/build_tokens.py new file mode 100644 index 0000000..8ca01e9 --- /dev/null +++ b/Design/build_tokens.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 +"""Reads Design/tokens.json and writes docs/design/tokens.md and docs/design/tokens-preview.html. +Also checks WCAG contrast for every text pairing; exits 1 if a text pair is below 4.5:1.""" +import json +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +tokens = json.loads((ROOT / "Design" / "tokens.json").read_text()) +HEX = re.compile(r"^#[0-9A-Fa-f]{6}$") + + +def lum(hexcolor): + h = hexcolor.lstrip("#") + out = [] + for i in (0, 2, 4): + c = int(h[i:i + 2], 16) / 255 + out.append(c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4) + return 0.2126 * out[0] + 0.7152 * out[1] + 0.0722 * out[2] + + +def contrast(a, b): + la, lb = sorted((lum(a), lum(b)), reverse=True) + return (la + 0.05) / (lb + 0.05) + + +# --- validation ------------------------------------------------------------- +errors = [] +for name, t in tokens["color"].items(): + for theme in ("light", "dark"): + if not HEX.match(t.get(theme, "")): + errors.append(f"color.{name}.{theme} is not a #RRGGBB value") +if errors: + print("\n".join(errors)) + sys.exit(1) + +# --- contrast --------------------------------------------------------------- +TEXT_PAIRS = [ + ("ink.primary", "surface.chassis"), ("ink.primary", "surface.panel"), ("ink.primary", "surface.paper"), + ("ink.primary", "surface.well"), ("ink.primary", "surface.selection"), ("ink.onMeter", "surface.meterFace"), + ("ink.primary", "surface.keyHigh"), ("ink.primary", "surface.keyLow"), + ("ink.secondary", "surface.chassis"), ("ink.secondary", "surface.panel"), ("ink.secondary", "surface.paper"), + ("ink.tertiary", "surface.chassis"), ("ink.tertiary", "surface.panel"), ("ink.tertiary", "surface.paper"), + ("ink.tertiary", "surface.metalHigh"), ("ink.tertiary", "surface.metalLow"), + ("ink.inverse", "accent.record"), + ("level.amber", "surface.display"), +] +rows, failed = [], False +for fg, bg in TEXT_PAIRS: + for theme in ("light", "dark"): + r = contrast(tokens["color"][fg][theme], tokens["color"][bg][theme]) + ok = r >= 4.5 + failed |= not ok + rows.append((fg, bg, theme, r, ok)) + +print(f"{'text':16} {'on':20} {'finish':6} ratio") +for fg, bg, theme, r, ok in rows: + print(f"{fg:16} {bg:20} {theme:6} {r:5.2f} {'ok' if ok else 'FAIL'}") + +# --- markdown --------------------------------------------------------------- +md = [] +add = md.append +m = tokens["meta"] +add(f"# {m['name']} — v{m['version']}\n") +add(f"**Status:** {m['status']}\n") +add(f"{m['concept']}\n") +add("Bron: `Design/tokens.json`. Dit bestand en `tokens-preview.html` worden gegenereerd met `python3 Design/build_tokens.py`; pas nooit alleen deze bestanden aan.\n") + + +def table(headers, body): + add("| " + " | ".join(headers) + " |") + add("|" + "|".join("---" for _ in headers) + "|") + for r in body: + add("| " + " | ".join(str(c) for c in r) + " |") + add("") + + +add("## Kleur\n") +add("Verwijs ernaar als `color.`. Kolommen: ALUMINIUM (licht) en GRAPHITE (donker).\n") +table(["Token", "Aluminium", "Graphite", "Gebruik"], + [(f"`color.{n}`", f"`{t['light']}`", f"`{t['dark']}`", t["use"]) for n, t in tokens["color"].items()]) +add("**Regel:** `accent.record` is het enige accent en wordt nooit voor lopende tekst gebruikt. Amber en groen zijn alleen voor niveau-indicatoren en markeringen.\n") +add("### Contrast (berekend)\n") +table(["Tekst", "Op", "Afwerking", "Verhouding", ""], + [(f"`{a}`", f"`{b}`", th, f"{r:.2f}:1", "ok" if ok else "**te laag**") for a, b, th, r, ok in rows]) + +add("## Opaciteit\n") +table(["Token", "Waarde", "Gebruik"], [(f"`opacity.{n}`", t["value"], t["use"]) for n, t in tokens["opacity"].items()]) + +add("## Lettertypen\n") +table(["Token", "Familie", "Terugval", "Gebruik"], + [(f"`font.{n}`", t["value"], t["fallback"], t["use"]) for n, t in tokens["font"].items()]) +add("## Tekststijlen\n") +table(["Token", "Familie", "Grootte", "Gewicht", "Spatiëring", "Regelhoogte", "Hoofdletters", "Gebruik"], + [(f"`type.{n}`", t["family"], f"{t['size']} pt", t["weight"], f"{t['tracking']} em", f"{t['line']} pt", + "ja" if t["caps"] else "nee", t["use"]) for n, t in tokens["type"].items()]) +add("## Segmentcijfers (getekend, geen lettertype)\n") +table(["Token", "Waarde", "Gebruik"], [(f"`segment.{n}`", t["value"], t["use"]) for n, t in tokens["segment"].items()]) + +add("## Ruimte\n") +table(["Token", "Waarde"], [(f"`space.{n}`", f"{v} pt") for n, v in tokens["space"].items()]) +add("## Indeling\n") +table(["Token", "Waarde", "Gebruik"], [(f"`layout.{n}`", t["value"], t["use"]) for n, t in tokens["layout"].items()]) +add("## Afmetingen\n") +table(["Token", "Waarde", "Gebruik"], [(f"`size.{n}`", t["value"], t["use"]) for n, t in tokens["size"].items()]) +add("## Hoekradius\n") +table(["Token", "Waarde", "Gebruik"], [(f"`radius.{n}`", t["value"], t["use"]) for n, t in tokens["radius"].items()]) +add("## Randen\n") +table(["Token", "Waarde", "Gebruik"], [(f"`border.{n}`", f"{t['value']} pt", t["use"]) for n, t in tokens["border"].items()]) + +add("## Schaduw en diepte\n") +for n, t in tokens["shadow"].items(): + add(f"### `shadow.{n}`\n\n{t['use']}" + (f" Dop verschuift {t['translateY']} pt omlaag." if "translateY" in t else "") + "\n") + table(["Laag", "Inset", "x", "y", "blur", "spread", "kleur", "alpha aluminium", "alpha graphite"], + [(i + 1, "ja" if l["inset"] else "nee", l["x"], l["y"], l["blur"], l["spread"], f"`{l['color']}`", + l["alpha"]["light"], l["alpha"]["dark"]) for i, l in enumerate(t["layers"])]) +add("## Textuur\n") +for n, t in tokens["texture"].items(): + add(f"**`texture.{n}`** — {t['use']}. Richting {t['direction']}; korrel {t['grain']['light']} (aluminium) / {t['grain']['dark']} (graphite); {t['gradient']}.\n") +add("## Meters\n") +table(["Token", "Waarde", "Gebruik"], [(f"`meter.{n}`", t["value"], t["use"]) for n, t in tokens["meter"].items()]) +add("## Beweging\n") +table(["Token", "Waarde", "Eenheid", "Gebruik"], + [(f"`motion.{n}`", t["value"], t.get("unit", ""), t["use"]) for n, t in tokens["motion"].items()]) + +add("## Wat bewust ontbreekt\n") +add("Geen neon, vaporwave, synthwave, paars-roze verlopen, gloeiende tekst, chromen letters of rasterhorizonten. Lampjes mogen een kleine gloed hebben (`shadow.lamp.on`); tekst nooit.\n") + +out_dir = ROOT / "docs" / "design" +out_dir.mkdir(parents=True, exist_ok=True) +(out_dir / "tokens.md").write_text("\n".join(md)) + +# --- preview ---------------------------------------------------------------- +template = (ROOT / "Design" / "preview.template.html").read_text() +blob = json.dumps(tokens, ensure_ascii=False).replace(" + + + + +Sst Ontwerp-tokens + + + + + +
+ +
+
+
Ontwerpsysteem · Sst
+

Ontwerp-tokens

+

+
+
+
Ter goedkeuring
+
+
Bron · Design/tokens.json
+
+
+ +
+
01Instrumenten in gebruikmeter.* · motion.needle.* · segment.*
+
+
Afwerking · Aluminiumlicht
+
Afwerking · Graphitedonker
+
+
+ +
+
02Kleurcolor.*
+
+
+ +
+
03Diepte en materiaalshadow.* · border.* · texture.*
+
+
+
+
+
+ +
+
04Typografiefont.* · type.*
+
+
+ +
+
05Ruimte, hoeken, randenspace.* · radius.* · border.*
+
+
Ruimte (pt)
+
+
Hoekradius
+
+
+ +
+
06Bewegingmotion.*
+
+
+
Toets en lampje
+
+

Toets: 50 ms in, 90 ms uit, zonder veer. Lampje: 30 ms aan, 200 ms uit — dooft als een gloeidraad.

+
+
+
Wijzer-ballistiek
+
+

Veer: 300 ms tot 99 %, demping 0,80 → ongeveer 1,5 % overschot. De meters bij 01 gebruiken dezelfde waarden.

+
+
+
+ +
+
07Wat het niet is
+
    +
  • Geen neon, vaporwave, synthwave, paars-roze verlopen, gloeiende tekst, chromen letters of rasterhorizonten.
  • +
  • Eén accent: het rode opnamelampje. Amber en groen alleen als niveau-indicator of markering, nooit als versiering.
  • +
  • Lampjes mogen een kleine gloed hebben. Tekst nooit.
  • +
  • Geen eenmalige waarden in componenten: elke afmeting, kleur en tijd komt uit deze tokens.
  • +
+
+
+ + + + diff --git a/Design/tokens.json b/Design/tokens.json new file mode 100644 index 0000000..0563c1d --- /dev/null +++ b/Design/tokens.json @@ -0,0 +1,204 @@ +{ + "meta": { + "name": "Sst Design Tokens", + "version": "0.1.0-draft", + "status": "Ter goedkeuring — nog niet gebruikt door de app", + "concept": "Draagbare veldrecorder / cassettedeck uit de jaren 80 (Sony TC-D5, Marantz PMD, Nakamichi, Braun). Twee afwerkingen: ALUMINIUM (licht) en GRAPHITE (donker). Eén accent: het rode opnamelampje. Amber en groen alleen voor niveau-indicatoren." + }, + + "color": { + "surface.chassis": { "light": "#CBC8C0", "dark": "#1C1B19", "use": "Venster-achtergrond; het 'huis' van het apparaat" }, + "surface.metalHigh": { "light": "#E3E1DB", "dark": "#3B3A36", "use": "Geborsteld metaal, bovenkant van de verloop" }, + "surface.metalLow": { "light": "#BDBAB2", "dark": "#262522", "use": "Geborsteld metaal, onderkant van de verloop" }, + "surface.panel": { "light": "#E2DFD5", "dark": "#2B2A27", "use": "Frontplaat van mat plastic; zijbalk en bedieningspanelen" }, + "surface.well": { "light": "#B4B1A8", "dark": "#131211", "use": "Verzonken vlak: invoervelden, display-vensters" }, + "surface.paper": { "light": "#EEE9D9", "dark": "#232220", "use": "Cassette-label crème: lijsten met transcripties en woordenboek" }, + "surface.keyHigh": { "light": "#E6E3DA", "dark": "#44433F", "use": "Toetsdop, bovenkant" }, + "surface.keyLow": { "light": "#CBC8BF", "dark": "#33322F", "use": "Toetsdop, onderkant" }, + "surface.keyPressed": { "light": "#BEBBB2", "dark": "#2A2927", "use": "Ingedrukte toets" }, + "surface.meterFace": { "light": "#EFE6C8", "dark": "#D8CEA6", "use": "Wijzerplaat van de VU-meter (blijft crème in beide afwerkingen)" }, + "surface.selection": { "light": "#D4CDB2", "dark": "#3A3828", "use": "Geselecteerde rij in een lijst" }, + "surface.display": { "light": "#1B1A18", "dark": "#0E0D0C", "use": "Displayglas van de teller; donker in beide afwerkingen" }, + "display.segmentUnlit": { "light": "#3A3320", "dark": "#2A2415", "use": "Gedoofd segment op het displayglas" }, + + "ink.primary": { "light": "#1E1D1B", "dark": "#ECE8DC", "use": "Hoofdtekst en wijzer" }, + "ink.secondary": { "light": "#403D38", "dark": "#C4BFB2", "use": "Secundaire tekst" }, + "ink.tertiary": { "light": "#4D4A44", "dark": "#ABA79B", "use": "Zeefdruk-labels en hulptekst" }, + "ink.disabled": { "light": "#8F8B82", "dark": "#6B685F", "use": "Uitgeschakeld; nooit voor essentiële tekst" }, + "ink.inverse": { "light": "#F4EFE3", "dark": "#F4EFE3", "use": "Tekst op rode of donkere vulling; crème in beide afwerkingen" }, + "ink.onMeter": { "light": "#1E1D1B", "dark": "#1E1D1B", "use": "Schaal, cijfers en wijzer op de wijzerplaat; vast donker in beide afwerkingen" }, + + "accent.record": { "light": "#C8281E", "dark": "#CC2A20", "use": "ENIGE accent: opnamelampje, REC-toets, piek-LED. Nooit voor lopende tekst" }, + "accent.recordUnlit": { "light": "#7A2E28", "dark": "#4A1F1B", "use": "Lampje uit" }, + + "level.green": { "light": "#5E9E48", "dark": "#6DB556", "use": "Niveau-segment, brandend" }, + "level.amber": { "light": "#D9A023", "dark": "#E5B032", "use": "Niveau-segment, waarschuwing, gecorrigeerd woord" }, + "level.greenUnlit": { "light": "#A8B79C", "dark": "#26331F", "use": "Groen segment, uit" }, + "level.amberUnlit": { "light": "#CDBA8A", "dark": "#3D3418", "use": "Amber segment, uit" }, + + "line.seamDark": { "light": "#8C8981", "dark": "#0A0A09", "use": "Naad tussen panelen, donkere helft" }, + "line.seamLight": { "light": "#F3F1EB", "dark": "#45433F", "use": "Naad tussen panelen, lichte helft" }, + "line.hairline": { "light": "#A9A69D", "dark": "#3A3834", "use": "Fijne scheidingslijn, zeefdruk-kader" }, + "line.focus": { "light": "#1E1D1B", "dark": "#ECE8DC", "use": "Toetsenbord-focus" } + }, + + "opacity": { + "mark": { "value": 0.30, "use": "Amber markering achter een gecorrigeerd woord" }, + "disabled": { "value": 0.45, "use": "Uitgeschakelde bediening" }, + "unlit": { "value": 1.00, "use": "Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie" } + }, + + "font": { + "family.chrome": { "value": "Helvetica Neue", "fallback": "Helvetica, Arial", "use": "Zeefdruk, toetsen, koppen: neutrale grotesk" }, + "family.text": { "value": "SF Pro Text", "fallback": "system-ui", "use": "Leesbare tekst: transcripties, woordenboek" }, + "family.mono": { "value": "SF Mono", "fallback": "Menlo, monospace", "use": "Teller, tijden, woordenboekbestand" } + }, + + "type": { + "label": { "family": "chrome", "size": 10, "weight": 500, "tracking": 0.09, "line": 12, "caps": true, "use": "Zeefdruk-label" }, + "labelSmall": { "family": "chrome", "size": 9, "weight": 500, "tracking": 0.10, "line": 11, "caps": true, "use": "Schaalmarkeringen en eenheden" }, + "control": { "family": "chrome", "size": 11, "weight": 500, "tracking": 0.06, "line": 14, "caps": true, "use": "Opschrift op toetsen" }, + "heading": { "family": "chrome", "size": 12, "weight": 500, "tracking": 0.12, "line": 16, "caps": true, "use": "Paneelkop" }, + "body": { "family": "text", "size": 13, "weight": 400, "tracking": 0, "line": 18, "caps": false, "use": "Standaardtekst" }, + "reading": { "family": "text", "size": 15, "weight": 400, "tracking": 0, "line": 22, "caps": false, "use": "Transcript" }, + "meta": { "family": "text", "size": 11, "weight": 400, "tracking": 0, "line": 14, "caps": false, "use": "Bijschriften" }, + "counter": { "family": "mono", "size": 22, "weight": 500, "tracking": 0, "line": 26, "caps": false, "tabular": true, "use": "Teller" }, + "counterSmall": { "family": "mono", "size": 12, "weight": 500, "tracking": 0, "line": 16, "caps": false, "tabular": true, "use": "Tijdstip in een rij" }, + "code": { "family": "mono", "size": 12, "weight": 400, "tracking": 0, "line": 18, "caps": false, "use": "Woordenboekregels" } + }, + + "segment": { + "digitHeight": { "value": 36, "use": "Hoogte van een getekend segmentcijfer" }, + "digitWidth": { "value": 20, "use": "Breedte van een segmentcijfer" }, + "thickness": { "value": 4, "use": "Dikte van één segment" }, + "gap": { "value": 1, "use": "Ruimte tussen segmenten" }, + "slant": { "value": 0, "use": "Schuinte in graden; 0 = rechtop" } + }, + + "space": { + "0": 0, "1": 2, "2": 4, "3": 8, "4": 12, "5": 16, "6": 24, "7": 32, "8": 48 + }, + + "layout": { + "window.minWidth": { "value": 800, "use": "Minimale vensterbreedte" }, + "window.minHeight": { "value": 560, "use": "Minimale vensterhoogte" }, + "window.defaultWidth": { "value": 1080, "use": "Standaard vensterbreedte" }, + "window.defaultHeight":{ "value": 700, "use": "Standaard vensterhoogte" }, + "sidebar.width": { "value": 232, "use": "Breedte zijbalk" }, + "settings.width": { "value": 560, "use": "Breedte instellingenvenster" }, + "panel.padding": { "value": 16, "use": "Binnenmarge van een paneel (= space.5)" }, + "panel.gap": { "value": 12, "use": "Afstand tussen panelen (= space.4)" } + }, + + "size": { + "control.height": { "value": 28, "use": "Standaardhoogte van toets of veld" }, + "key.minWidth": { "value": 56, "use": "Minimale breedte van een toets" }, + "key.recWidth": { "value": 64, "use": "REC-toets, breedte" }, + "key.recHeight": { "value": 44, "use": "REC-toets, hoogte" }, + "lamp.diameter": { "value": 10, "use": "Standaard lampje" }, + "lamp.large": { "value": 14, "use": "Opnamelampje" }, + "switch.width": { "value": 40, "use": "Tuimelschakelaar, breedte" }, + "switch.height": { "value": 22, "use": "Tuimelschakelaar, hoogte" }, + "row.height": { "value": 40, "use": "Rij in transcriptielijst" }, + "icon.small": { "value": 16, "use": "Klein pictogram" }, + "icon.medium": { "value": 20, "use": "Normaal pictogram" }, + "hit.min": { "value": 24, "use": "Minimaal klikdoel" } + }, + + "radius": { + "none": { "value": 0, "use": "Panelen en velden: scherpe randen" }, + "sharp": { "value": 1, "use": "Toetsranden: bijna scherp" }, + "sm": { "value": 2, "use": "Toetsen, velden" }, + "md": { "value": 4, "use": "Grote panelen" }, + "bezel": { "value": 6, "use": "Rand van de VU-meter" }, + "lamp": { "value": 999, "use": "Lampjes: rond" } + }, + + "border": { + "hairline": { "value": 1, "use": "Fijne lijn" }, + "seam": { "value": 2, "use": "Naad: 1 donkere lijn boven, 1 lichte lijn eronder, leest als gegraveerde groef" }, + "bezel": { "value": 3, "use": "Rand om de VU-meter" }, + "focus": { "value": 2, "use": "Focusring, 2 pt offset" } + }, + + "shadow": { + "key.raised": { + "use": "Toets in rust: lichtvang boven, donkere onderrand, valschaduw", + "layers": [ + { "inset": true, "x": 0, "y": 1, "blur": 0, "spread": 0, "color": "#FFFFFF", "alpha": { "light": 0.65, "dark": 0.10 } }, + { "inset": true, "x": 0, "y": -1, "blur": 0, "spread": 0, "color": "#000000", "alpha": { "light": 0.25, "dark": 0.55 } }, + { "inset": false, "x": 0, "y": 2, "blur": 2, "spread": 0, "color": "#000000", "alpha": { "light": 0.35, "dark": 0.60 } } + ] + }, + "key.pressed": { + "use": "Ingedrukte toets: dop 1 pt omlaag, geen valschaduw", + "layers": [ + { "inset": true, "x": 0, "y": 2, "blur": 4, "spread": 0, "color": "#000000", "alpha": { "light": 0.50, "dark": 0.70 } }, + { "inset": true, "x": 0, "y": 0, "blur": 0, "spread": 1, "color": "#000000", "alpha": { "light": 0.35, "dark": 0.60 } } + ], + "translateY": 1 + }, + "well": { + "use": "Verzonken vlak met lichtvang op de onderlip", + "layers": [ + { "inset": true, "x": 0, "y": 1, "blur": 3, "spread": 0, "color": "#000000", "alpha": { "light": 0.40, "dark": 0.70 } }, + { "inset": false, "x": 0, "y": 1, "blur": 0, "spread": 0, "color": "#FFFFFF", "alpha": { "light": 0.55, "dark": 0.07 } } + ] + }, + "panel": { + "use": "Frontplaat boven het chassis", + "layers": [ + { "inset": true, "x": 0, "y": 1, "blur": 0, "spread": 0, "color": "#FFFFFF", "alpha": { "light": 0.60, "dark": 0.08 } }, + { "inset": false, "x": 0, "y": 3, "blur": 8, "spread": 0, "color": "#000000", "alpha": { "light": 0.22, "dark": 0.55 } } + ] + }, + "lamp.on": { + "use": "Brandend lampje: kleine gloed, alleen voor lampjes en nooit voor tekst", + "layers": [ + { "inset": false, "x": 0, "y": 0, "blur": 6, "spread": 1, "color": "#CC2A20", "alpha": { "light": 0.45, "dark": 0.55 } }, + { "inset": true, "x": 0, "y": -1, "blur": 1, "spread": 0, "color": "#000000", "alpha": { "light": 0.35, "dark": 0.45 } } + ] + } + }, + + "texture": { + "brushed": { + "use": "Geborsteld aluminium voor chassis en zijbalk", + "direction": "horizontaal", + "grain": { "light": 0.035, "dark": 0.05 }, + "gradient": "surface.metalHigh naar surface.metalLow, van boven naar beneden" + } + }, + + "meter": { + "vu.referenceDbfs": { "value": -20, "use": "0 VU = -20 dBFS (RMS)" }, + "vu.min": { "value": -20, "use": "Onderkant van de schaal in VU" }, + "vu.max": { "value": 3, "use": "Bovenkant van de schaal in VU; vanaf 0 VU rood" }, + "vu.arcDegrees": { "value": 90, "use": "Hoek tussen minimale en maximale stand" }, + "vu.width": { "value": 260, "use": "Breedte van de meter" }, + "vu.height": { "value": 150, "use": "Hoogte van de meter" }, + "vu.needleWidth": { "value": 1.5, "use": "Dikte van de wijzer" }, + "vu.ticks": { "value": "-20 -10 -7 -5 -3 0 +3", "use": "Genummerde schaalmarkeringen" }, + "led.segments": { "value": 12, "use": "Aantal segmenten: 8 groen, 3 amber, 1 rood" }, + "led.segmentSize": { "value": "6x14", "use": "Segment in pt (breedte x hoogte)" }, + "led.gap": { "value": 2, "use": "Ruimte tussen segmenten" }, + "peak.holdMs": { "value": 1000, "use": "Piek-LED blijft zo lang branden" } + }, + + "motion": { + "duration.instant": { "value": 0, "unit": "ms", "use": "Geen overgang" }, + "duration.fast": { "value": 60, "unit": "ms", "use": "Toets in/uit" }, + "duration.base": { "value": 120, "unit": "ms", "use": "Standaardovergang" }, + "duration.slow": { "value": 220, "unit": "ms", "use": "Paneel of venster" }, + "easing.mechanical":{ "value": "cubic-bezier(0.2, 0, 0, 1)", "use": "Toetsen: direct, zonder veer" }, + "easing.standard": { "value": "cubic-bezier(0.4, 0, 0.2, 1)", "use": "Overige overgangen" }, + "key.press": { "value": 50, "unit": "ms", "use": "Toets indrukken" }, + "key.release": { "value": 90, "unit": "ms", "use": "Toets loslaten" }, + "lamp.on": { "value": 30, "unit": "ms", "use": "Lampje aan" }, + "lamp.off": { "value": 200, "unit": "ms", "use": "Lampje uit: dooft langzaam, als een gloeidraad" }, + "needle.response": { "value": 0.30, "unit": "s", "use": "VU-ballistiek: 300 ms om de stand te bereiken" }, + "needle.damping": { "value": 0.80, "unit": "ratio", "use": "Veerdemping; geeft ongeveer 1,5 % overschot" }, + "needle.updateHz": { "value": 60, "unit": "Hz", "use": "Verversing van de wijzer" }, + "reducedMotion": { "value": "Overgangen 0 ms; wijzer blijft bewegen maar met demping 1.0 (geen overschot)", "use": "Systeeminstelling 'Verminder beweging'" } + } +} diff --git a/docs/design/tokens-preview.html b/docs/design/tokens-preview.html new file mode 100644 index 0000000..de04656 --- /dev/null +++ b/docs/design/tokens-preview.html @@ -0,0 +1,380 @@ + + + + + +Sst Ontwerp-tokens + + + + + +
+ +
+
+
Ontwerpsysteem · Sst
+

Ontwerp-tokens

+

+
+
+
Ter goedkeuring
+
+
Bron · Design/tokens.json
+
+
+ +
+
01Instrumenten in gebruikmeter.* · motion.needle.* · segment.*
+
+
Afwerking · Aluminiumlicht
+
Afwerking · Graphitedonker
+
+
+ +
+
02Kleurcolor.*
+
+
+ +
+
03Diepte en materiaalshadow.* · border.* · texture.*
+
+
+
+
+
+ +
+
04Typografiefont.* · type.*
+
+
+ +
+
05Ruimte, hoeken, randenspace.* · radius.* · border.*
+
+
Ruimte (pt)
+
+
Hoekradius
+
+
+ +
+
06Bewegingmotion.*
+
+
+
Toets en lampje
+
+

Toets: 50 ms in, 90 ms uit, zonder veer. Lampje: 30 ms aan, 200 ms uit — dooft als een gloeidraad.

+
+
+
Wijzer-ballistiek
+
+

Veer: 300 ms tot 99 %, demping 0,80 → ongeveer 1,5 % overschot. De meters bij 01 gebruiken dezelfde waarden.

+
+
+
+ +
+
07Wat het niet is
+
    +
  • Geen neon, vaporwave, synthwave, paars-roze verlopen, gloeiende tekst, chromen letters of rasterhorizonten.
  • +
  • Eén accent: het rode opnamelampje. Amber en groen alleen als niveau-indicator of markering, nooit als versiering.
  • +
  • Lampjes mogen een kleine gloed hebben. Tekst nooit.
  • +
  • Geen eenmalige waarden in componenten: elke afmeting, kleur en tijd komt uit deze tokens.
  • +
+
+
+ + + + diff --git a/docs/design/tokens.md b/docs/design/tokens.md new file mode 100644 index 0000000..0a93212 --- /dev/null +++ b/docs/design/tokens.md @@ -0,0 +1,283 @@ +# Sst Design Tokens — v0.1.0-draft + +**Status:** Ter goedkeuring — nog niet gebruikt door de app + +Draagbare veldrecorder / cassettedeck uit de jaren 80 (Sony TC-D5, Marantz PMD, Nakamichi, Braun). Twee afwerkingen: ALUMINIUM (licht) en GRAPHITE (donker). Eén accent: het rode opnamelampje. Amber en groen alleen voor niveau-indicatoren. + +Bron: `Design/tokens.json`. Dit bestand en `tokens-preview.html` worden gegenereerd met `python3 Design/build_tokens.py`; pas nooit alleen deze bestanden aan. + +## Kleur + +Verwijs ernaar als `color.`. Kolommen: ALUMINIUM (licht) en GRAPHITE (donker). + +| Token | Aluminium | Graphite | Gebruik | +|---|---|---|---| +| `color.surface.chassis` | `#CBC8C0` | `#1C1B19` | Venster-achtergrond; het 'huis' van het apparaat | +| `color.surface.metalHigh` | `#E3E1DB` | `#3B3A36` | Geborsteld metaal, bovenkant van de verloop | +| `color.surface.metalLow` | `#BDBAB2` | `#262522` | Geborsteld metaal, onderkant van de verloop | +| `color.surface.panel` | `#E2DFD5` | `#2B2A27` | Frontplaat van mat plastic; zijbalk en bedieningspanelen | +| `color.surface.well` | `#B4B1A8` | `#131211` | Verzonken vlak: invoervelden, display-vensters | +| `color.surface.paper` | `#EEE9D9` | `#232220` | Cassette-label crème: lijsten met transcripties en woordenboek | +| `color.surface.keyHigh` | `#E6E3DA` | `#44433F` | Toetsdop, bovenkant | +| `color.surface.keyLow` | `#CBC8BF` | `#33322F` | Toetsdop, onderkant | +| `color.surface.keyPressed` | `#BEBBB2` | `#2A2927` | Ingedrukte toets | +| `color.surface.meterFace` | `#EFE6C8` | `#D8CEA6` | Wijzerplaat van de VU-meter (blijft crème in beide afwerkingen) | +| `color.surface.selection` | `#D4CDB2` | `#3A3828` | Geselecteerde rij in een lijst | +| `color.surface.display` | `#1B1A18` | `#0E0D0C` | Displayglas van de teller; donker in beide afwerkingen | +| `color.display.segmentUnlit` | `#3A3320` | `#2A2415` | Gedoofd segment op het displayglas | +| `color.ink.primary` | `#1E1D1B` | `#ECE8DC` | Hoofdtekst en wijzer | +| `color.ink.secondary` | `#403D38` | `#C4BFB2` | Secundaire tekst | +| `color.ink.tertiary` | `#4D4A44` | `#ABA79B` | Zeefdruk-labels en hulptekst | +| `color.ink.disabled` | `#8F8B82` | `#6B685F` | Uitgeschakeld; nooit voor essentiële tekst | +| `color.ink.inverse` | `#F4EFE3` | `#F4EFE3` | Tekst op rode of donkere vulling; crème in beide afwerkingen | +| `color.ink.onMeter` | `#1E1D1B` | `#1E1D1B` | Schaal, cijfers en wijzer op de wijzerplaat; vast donker in beide afwerkingen | +| `color.accent.record` | `#C8281E` | `#CC2A20` | ENIGE accent: opnamelampje, REC-toets, piek-LED. Nooit voor lopende tekst | +| `color.accent.recordUnlit` | `#7A2E28` | `#4A1F1B` | Lampje uit | +| `color.level.green` | `#5E9E48` | `#6DB556` | Niveau-segment, brandend | +| `color.level.amber` | `#D9A023` | `#E5B032` | Niveau-segment, waarschuwing, gecorrigeerd woord | +| `color.level.greenUnlit` | `#A8B79C` | `#26331F` | Groen segment, uit | +| `color.level.amberUnlit` | `#CDBA8A` | `#3D3418` | Amber segment, uit | +| `color.line.seamDark` | `#8C8981` | `#0A0A09` | Naad tussen panelen, donkere helft | +| `color.line.seamLight` | `#F3F1EB` | `#45433F` | Naad tussen panelen, lichte helft | +| `color.line.hairline` | `#A9A69D` | `#3A3834` | Fijne scheidingslijn, zeefdruk-kader | +| `color.line.focus` | `#1E1D1B` | `#ECE8DC` | Toetsenbord-focus | + +**Regel:** `accent.record` is het enige accent en wordt nooit voor lopende tekst gebruikt. Amber en groen zijn alleen voor niveau-indicatoren en markeringen. + +### Contrast (berekend) + +| Tekst | Op | Afwerking | Verhouding | | +|---|---|---|---|---| +| `ink.primary` | `surface.chassis` | light | 10.08:1 | ok | +| `ink.primary` | `surface.chassis` | dark | 14.05:1 | ok | +| `ink.primary` | `surface.panel` | light | 12.63:1 | ok | +| `ink.primary` | `surface.panel` | dark | 11.72:1 | ok | +| `ink.primary` | `surface.paper` | light | 13.87:1 | ok | +| `ink.primary` | `surface.paper` | dark | 12.98:1 | ok | +| `ink.primary` | `surface.well` | light | 7.86:1 | ok | +| `ink.primary` | `surface.well` | dark | 15.27:1 | ok | +| `ink.primary` | `surface.selection` | light | 10.57:1 | ok | +| `ink.primary` | `surface.selection` | dark | 9.65:1 | ok | +| `ink.onMeter` | `surface.meterFace` | light | 13.49:1 | ok | +| `ink.onMeter` | `surface.meterFace` | dark | 10.67:1 | ok | +| `ink.primary` | `surface.keyHigh` | light | 13.13:1 | ok | +| `ink.primary` | `surface.keyHigh` | dark | 8.08:1 | ok | +| `ink.primary` | `surface.keyLow` | light | 10.07:1 | ok | +| `ink.primary` | `surface.keyLow` | dark | 10.47:1 | ok | +| `ink.secondary` | `surface.chassis` | light | 6.47:1 | ok | +| `ink.secondary` | `surface.chassis` | dark | 9.38:1 | ok | +| `ink.secondary` | `surface.panel` | light | 8.11:1 | ok | +| `ink.secondary` | `surface.panel` | dark | 7.82:1 | ok | +| `ink.secondary` | `surface.paper` | light | 8.90:1 | ok | +| `ink.secondary` | `surface.paper` | dark | 8.66:1 | ok | +| `ink.tertiary` | `surface.chassis` | light | 5.28:1 | ok | +| `ink.tertiary` | `surface.chassis` | dark | 7.16:1 | ok | +| `ink.tertiary` | `surface.panel` | light | 6.62:1 | ok | +| `ink.tertiary` | `surface.panel` | dark | 5.97:1 | ok | +| `ink.tertiary` | `surface.paper` | light | 7.27:1 | ok | +| `ink.tertiary` | `surface.paper` | dark | 6.61:1 | ok | +| `ink.tertiary` | `surface.metalHigh` | light | 6.75:1 | ok | +| `ink.tertiary` | `surface.metalHigh` | dark | 4.73:1 | ok | +| `ink.tertiary` | `surface.metalLow` | light | 4.55:1 | ok | +| `ink.tertiary` | `surface.metalLow` | dark | 6.37:1 | ok | +| `ink.inverse` | `accent.record` | light | 4.84:1 | ok | +| `ink.inverse` | `accent.record` | dark | 4.67:1 | ok | +| `level.amber` | `surface.display` | light | 7.46:1 | ok | +| `level.amber` | `surface.display` | dark | 9.79:1 | ok | + +## Opaciteit + +| Token | Waarde | Gebruik | +|---|---|---| +| `opacity.mark` | 0.3 | Amber markering achter een gecorrigeerd woord | +| `opacity.disabled` | 0.45 | Uitgeschakelde bediening | +| `opacity.unlit` | 1.0 | Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie | + +## Lettertypen + +| Token | Familie | Terugval | Gebruik | +|---|---|---|---| +| `font.family.chrome` | Helvetica Neue | Helvetica, Arial | Zeefdruk, toetsen, koppen: neutrale grotesk | +| `font.family.text` | SF Pro Text | system-ui | Leesbare tekst: transcripties, woordenboek | +| `font.family.mono` | SF Mono | Menlo, monospace | Teller, tijden, woordenboekbestand | + +## Tekststijlen + +| Token | Familie | Grootte | Gewicht | Spatiëring | Regelhoogte | Hoofdletters | Gebruik | +|---|---|---|---|---|---|---|---| +| `type.label` | chrome | 10 pt | 500 | 0.09 em | 12 pt | ja | Zeefdruk-label | +| `type.labelSmall` | chrome | 9 pt | 500 | 0.1 em | 11 pt | ja | Schaalmarkeringen en eenheden | +| `type.control` | chrome | 11 pt | 500 | 0.06 em | 14 pt | ja | Opschrift op toetsen | +| `type.heading` | chrome | 12 pt | 500 | 0.12 em | 16 pt | ja | Paneelkop | +| `type.body` | text | 13 pt | 400 | 0 em | 18 pt | nee | Standaardtekst | +| `type.reading` | text | 15 pt | 400 | 0 em | 22 pt | nee | Transcript | +| `type.meta` | text | 11 pt | 400 | 0 em | 14 pt | nee | Bijschriften | +| `type.counter` | mono | 22 pt | 500 | 0 em | 26 pt | nee | Teller | +| `type.counterSmall` | mono | 12 pt | 500 | 0 em | 16 pt | nee | Tijdstip in een rij | +| `type.code` | mono | 12 pt | 400 | 0 em | 18 pt | nee | Woordenboekregels | + +## Segmentcijfers (getekend, geen lettertype) + +| Token | Waarde | Gebruik | +|---|---|---| +| `segment.digitHeight` | 36 | Hoogte van een getekend segmentcijfer | +| `segment.digitWidth` | 20 | Breedte van een segmentcijfer | +| `segment.thickness` | 4 | Dikte van één segment | +| `segment.gap` | 1 | Ruimte tussen segmenten | +| `segment.slant` | 0 | Schuinte in graden; 0 = rechtop | + +## Ruimte + +| Token | Waarde | +|---|---| +| `space.0` | 0 pt | +| `space.1` | 2 pt | +| `space.2` | 4 pt | +| `space.3` | 8 pt | +| `space.4` | 12 pt | +| `space.5` | 16 pt | +| `space.6` | 24 pt | +| `space.7` | 32 pt | +| `space.8` | 48 pt | + +## Indeling + +| Token | Waarde | Gebruik | +|---|---|---| +| `layout.window.minWidth` | 800 | Minimale vensterbreedte | +| `layout.window.minHeight` | 560 | Minimale vensterhoogte | +| `layout.window.defaultWidth` | 1080 | Standaard vensterbreedte | +| `layout.window.defaultHeight` | 700 | Standaard vensterhoogte | +| `layout.sidebar.width` | 232 | Breedte zijbalk | +| `layout.settings.width` | 560 | Breedte instellingenvenster | +| `layout.panel.padding` | 16 | Binnenmarge van een paneel (= space.5) | +| `layout.panel.gap` | 12 | Afstand tussen panelen (= space.4) | + +## Afmetingen + +| Token | Waarde | Gebruik | +|---|---|---| +| `size.control.height` | 28 | Standaardhoogte van toets of veld | +| `size.key.minWidth` | 56 | Minimale breedte van een toets | +| `size.key.recWidth` | 64 | REC-toets, breedte | +| `size.key.recHeight` | 44 | REC-toets, hoogte | +| `size.lamp.diameter` | 10 | Standaard lampje | +| `size.lamp.large` | 14 | Opnamelampje | +| `size.switch.width` | 40 | Tuimelschakelaar, breedte | +| `size.switch.height` | 22 | Tuimelschakelaar, hoogte | +| `size.row.height` | 40 | Rij in transcriptielijst | +| `size.icon.small` | 16 | Klein pictogram | +| `size.icon.medium` | 20 | Normaal pictogram | +| `size.hit.min` | 24 | Minimaal klikdoel | + +## Hoekradius + +| Token | Waarde | Gebruik | +|---|---|---| +| `radius.none` | 0 | Panelen en velden: scherpe randen | +| `radius.sharp` | 1 | Toetsranden: bijna scherp | +| `radius.sm` | 2 | Toetsen, velden | +| `radius.md` | 4 | Grote panelen | +| `radius.bezel` | 6 | Rand van de VU-meter | +| `radius.lamp` | 999 | Lampjes: rond | + +## Randen + +| Token | Waarde | Gebruik | +|---|---|---| +| `border.hairline` | 1 pt | Fijne lijn | +| `border.seam` | 2 pt | Naad: 1 donkere lijn boven, 1 lichte lijn eronder, leest als gegraveerde groef | +| `border.bezel` | 3 pt | Rand om de VU-meter | +| `border.focus` | 2 pt | Focusring, 2 pt offset | + +## Schaduw en diepte + +### `shadow.key.raised` + +Toets in rust: lichtvang boven, donkere onderrand, valschaduw + +| Laag | Inset | x | y | blur | spread | kleur | alpha aluminium | alpha graphite | +|---|---|---|---|---|---|---|---|---| +| 1 | ja | 0 | 1 | 0 | 0 | `#FFFFFF` | 0.65 | 0.1 | +| 2 | ja | 0 | -1 | 0 | 0 | `#000000` | 0.25 | 0.55 | +| 3 | nee | 0 | 2 | 2 | 0 | `#000000` | 0.35 | 0.6 | + +### `shadow.key.pressed` + +Ingedrukte toets: dop 1 pt omlaag, geen valschaduw Dop verschuift 1 pt omlaag. + +| Laag | Inset | x | y | blur | spread | kleur | alpha aluminium | alpha graphite | +|---|---|---|---|---|---|---|---|---| +| 1 | ja | 0 | 2 | 4 | 0 | `#000000` | 0.5 | 0.7 | +| 2 | ja | 0 | 0 | 0 | 1 | `#000000` | 0.35 | 0.6 | + +### `shadow.well` + +Verzonken vlak met lichtvang op de onderlip + +| Laag | Inset | x | y | blur | spread | kleur | alpha aluminium | alpha graphite | +|---|---|---|---|---|---|---|---|---| +| 1 | ja | 0 | 1 | 3 | 0 | `#000000` | 0.4 | 0.7 | +| 2 | nee | 0 | 1 | 0 | 0 | `#FFFFFF` | 0.55 | 0.07 | + +### `shadow.panel` + +Frontplaat boven het chassis + +| Laag | Inset | x | y | blur | spread | kleur | alpha aluminium | alpha graphite | +|---|---|---|---|---|---|---|---|---| +| 1 | ja | 0 | 1 | 0 | 0 | `#FFFFFF` | 0.6 | 0.08 | +| 2 | nee | 0 | 3 | 8 | 0 | `#000000` | 0.22 | 0.55 | + +### `shadow.lamp.on` + +Brandend lampje: kleine gloed, alleen voor lampjes en nooit voor tekst + +| Laag | Inset | x | y | blur | spread | kleur | alpha aluminium | alpha graphite | +|---|---|---|---|---|---|---|---|---| +| 1 | nee | 0 | 0 | 6 | 1 | `#CC2A20` | 0.45 | 0.55 | +| 2 | ja | 0 | -1 | 1 | 0 | `#000000` | 0.35 | 0.45 | + +## Textuur + +**`texture.brushed`** — Geborsteld aluminium voor chassis en zijbalk. Richting horizontaal; korrel 0.035 (aluminium) / 0.05 (graphite); surface.metalHigh naar surface.metalLow, van boven naar beneden. + +## Meters + +| Token | Waarde | Gebruik | +|---|---|---| +| `meter.vu.referenceDbfs` | -20 | 0 VU = -20 dBFS (RMS) | +| `meter.vu.min` | -20 | Onderkant van de schaal in VU | +| `meter.vu.max` | 3 | Bovenkant van de schaal in VU; vanaf 0 VU rood | +| `meter.vu.arcDegrees` | 90 | Hoek tussen minimale en maximale stand | +| `meter.vu.width` | 260 | Breedte van de meter | +| `meter.vu.height` | 150 | Hoogte van de meter | +| `meter.vu.needleWidth` | 1.5 | Dikte van de wijzer | +| `meter.vu.ticks` | -20 -10 -7 -5 -3 0 +3 | Genummerde schaalmarkeringen | +| `meter.led.segments` | 12 | Aantal segmenten: 8 groen, 3 amber, 1 rood | +| `meter.led.segmentSize` | 6x14 | Segment in pt (breedte x hoogte) | +| `meter.led.gap` | 2 | Ruimte tussen segmenten | +| `meter.peak.holdMs` | 1000 | Piek-LED blijft zo lang branden | + +## Beweging + +| Token | Waarde | Eenheid | Gebruik | +|---|---|---|---| +| `motion.duration.instant` | 0 | ms | Geen overgang | +| `motion.duration.fast` | 60 | ms | Toets in/uit | +| `motion.duration.base` | 120 | ms | Standaardovergang | +| `motion.duration.slow` | 220 | ms | Paneel of venster | +| `motion.easing.mechanical` | cubic-bezier(0.2, 0, 0, 1) | | Toetsen: direct, zonder veer | +| `motion.easing.standard` | cubic-bezier(0.4, 0, 0.2, 1) | | Overige overgangen | +| `motion.key.press` | 50 | ms | Toets indrukken | +| `motion.key.release` | 90 | ms | Toets loslaten | +| `motion.lamp.on` | 30 | ms | Lampje aan | +| `motion.lamp.off` | 200 | ms | Lampje uit: dooft langzaam, als een gloeidraad | +| `motion.needle.response` | 0.3 | s | VU-ballistiek: 300 ms om de stand te bereiken | +| `motion.needle.damping` | 0.8 | ratio | Veerdemping; geeft ongeveer 1,5 % overschot | +| `motion.needle.updateHz` | 60 | Hz | Verversing van de wijzer | +| `motion.reducedMotion` | Overgangen 0 ms; wijzer blijft bewegen maar met demping 1.0 (geen overschot) | | Systeeminstelling 'Verminder beweging' | + +## Wat bewust ontbreekt + +Geen neon, vaporwave, synthwave, paars-roze verlopen, gloeiende tekst, chromen letters of rasterhorizonten. Lampjes mogen een kleine gloed hebben (`shadow.lamp.on`); tekst nooit. From 7789076f414a87c7abde5171a688288b9788f951 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 12:35:05 +0200 Subject: [PATCH 02/43] docs: add macOS app design spec Co-Authored-By: Claude Sonnet 5.5 --- .../specs/2026-10-01-macos-app-design.md | 259 ++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 docs/superpowers/specs/2026-10-01-macos-app-design.md diff --git a/docs/superpowers/specs/2026-10-01-macos-app-design.md b/docs/superpowers/specs/2026-10-01-macos-app-design.md new file mode 100644 index 0000000..7919a75 --- /dev/null +++ b/docs/superpowers/specs/2026-10-01-macos-app-design.md @@ -0,0 +1,259 @@ +# Sst als volwaardige macOS-app — ontwerp + +Datum: 2026-10-01 · Status: concept ter review · Bouwt voort op `2026-10-01-push-to-talk-dictation-design.md` (de engine) en de goedgekeurde tokens in `docs/design/tokens.md` (bron: `Design/tokens.json`, v0.1.0-draft) + +## Doel + +Van menubalk-hulpprogramma naar een echte Mac-app: Dock-icoon, app-menu, een vrij schaalbaar hoofdvenster, een instellingenvenster (⌘,) en een dubbelklikbaar `Sst.app` dat in Programma's kan staan. Het menubalkitem blijft als secundaire status- en sneltoetsweergave. De interface volgt het goedgekeurde ontwerpsysteem: een jaren-80 veldrecorder, met uitsluitend waarden uit de tokens. + +## Wat de gebruiker krijgt + +- **Hoofdvenster:** doorzoekbare transcriptiegeschiedenis met een kopieerknop per rij; een live VU-meter, teller en LED-balk tijdens het opnemen; start- en stopknop; het woordenboek. +- **Instellingen (⌘,):** sneltoets en model. +- **Woordenboek:** woorden en correctieparen, bewerkbaar in de app én als gewoon tekstbestand. Werkt op twee manieren: voorrang bij het model (bias) en een gegarandeerde correctieronde achteraf. +- **Geschiedenis toont correcties:** wanneer het woordenboek iets wijzigt, zie je wat er stond en wat het werd. + +## Buiten scope + +- Overlay-pill tijdens het dicteren, AI-cleanup (taalmodel), streaming-transcriptie. +- Meerdere woordenboeken of profielen, per-app-instellingen, import of export van de geschiedenis, synchronisatie. +- Notarisatie, DMG, automatische updates, automatisch starten bij inloggen. +- Toggle-modus (eenmaal drukken start, nogmaals stopt). Alleen vasthouden. +- Definitieve naam en merk. "Sst" is de werktitel; de naam staat op één plek (`AppIdentity`) zodat hernoemen goedkoop blijft. +- Een app-icoon in volledige merkkwaliteit; wel een eerste icoon in het goedgekeurde ontwerp. + +## Beslissingen en aannames + +1. **De engine blijft in gedrag identiek.** Er komen vier kleine, additieve raakpunten bij (zie "Engine-raakpunten"). Bestaande tests blijven ongewijzigd groen. +2. **"Een kopie van elk document"** is een **kopieerknop per transcriptie** (kopieert de uiteindelijke tekst). +3. **UI-taal is Nederlands**, in één bestand met teksten (`Strings`), zodat vertalen later kan. Systeemmenu's (Hide, Quit, enz.) volgen de taal van macOS. +4. **Geschiedenis staat standaard aan** en bewaart alleen tekst, nooit audio. Een schakelaar in Instellingen zet bewaren uit en een knop wist alles. +5. **Sluit je het hoofdvenster, dan draait de app door** (Dock-icoon en menubalkitem blijven). Dicteren in andere apps blijft werken. ⌘Q sluit de app echt af. Klikken op het Dock-icoon opent het venster weer. +6. **Is Sst zelf de voorgrond-app** (of start je de opname met de knop in het venster), dan wordt de tekst **niet geplakt** maar alleen in de geschiedenis bewaard. Anders zou het dictaat in het zoekveld van Sst terechtkomen. +7. **Ondertekening:** met je Apple Development-certificaat, zodat macOS je rechten (Microfoon, Accessibility, Input Monitoring) na een herbouw niet vergeet. Het script zoekt zelf de eerste "Apple Development"-identiteit en valt anders terug op ad-hoc, met een duidelijke waarschuwing. Geen identiteit staat in de repo. +8. **Sneltoetsen:** alleen modifier-toetsen, vasthouden. Keuze: Fn (standaard), Rechter Option, Rechter Command, Rechter Control, Linker Control. +9. **Afwerking** (Aluminium of Graphite) volgt het uiterlijk van macOS. + +## Architectuur + +Eén Swift Package, vier lagen, steeds in één richting afhankelijk: + +``` +Sst (app) → SstUI → (tokens, componenten) + │ + ├────────→ SstData → (woordenboek, correcties, geschiedenis; alleen Foundation) + └────────→ SstCore + SstWhisper (de engine; dunne uitbreidingen) +``` + +| Module | Verantwoordelijkheid | Testbaar zonder UI | +|---|---|---| +| `SstCore` / `SstWhisper` | Engine (bestaat). Vier additieve raakpunten. | ja | +| `SstData` | `DictionaryFile`, `CorrectionEngine`, `EntryWarnings`, `VocabularyBuilder`, `HistoryStore`, `HistorySearch` | ja | +| `SstUI` | Gegenereerde `DesignTokens`, componenten (toets, lampje, VU-meter, segmentteller, LED-balk, paneel, verzonken vlak, zeefdruk-label) | gedeeltelijk | +| `Sst` | Scenes, menu's, `AppModel`, vensters, bundelscript | handmatig | + +### Engine-raakpunten (allemaal additief, met standaardwaarden) + +1. **Voorrang voor het model.** `Transcriber` krijgt `transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String`. De oude methode blijft bestaan als extensie met een lege lijst. `WhisperKitTranscriber` zet de woorden om in `DecodingOptions.promptTokens`: tekst `" " + woorden.joined(", ")`, via `tokenizer.encode(text:)`, gefilterd op tokens onder `specialTokens.specialTokenBegin` (zo doet de WhisperKit-CLI het ook). +2. **Niveau-uitvoer.** `AudioRecorder` krijgt `onLevel: ((Float) -> Void)?`: RMS in dBFS per buffer van 1024 frames (~21 ms bij 48 kHz). De bestaande buffer van 4096 blijft ongewijzigd voor de opname zelf; de niveau-tap rekent op dezelfde audio. +3. **Resultaat-hook en correctie.** `DictationController` krijgt een optionele `corrector: TextCorrecting?` en `onResult: ((DictationResult) -> Void)?`. `DictationResult` bevat `rawText`, `finalText`, `corrections`, `duration`, `date`. Zonder corrector en hook gedraagt de controller zich exact als nu. +4. **Instelbare toets.** `HotkeyMonitor` krijgt een `HotkeyBinding` (keycode + vlagmasker) via een nieuwe `ModifierKeyTracker`. `FnKeyTracker` blijft bestaan en wordt de Fn-variant. Het keycode-filter (nu 63) wordt de keycode van de gekozen binding. + +## App-shell + +### Scenes + +``` +Window("Sst", id: "main") — één hoofdvenster, geen documentvensters +Settings — ⌘, +MenuBarExtra — secundair +.commands { … } — app-menu en menu's +``` + +Zonder `LSUIElement`, `NSPrincipalClass` standaard. `applicationShouldHandleReopen` opent het hoofdvenster als alle vensters dicht zijn. Vensterafmetingen komen uit `layout.window.*`: minimaal 800×560, standaard 1080×700; macOS onthoudt de laatste grootte. + +### Menu's + +| Menu | Inhoud | +|---|---| +| **Sst** | Over Sst, Instellingen… (⌘,), Verberg, Verberg anderen, Toon alles, Stop Sst (⌘Q) | +| **Archief** | Sluit venster (⌘W) | +| **Bewerken** | Standaard (Kopieer, Selecteer alles, Zoek…); Verwijder transcriptie (⌘⌫); Wis geschiedenis… | +| **Opname** | Start of stop opname (⌥⌘R); Transcripties (⌘1); Woordenboek (⌘2); Zoek (⌘F) | +| **Venster** / **Help** | Standaard | + +### Menubalkitem (secundair) + +Statusicoon met SF Symbols (`mic`, `mic.fill`, `waveform`, `exclamationmark.triangle`), geen aangepast beeld. Menu: één statusregel, de gekozen sneltoets ("Houd RECHTER OPTION ingedrukt"), "Open Sst", "Instellingen…", "Stop Sst". Geen geschiedenis, geen meter. + +### Bundel en installatie + +`scripts/make-app.sh` bouwt `build/Sst.app` met: `CFBundleIdentifier` (huidige `local.sst.app` blijft, zodat bestaande rechten blijven werken), versie, `LSApplicationCategoryType` productivity, `CFBundleIconFile`, `NSMicrophoneUsageDescription` in het Nederlands, **zonder `LSUIElement`**. Het script ondertekent met de gevonden identiteit. `--install` kopieert naar `/Applications` (alleen door de gebruiker uit te voeren). Het icoon komt uit een script dat een SwiftUI-weergave met de tokens naar een `.icns` rendert (`Design/make_icon`). + +### Rechten en eerste start + +Een blok "CONTROLES" onderaan de zijbalk toont vier lampjes: Microfoon, Accessibility, Input Monitoring, Model. Elk heeft een knop naar de juiste Systeeminstellingen. Rechten worden **opnieuw gelezen zodra de app actief wordt** en elke 2 s zolang het blok zichtbaar is, zodat de weergave niet meer verouderd raakt. Tot alle vier branden staat de REC-toets uit met een duidelijke reden. + +## Hoofdvenster + +Indeling (afmetingen en kleuren uitsluitend uit tokens): + +``` +┌─ zijbalk 232 ─┬─ deck (vast, bovenaan) ───────────────────────┐ +│ TRANSCRIPTIES │ [VU-meter] [00:12.4] [LED-balk] ● REC STOP │ +│ WOORDENBOEK ├────────────────────────────────────────────────┤ +│ │ zoekveld │ +│ CONTROLES │ lijst (papier) of woordenboek │ +└───────────────┴────────────────────────────────────────────────┘ +``` + +### Deck + +- **VU-meter:** 0 VU = −20 dBFS. `vu = rmsDbfs + 20`, schaal −20…+3. De wijzer volgt `onLevel` met de veer uit `motion.needle.*` (respons 0,30 s, demping 0,80; met "Verminder beweging" demping 1,0). Piek-LED 1000 ms. Getekend met `Canvas`, ververst met 60 Hz via `TimelineView` **alleen wanneer het venster zichtbaar is én er wordt opgenomen**; daarna valt de wijzer terug en de timer stopt (geen CPU in rust). +- **Teller:** segmentcijfers (`segment.*`) op `surface.display`, ghost-segmenten voor gedoofde delen. Toont de opnametijd; in rust de duur van de laatste opname. +- **LED-balk:** 12 segmenten (8 groen, 3 amber, 1 rood) op hetzelfde signaal. +- **REC / STOP:** REC start, STOP beëindigt; de REC-toets blijft ingedrukt zolang er wordt opgenomen, met brandend lampje. Een sessie vanuit het venster slaat alleen op (zie beslissing 6). +- **Fout:** een amber lampje met een korte regel; wordt gewist bij de volgende opname. + +### Transcripties + +- Gesorteerd nieuw → oud, gegroepeerd per dag (zeefdruk-kop). Rij: tijd (`type.counterSmall`), tekst (`type.reading`), kopieerknop, minimale hoogte `size.row.height`. +- **Zoeken** (⌘F): live, niet hoofdlettergevoelig en diakriet-ongevoelig, op zowel de uiteindelijke als de oorspronkelijke tekst; woorden hoeven niet aaneen te staan (alle zoekwoorden moeten voorkomen). +- **Correcties zichtbaar:** vervangen stukken krijgen de amber markering (`opacity.mark`); onder de rij staat "2 CORRECTIES · cloud code → Claude Code". Geselecteerd toont de rij de oorspronkelijke tekst naast de uiteindelijke. +- Kopieerknop kopieert de uiteindelijke tekst; rechtsklik: kopieer origineel, verwijder. Lege staat: korte zeefdruk-tekst, geen illustratie. + +### Woordenboek + +Tabel op papier-oppervlak met kolommen GEHOORD → SCHRIJF. Een regel zonder pijl is een term (alleen SCHRIJF). Toevoegen met `+`, bewerken met dubbelklik, verwijderen met ⌫, ongedaan maken met ⌘Z, zoeken met het zoekveld. Een knop "Open bestand" toont het bestand in de standaardeditor. Twee kleine schakelaars: "Voorrang" en "Correctie" (beide standaard aan), zodat je kunt zien wat elk mechanisme doet. Een amber lampje op de rij toont een waarschuwing (zie hieronder). + +## Instellingen + +Venster `layout.settings.width` breed, drie tabbladen als zeefdruk-toetsen. + +- **Sneltoets:** de gekozen toets wordt vastgelegd door erop te drukken (de app is dan actief en luistert met een lokale event-monitor). Alleen de toegestane modifiers uit beslissing 8; andere toetsen worden afgewezen met uitleg. Bij Fn staat een hint over de Globe-instelling van macOS. +- **Model:** vaste lijst van varianten (op dit moment: `large-v3-v20240930_turbo` standaard, de 632 MB-variant en `small`; exacte namen worden bij de bouw tegen WhisperKit gecontroleerd). Per model: status (aanwezig, gekozen, niet gedownload), download- of verwijderknop en voortgang als LED-balk. Wisselen laadt het nieuwe model en zet dicteren uit tot het klaar is; mislukt het laden, dan blijft het vorige model actief met een duidelijke melding. Toont de opslaglocatie met "Toon in Finder". +- **Privacy:** schakelaar "Transcripties bewaren" en knop "Wis geschiedenis…" (met bevestiging). + +## Woordenboek — het ontwerp in detail + +### Bestand + +`~/Library/Application Support/Sst/dictionary.txt`, UTF-8, één invoer per regel: + +``` +# Opmerkingen beginnen met # +Anthropic +Vercel +Supabase +cloud code => Claude Code +``` + +- Een regel zonder `=>` is een **term**. Een regel met `=>` is een **correctiepaar** (links gehoord, rechts schrijven). Spaties rond `=>` en aan de uiteinden worden genegeerd. +- Opmerkingen, lege regels en de volgorde blijven bij opslaan **behouden**: de app leest naar een lijst van regels (invoer, opmerking, leeg) en schrijft die letterlijk terug. Een onleesbare regel blijft staan en krijgt een waarschuwing, ze wordt nooit stilletjes weggegooid. +- De app schrijft atomisch en let op wijzigingen door een externe editor (bestandsmonitor, 200 ms debounce) en herlaadt dan. Verdwijnt de invoer die je aan het bewerken was, dan sluit de editor met een melding. + +### Mechanisme 1 — voorrang bij het model + +`VocabularyBuilder` maakt een korte lijst: eerst de termen (bestandsvolgorde), dan de unieke rechterkanten van de correctieparen. **Maximaal 24 woorden en 64 tokens**; past er meer niet, dan vallen de laatste woorden af. Doorgegeven als `promptTokens` (zie raakpunt 1). + +Tegen verzinnen op stilte: + +- Is het RMS van de opname lager dan **0,015** (het stiltefilter van 0,005 blijft erboven), dan wordt **geen prompt** meegegeven. +- Bestaat het resultaat uitsluitend uit de prompt zelf (genormaliseerd) bij zo'n zachte opname, dan wordt het verworpen. +- WhisperKit zet de snelle prefill-cache uit zodra een prompt is gezet; de snelheid wordt bij de bouw gemeten met en zonder prompt en in het plan vastgelegd. Doel: onder 1,5 s voor 8 s spraak. + +### Mechanisme 2 — correctieronde (het gegarandeerde pad) + +`CorrectionEngine` loopt na de transcriptie over de tekst: + +- **Hele woorden**, niet hoofdlettergevoelig, **één doorgang** van links naar rechts. De langste overeenkomst wint (bij gelijke lengte de eerste in het bestand). Vervangen tekst wordt niet opnieuw gescand, dus A→B en B→C vormen nooit een ketting. +- **Aaneengeplakte en gekoppelde vormen:** de bronzin wordt gesplitst op spaties en koppeltekens; de delen worden verbonden met `[spatie, koppelteken]*`. "cloud code" herkent dus "cloud code", "cloudcode", "Cloud-Code" en "CLOUD CODE". Een bron van één deel (geen splitsing) herkent alleen zichzelf, hoofdletter-onafhankelijk. +- **Woordgrenzen** zijn Unicode-bewust: de overeenkomst mag niet direct aan een letter of cijfer grenzen. Daarom raakt "cloud code" nooit "Cloudflare", "cloud codex" of het losse woord "cloud". +- **Termen** (invoer zonder pijl) krijgen een normaliserende regel: dezelfde letters, maar met de gewenste schrijfwijze (hoofdletters en spatiëring). "claude code", "claudecode" en "Claude-Code" worden "Claude Code". **Uitzondering:** is de term in kleine letters een gewoon woord (bijvoorbeeld "Reef" of "Slack"), dan krijgt hij alleen voorrang bij het model en géén automatische hoofdletterregel; de rij laat dat zien. +- De vervanging is letterlijk (geen sjablonen); `$` en `\` blijven staan. +- De engine geeft per vervanging terug: gehoord (zoals het in de tekst stond), geschreven, de regel, en de plaats in de uiteindelijke tekst. + +### Waarschuwingen bij invoer die veel voorkomt + +`EntryWarnings` bepaalt bij toevoegen en bewerken, met een injecteerbare `WordKnowledge` (standaard: `NSSpellChecker` voor nl en en, plus een meegeleverde lijst van de ~300 meest voorkomende functiewoorden in beide talen): + +| Niveau | Wanneer | +|---|---| +| **Waarschuwing** | De bron is één woord dat in nl of en bestaat ("cloud" → wordt overal vervangen) | +| **Waarschuwing** | De bron is korter dan 3 letters | +| **Waarschuwing** | Een deel van een meerdelige bron is een veelvoorkomend functiewoord ("in de") | +| **Waarschuwing** | De aaneengeplakte vorm is zelf een bestaand woord ("some thing" → "something") | +| **Let op** | Alle delen zijn gewone woorden: de correctie werkt alleen op de hele combinatie (jouw voorbeeld "cloud code") | +| **Fout** | Dubbele bron (de eerste telt) of een lege kant | + +Een waarschuwing blokkeert het opslaan niet, maar toont een amber lampje op de rij en de reden in de editor. + +### Zichtbaar in de geschiedenis + +Elke opname bewaart `rawText`, `finalText` en `corrections`. De rij toont de markeringen en de samenvatting. Is er geen correctie gedaan, dan staat er niets extra. Zo zie je direct of het woordenboek iets doet. + +## Geschiedenis + +`HistoryStore` bewaart `~/Library/Application Support/Sst/history.json`: + +```json +{ "version": 1, "entries": [ + { "id": "UUID", "date": "ISO-8601", "duration": 8.4, + "rawText": "…", "finalText": "…", + "corrections": [ { "heard": "cloud code", "wrote": "Claude Code", "rule": "cloud code", "start": 12, "length": 11 } ] } ] } +``` + +Atomisch schrijven, met korte debounce; geheugen-eerst. Meer dan 10 000 regels is geen doel van deze versie. Onleesbaar bestand: hernoemen naar `history.corrupt-.json` en vers beginnen, met een melding. Staat bewaren uit, dan wordt niets geschreven en toont het hoofdvenster een regel dat de geschiedenis uit staat. + +## Ontwerpsysteem in code + +`Design/build_tokens.py` krijgt een Swift-uitvoer (`--swift`) die `Sources/SstUI/DesignTokens.swift` genereert uit `Design/tokens.json`: + +- Kleuren als dynamische `NSColor`/`Color` met aluminium- en graphite-waarden, schaduwen als lagenlijsten, afmetingen, typografie en beweging als constanten. +- Componenten gebruiken **uitsluitend** deze constanten. Een test zoekt in `Sources/SstUI` en `Sources/Sst` naar losse hex-kleuren en vaste puntafmetingen buiten de gegenereerde file en faalt als ze er zijn. +- Een test controleert dat het gegenereerde bestand overeenkomt met een verse generatie (geen drift) en dat de contrastcontrole slaagt. +- Componenten: `KeyButtonStyle` (rust, ingedrukt, REC), `LampView`, `PanelBackground`, `WellBackground`, `SeamDivider`, `SilkLabel`, `VUMeterView`, `SegmentCounterView`, `LEDBarView`, `PaperList`. De geborstelde textuur wordt één keer gegenereerd en gecachet. + +## Toegankelijkheid + +- Alle bediening bereikbaar met het toetsenbord; zichtbare focusring (`border.focus`, `line.focus`). +- VoiceOver: toetsen en lampjes hebben labels ("Opname starten", "Microfoon: toegestaan"); de VU-meter meldt zijn waarde ("niveau min 12 VU") met 2 Hz, niet met 60 Hz. +- "Verminder beweging" wordt gerespecteerd (zie `motion.reducedMotion`). +- Alle tekstparen halen ≥ 4,5:1 (gecontroleerd door het tokenscript). + +## Foutafhandeling + +| Situatie | Gedrag | +|---|---| +| Model niet gedownload / laden mislukt | Controle-lampje uit, REC uitgeschakeld, melding met knop "Opnieuw proberen"; vorige model blijft bij wisselen actief | +| Geen netwerk bij eerste download | Duidelijke melding; geen herhaalde verzoeken op de achtergrond | +| Rechten ontbreken | Lampje en knop in CONTROLES; hotkey-opnames geven een korte fout | +| Plakken mislukt | Tekst blijft op het klembord en staat wel in de geschiedenis | +| Woordenboekbestand ontbreekt | Wordt aangemaakt met een korte kop; leeg is geldig | +| Woordenboekregel onleesbaar | Regel blijft behouden, krijgt een waarschuwing | +| Geschiedenisbestand beschadigd | Hernoemen, vers beginnen, melding | + +## Testen + +- **Unit (zonder UI):** `DictionaryFile` (ronde parse → schrijf met opmerkingen en volgorde), `CorrectionEngine` (spatie/koppelteken/aaneengeplakt, "Cloudflare" en "cloud codex" blijven onaangeroerd, langste eerst, geen ketens, hoofdletters, Unicode, `$`/`\` letterlijk), `EntryWarnings` met een valse `WordKnowledge`, `VocabularyBuilder` (limieten, volgorde), stilte-bias-regels, `HistoryStore` (lezen, schrijven, beschadigd), `HistorySearch` (diakrieten, meerdere woorden), VU-afbeelding (dBFS → VU → hoek), `ModifierKeyTracker` met meerdere bindingen, en de engine-raakpunten met nep-onderdelen. Het bestaande 13-testen-pakket blijft groen. +- **Tokens:** parity- en contrasttest (zie "Ontwerpsysteem in code"). +- **Handmatig** (met de gebruiker, zoals bij het skeleton): Dock-icoon en app-menu; venster vrij schalen; ⌘, ; sluiten en weer openen via het Dock; dicteren in TextEdit, browser en terwijl Sst achter staat; dicteren terwijl Sst voorop staat (alleen opslaan); woordenboek: term, correctiepaar, "cloud-code"-variaties, "Cloudflare" blijft; waarschuwing bij "cloud"; bestand in een editor wijzigen en zien herladen; model wisselen; rechten intrekken en terugzien; VoiceOver kort; "Verminder beweging". + +## Bouwvolgorde + +Elke fase levert op zichzelf testbare software en krijgt een eigen plan: + +1. **Engine-raakpunten** (SstCore/SstWhisper) met tests. +2. **SstData:** woordenboek, correcties, waarschuwingen, voorrang, geschiedenis, zoeken, met tests. +3. **SstUI:** tokens genereren in Swift, componenten, parity- en contrasttest, een klein ontwikkelvenster om componenten te bekijken. +4. **App-shell:** scenes, menu's, menubalkitem, Instellingen (sneltoets en model), bundelscript, ondertekening, icoon. +5. **Hoofdvenster:** deck met meter, geschiedenis, woordenboekweergave, bedrading met `AppModel`; handmatige rooktest. + +## Open punten (graag bevestigen) + +1. De vier **engine-raakpunten**, in het bijzonder dat `Transcriber` een woordenlijst accepteert. +2. **"Kopie van elk document"** als kopieerknop per transcriptie. +3. **Nederlandse UI-teksten** met macOS-systeemmenu's in de taal van het systeem. +4. De vaste lijst **toegestane sneltoetsen** en alleen vasthouden. +5. Dat Sst **niet plakt** wanneer Sst zelf voorop staat of de opname via het venster start. +6. De keuze om **geschiedenis standaard aan** te zetten. From bc5b5dfc98de3432027dc0dabc8c6b55c8934726 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:16:10 +0200 Subject: [PATCH 03/43] docs: refine app spec (prompt-echo rule, module dependency) Co-Authored-By: Claude Sonnet 5.5 --- docs/superpowers/specs/2026-10-01-macos-app-design.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/specs/2026-10-01-macos-app-design.md b/docs/superpowers/specs/2026-10-01-macos-app-design.md index 7919a75..865de69 100644 --- a/docs/superpowers/specs/2026-10-01-macos-app-design.md +++ b/docs/superpowers/specs/2026-10-01-macos-app-design.md @@ -39,16 +39,15 @@ Van menubalk-hulpprogramma naar een echte Mac-app: Dock-icoon, app-menu, een vri Eén Swift Package, vier lagen, steeds in één richting afhankelijk: ``` -Sst (app) → SstUI → (tokens, componenten) - │ - ├────────→ SstData → (woordenboek, correcties, geschiedenis; alleen Foundation) - └────────→ SstCore + SstWhisper (de engine; dunne uitbreidingen) +Sst (app) → SstUI (tokens, componenten) + ├────────→ SstData → SstCore (woordenboek, correcties, geschiedenis) + └────────→ SstWhisper → SstCore (de engine; dunne uitbreidingen) ``` | Module | Verantwoordelijkheid | Testbaar zonder UI | |---|---|---| | `SstCore` / `SstWhisper` | Engine (bestaat). Vier additieve raakpunten. | ja | -| `SstData` | `DictionaryFile`, `CorrectionEngine`, `EntryWarnings`, `VocabularyBuilder`, `HistoryStore`, `HistorySearch` | ja | +| `SstData` | `DictionaryFile`, `CorrectionEngine`, `EntryWarnings`, `VocabularyBuilder`, `HistoryStore`, `HistorySearch`, `AppSettings`. Deelt de typen `AppliedCorrection` en `TextCorrecting` met `SstCore`. | ja | | `SstUI` | Gegenereerde `DesignTokens`, componenten (toets, lampje, VU-meter, segmentteller, LED-balk, paneel, verzonken vlak, zeefdruk-label) | gedeeltelijk | | `Sst` | Scenes, menu's, `AppModel`, vensters, bundelscript | handmatig | @@ -159,7 +158,7 @@ cloud code => Claude Code Tegen verzinnen op stilte: - Is het RMS van de opname lager dan **0,015** (het stiltefilter van 0,005 blijft erboven), dan wordt **geen prompt** meegegeven. -- Bestaat het resultaat uitsluitend uit de prompt zelf (genormaliseerd) bij zo'n zachte opname, dan wordt het verworpen. +- Bestaat het resultaat (genormaliseerd: kleine letters, zonder leestekens) uitsluitend uit de eerste *k* termen van de prompt met *k* ≥ 2, dan wordt het verworpen: dat is een echo van de prompt, geen spraak. Eén term alleen ("Anthropic") is wel geldige spraak. - WhisperKit zet de snelle prefill-cache uit zodra een prompt is gezet; de snelheid wordt bij de bouw gemeten met en zonder prompt en in het plan vastgelegd. Doel: onder 1,5 s voor 8 s spraak. ### Mechanisme 2 — correctieronde (het gegarandeerde pad) From 8c5d561e0197fb78f35da5cd74a02280796987a2 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:23:12 +0200 Subject: [PATCH 04/43] docs: add app plans 1 (engine hooks) and 2 (data); refine word-knowledge in spec Co-Authored-By: Claude Sonnet 5.5 --- .../plans/2026-10-01-app-1-engine-hooks.md | 1101 +++++++++++++ .../plans/2026-10-01-app-2-data.md | 1413 +++++++++++++++++ .../specs/2026-10-01-macos-app-design.md | 2 +- 3 files changed, 2515 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md create mode 100644 docs/superpowers/plans/2026-10-01-app-2-data.md diff --git a/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md b/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md new file mode 100644 index 0000000..9716a51 --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md @@ -0,0 +1,1101 @@ +# App fase 1 — Engine-raakpunten Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** De engine krijgt vier additieve uitbreidingen (woordenlijst voor de transcriber, niveau-uitvoer, resultaat-hook met correctie, instelbare sneltoets) zonder dat bestaand gedrag verandert. + +**Architecture:** Alle wijzigingen zijn additief met standaardwaarden die het huidige gedrag behouden. Nieuwe pure logica (`PromptEcho`, `AudioLevel`, `HotkeyBinding`, `ModifierKeyTracker`, `promptTokens`) staat los van hardware en wordt met unit-tests gedekt; de hardware-afhankelijke stukken (`AudioRecorder`, `HotkeyMonitor`, WhisperKit) zijn dun en worden handmatig gecontroleerd. + +**Tech Stack:** Swift 6.4 (language mode 5), Swift Testing, AVFoundation, CoreGraphics, WhisperKit 0.18. + +**Spec:** `docs/superpowers/specs/2026-10-01-macos-app-design.md` (secties "Engine-raakpunten", "Woordenboek — mechanisme 1") + +## Global Constraints + +- Bestaande tests (13 stuks) blijven ongewijzigd groen; bestaande testbestanden worden alleen *aangevuld* (`Fakes.swift`) of krijgen er een nieuw bestand naast. +- Nieuwe parameters hebben standaardwaarden die het huidige gedrag behouden. +- Vocabulaire: maximaal 64 tokens in de prompt; prompt alleen bij opnames met RMS ≥ 0,015; stiltefilter blijft 0,005. +- Prompt-echo: resultaat dat uitsluitend uit de eerste *k* ≥ 2 termen van de prompt bestaat wordt verworpen; één term alleen niet. +- Niveau: `onLevel` levert RMS in dBFS; 0 VU = −20 dBFS. +- Toegestane sneltoetsen (exact): Fn (keycode 63), Rechter Option (61), Rechter Command (54), Rechter Control (62), Linker Control (59). +- Elke commit eindigt met de trailer `Co-Authored-By: Claude Sonnet 5.5 ` (tweede `-m`). + +## Bestandsstructuur + +``` +Sources/SstCore/Models.swift (wijzig) Transcriber + AppliedCorrection, CorrectionResult, TextCorrecting, DictationResult +Sources/SstCore/PromptEcho.swift (nieuw) +Sources/SstCore/DictationController.swift(vervang) corrector, onResult, vocabularyProvider +Sources/SstCore/HotkeyBinding.swift (nieuw) +Sources/SstCore/ModifierKeyTracker.swift (nieuw) +Sources/SstCore/HotkeyMonitor.swift (vervang) binding +Sources/SstCore/AudioLevel.swift (nieuw) +Sources/SstCore/AudioRecorder.swift (wijzig) onLevel +Sources/SstWhisper/WhisperKitTranscriber.swift (wijzig) vocabulary → promptTokens +Sources/SstProbe/RecordProbe.swift, TranscribeProbe.swift (wijzig) handmatige controle +Package.swift (wijzig) SstWhisperTests +Tests/SstCoreTests/Fakes.swift (aanvullen) +Tests/SstCoreTests/TranscriberVocabularyTests.swift, PromptEchoTests.swift, + DictationControllerHooksTests.swift, HotkeyBindingTests.swift, + ModifierKeyTrackerTests.swift, AudioLevelTests.swift (nieuw) +Tests/SstWhisperTests/PromptTokensTests.swift (nieuw) +``` + +--- + +### Task 1: Gedeelde typen en woordenlijst op `Transcriber` + +**Files:** +- Modify: `Sources/SstCore/Models.swift` +- Test: `Tests/SstCoreTests/TranscriberVocabularyTests.swift` + +**Interfaces:** +- Produces (gebruikt door alle latere fasen): + - `Transcriber.transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String` (protocol-eis, met standaardimplementatie die `transcribe(_:)` aanroept) + - `struct AppliedCorrection: Equatable, Codable, Sendable { heard, wrote, rule: String; start, length: Int }` met publieke init `init(heard:wrote:rule:start:length:)` + - `struct CorrectionResult: Equatable, Sendable { text: String; corrections: [AppliedCorrection] }` met publieke init `init(text:corrections:)` + - `protocol TextCorrecting { func correct(_ text: String) -> CorrectionResult }` + - `struct DictationResult: Equatable, Sendable { date: Date; duration: TimeInterval; rawText: String; finalText: String; corrections: [AppliedCorrection] }` met publieke init in die volgorde + +- [ ] **Step 1: Schrijf de falende test** + +`Tests/SstCoreTests/TranscriberVocabularyTests.swift`: + +```swift +import Testing +@testable import SstCore + +private struct PlainTranscriber: Transcriber { + func transcribe(_ samples: [Float]) async throws -> String { "plain \(samples.count)" } +} + +struct TranscriberVocabularyTests { + @Test func vocabularyVariantFallsBackToPlainTranscribe() async throws { + let result = try await PlainTranscriber().transcribe([0, 0, 0], vocabulary: ["Anthropic"]) + #expect(result == "plain 3") + } + + @Test func appliedCorrectionRoundTripsThroughJSON() throws { + let original = AppliedCorrection(heard: "cloud code", wrote: "Claude Code", rule: "cloud code", start: 12, length: 11) + let data = try JSONEncoder().encode(original) + let decoded = try JSONDecoder().decode(AppliedCorrection.self, from: data) + #expect(decoded == original) + } +} +``` + +- [ ] **Step 2: Draai de test, moet falen** + +Run: `swift test --filter TranscriberVocabularyTests` +Expected: compilatiefout `extra argument 'vocabulary' in call` / `cannot find 'AppliedCorrection' in scope`. + +- [ ] **Step 3: Implementatie** + +In `Sources/SstCore/Models.swift` vervang het blok + +```swift +public protocol Transcriber { + func transcribe(_ samples: [Float]) async throws -> String +} +``` + +door: + +```swift +public protocol Transcriber { + func transcribe(_ samples: [Float]) async throws -> String + /// Biases decoding towards `vocabulary`. Engines without bias support ignore the list. + func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String +} + +public extension Transcriber { + func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + try await transcribe(samples) + } +} + +/// One replacement made by the dictionary correction pass. +/// `start`/`length` are UTF-16 offsets into the corrected text. +public struct AppliedCorrection: Equatable, Codable, Sendable { + public var heard: String + public var wrote: String + public var rule: String + public var start: Int + public var length: Int + + public init(heard: String, wrote: String, rule: String, start: Int, length: Int) { + self.heard = heard + self.wrote = wrote + self.rule = rule + self.start = start + self.length = length + } +} + +public struct CorrectionResult: Equatable, Sendable { + public var text: String + public var corrections: [AppliedCorrection] + + public init(text: String, corrections: [AppliedCorrection]) { + self.text = text + self.corrections = corrections + } +} + +public protocol TextCorrecting { + func correct(_ text: String) -> CorrectionResult +} + +/// A finished dictation, emitted just before the text is inserted. +public struct DictationResult: Equatable, Sendable { + public var date: Date + public var duration: TimeInterval + public var rawText: String + public var finalText: String + public var corrections: [AppliedCorrection] + + public init(date: Date, duration: TimeInterval, rawText: String, finalText: String, corrections: [AppliedCorrection]) { + self.date = date + self.duration = duration + self.rawText = rawText + self.finalText = finalText + self.corrections = corrections + } +} +``` + +- [ ] **Step 4: Draai alle tests** + +Run: `swift test` +Expected: alle tests PASS (13 bestaande + 2 nieuwe). + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstCore/Models.swift Tests/SstCoreTests/TranscriberVocabularyTests.swift +git commit -m "feat: add vocabulary-aware Transcriber and shared correction/result types" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 2: `DictationController` — voorrang, echo-filter, correctie en resultaat-hook + +**Files:** +- Create: `Sources/SstCore/PromptEcho.swift` +- Replace: `Sources/SstCore/DictationController.swift` +- Modify: `Tests/SstCoreTests/Fakes.swift` (aanvullen onderaan) +- Test: `Tests/SstCoreTests/PromptEchoTests.swift`, `Tests/SstCoreTests/DictationControllerHooksTests.swift` + +**Interfaces:** +- Consumes: Task 1 (`Transcriber.transcribe(_:vocabulary:)`, `TextCorrecting`, `CorrectionResult`, `DictationResult`). +- Produces: + - `PromptEcho.isEcho(_ text: String, vocabulary: [String]) -> Bool` + - `DictationController.init(recorder:transcriber:cleaner:inserter:minimumSamples:silenceThreshold:corrector:vocabularyProvider:biasMinimumRMS:)` — de nieuwe parameters zijn `corrector: TextCorrecting? = nil`, `vocabularyProvider: @escaping () -> [String] = { [] }`, `biasMinimumRMS: Float = 0.015` + - `DictationController.onResult: ((DictationResult) -> Void)?` + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstCoreTests/PromptEchoTests.swift`: + +```swift +import Testing +@testable import SstCore + +struct PromptEchoTests { + let vocabulary = ["Anthropic", "Vercel", "Supabase"] + + @Test func firstTwoTermsAreAnEcho() { + #expect(PromptEcho.isEcho("Anthropic, Vercel.", vocabulary: vocabulary)) + } + + @Test func wholeListIsAnEcho() { + #expect(PromptEcho.isEcho("anthropic vercel supabase", vocabulary: vocabulary)) + } + + @Test func singleTermIsRealSpeech() { + #expect(!PromptEcho.isEcho("Anthropic", vocabulary: vocabulary)) + } + + @Test func termsOutOfOrderAreNotAnEcho() { + #expect(!PromptEcho.isEcho("Vercel, Anthropic", vocabulary: vocabulary)) + } + + @Test func sentenceContainingTermsIsNotAnEcho() { + #expect(!PromptEcho.isEcho("Ik gebruik Anthropic en Vercel", vocabulary: vocabulary)) + } + + @Test func tooShortVocabularyNeverEchoes() { + #expect(!PromptEcho.isEcho("Anthropic", vocabulary: ["Anthropic"])) + #expect(!PromptEcho.isEcho("", vocabulary: vocabulary)) + } +} +``` + +Voeg onderaan `Tests/SstCoreTests/Fakes.swift` toe: + +```swift + +final class FakeVocabularyTranscriber: Transcriber { + var result = "hallo wereld" + private(set) var vocabularies: [[String]] = [] + + func transcribe(_ samples: [Float]) async throws -> String { result } + + func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + vocabularies.append(vocabulary) + return result + } +} + +struct UppercaseCorrector: TextCorrecting { + func correct(_ text: String) -> CorrectionResult { + let upper = text.uppercased() + return CorrectionResult( + text: upper, + corrections: [AppliedCorrection(heard: text, wrote: upper, rule: "upper", start: 0, length: upper.utf16.count)] + ) + } +} +``` + +`Tests/SstCoreTests/DictationControllerHooksTests.swift`: + +```swift +import Foundation +import Testing +@testable import SstCore + +@MainActor +struct DictationControllerHooksTests { + let recorder = FakeRecorder() + let transcriber = FakeVocabularyTranscriber() + let inserter = FakeInserter() + static let loud = [Float](repeating: 0.1, count: 16_000) + static let quiet = [Float](repeating: 0.01, count: 16_000) + + func makeController( + cleaner: Cleaner = FakeCleaner(), + corrector: TextCorrecting? = nil, + vocabulary: [String] = [] + ) -> DictationController { + DictationController( + recorder: recorder, + transcriber: transcriber, + cleaner: cleaner, + inserter: inserter, + corrector: corrector, + vocabularyProvider: { vocabulary } + ) + } + + func dictate(_ controller: DictationController) async { + await controller.handle(.pressed) + await controller.handle(.released) + } + + @Test func loudRecordingPassesVocabularyToTranscriber() async { + recorder.samples = Self.loud + await dictate(makeController(vocabulary: ["Anthropic", "Vercel"])) + #expect(transcriber.vocabularies == [["Anthropic", "Vercel"]]) + } + + @Test func quietRecordingSkipsVocabularyButStillTranscribes() async { + recorder.samples = Self.quiet // RMS 0.01: above silence (0.005), below bias minimum (0.015) + await dictate(makeController(vocabulary: ["Anthropic", "Vercel"])) + #expect(transcriber.vocabularies == [[]]) + #expect(inserter.inserted == ["[hallo wereld]"]) + } + + @Test func promptEchoIsDiscarded() async { + recorder.samples = Self.loud + transcriber.result = "Anthropic, Vercel." + let controller = makeController(vocabulary: ["Anthropic", "Vercel", "Supabase"]) + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + + await dictate(controller) + + #expect(inserter.inserted.isEmpty) + #expect(results.isEmpty) + #expect(controller.state == .idle) + } + + @Test func singleVocabularyTermIsKept() async { + recorder.samples = Self.loud + transcriber.result = "Anthropic" + await dictate(makeController(vocabulary: ["Anthropic", "Vercel", "Supabase"])) + #expect(inserter.inserted == ["[Anthropic]"]) + } + + @Test func correctorRunsBeforeCleanerAndCorrectionsSurviveAPassthroughCleaner() async { + recorder.samples = Self.loud + transcriber.result = "hallo" + let controller = makeController(cleaner: PassthroughCleaner(), corrector: UppercaseCorrector()) + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + + await dictate(controller) + + #expect(inserter.inserted == ["HALLO"]) + #expect(results.count == 1) + #expect(results[0].rawText == "hallo") + #expect(results[0].finalText == "HALLO") + #expect(results[0].corrections.count == 1) + } + + @Test func correctionsAreDroppedWhenTheCleanerChangesTheText() async { + recorder.samples = Self.loud + transcriber.result = "hallo" + let controller = makeController(cleaner: FakeCleaner(), corrector: UppercaseCorrector()) + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + + await dictate(controller) + + #expect(results[0].finalText == "[HALLO]") + #expect(results[0].corrections.isEmpty) + } + + @Test func resultIsReportedEvenWhenInsertionFails() async { + recorder.samples = Self.loud + inserter.error = TestError() + let controller = makeController() + var results: [DictationResult] = [] + var errors: [String] = [] + controller.onResult = { results.append($0) } + controller.onError = { errors.append($0) } + + await dictate(controller) + + #expect(results.count == 1) + #expect(errors.count == 1) + } + + @Test func resultCarriesDurationAndDate() async { + recorder.samples = Self.loud // 16 000 samples = 1 s + let controller = makeController() + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + let before = Date() + + await dictate(controller) + + #expect(results[0].duration == 1.0) + #expect(results[0].date >= before) + #expect(results[0].corrections.isEmpty) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "PromptEchoTests|DictationControllerHooksTests"` +Expected: compilatiefouten (`cannot find 'PromptEcho'`, `extra argument 'corrector'`). + +- [ ] **Step 3: Implementatie** + +`Sources/SstCore/PromptEcho.swift`: + +```swift +import Foundation + +/// Whisper sometimes answers a vocabulary prompt with the prompt itself when there is little speech. +public enum PromptEcho { + static func normalize(_ text: String) -> String { + String(text.lowercased().map { $0.isLetter || $0.isNumber ? $0 : " " }) + .split(separator: " ") + .joined(separator: " ") + } + + /// True when `text` is nothing but the first k ≥ 2 terms of the prompt. + /// A single term on its own is valid speech ("Anthropic"). + public static func isEcho(_ text: String, vocabulary: [String]) -> Bool { + guard vocabulary.count >= 2 else { return false } + let normalized = normalize(text) + guard !normalized.isEmpty else { return false } + return (2...vocabulary.count).contains { k in + normalize(vocabulary.prefix(k).joined(separator: " ")) == normalized + } + } +} +``` + +Vervang `Sources/SstCore/DictationController.swift` volledig door: + +```swift +import Foundation + +@MainActor +public final class DictationController { + public private(set) var state: DictationState = .idle { + didSet { onStateChange?(state) } + } + public var onStateChange: ((DictationState) -> Void)? + public var onError: ((String) -> Void)? + /// Called with every finished transcription, just before it is inserted. + public var onResult: ((DictationResult) -> Void)? + + private let recorder: AudioRecording + private let transcriber: Transcriber + private let cleaner: Cleaner + private let inserter: TextInserting + private let minimumSamples: Int + private let silenceThreshold: Float + private let corrector: TextCorrecting? + private let vocabularyProvider: () -> [String] + private let biasMinimumRMS: Float + + /// - Parameter minimumSamples: 16 kHz samples; default is 0.3 s. + /// - Parameter silenceThreshold: RMS below which a recording counts as silence and is skipped. + /// - Parameter corrector: optional dictionary correction applied to the transcript. + /// - Parameter vocabularyProvider: words to bias the transcriber towards. + /// - Parameter biasMinimumRMS: below this RMS no vocabulary is passed (guards against invented text on quiet audio). + public init( + recorder: AudioRecording, + transcriber: Transcriber, + cleaner: Cleaner, + inserter: TextInserting, + minimumSamples: Int = 4_800, + silenceThreshold: Float = 0.005, + corrector: TextCorrecting? = nil, + vocabularyProvider: @escaping () -> [String] = { [] }, + biasMinimumRMS: Float = 0.015 + ) { + self.recorder = recorder + self.transcriber = transcriber + self.cleaner = cleaner + self.inserter = inserter + self.minimumSamples = minimumSamples + self.silenceThreshold = silenceThreshold + self.corrector = corrector + self.vocabularyProvider = vocabularyProvider + self.biasMinimumRMS = biasMinimumRMS + } + + public func handle(_ event: HotkeyEvent) async { + switch (event, state) { + case (.pressed, .idle): + do { + try recorder.start() + state = .recording + } catch { + onError?(error.localizedDescription) + } + case (.released, .recording): + let samples = recorder.stop() + guard samples.count >= minimumSamples else { + state = .idle + return + } + let energy = samples.reduce(0) { $0 + $1 * $1 } + let rms = (energy / Float(samples.count)).squareRoot() + guard rms >= silenceThreshold else { + state = .idle + return + } + await process(samples, rms: rms) + default: + break + } + } + + private func process(_ samples: [Float], rms: Float) async { + defer { state = .idle } + do { + state = .transcribing + let vocabulary = rms >= biasMinimumRMS ? vocabularyProvider() : [] + let raw = try await transcriber.transcribe(samples, vocabulary: vocabulary) + let text = raw.trimmingCharacters(in: .whitespacesAndNewlines) + guard !text.isEmpty, !PromptEcho.isEcho(text, vocabulary: vocabulary) else { return } + let corrected = corrector?.correct(text) ?? CorrectionResult(text: text, corrections: []) + let cleaned = try await cleaner.clean(corrected.text, context: CleaningContext()) + onResult?(DictationResult( + date: Date(), + duration: Double(samples.count) / 16_000, + rawText: text, + finalText: cleaned, + // Offsets only stay valid if the cleaner left the corrected text alone. + corrections: cleaned == corrected.text ? corrected.corrections : [] + )) + state = .inserting + try await inserter.insert(cleaned) + } catch { + onError?(error.localizedDescription) + } + } +} +``` + +- [ ] **Step 4: Draai alle tests** + +Run: `swift test` +Expected: alle tests PASS (de 13 + 2 + 6 + 8 nieuwe = 29). + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstCore Tests/SstCoreTests +git commit -m "feat: controller supports vocabulary bias, prompt-echo guard, corrector and result hook" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 3: Instelbare sneltoets — `HotkeyBinding`, `ModifierKeyTracker`, `HotkeyMonitor` + +**Files:** +- Create: `Sources/SstCore/HotkeyBinding.swift`, `Sources/SstCore/ModifierKeyTracker.swift` +- Replace: `Sources/SstCore/HotkeyMonitor.swift` +- Test: `Tests/SstCoreTests/HotkeyBindingTests.swift`, `Tests/SstCoreTests/ModifierKeyTrackerTests.swift` + +**Interfaces:** +- Consumes: `HotkeyEvent` uit Models.swift. +- Produces: + - `struct HotkeyBinding: Hashable, Sendable, Identifiable` met `id: String`, `displayName: String`, `keyCode: Int`, `flagRawValue: UInt64`, `flag: CGEventFlags`; statics `.fn`, `.rightOption`, `.rightCommand`, `.rightControl`, `.leftControl`, `allowed: [HotkeyBinding]`, `binding(id:)`, `binding(keyCode:)` + - `struct ModifierKeyTracker { init(); mutating func process(isDown: Bool) -> HotkeyEvent? }` + - `HotkeyMonitor.binding: HotkeyBinding` (standaard `.fn`; wisselen herstelt de tracker) + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstCoreTests/HotkeyBindingTests.swift`: + +```swift +import Testing +@testable import SstCore + +struct HotkeyBindingTests { + @Test func allowedBindingsMatchTheSpec() { + #expect(HotkeyBinding.allowed.map(\.id) == ["fn", "rightOption", "rightCommand", "rightControl", "leftControl"]) + #expect(HotkeyBinding.allowed.map(\.keyCode) == [63, 61, 54, 62, 59]) + } + + @Test func idsAndKeyCodesAreUnique() { + #expect(Set(HotkeyBinding.allowed.map(\.id)).count == HotkeyBinding.allowed.count) + #expect(Set(HotkeyBinding.allowed.map(\.keyCode)).count == HotkeyBinding.allowed.count) + } + + @Test func lookupByKeyCodeAndId() { + #expect(HotkeyBinding.binding(keyCode: 61) == .rightOption) + #expect(HotkeyBinding.binding(id: "leftControl") == .leftControl) + } + + @Test func unsupportedKeysAreRejected() { + #expect(HotkeyBinding.binding(keyCode: 56) == nil) // left shift + #expect(HotkeyBinding.binding(keyCode: 57) == nil) // caps lock + #expect(HotkeyBinding.binding(id: "nope") == nil) + } + + @Test func everyBindingHasAFlagAndName() { + for binding in HotkeyBinding.allowed { + #expect(binding.flagRawValue != 0) + #expect(!binding.displayName.isEmpty) + } + } +} +``` + +`Tests/SstCoreTests/ModifierKeyTrackerTests.swift`: + +```swift +import Testing +@testable import SstCore + +struct ModifierKeyTrackerTests { + @Test func emitsPressedThenReleasedOnlyOnTransitions() { + var tracker = ModifierKeyTracker() + #expect(tracker.process(isDown: false) == nil) + #expect(tracker.process(isDown: true) == .pressed) + #expect(tracker.process(isDown: true) == nil) + #expect(tracker.process(isDown: false) == .released) + #expect(tracker.process(isDown: false) == nil) + } + + @Test func freshTrackerStartsReleased() { + var tracker = ModifierKeyTracker() + #expect(tracker.process(isDown: true) == .pressed) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "HotkeyBindingTests|ModifierKeyTrackerTests"` +Expected: compilatiefout `cannot find 'HotkeyBinding' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstCore/ModifierKeyTracker.swift`: + +```swift +public struct ModifierKeyTracker { + private var isDown = false + + public init() {} + + /// Feed the current state of the bound modifier flag; returns an event only on a transition. + public mutating func process(isDown: Bool) -> HotkeyEvent? { + guard isDown != self.isDown else { return nil } + self.isDown = isDown + return isDown ? .pressed : .released + } +} +``` + +`Sources/SstCore/HotkeyBinding.swift`: + +```swift +import CoreGraphics + +/// A modifier key that can be held to dictate. Left/right keys share a flag, so the key code decides. +public struct HotkeyBinding: Hashable, Sendable, Identifiable { + public let id: String + public let displayName: String + public let keyCode: Int + public let flagRawValue: UInt64 + + public var flag: CGEventFlags { CGEventFlags(rawValue: flagRawValue) } + + public static let fn = HotkeyBinding( + id: "fn", displayName: "Fn", keyCode: 63, flagRawValue: CGEventFlags.maskSecondaryFn.rawValue) + public static let rightOption = HotkeyBinding( + id: "rightOption", displayName: "Rechter Option", keyCode: 61, flagRawValue: CGEventFlags.maskAlternate.rawValue) + public static let rightCommand = HotkeyBinding( + id: "rightCommand", displayName: "Rechter Command", keyCode: 54, flagRawValue: CGEventFlags.maskCommand.rawValue) + public static let rightControl = HotkeyBinding( + id: "rightControl", displayName: "Rechter Control", keyCode: 62, flagRawValue: CGEventFlags.maskControl.rawValue) + public static let leftControl = HotkeyBinding( + id: "leftControl", displayName: "Linker Control", keyCode: 59, flagRawValue: CGEventFlags.maskControl.rawValue) + + public static let allowed: [HotkeyBinding] = [.fn, .rightOption, .rightCommand, .rightControl, .leftControl] + + public static func binding(id: String) -> HotkeyBinding? { allowed.first { $0.id == id } } + public static func binding(keyCode: Int) -> HotkeyBinding? { allowed.first { $0.keyCode == keyCode } } +} +``` + +Vervang `Sources/SstCore/HotkeyMonitor.swift` volledig door: + +```swift +import ApplicationServices +import CoreGraphics + +public final class HotkeyMonitor { + public enum HotkeyError: Error, LocalizedError { + case tapUnavailable + + public var errorDescription: String? { + "Kan de sneltoets niet volgen: geef Input Monitoring-rechten in Systeeminstellingen en probeer opnieuw (soms is een herstart van de app nodig)." + } + } + + public var onEvent: ((HotkeyEvent) -> Void)? + + /// The key that has to be held. Changing it resets the press tracking. + public var binding: HotkeyBinding = .fn { + didSet { tracker = ModifierKeyTracker() } + } + + private var tracker = ModifierKeyTracker() + private var tap: CFMachPort? + private var source: CFRunLoopSource? + + public init() {} + + public func start() throws { + guard tap == nil else { return } + let mask = CGEventMask(1 << CGEventType.flagsChanged.rawValue) + let callback: CGEventTapCallBack = { _, type, event, refcon in + if let refcon { + let monitor = Unmanaged.fromOpaque(refcon).takeUnretainedValue() + monitor.handle(type: type, event: event) + } + return Unmanaged.passUnretained(event) + } + guard let tap = CGEvent.tapCreate( + tap: .cgSessionEventTap, + place: .headInsertEventTap, + options: .listenOnly, + eventsOfInterest: mask, + callback: callback, + userInfo: Unmanaged.passUnretained(self).toOpaque() + ) else { throw HotkeyError.tapUnavailable } + + let source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0) + CFRunLoopAddSource(CFRunLoopGetMain(), source, .commonModes) + CGEvent.tapEnable(tap: tap, enable: true) + self.tap = tap + self.source = source + } + + public func stop() { + if let source { CFRunLoopRemoveSource(CFRunLoopGetMain(), source, .commonModes) } + if let tap { CGEvent.tapEnable(tap: tap, enable: false) } + tap = nil + source = nil + } + + private func handle(type: CGEventType, event: CGEvent) { + switch type { + case .tapDisabledByTimeout, .tapDisabledByUserInput: + if let tap { CGEvent.tapEnable(tap: tap, enable: true) } + case .flagsChanged: + // Flags are shared between left and right keys and with arrows/F-keys: only react to the bound key itself. + guard event.getIntegerValueField(.keyboardEventKeycode) == Int64(binding.keyCode) else { return } + let isDown = event.flags.contains(binding.flag) + if let hotkeyEvent = tracker.process(isDown: isDown) { + onEvent?(hotkeyEvent) + } + default: + break + } + } +} +``` + +> `FnKeyTracker` en zijn test blijven ongewijzigd bestaan; `HotkeyMonitor` gebruikt hem niet meer. + +- [ ] **Step 4: Draai alle tests en bouw** + +Run: `swift test && swift build` +Expected: alle tests PASS; build slaagt (de app en `SstProbe` gebruiken `HotkeyMonitor()` zonder argumenten en blijven dus compileren). + +- [ ] **Step 5: Handmatige controle (met de gebruiker, na Task 5)** + +Wordt gebundeld in Task 5, stap 6. + +- [ ] **Step 6: Commit** + +```bash +git add Sources/SstCore Tests/SstCoreTests +git commit -m "feat: configurable hotkey binding and ModifierKeyTracker" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 4: Niveau-uitvoer — `AudioLevel` en `AudioRecorder.onLevel` + +**Files:** +- Create: `Sources/SstCore/AudioLevel.swift` +- Modify: `Sources/SstCore/AudioRecorder.swift` +- Modify: `Sources/SstProbe/RecordProbe.swift` +- Test: `Tests/SstCoreTests/AudioLevelTests.swift` + +**Interfaces:** +- Produces: + - `AudioLevel.rmsDbfs(_ samples: [Float]) -> Float` en `rmsDbfs(_ samples: UnsafeBufferPointer) -> Float` (−120 voor stilte of leeg) + - `AudioLevel.vu(fromDbfs:referenceDbfs:) -> Float` (standaard referentie −20) + - `AudioRecorder.onLevel: ((Float) -> Void)?` — RMS in dBFS per aangeleverde buffer, aangeroepen op de audio-thread; vóór `start()` instellen. + +- [ ] **Step 1: Schrijf de falende test** + +`Tests/SstCoreTests/AudioLevelTests.swift`: + +```swift +import Testing +@testable import SstCore + +struct AudioLevelTests { + @Test func constantTenthIsMinusTwentyDbfs() { + let level = AudioLevel.rmsDbfs([Float](repeating: 0.1, count: 1_000)) + #expect(abs(level - (-20)) < 0.01) + } + + @Test func fullScaleIsZeroDbfs() { + let level = AudioLevel.rmsDbfs([Float](repeating: 1.0, count: 100)) + #expect(abs(level) < 0.01) + } + + @Test func negativeSamplesCountLikePositiveOnes() { + let level = AudioLevel.rmsDbfs([Float](repeating: -0.1, count: 1_000)) + #expect(abs(level - (-20)) < 0.01) + } + + @Test func emptyAndSilentBuffersHitTheFloor() { + #expect(AudioLevel.rmsDbfs([]) == AudioLevel.silenceFloorDbfs) + #expect(AudioLevel.rmsDbfs([Float](repeating: 0, count: 64)) == AudioLevel.silenceFloorDbfs) + } + + @Test func vuIsRelativeToTheReference() { + #expect(AudioLevel.vu(fromDbfs: -20) == 0) + #expect(AudioLevel.vu(fromDbfs: -40) == -20) + #expect(AudioLevel.vu(fromDbfs: -17) == 3) + #expect(AudioLevel.vu(fromDbfs: -30, referenceDbfs: -18) == -12) + } +} +``` + +- [ ] **Step 2: Draai de test, moet falen** + +Run: `swift test --filter AudioLevelTests` +Expected: compilatiefout `cannot find 'AudioLevel' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstCore/AudioLevel.swift`: + +```swift +import Foundation + +public enum AudioLevel { + /// Reported for empty or silent buffers (log of zero would be -infinity). + public static let silenceFloorDbfs: Float = -120 + + public static func rmsDbfs(_ samples: [Float]) -> Float { + samples.withUnsafeBufferPointer { rmsDbfs($0) } + } + + public static func rmsDbfs(_ samples: UnsafeBufferPointer) -> Float { + guard !samples.isEmpty else { return silenceFloorDbfs } + var sum: Float = 0 + for sample in samples { sum += sample * sample } + let rms = (sum / Float(samples.count)).squareRoot() + guard rms > 0 else { return silenceFloorDbfs } + return max(silenceFloorDbfs, 20 * log10(rms)) + } + + /// VU reading for a dBFS level, where `referenceDbfs` is 0 VU. + public static func vu(fromDbfs dbfs: Float, referenceDbfs: Float = -20) -> Float { + dbfs - referenceDbfs + } +} +``` + +In `Sources/SstCore/AudioRecorder.swift`: + +1. Voeg onder `private var buffer: [Float] = []` toe: + +```swift + + /// RMS level (dBFS) of every delivered input buffer. Called on the audio thread; set before `start()`. + public var onLevel: ((Float) -> Void)? +``` + +2. Vervang in de tap-closure de regel `guard let self else { return }` door: + +```swift + guard let self else { return } + if let onLevel = self.onLevel, let channel = pcm.floatChannelData?[0] { + onLevel(AudioLevel.rmsDbfs(UnsafeBufferPointer(start: channel, count: Int(pcm.frameLength)))) + } +``` + +3. Vervang `bufferSize: 4096` door `bufferSize: 1024` (macOS mag een grotere buffer leveren; de meter werkt met elke grootte). + +`Sources/SstProbe/RecordProbe.swift` — vervang volledig door: + +```swift +import AVFoundation +import SstCore + +/// Records `seconds` of microphone audio; returns nil if access is denied or recording fails. +func recordSamples(seconds: Double, onLevel: ((Float) -> Void)? = nil) async -> [Float]? { + guard await AVCaptureDevice.requestAccess(for: .audio) else { + print("Microfoon-toegang geweigerd (Systeeminstellingen → Privacy → Microfoon).") + return nil + } + let recorder = AudioRecorder() + recorder.onLevel = onLevel + do { try recorder.start() } catch { + print("start mislukt:", error.localizedDescription) + return nil + } + print("Neem \(Int(seconds)) s op — praat nu") + try? await Task.sleep(for: .seconds(seconds)) + return recorder.stop() +} + +func runRecordProbe() async { + final class Levels: @unchecked Sendable { + let lock = NSLock() + var values: [Float] = [] + func add(_ v: Float) { lock.withLock { values.append(v) } } + } + let levels = Levels() + guard let samples = await recordSamples(seconds: 3, onLevel: { levels.add($0) }) else { return } + let peak = samples.map { abs($0) }.max() ?? 0 + print("samples: \(samples.count) (verwacht ≈ 48000), piek: \(peak)") + let values = levels.lock.withLock { levels.values } + if let lo = values.min(), let hi = values.max() { + print(String(format: "niveau-meldingen: %d (≈ %.0f per s), bereik %.1f … %.1f dBFS", values.count, Double(values.count) / 3, lo, hi)) + } else { + print("geen niveau-meldingen ontvangen") + } +} +``` + +- [ ] **Step 4: Draai alle tests en bouw** + +Run: `swift test && swift build` +Expected: alle tests PASS; build slaagt. + +- [ ] **Step 5: Handmatige controle (met de gebruiker, in Task 5, stap 6)** + +Hier alleen: de code compileert. + +- [ ] **Step 6: Commit** + +```bash +git add Sources Tests +git commit -m "feat: AudioLevel helper and AudioRecorder.onLevel" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 5: WhisperKit-voorrang — woordenlijst als `promptTokens` + +**Files:** +- Modify: `Package.swift` (testtarget `SstWhisperTests`) +- Modify: `Sources/SstWhisper/WhisperKitTranscriber.swift` +- Modify: `Sources/SstProbe/TranscribeProbe.swift` +- Test: `Tests/SstWhisperTests/PromptTokensTests.swift` + +**Interfaces:** +- Consumes: Task 1 (`Transcriber.transcribe(_:vocabulary:)`). +- Produces: `WhisperKitTranscriber.transcribe(_:vocabulary:)`; intern `static func promptTokens(vocabulary:maxTokens:specialTokenBegin:encode:) -> [Int]?`. + +- [ ] **Step 1: Testtarget toevoegen** + +In `Package.swift` voeg onder de bestaande `.testTarget(name: "SstCoreTests", …)` toe: + +```swift + .testTarget(name: "SstWhisperTests", dependencies: ["SstWhisper"]), +``` + +- [ ] **Step 2: Schrijf de falende test** + +`Tests/SstWhisperTests/PromptTokensTests.swift`: + +```swift +import Testing +@testable import SstWhisper + +struct PromptTokensTests { + /// Fake tokenizer: one token (id 7) per word, plus one special token (id 2000) that must be filtered out. + let encode: (String) -> [Int] = { text in + text.split(whereSeparator: { !$0.isLetter }).map { _ in 7 } + [2000] + } + + @Test func allTermsFitAndSpecialTokensAreFiltered() { + let tokens = WhisperKitTranscriber.promptTokens( + vocabulary: ["Anthropic", "Vercel", "Supabase"], maxTokens: 64, specialTokenBegin: 1000, encode: encode) + #expect(tokens == [7, 7, 7]) + } + + @Test func termsThatDoNotFitAreDroppedFromTheEnd() { + let vocab = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + let tokens = WhisperKitTranscriber.promptTokens( + vocabulary: vocab, maxTokens: 4, specialTokenBegin: 1000, encode: encode) + #expect(tokens?.count == 4) + } + + @Test func emptyVocabularyGivesNoPrompt() { + #expect(WhisperKitTranscriber.promptTokens( + vocabulary: [], maxTokens: 64, specialTokenBegin: 1000, encode: encode) == nil) + } + + @Test func firstTermAloneTooLongGivesNoPrompt() { + #expect(WhisperKitTranscriber.promptTokens( + vocabulary: ["one two three"], maxTokens: 2, specialTokenBegin: 1000, encode: encode) == nil) + } +} +``` + +- [ ] **Step 3: Draai de test, moet falen** + +Run: `swift test --filter PromptTokensTests` +Expected: compilatiefout `type 'WhisperKitTranscriber' has no member 'promptTokens'`. + +- [ ] **Step 4: Implementatie** + +In `Sources/SstWhisper/WhisperKitTranscriber.swift` vervang de hele methode `public func transcribe(_ samples: [Float]) async throws -> String { … }` (onderaan het bestand) door: + +```swift + public func transcribe(_ samples: [Float]) async throws -> String { + try await transcribe(samples, vocabulary: []) + } + + /// Terms are added one by one while the encoded prompt stays within `maxTokens`. + /// Returns nil when nothing fits or the list is empty. + static func promptTokens( + vocabulary: [String], + maxTokens: Int = 64, + specialTokenBegin: Int, + encode: (String) -> [Int] + ) -> [Int]? { + var kept: [String] = [] + var tokens: [Int] = [] + for term in vocabulary { + let candidate = kept + [term] + let encoded = encode(" " + candidate.joined(separator: ", ")).filter { $0 < specialTokenBegin } + if encoded.count > maxTokens { break } + kept = candidate + tokens = encoded + } + return tokens.isEmpty ? nil : tokens + } + + public func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + guard let pipeline else { throw TranscriberError.notLoaded } + // language nil + detectLanguage: Whisper picks NL or EN per utterance. + var options = DecodingOptions( + task: .transcribe, + language: nil, + detectLanguage: true, + skipSpecialTokens: true, + withoutTimestamps: true + ) + if !vocabulary.isEmpty, let tokenizer = pipeline.tokenizer { + options.promptTokens = Self.promptTokens( + vocabulary: vocabulary, + specialTokenBegin: tokenizer.specialTokens.specialTokenBegin, + encode: { tokenizer.encode(text: $0) } + ) + } + let results = try await pipeline.transcribe(audioArray: samples, decodeOptions: options) + return results + .map(\.text) + .joined(separator: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + } +``` + +> Wijkt de WhisperKit-API licht af (bijvoorbeeld de plaats van `specialTokens`), pas dan alleen dit bestand aan en behoud de signatuur van `promptTokens` zodat de test geldig blijft. + +`Sources/SstProbe/TranscribeProbe.swift` — vervang regel `let text = try await transcriber.transcribe(samples)` door: + +```swift + let vocabulary = (ProcessInfo.processInfo.environment["SST_VOCAB"] ?? "") + .split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) }.filter { !$0.isEmpty } + if !vocabulary.isEmpty { print("voorrang: \(vocabulary.joined(separator: ", "))") } + let text = try await transcriber.transcribe(samples, vocabulary: vocabulary) +``` + +- [ ] **Step 5: Draai alle tests en bouw** + +Run: `swift test && swift build` +Expected: alle tests PASS (incl. 4 nieuwe in `SstWhisperTests`); build slaagt. + +- [ ] **Step 6: Handmatige controle samen met de gebruiker (hardware)** + +Doe in deze volgorde en noteer de uitkomsten in het commit-bericht van stap 7: + +1. `swift run SstProbe record` → er verschijnt een regel `niveau-meldingen: N (≈ X per s), bereik … dBFS`. Verwacht: meer dan 5 meldingen per seconde en een bereik dat bij praten duidelijk hoger ligt dan bij stilte. Noteer X (macOS levert soms ~10 per s in plaats van ~47). +2. `swift run SstProbe transcribe` — zeg een zin, noteer de tijd **zonder** voorrang. +3. `SST_VOCAB="Anthropic,Vercel,Supabase,Claude Code" swift run SstProbe transcribe` — zeg dezelfde zin met een of twee van die woorden erin ("Ik werk met Supabase en Claude Code"); noteer de tijd en of de spelling beter is. Verwacht: tijd onder 1,5 s voor 8 s spraak. Is het trager, noteer hoeveel; dat is geen blokkade, wel informatie voor de spec. +4. Zeg niets (8 s stilte) met `SST_VOCAB` gezet: het resultaat mag geen woorden uit de lijst bevatten. +5. Hotkey-controle: `swift run SstProbe hotkey` toont nog steeds `pressed`/`released` voor Fn. + +- [ ] **Step 7: Commit** + +```bash +git add Package.swift Sources Tests +git commit -m "feat: WhisperKit transcriber biases decoding with vocabulary prompt tokens" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +## Self-review + +- **Spec-dekking:** raakpunt 1 (woordenlijst → `promptTokens`, Task 1+5), 2 (niveau, Task 4), 3 (corrector + `onResult`, Task 1+2), 4 (instelbare toets, Task 3). Voorrang-regels: RMS-grens 0,015 (Task 2 test `quietRecordingSkipsVocabularyButStillTranscribes`), echo-filter (Task 2), 64 tokens (Task 5). Niet in deze fase, wel in fase 2: het opbouwen van de woordenlijst (`VocabularyBuilder`) en de correctie zelf. +- **Placeholders:** geen; de waarschuwing in Task 5 stap 4 is een gerichte terugval, geen open eind. +- **Typeconsistentie:** `AppliedCorrection(heard:wrote:rule:start:length:)`, `CorrectionResult(text:corrections:)` en `DictationResult(date:duration:rawText:finalText:corrections:)` worden in Task 2 precies zo gebruikt als in Task 1 gedefinieerd. `HotkeyMonitor.binding` gebruikt `HotkeyBinding.keyCode: Int` en `flag`. +- **Risico's:** de bufferlevering van `installTap` (Task 4, stap 6.1) en de snelheid met prompt (stap 6.3) zijn de enige onbekenden; beide worden gemeten. diff --git a/docs/superpowers/plans/2026-10-01-app-2-data.md b/docs/superpowers/plans/2026-10-01-app-2-data.md new file mode 100644 index 0000000..6db5f90 --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-app-2-data.md @@ -0,0 +1,1413 @@ +# App fase 2 — SstData (woordenboek, correcties, geschiedenis) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** De gegevenslaag van de app: het woordenboek (bestand, correctieronde, waarschuwingen, voorrangslijst) en de transcriptiegeschiedenis (opslag, zoeken, groeperen), allemaal los van de interface en volledig getest. + +**Architecture:** Eén nieuw target `SstData` dat alleen van `SstCore` afhangt (voor `AppliedCorrection`, `CorrectionResult`, `TextCorrecting`, `DictationResult`). Pure logica (`DictionaryFile`, `CorrectionEngine`, `EntryWarnings`, `VocabularyBuilder`, `HistorySearch`, `HistoryGrouping`) is hardware- en UI-vrij; bestandstoegang zit in `DictionaryStore` en `HistoryStore`. Woordkennis zit achter het protocol `WordKnowledge` zodat tests deterministisch zijn. + +**Tech Stack:** Swift 6.4 (language mode 5), Swift Testing, Foundation, NaturalLanguage, AppKit (spellingcontrole). + +**Spec:** `docs/superpowers/specs/2026-10-01-macos-app-design.md` (secties "Woordenboek — het ontwerp in detail", "Geschiedenis") + +**Voorwaarde:** fase 1 (`2026-10-01-app-1-engine-hooks.md`) is afgerond: `AppliedCorrection`, `CorrectionResult`, `TextCorrecting` en `DictationResult` bestaan in `SstCore`. + +## Global Constraints + +- Dictionary-bestand: UTF-8, één invoer per regel; `#` = opmerking; `gehoord => schrijf` = correctiepaar; een regel zonder `=>` is een term. Opmerkingen, lege regels en volgorde blijven bij opslaan behouden; onleesbare regels blijven staan (`invalid`). +- Correctie: hele woorden, hoofdletter-onafhankelijk, één doorgang van links naar rechts, langste overeenkomst eerst (gelijke lengte: eerste in het bestand), vervangen tekst wordt niet opnieuw gescand, vervanging is letterlijk. Delen van een bron worden verbonden door nul of meer spaties/koppeltekens (`[\s\-‐‑–]*`). Woordgrenzen: niet direct naast een letter of cijfer (`\p{L}`, `\p{N}`). +- Een term van één woord dat een gewoon woord is (spellingcontrole of Engelse woordenlijst) krijgt géén automatische hoofdletterregel (wel voorrang bij het model). +- Voorrangslijst: eerst termen (bestandsvolgorde), dan unieke rechterkanten van paren; maximaal 24 woorden. +- Geschiedenis: JSON `{ "version": 1, "entries": [...] }`, ISO-8601 datums, nieuwste eerst, atomisch schrijven met 300 ms debounce; beschadigd bestand wordt hernoemd naar `history.corrupt-.json`. +- Zoeken: hoofdletter- en diakriet-ongevoelig; alle zoekwoorden moeten voorkomen in de uiteindelijke of oorspronkelijke tekst. +- Elke commit eindigt met de trailer `Co-Authored-By: Claude Sonnet 5.5 ` (tweede `-m`). + +## Bestandsstructuur + +``` +Package.swift (wijzig) target SstData + SstDataTests +Sources/SstData/WordKnowledge.swift (nieuw) WordKnowledge, EntryKey, CommonWords, SystemWordKnowledge +Sources/SstData/DictionaryFile.swift (nieuw) DictionaryEntry, DictionaryLine, DictionaryFile +Sources/SstData/CorrectionEngine.swift (nieuw) +Sources/SstData/EntryWarnings.swift (nieuw) +Sources/SstData/VocabularyBuilder.swift (nieuw) +Sources/SstData/History.swift (nieuw) HistoryEntry, HistoryStore, HistorySearch, HistoryGrouping +Sources/SstData/DictionaryStore.swift (nieuw) DictionaryStore, DictionaryCorrector +Tests/SstDataTests/Fakes.swift, DictionaryFileTests.swift, SystemWordKnowledgeTests.swift, + CorrectionEngineTests.swift, EntryWarningsTests.swift, VocabularyBuilderTests.swift, + HistoryTests.swift, DictionaryStoreTests.swift +``` + +--- + +### Task 1: Target, woordkennis en `DictionaryFile` + +**Files:** +- Modify: `Package.swift` +- Create: `Sources/SstData/WordKnowledge.swift`, `Sources/SstData/DictionaryFile.swift` +- Test: `Tests/SstDataTests/Fakes.swift`, `Tests/SstDataTests/DictionaryFileTests.swift`, `Tests/SstDataTests/SystemWordKnowledgeTests.swift` + +**Interfaces:** +- Produces (gebruikt door Task 2–5 en de latere fasen): + - `protocol WordKnowledge { func isWord(_:) -> Bool; func isFunctionWord(_:) -> Bool }` + - `final class SystemWordKnowledge: WordKnowledge` (`init()`) + - `enum EntryKey { static func parts(_:) -> [String]; static func key(_:) -> String }` (intern) + - `struct DictionaryEntry: Identifiable, Equatable` met `id`, `kind: Kind` (`.term(String)` | `.correction(heard:wrote:)`), `source`, `wrote` + - `enum DictionaryLine { entry, comment, blank, invalid }` + - `struct DictionaryFile` met `init(parsing:)`, `serialized()`, `entries`, `append(_:) -> UUID`, `update(id:kind:)`, `remove(id:)` + +- [ ] **Step 1: Targets toevoegen** + +In `Package.swift`, voeg bij `targets` toe (naast de bestaande regels): + +```swift + .target(name: "SstData", dependencies: ["SstCore"]), +``` + +en bij de testtargets: + +```swift + .testTarget(name: "SstDataTests", dependencies: ["SstData", "SstCore"]), +``` + +- [ ] **Step 2: Schrijf de falende tests** + +`Tests/SstDataTests/Fakes.swift`: + +```swift +import Foundation +@testable import SstData + +/// Deterministic stand-in for the macOS spell checker. +struct FakeKnowledge: WordKnowledge { + var words: Set = ["cloud", "code", "some", "thing", "something", "reef", "slack", "in", "de", "the", "of"] + var functionWords: Set = ["in", "de", "the", "of"] + func isWord(_ word: String) -> Bool { words.contains(word.lowercased()) } + func isFunctionWord(_ word: String) -> Bool { functionWords.contains(word.lowercased()) } +} + +func term(_ text: String) -> DictionaryEntry { DictionaryEntry(kind: .term(text)) } +func pair(_ heard: String, _ wrote: String) -> DictionaryEntry { DictionaryEntry(kind: .correction(heard: heard, wrote: wrote)) } +``` + +`Tests/SstDataTests/DictionaryFileTests.swift`: + +```swift +import Testing +@testable import SstData + +struct DictionaryFileTests { + let sample = "# Opmerking\nAnthropic\n\ncloud code => Claude Code\n" + + @Test func parsesTermsPairsCommentsAndBlanks() { + let file = DictionaryFile(parsing: sample) + #expect(file.lines.count == 4) + #expect(file.entries == [term("Anthropic"), pair("cloud code", "Claude Code")]) + #expect(file.lines[0] == .comment("# Opmerking")) + #expect(file.lines[2] == .blank) + } + + @Test func roundTripKeepsCommentsBlankLinesAndOrder() { + #expect(DictionaryFile(parsing: sample).serialized() == sample) + } + + @Test func normalisesSpacingAroundTheArrow() { + #expect(DictionaryFile(parsing: "a=>b\n").serialized() == "a => b\n") + #expect(DictionaryFile(parsing: " cloud code => Claude Code \n").entries == [pair("cloud code", "Claude Code")]) + } + + @Test func unreadableLinesAreKeptAsInvalid() { + let file = DictionaryFile(parsing: "oops =>\n=> nothing\nok\n") + #expect(file.lines[0] == .invalid("oops =>")) + #expect(file.lines[1] == .invalid("=> nothing")) + #expect(file.serialized() == "oops =>\n=> nothing\nok\n") + } + + @Test func handlesWindowsLineEndingsAndMissingFinalNewline() { + let file = DictionaryFile(parsing: "a\r\nb => c") + #expect(file.entries == [term("a"), pair("b", "c")]) + #expect(file.serialized() == "a\nb => c\n") + } + + @Test func emptyTextIsAValidEmptyFile() { + #expect(DictionaryFile(parsing: "").entries.isEmpty) + #expect(DictionaryFile(parsing: "").serialized() == "") + } + + @Test func appendUpdateAndRemove() { + var file = DictionaryFile(parsing: "# c\n") + let id = file.append(.term("Vercel")) + #expect(file.entries == [term("Vercel")]) + file.update(id: id, kind: .correction(heard: "versel", wrote: "Vercel")) + #expect(file.entries == [pair("versel", "Vercel")]) + file.remove(id: id) + #expect(file.entries.isEmpty) + #expect(file.serialized() == "# c\n") + } +} +``` + +`Tests/SstDataTests/SystemWordKnowledgeTests.swift`: + +```swift +import Testing +@testable import SstData + +struct SystemWordKnowledgeTests { + let knowledge = SystemWordKnowledge() + + @Test func recognisesEnglishAndDutchWords() { + #expect(knowledge.isWord("cloud")) + #expect(knowledge.isWord("Cloud")) + #expect(knowledge.isWord("vandaag")) + } + + @Test func doesNotTreatProductNamesAsWords() { + #expect(!knowledge.isWord("vercel")) + #expect(!knowledge.isWord("supabase")) + #expect(!knowledge.isWord("anthropic")) + } + + @Test func functionWordsComeFromTheBuiltInList() { + #expect(knowledge.isFunctionWord("de")) + #expect(knowledge.isFunctionWord("The")) + #expect(!knowledge.isFunctionWord("vercel")) + } +} +``` + +- [ ] **Step 3: Draai de tests, moeten falen** + +Run: `swift test --filter SstDataTests` +Expected: compilatiefouten (`cannot find 'DictionaryFile'`, `cannot find type 'WordKnowledge'`). + +- [ ] **Step 4: Implementatie** + +`Sources/SstData/WordKnowledge.swift`: + +```swift +import AppKit +import Foundation +import NaturalLanguage + +public protocol WordKnowledge { + /// A common word in Dutch or English (case-insensitive). Rare words, names and product names are not. + func isWord(_ word: String) -> Bool + /// A very frequent function word ("in", "de", "the", "of"). + func isFunctionWord(_ word: String) -> Bool +} + +/// Splitting and normalising the left-hand side of an entry, shared by the engine and the warnings. +enum EntryKey { + static func parts(_ source: String) -> [String] { + source.split(whereSeparator: { $0.isWhitespace || "-‐‑–".contains($0) }).map(String.init) + } + + /// Spacing, hyphens and case do not matter: "Cloud-Code", "cloud code" and "cloudcode" are the same source. + static func key(_ source: String) -> String { + parts(source).joined().lowercased() + } +} + +/// The most frequent Dutch and English function words. +enum CommonWords { + static let english: Set = [ + "a", "about", "after", "all", "also", "an", "and", "any", "are", "as", "at", "be", "because", "been", "but", + "by", "can", "could", "did", "do", "does", "for", "from", "had", "has", "have", "he", "her", "here", "him", + "his", "how", "i", "if", "in", "into", "is", "it", "its", "just", "me", "more", "most", "my", "no", "not", + "now", "of", "on", "one", "only", "or", "other", "our", "out", "over", "she", "so", "some", "such", "than", + "that", "the", "their", "them", "then", "there", "these", "they", "this", "those", "to", "up", "us", "very", + "was", "we", "were", "what", "when", "which", "who", "will", "with", "would", "you", "your", + ] + static let dutch: Set = [ + "aan", "al", "als", "ben", "bij", "dan", "dat", "de", "den", "der", "deze", "die", "dit", "doen", "door", + "dus", "een", "en", "er", "ga", "had", "heb", "heeft", "hem", "het", "hier", "hij", "hoe", "ik", "in", "is", + "ja", "je", "jij", "kan", "kon", "maar", "me", "mij", "mijn", "moet", "na", "naar", "niet", "nog", "nu", + "of", "om", "ons", "onze", "ook", "op", "over", "te", "ten", "ter", "toch", "toen", "tot", "u", "uit", "van", + "veel", "voor", "was", "wat", "we", "wel", "werd", "wij", "wil", "worden", "wordt", "zal", "ze", "zei", + "zich", "zij", "zijn", "zo", "zou", + ] + static let all: Set = english.union(dutch) +} + +/// English: the on-device word list of `NaturalLanguage` (common words only, so "cloud" and "reef" are in, "anthropic" and "vercel" are not). +/// Dutch has no such list, so the spell checker decides. Function words come from the built-in list. +public final class SystemWordKnowledge: WordKnowledge, @unchecked Sendable { + private let englishWords = NLEmbedding.wordEmbedding(for: .english) + + public init() {} + + public func isWord(_ word: String) -> Bool { + let lowered = word.lowercased() + guard !lowered.isEmpty else { return false } + if let englishWords { + if englishWords.contains(lowered) { return true } + } else if spellChecks(lowered, language: "en") { + return true + } + return spellChecks(lowered, language: "nl") + } + + public func isFunctionWord(_ word: String) -> Bool { + CommonWords.all.contains(word.lowercased()) + } + + private func spellChecks(_ word: String, language: String) -> Bool { + NSSpellChecker.shared.checkSpelling( + of: word, startingAt: 0, language: language, wrap: false, + inSpellDocumentWithTag: 0, wordCount: nil).location == NSNotFound + } +} +``` + +`Sources/SstData/DictionaryFile.swift`: + +```swift +import Foundation + +public struct DictionaryEntry: Identifiable, Equatable, Sendable { + public enum Kind: Equatable, Sendable { + case term(String) + case correction(heard: String, wrote: String) + } + + public let id: UUID + public var kind: Kind + + public init(id: UUID = UUID(), kind: Kind) { + self.id = id + self.kind = kind + } + + /// Equality ignores `id`: two entries with the same content are the same entry. + public static func == (lhs: DictionaryEntry, rhs: DictionaryEntry) -> Bool { lhs.kind == rhs.kind } + + /// The text the correction pass listens for (a term listens for itself). + public var source: String { + switch kind { + case .term(let term): term + case .correction(let heard, _): heard + } + } + + /// The text that ends up in the transcript. + public var wrote: String { + switch kind { + case .term(let term): term + case .correction(_, let wrote): wrote + } + } +} + +public enum DictionaryLine: Equatable, Sendable { + case entry(DictionaryEntry) + case comment(String) + case blank + case invalid(String) +} + +/// The dictionary as a list of lines, so comments, blank lines and order survive an edit. +public struct DictionaryFile: Equatable, Sendable { + public var lines: [DictionaryLine] + + public init(lines: [DictionaryLine] = []) { + self.lines = lines + } + + public init(parsing text: String) { + var raw = text.replacingOccurrences(of: "\r\n", with: "\n") + .split(separator: "\n", omittingEmptySubsequences: false) + .map(String.init) + if raw.last == "" { raw.removeLast() } + lines = raw.map(Self.parseLine) + } + + static func parseLine(_ line: String) -> DictionaryLine { + let trimmed = line.trimmingCharacters(in: .whitespaces) + if trimmed.isEmpty { return .blank } + if trimmed.hasPrefix("#") { return .comment(line) } + if let arrow = trimmed.range(of: "=>") { + let heard = trimmed[.. String { + let out = lines.map { line -> String in + switch line { + case .entry(let entry): + switch entry.kind { + case .term(let term): return term + case .correction(let heard, let wrote): return "\(heard) => \(wrote)" + } + case .comment(let text), .invalid(let text): return text + case .blank: return "" + } + } + return out.isEmpty ? "" : out.joined(separator: "\n") + "\n" + } + + public var entries: [DictionaryEntry] { + lines.compactMap { line in + if case .entry(let entry) = line { return entry } + return nil + } + } + + @discardableResult + public mutating func append(_ kind: DictionaryEntry.Kind) -> UUID { + let entry = DictionaryEntry(kind: kind) + lines.append(.entry(entry)) + return entry.id + } + + public mutating func update(id: UUID, kind: DictionaryEntry.Kind) { + guard let index = lines.firstIndex(where: { if case .entry(let e) = $0 { return e.id == id } else { return false } }) else { return } + lines[index] = .entry(DictionaryEntry(id: id, kind: kind)) + } + + public mutating func remove(id: UUID) { + lines.removeAll { if case .entry(let e) = $0 { return e.id == id } else { return false } } + } +} +``` + +- [ ] **Step 5: Draai de tests** + +Run: `swift test --filter SstDataTests` +Expected: alle tests PASS (7 `DictionaryFile`, 3 `SystemWordKnowledge`). + +> De Engelse woordenlijst komt uit `NLEmbedding.wordEmbedding(for: .english)`. Is die op de machine niet beschikbaar, dan valt `isWord` terug op de Engelse spellingcontrole; de tests verwachten dan dat "anthropic" óók een woord is en kunnen falen. Meld dat als BLOCKED met de uitvoer van `swift test`, wijzig de test niet stilletjes. + +- [ ] **Step 6: Commit** + +```bash +git add Package.swift Sources/SstData Tests/SstDataTests +git commit -m "feat: add SstData with DictionaryFile and word knowledge" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 2: `CorrectionEngine` + +**Files:** +- Create: `Sources/SstData/CorrectionEngine.swift` +- Test: `Tests/SstDataTests/CorrectionEngineTests.swift` + +**Interfaces:** +- Consumes: Task 1 (`DictionaryEntry`, `WordKnowledge`, `EntryKey`) en `SstCore.TextCorrecting`/`CorrectionResult`/`AppliedCorrection`. +- Produces: `struct CorrectionEngine: TextCorrecting` met `init(entries: [DictionaryEntry], knowledge: WordKnowledge)` en `correct(_:) -> CorrectionResult`. + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstDataTests/CorrectionEngineTests.swift`: + +```swift +import Foundation +import Testing +import SstCore +@testable import SstData + +struct CorrectionEngineTests { + let knowledge = FakeKnowledge() + + func engine(_ entries: [DictionaryEntry]) -> CorrectionEngine { + CorrectionEngine(entries: entries, knowledge: knowledge) + } + + @Test func replacesAPairAndReportsIt() { + let result = engine([pair("cloud code", "Claude Code")]).correct("ik gebruik cloud code vandaag") + #expect(result.text == "ik gebruik Claude Code vandaag") + #expect(result.corrections.count == 1) + let c = result.corrections[0] + #expect(c.heard == "cloud code" && c.wrote == "Claude Code" && c.rule == "cloud code") + #expect((result.text as NSString).substring(with: NSRange(location: c.start, length: c.length)) == "Claude Code") + } + + @Test func findsGluedHyphenatedAndSpacedVariants() { + let e = engine([pair("cloud code", "Claude Code")]) + for variant in ["CloudCode", "Cloud-Code", "CLOUD CODE", "cloud\u{2011}code", "cloud code"] { + #expect(e.correct(variant).text == "Claude Code", "variant \(variant)") + } + } + + @Test func neverTouchesRealWordsThatMerelyStartTheSame() { + let e = engine([pair("cloud code", "Claude Code")]) + #expect(e.correct("Cloudflare is handig").text == "Cloudflare is handig") + #expect(e.correct("cloud codex").text == "cloud codex") + #expect(e.correct("de cloud en de code").text == "de cloud en de code") + #expect(e.correct("the cloud").corrections.isEmpty) + } + + @Test func respectsPunctuationBoundaries() { + let e = engine([pair("cloud code", "Claude Code")]) + #expect(e.correct("(cloud code)").text == "(Claude Code)") + #expect(e.correct("cloud code.").text == "Claude Code.") + #expect(e.correct("Cloud Code, Cloud Code").text == "Claude Code, Claude Code") + } + + @Test func longestMatchWins() { + let e = engine([pair("cloud code", "Claude Code"), pair("cloud code review", "Claude Code Review")]) + #expect(e.correct("cloud code review nu").text == "Claude Code Review nu") + #expect(e.correct("cloud-code-review").text == "Claude Code Review") + } + + @Test func replacedTextIsNeverRescanned() { + let e = engine([pair("a", "b"), pair("b", "c")]) + #expect(e.correct("a b").text == "b c") + } + + @Test func termsFixCasingAndSpacing() { + let e = engine([term("Anthropic"), term("Claude Code")]) + #expect(e.correct("anthropic ANTHROPIC").text == "Anthropic Anthropic") + #expect(e.correct("claudecode en claude-code").text == "Claude Code en Claude Code") + } + + @Test func termAlreadyRightIsNotReportedAsACorrection() { + let result = engine([term("Anthropic")]).correct("Anthropic bouwt Claude") + #expect(result.text == "Anthropic bouwt Claude") + #expect(result.corrections.isEmpty) + } + + @Test func singleWordTermThatIsARealWordGetsNoCasingRule() { + let e = engine([term("Reef"), term("Slack")]) + #expect(e.correct("het reef en slack").text == "het reef en slack") + } + + @Test func firstEntryWinsOnDuplicateSources() { + let e = engine([pair("cloud code", "Claude Code"), pair("Cloud-Code", "Other")]) + #expect(e.correct("cloudcode").text == "Claude Code") + } + + @Test func replacementIsLiteral() { + let e = engine([pair("price", "US$1 \\0 $1")]) + #expect(e.correct("the price").text == "the US$1 \\0 $1") + } + + @Test func handlesDiacritics() { + let e = engine([term("Café")]) + #expect(e.correct("een CAFÉ om de hoek").text == "een Café om de hoek") + } + + @Test func offsetsAreRelativeToTheCorrectedText() { + let result = engine([pair("a", "xxx"), pair("b", "y")]).correct("a b a") + #expect(result.text == "xxx y xxx") + #expect(result.corrections.map(\.start) == [0, 4, 6]) + #expect(result.corrections.map(\.length) == [3, 1, 3]) + } + + @Test func emptyDictionaryAndEmptyTextAreNoOps() { + #expect(engine([]).correct("hallo").text == "hallo") + #expect(engine([pair("a", "b")]).correct("").text == "") + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter CorrectionEngineTests` +Expected: compilatiefout `cannot find 'CorrectionEngine' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstData/CorrectionEngine.swift`: + +```swift +import Foundation +import SstCore + +/// Whole-word, case-insensitive, single-pass replacement. Longest match wins, replaced text is never rescanned. +public struct CorrectionEngine: TextCorrecting { + private struct Rule { + let source: String + let wrote: String + } + + private let rules: [Rule] + private let regex: NSRegularExpression? + + public init(entries: [DictionaryEntry], knowledge: WordKnowledge) { + var seen = Set() + var built: [(rule: Rule, order: Int)] = [] + for (order, entry) in entries.enumerated() { + let source = entry.source.trimmingCharacters(in: .whitespacesAndNewlines) + let key = EntryKey.key(source) + guard !key.isEmpty, !seen.contains(key) else { continue } + // A single-word term that is also a real word only biases the model; capitalising it everywhere would corrupt normal text. + if case .term(let term) = entry.kind, EntryKey.parts(term).count == 1, knowledge.isWord(term) { continue } + seen.insert(key) + built.append((Rule(source: source, wrote: entry.wrote), order)) + } + let ordered = built.sorted { a, b in + let la = a.rule.source.filter(\.isLetter).count + let lb = b.rule.source.filter(\.isLetter).count + return la != lb ? la > lb : a.order < b.order + }.map(\.rule) + rules = ordered + if ordered.isEmpty { + regex = nil + } else { + let alternatives = ordered.map { "(\(Self.pattern($0.source)))" }.joined(separator: "|") + regex = try? NSRegularExpression( + pattern: "(? String { + EntryKey.parts(source) + .map { NSRegularExpression.escapedPattern(for: $0) } + .joined(separator: "[\\s\\-‐‑–]*") + } + + public func correct(_ text: String) -> CorrectionResult { + guard let regex, !text.isEmpty else { return CorrectionResult(text: text, corrections: []) } + let ns = text as NSString + var output = "" + var outputLength = 0 + var last = 0 + var applied: [AppliedCorrection] = [] + + for match in regex.matches(in: text, range: NSRange(location: 0, length: ns.length)) { + let gap = ns.substring(with: NSRange(location: last, length: match.range.location - last)) + output += gap + outputLength += gap.utf16.count + + let heard = ns.substring(with: match.range) + let group = (1.." +``` + +--- + +### Task 3: `EntryWarnings` en `VocabularyBuilder` + +**Files:** +- Create: `Sources/SstData/EntryWarnings.swift`, `Sources/SstData/VocabularyBuilder.swift` +- Test: `Tests/SstDataTests/EntryWarningsTests.swift`, `Tests/SstDataTests/VocabularyBuilderTests.swift` + +**Interfaces:** +- Consumes: Task 1. +- Produces: + - `struct EntryWarning { level: Level (.note < .warning < .error); message: String }` + - `EntryWarnings.check(_ kind: DictionaryEntry.Kind, among others: [DictionaryEntry], knowledge: WordKnowledge) -> [EntryWarning]` (meest ernstig eerst; `others` = alle andere invoeren) + - `VocabularyBuilder.build(from: [DictionaryEntry], maxTerms: Int = 24) -> [String]` + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstDataTests/EntryWarningsTests.swift`: + +```swift +import Testing +@testable import SstData + +struct EntryWarningsTests { + let knowledge = FakeKnowledge() + + func check(_ kind: DictionaryEntry.Kind, others: [DictionaryEntry] = []) -> [EntryWarning] { + EntryWarnings.check(kind, among: others, knowledge: knowledge) + } + + @Test func singleCommonWordIsAWarning() { + let w = check(.correction(heard: "cloud", wrote: "Claude")) + #expect(w.contains { $0.level == .warning && $0.message.contains("cloud") }) + } + + @Test func veryShortSourceIsAWarning() { + #expect(check(.correction(heard: "ab", wrote: "AB")).contains { $0.level == .warning }) + } + + @Test func functionWordInsideAPhraseIsAWarning() { + #expect(check(.correction(heard: "in de", wrote: "indeed")).contains { $0.level == .warning }) + } + + @Test func gluedFormThatIsARealWordIsAWarning() { + let w = check(.correction(heard: "some thing", wrote: "Something")) + #expect(w.contains { $0.level == .warning && $0.message.contains("something") }) + } + + @Test func phraseOfCommonWordsOnlyGetsANote() { + let w = check(.correction(heard: "cloud code", wrote: "Claude Code")) + #expect(w.count == 1) + #expect(w[0].level == .note) + } + + @Test func distinctiveEntriesGetNoWarnings() { + #expect(check(.correction(heard: "versel", wrote: "Vercel")).isEmpty) + #expect(check(.term("Supabase")).isEmpty) + } + + @Test func duplicateSourceIsAnErrorEvenWithDifferentSpelling() { + let others = [pair("cloud code", "Claude Code")] + let w = check(.correction(heard: "Cloud-Code", wrote: "X"), others: others) + #expect(w.first?.level == .error) + } + + @Test func termThatIsARealWordGetsANoteAboutBiasOnly() { + let w = check(.term("Reef")) + #expect(w.count == 1) + #expect(w[0].level == .note) + } + + @Test func emptySourceIsAnError() { + #expect(check(.term(" ")).first?.level == .error) + } + + @Test func warningsAreSortedMostSevereFirst() { + let w = check(.correction(heard: "cloud", wrote: "Claude"), others: [pair("cloud", "x")]) + #expect(w.map(\.level) == w.map(\.level).sorted(by: >)) + } +} +``` + +`Tests/SstDataTests/VocabularyBuilderTests.swift`: + +```swift +import Testing +@testable import SstData + +struct VocabularyBuilderTests { + @Test func termsComeFirstThenCorrectionTargetsWithoutDuplicates() { + let entries = [pair("cloud code", "Claude Code"), term("Anthropic"), term("anthropic"), pair("versel", "Vercel"), term("Supabase")] + #expect(VocabularyBuilder.build(from: entries) == ["Anthropic", "Supabase", "Claude Code", "Vercel"]) + } + + @Test func listIsCapped() { + let entries = (1...40).map { term("Woord\($0)") } + #expect(VocabularyBuilder.build(from: entries).count == 24) + #expect(VocabularyBuilder.build(from: entries, maxTerms: 5).count == 5) + } + + @Test func emptyDictionaryGivesNoVocabulary() { + #expect(VocabularyBuilder.build(from: []).isEmpty) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "EntryWarningsTests|VocabularyBuilderTests"` +Expected: compilatiefouten (`cannot find 'EntryWarnings'`, `cannot find 'VocabularyBuilder'`). + +- [ ] **Step 3: Implementatie** + +`Sources/SstData/EntryWarnings.swift`: + +```swift +import Foundation + +public struct EntryWarning: Equatable, Sendable { + public enum Level: Int, Comparable, Sendable { + case note = 0, warning, error + public static func < (a: Level, b: Level) -> Bool { a.rawValue < b.rawValue } + } + + public let level: Level + public let message: String + + public init(level: Level, message: String) { + self.level = level + self.message = message + } +} + +/// Flags dictionary entries that are likely to corrupt normal text. +public enum EntryWarnings { + /// - Parameter others: all entries except the one being checked. + public static func check(_ kind: DictionaryEntry.Kind, among others: [DictionaryEntry], knowledge: WordKnowledge) -> [EntryWarning] { + let source: String + let isCorrection: Bool + switch kind { + case .term(let term): (source, isCorrection) = (term, false) + case .correction(let heard, _): (source, isCorrection) = (heard, true) + } + + let parts = EntryKey.parts(source) + guard !parts.isEmpty else { return [EntryWarning(level: .error, message: "Leeg: vul een woord in.")] } + + var result: [EntryWarning] = [] + if others.contains(where: { EntryKey.key($0.source) == EntryKey.key(source) }) { + result.append(EntryWarning(level: .error, message: "Dubbele invoer: deze bron staat er al, de eerste telt.")) + } + + if !isCorrection { + if parts.count == 1, knowledge.isWord(source) { + result.append(EntryWarning(level: .note, message: "Gewoon woord: krijgt alleen voorrang bij het model, geen automatische hoofdletterregel.")) + } + return result.sorted { $0.level > $1.level } + } + + if parts.count == 1, knowledge.isWord(parts[0]) { + result.append(EntryWarning(level: .warning, message: "'\(parts[0])' is een gewoon woord en wordt overal in je tekst vervangen.")) + } + if source.filter(\.isLetter).count < 3 { + result.append(EntryWarning(level: .warning, message: "Erg kort (minder dan 3 letters): veel kans op ongewenste vervangingen.")) + } + if parts.count > 1 { + var flagged = false + if let common = parts.first(where: { knowledge.isFunctionWord($0) }) { + result.append(EntryWarning(level: .warning, message: "Bevat het veelvoorkomende woord '\(common)'; dit kan gewone zinnen raken.")) + flagged = true + } + let glued = parts.joined() + if knowledge.isWord(glued) { + result.append(EntryWarning(level: .warning, message: "Aaneengeschreven ('\(glued)') is een bestaand woord en wordt ook vervangen.")) + flagged = true + } + if !flagged, parts.allSatisfy({ knowledge.isWord($0) }) { + result.append(EntryWarning(level: .note, message: "Alle delen zijn gewone woorden: alleen de hele combinatie wordt vervangen.")) + } + } + return result.sorted { $0.level > $1.level } + } +} +``` + +`Sources/SstData/VocabularyBuilder.swift`: + +```swift +import Foundation + +/// The short word list handed to the speech engine as context. +public enum VocabularyBuilder { + /// Terms first (file order), then the unique right-hand sides of correction pairs; at most `maxTerms` words. + public static func build(from entries: [DictionaryEntry], maxTerms: Int = 24) -> [String] { + var out: [String] = [] + var seen = Set() + func add(_ word: String) { + let key = word.lowercased() + guard !word.isEmpty, !seen.contains(key) else { return } + seen.insert(key) + out.append(word) + } + for entry in entries { if case .term(let term) = entry.kind { add(term) } } + for entry in entries { if case .correction(_, let wrote) = entry.kind { add(wrote) } } + return Array(out.prefix(maxTerms)) + } +} +``` + +- [ ] **Step 4: Draai de tests** + +Run: `swift test --filter "EntryWarningsTests|VocabularyBuilderTests"` +Expected: 10 + 3 tests PASS. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstData Tests/SstDataTests +git commit -m "feat: add EntryWarnings and VocabularyBuilder" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 4: Geschiedenis — opslag, zoeken, groeperen + +**Files:** +- Create: `Sources/SstData/History.swift` +- Test: `Tests/SstDataTests/HistoryTests.swift` + +**Interfaces:** +- Consumes: `SstCore.DictationResult`, `SstCore.AppliedCorrection`. +- Produces: + - `struct HistoryEntry: Codable, Equatable, Identifiable` (`id`, `date`, `duration`, `rawText`, `finalText`, `corrections`; `init(result:)`) + - `@MainActor final class HistoryStore` met `init(fileURL:)`, `entries` (nieuwste eerst), `load() -> LoadOutcome` (`.empty | .loaded | .recoveredCorrupt(backup:)`), `add(_:)`, `remove(id:)`, `removeAll()`, `flush()`, `onChange`, `lastError` + - `HistorySearch.filter(_:query:) -> [HistoryEntry]` + - `HistoryGrouping.byDay(_:calendar:) -> [HistoryDay]` (`HistoryDay`: `day: Date`, `entries`) + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstDataTests/HistoryTests.swift`: + +```swift +import Foundation +import Testing +import SstCore +@testable import SstData + +@MainActor +struct HistoryStoreTests { + func tempFile() -> URL { + FileManager.default.temporaryDirectory.appending(path: "sst-tests-\(UUID().uuidString)/history.json") + } + + func entry(_ text: String, at seconds: TimeInterval) -> HistoryEntry { + HistoryEntry(date: Date(timeIntervalSince1970: 1_700_000_000 + seconds), duration: 2, rawText: text, finalText: text) + } + + @Test func newestEntryComesFirstAndSurvivesARoundTrip() { + let url = tempFile() + let store = HistoryStore(fileURL: url) + store.add(entry("eerste", at: 0)) + store.add(HistoryEntry( + date: Date(timeIntervalSince1970: 1_700_000_100), duration: 3, rawText: "cloud code", finalText: "Claude Code", + corrections: [AppliedCorrection(heard: "cloud code", wrote: "Claude Code", rule: "cloud code", start: 0, length: 11)])) + store.flush() + + let reloaded = HistoryStore(fileURL: url) + #expect(reloaded.load() == .loaded) + #expect(reloaded.entries.map(\.finalText) == ["Claude Code", "eerste"]) + #expect(reloaded.entries == store.entries) + } + + @Test func missingFileIsEmpty() { + let store = HistoryStore(fileURL: tempFile()) + #expect(store.load() == .empty) + #expect(store.entries.isEmpty) + } + + @Test func removeAndRemoveAll() { + let store = HistoryStore(fileURL: tempFile()) + let a = entry("a", at: 0), b = entry("b", at: 1) + store.add(a); store.add(b) + store.remove(id: a.id) + #expect(store.entries.map(\.finalText) == ["b"]) + store.removeAll() + #expect(store.entries.isEmpty) + } + + @Test func corruptFileIsMovedAsideAndStoreStartsFresh() throws { + let url = tempFile() + try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true) + try "dit is geen json".write(to: url, atomically: true, encoding: .utf8) + let store = HistoryStore(fileURL: url) + guard case .recoveredCorrupt(let backup) = store.load() else { + Issue.record("expected recoveredCorrupt"); return + } + #expect(FileManager.default.fileExists(atPath: backup.path)) + #expect(!FileManager.default.fileExists(atPath: url.path)) + #expect(store.entries.isEmpty) + } + + @Test func onChangeFiresForEdits() { + let store = HistoryStore(fileURL: tempFile()) + var calls = 0 + store.onChange = { calls += 1 } + store.add(entry("a", at: 0)) + store.removeAll() + #expect(calls == 2) + } + + @Test func entryFromDictationResultKeepsEverything() { + let result = DictationResult( + date: Date(timeIntervalSince1970: 1_700_000_000), duration: 4.5, rawText: "raw", finalText: "final", + corrections: [AppliedCorrection(heard: "a", wrote: "b", rule: "a", start: 0, length: 1)]) + let e = HistoryEntry(result: result) + #expect(e.rawText == "raw" && e.finalText == "final" && e.duration == 4.5 && e.corrections.count == 1) + } +} + +struct HistorySearchTests { + let entries = [ + HistoryEntry(date: .now, duration: 1, rawText: "ik ga naar het café", finalText: "Ik ga naar het Café"), + HistoryEntry(date: .now, duration: 1, rawText: "cloud code is handig", finalText: "Claude Code is handig"), + ] + + @Test func emptyQueryReturnsEverything() { + #expect(HistorySearch.filter(entries, query: " ").count == 2) + } + + @Test func ignoresCaseAndDiacritics() { + #expect(HistorySearch.filter(entries, query: "CAFE").count == 1) + #expect(HistorySearch.filter(entries, query: "café").count == 1) + } + + @Test func allWordsMustMatchInAnyOrder() { + #expect(HistorySearch.filter(entries, query: "handig claude").count == 1) + #expect(HistorySearch.filter(entries, query: "handig café").isEmpty) + } + + @Test func searchesTheOriginalTextToo() { + #expect(HistorySearch.filter(entries, query: "cloud").count == 1) + } +} + +struct HistoryGroupingTests { + @Test func groupsByDayKeepingOrder() { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(identifier: "UTC")! + func at(_ day: Int, _ hour: Int) -> Date { + calendar.date(from: DateComponents(year: 2026, month: 10, day: day, hour: hour))! + } + let entries = [at(2, 15), at(2, 9), at(1, 20)].map { + HistoryEntry(date: $0, duration: 1, rawText: "x", finalText: "x") + } + let days = HistoryGrouping.byDay(entries, calendar: calendar) + #expect(days.count == 2) + #expect(days[0].entries.count == 2) + #expect(days[1].entries.count == 1) + #expect(days[0].day > days[1].day) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "HistoryStoreTests|HistorySearchTests|HistoryGroupingTests"` +Expected: compilatiefout `cannot find 'HistoryStore' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstData/History.swift`: + +```swift +import Foundation +import SstCore + +public struct HistoryEntry: Codable, Equatable, Identifiable, Sendable { + public var id: UUID + public var date: Date + public var duration: TimeInterval + public var rawText: String + public var finalText: String + public var corrections: [AppliedCorrection] + + public init( + id: UUID = UUID(), date: Date, duration: TimeInterval, + rawText: String, finalText: String, corrections: [AppliedCorrection] = [] + ) { + self.id = id + self.date = date + self.duration = duration + self.rawText = rawText + self.finalText = finalText + self.corrections = corrections + } + + public init(result: DictationResult) { + self.init( + date: result.date, duration: result.duration, rawText: result.rawText, + finalText: result.finalText, corrections: result.corrections) + } +} + +/// Newest first, stored as one JSON file; saves are debounced. +@MainActor +public final class HistoryStore { + public enum LoadOutcome: Equatable { + case empty + case loaded + case recoveredCorrupt(backup: URL) + } + + private struct Document: Codable { + var version: Int + var entries: [HistoryEntry] + } + + public private(set) var entries: [HistoryEntry] = [] + public private(set) var lastError: String? + public var onChange: (() -> Void)? + + private let fileURL: URL + private var saveTask: Task? + + public init(fileURL: URL) { + self.fileURL = fileURL + } + + private static func makeEncoder() -> JSONEncoder { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + return encoder + } + + private static func makeDecoder() -> JSONDecoder { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + return decoder + } + + @discardableResult + public func load() -> LoadOutcome { + guard let data = try? Data(contentsOf: fileURL) else { + entries = [] + return .empty + } + do { + entries = try Self.makeDecoder().decode(Document.self, from: data).entries + return .loaded + } catch { + let stamp = ISO8601DateFormatter().string(from: Date()).replacingOccurrences(of: ":", with: "-") + let backup = fileURL.deletingLastPathComponent().appending(path: "history.corrupt-\(stamp).json") + try? FileManager.default.moveItem(at: fileURL, to: backup) + entries = [] + return .recoveredCorrupt(backup: backup) + } + } + + public func add(_ entry: HistoryEntry) { + entries.insert(entry, at: 0) + changed() + } + + public func remove(id: UUID) { + entries.removeAll { $0.id == id } + changed() + } + + public func removeAll() { + entries = [] + changed() + } + + /// Writes immediately (also call this when the app quits). + public func flush() { + saveTask?.cancel() + saveTask = nil + do { + try FileManager.default.createDirectory( + at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true) + let data = try Self.makeEncoder().encode(Document(version: 1, entries: entries)) + try data.write(to: fileURL, options: .atomic) + lastError = nil + } catch { + lastError = error.localizedDescription + } + } + + private func changed() { + onChange?() + saveTask?.cancel() + saveTask = Task { [weak self] in + try? await Task.sleep(for: .milliseconds(300)) + guard !Task.isCancelled else { return } + self?.flush() + } + } +} + +public enum HistorySearch { + static func fold(_ text: String) -> String { + text.folding(options: [.caseInsensitive, .diacriticInsensitive], locale: nil) + } + + /// Every query word must occur in the final or the original text (case- and diacritic-insensitive). + public static func filter(_ entries: [HistoryEntry], query: String) -> [HistoryEntry] { + let words = fold(query).split(whereSeparator: \.isWhitespace).map(String.init) + guard !words.isEmpty else { return entries } + return entries.filter { entry in + let haystack = fold(entry.finalText + " " + entry.rawText) + return words.allSatisfy { haystack.contains($0) } + } + } +} + +public struct HistoryDay: Identifiable, Equatable, Sendable { + public let day: Date + public let entries: [HistoryEntry] + public var id: Date { day } +} + +public enum HistoryGrouping { + /// Groups newest-first entries by calendar day, keeping their order. + public static func byDay(_ entries: [HistoryEntry], calendar: Calendar = .current) -> [HistoryDay] { + var days: [HistoryDay] = [] + for entry in entries { + let start = calendar.startOfDay(for: entry.date) + if let last = days.last, last.day == start { + days[days.count - 1] = HistoryDay(day: start, entries: last.entries + [entry]) + } else { + days.append(HistoryDay(day: start, entries: [entry])) + } + } + return days + } +} +``` + +- [ ] **Step 4: Draai de tests** + +Run: `swift test --filter "HistoryStoreTests|HistorySearchTests|HistoryGroupingTests"` +Expected: 6 + 4 + 1 tests PASS. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstData Tests/SstDataTests +git commit -m "feat: add history store, search and day grouping" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 5: `DictionaryStore` en `DictionaryCorrector` + +**Files:** +- Create: `Sources/SstData/DictionaryStore.swift` +- Test: `Tests/SstDataTests/DictionaryStoreTests.swift` + +**Interfaces:** +- Consumes: Task 1 (`DictionaryFile`), Task 2 (`CorrectionEngine`). +- Produces: + - `@MainActor final class DictionaryStore` met `init(fileURL:)`, `fileURL`, `file`, `entries`, `load() throws`, `apply(_ mutate: (inout DictionaryFile) -> Void) throws`, `reloadIfChanged()`, `startWatching()`, `stopWatching()`, `onChange`, `static header` + - `final class DictionaryCorrector: TextCorrecting` met `init(knowledge:)`, `update(entries:)`, `setEnabled(_:)` + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstDataTests/DictionaryStoreTests.swift`: + +```swift +import Foundation +import Testing +import SstCore +@testable import SstData + +@MainActor +struct DictionaryStoreTests { + func tempFile() -> URL { + FileManager.default.temporaryDirectory.appending(path: "sst-tests-\(UUID().uuidString)/dictionary.txt") + } + + @Test func loadCreatesAFileWithAHeaderWhenMissing() throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + #expect(FileManager.default.fileExists(atPath: url.path)) + #expect(store.entries.isEmpty) + #expect(try String(contentsOf: url, encoding: .utf8).hasPrefix("# Sst woordenboek")) + } + + @Test func applyWritesAtomicallyAndKeepsComments() throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + try store.apply { $0.append(.correction(heard: "cloud code", wrote: "Claude Code")) } + let text = try String(contentsOf: url, encoding: .utf8) + #expect(text.hasPrefix("# Sst woordenboek")) + #expect(text.hasSuffix("cloud code => Claude Code\n")) + #expect(store.entries == [pair("cloud code", "Claude Code")]) + } + + @Test func externalEditIsPickedUp() throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + var changes = 0 + store.onChange = { changes += 1 } + try "Vercel\n".write(to: url, atomically: true, encoding: .utf8) + store.reloadIfChanged() + #expect(store.entries == [term("Vercel")]) + #expect(changes == 1) + store.reloadIfChanged() // unchanged text: no second notification + #expect(changes == 1) + } + + @Test func ourOwnWriteDoesNotTriggerAReload() throws { + let store = DictionaryStore(fileURL: tempFile()) + try store.load() + try store.apply { $0.append(.term("Supabase")) } + var changes = 0 + store.onChange = { changes += 1 } + store.reloadIfChanged() + #expect(changes == 0) + } + + @Test func watcherNoticesAnEditorSavingTheFile() async throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + var changed = false + store.onChange = { changed = true } + store.startWatching() + try "Anthropic\n".write(to: url, atomically: true, encoding: .utf8) + for _ in 0..<40 where !changed { try await Task.sleep(for: .milliseconds(100)) } + store.stopWatching() + #expect(changed) + #expect(store.entries == [term("Anthropic")]) + } +} + +struct DictionaryCorrectorTests { + @Test func correctsWhenEnabledAndPassesThroughWhenDisabled() { + let corrector = DictionaryCorrector(knowledge: FakeKnowledge()) + corrector.update(entries: [pair("cloud code", "Claude Code")]) + #expect(corrector.correct("cloud code").text == "Claude Code") + corrector.setEnabled(false) + #expect(corrector.correct("cloud code").text == "cloud code") + corrector.setEnabled(true) + corrector.update(entries: []) + #expect(corrector.correct("cloud code").text == "cloud code") + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "DictionaryStoreTests|DictionaryCorrectorTests"` +Expected: compilatiefout `cannot find 'DictionaryStore' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstData/DictionaryStore.swift`: + +```swift +import Foundation +import SstCore + +/// Owns `dictionary.txt`: reads, writes atomically, and notices edits made in another editor. +@MainActor +public final class DictionaryStore { + public static let header = """ + # Sst woordenboek + # Eén invoer per regel. Een los woord is een term (voorrang bij het model). + # 'gehoord => schrijf' is een correctie, bijvoorbeeld: cloud code => Claude Code + + """ + + public let fileURL: URL + public private(set) var file = DictionaryFile() + public var onChange: (() -> Void)? + + private var lastText = "" + private var watcher: DispatchSourceFileSystemObject? + private var debounce: Task? + + public init(fileURL: URL) { + self.fileURL = fileURL + } + + public var entries: [DictionaryEntry] { file.entries } + + /// Reads the file; creates it with a short header when it does not exist yet. + public func load() throws { + try FileManager.default.createDirectory( + at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true) + if !FileManager.default.fileExists(atPath: fileURL.path) { + try Self.header.write(to: fileURL, atomically: true, encoding: .utf8) + } + let text = try String(contentsOf: fileURL, encoding: .utf8) + lastText = text + file = DictionaryFile(parsing: text) + onChange?() + } + + /// Applies an edit and writes the file straight away. + public func apply(_ mutate: (inout DictionaryFile) -> Void) throws { + var updated = file + mutate(&updated) + let text = updated.serialized() + try text.write(to: fileURL, atomically: true, encoding: .utf8) + file = updated + lastText = text + onChange?() + } + + /// Re-reads the file when its text differs from what we last wrote or read. + public func reloadIfChanged() { + guard let text = try? String(contentsOf: fileURL, encoding: .utf8), text != lastText else { return } + lastText = text + file = DictionaryFile(parsing: text) + onChange?() + } + + /// Watches the folder (editors replace files instead of editing them in place). + public func startWatching() { + guard watcher == nil else { return } + let descriptor = open(fileURL.deletingLastPathComponent().path, O_EVTONLY) + guard descriptor >= 0 else { return } + let source = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: descriptor, eventMask: .write, queue: .main) + source.setEventHandler { [weak self] in + MainActor.assumeIsolated { self?.scheduleReload() } + } + source.setCancelHandler { close(descriptor) } + source.resume() + watcher = source + } + + public func stopWatching() { + watcher?.cancel() + watcher = nil + debounce?.cancel() + } + + private func scheduleReload() { + debounce?.cancel() + debounce = Task { [weak self] in + try? await Task.sleep(for: .milliseconds(200)) + guard !Task.isCancelled else { return } + self?.reloadIfChanged() + } + } +} + +/// Bridges the dictionary to the controller: thread-safe, can be switched off, rebuilt when the dictionary changes. +public final class DictionaryCorrector: TextCorrecting, @unchecked Sendable { + private let lock = NSLock() + private let knowledge: WordKnowledge + private var engine: CorrectionEngine + private var enabled = true + + public init(knowledge: WordKnowledge) { + self.knowledge = knowledge + self.engine = CorrectionEngine(entries: [], knowledge: knowledge) + } + + public func update(entries: [DictionaryEntry]) { + let rebuilt = CorrectionEngine(entries: entries, knowledge: knowledge) + lock.withLock { engine = rebuilt } + } + + public func setEnabled(_ value: Bool) { + lock.withLock { enabled = value } + } + + public func correct(_ text: String) -> CorrectionResult { + let (current, isOn) = lock.withLock { (engine, enabled) } + return isOn ? current.correct(text) : CorrectionResult(text: text, corrections: []) + } +} +``` + +- [ ] **Step 4: Draai alle tests** + +Run: `swift test` +Expected: alle tests PASS (engine-tests uit fase 1 + 54 in `SstDataTests`). De test `watcherNoticesAnEditorSavingTheFile` wacht maximaal 4 s; faalt hij sporadisch, draai hem apart opnieuw voordat je de implementatie wijzigt. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstData Tests/SstDataTests +git commit -m "feat: add DictionaryStore with file watching and DictionaryCorrector" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +## Self-review + +- **Spec-dekking:** bestandsformaat en behoud van opmerkingen/volgorde/ongeldige regels (Task 1), alle correctieregels uit de spec — hele woorden, glue/koppelteken, langste eerst, geen ketens, termen die gewone woorden zijn, letterlijke vervanging, Cloudflare/cloud codex (Task 2), waarschuwingstabel (Task 3), voorrangslijst met limiet 24 (Task 3), geschiedenis met zoeken, groeperen, beschadigd bestand (Task 4), bestandsmonitor en uit/aan-schakelaar (Task 5). Niet in deze fase: de plaats van de bestanden in Application Support, de koppeling aan `DictationController` en de UI (fase 4 en 5). +- **Placeholders:** geen; alle code is uit een werkende, geteste versie overgenomen. +- **Typeconsistentie:** `DictionaryEntry.Kind` (`.term`, `.correction(heard:wrote:)`), `CorrectionEngine(entries:knowledge:)`, `EntryWarnings.check(_:among:knowledge:)`, `HistoryEntry(result:)` en `DictionaryStore.apply` komen overeen met wat Task 2–5 en de latere fasen gebruiken. +- **Afwijking van de spec (bewust):** "gewoon woord" gebruikt de Engelse woordenlijst van `NaturalLanguage` (alleen gewone woorden) plus de Nederlandse spellingcontrole, niet de Engelse spellingcontrole: die rekent "anthropic" tot de woorden en zou jouw eigen voorbeeldterm onterecht uitsluiten van de hoofdletterregel. diff --git a/docs/superpowers/specs/2026-10-01-macos-app-design.md b/docs/superpowers/specs/2026-10-01-macos-app-design.md index 865de69..6f468c9 100644 --- a/docs/superpowers/specs/2026-10-01-macos-app-design.md +++ b/docs/superpowers/specs/2026-10-01-macos-app-design.md @@ -174,7 +174,7 @@ Tegen verzinnen op stilte: ### Waarschuwingen bij invoer die veel voorkomt -`EntryWarnings` bepaalt bij toevoegen en bewerken, met een injecteerbare `WordKnowledge` (standaard: `NSSpellChecker` voor nl en en, plus een meegeleverde lijst van de ~300 meest voorkomende functiewoorden in beide talen): +`EntryWarnings` bepaalt bij toevoegen en bewerken, met een injecteerbare `WordKnowledge`. Standaard: de Engelse woordenlijst van `NaturalLanguage` (alleen gewone woorden; "anthropic" en "vercel" staan er niet in, "cloud" en "reef" wel), de Nederlandse spellingcontrole van macOS, en een meegeleverde lijst van de meest voorkomende functiewoorden in beide talen (circa 170): | Niveau | Wanneer | |---|---| From 2d75e17d1c32e29f3ebbc32400b225feb0bafd48 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:32:04 +0200 Subject: [PATCH 05/43] docs: add app plan 3 (UI kit) Co-Authored-By: Claude Sonnet 5.5 --- .../plans/2026-10-01-app-3-ui-kit.md | 1558 +++++++++++++++++ 1 file changed, 1558 insertions(+) create mode 100644 docs/superpowers/plans/2026-10-01-app-3-ui-kit.md diff --git a/docs/superpowers/plans/2026-10-01-app-3-ui-kit.md b/docs/superpowers/plans/2026-10-01-app-3-ui-kit.md new file mode 100644 index 0000000..76736a7 --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-app-3-ui-kit.md @@ -0,0 +1,1558 @@ +# App fase 3 — SstUI (ontwerpsysteem in code en componenten) Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** De goedgekeurde ontwerp-tokens als Swift-code, plus de herbruikbare componenten van de veldrecorder-stijl (oppervlakken, toetsen, lampjes, VU-meter, segmentteller, LED-balk) en een galerij om ze in beide afwerkingen te bekijken. + +**Architecture:** `Design/tokens.json` blijft de enige bron. `Design/build_tokens.py` genereert daaruit nu ook `Sources/SstUI/DesignTokens.swift` (en `--check` bewaakt dat ze gelijk blijven). Componenten gebruiken uitsluitend die constanten; een test scant de broncode op losse kleuren en maten. Instrument-logica (schaal, wijzerveer, segmenten) is puur en getest; tekenwerk zit in `Canvas`-views. Het target `SstUI` hangt van niets anders in het pakket af. + +**Tech Stack:** Swift 6.4 (language mode 5), SwiftUI, AppKit (`NSColor`, `NSImage`), Swift Testing, Python 3 (generator). + +**Spec:** `docs/superpowers/specs/2026-10-01-macos-app-design.md` (sectie "Ontwerpsysteem in code") en `docs/design/tokens.md` + +## Global Constraints + +- Geen losse waarden in componenten: kleuren, maten, hoeken, randen, tijden komen uit `Palette`, `Space`, `Radius`, `Border`, `Size`, `Layout`, `Opacity`, `Typography`, `Shadows`, `Motion`, `MeterTokens`, `Segment`, `Texture`. Een regel mag afwijken met `// token-exempt: reden`. +- Twee afwerkingen via dynamische kleuren: Aluminium (licht) en Graphite (donker), gevolgd uit het uiterlijk van macOS. +- Eén accent: `Palette.accentRecord`; amber en groen alleen voor niveau-indicatoren en markeringen; gloed alleen op lampjes, nooit op tekst. +- Wijzer: 300 ms respons, demping 0,80 (1,0 bij "Verminder beweging"); verversing 60 Hz en alleen terwijl de meter actief is (plus 1,5 s uitzweven). +- 0 VU = −20 dBFS; schaal −20…+3 VU; vanaf 0 VU rood. +- Toegankelijkheid: tekstparen ≥ 4,5:1 (bewaakt door het tokenscript); focus-ring `Border.focus` + `Palette.lineFocus`; meter en teller hebben een toegankelijkheidslabel. +- Elke commit eindigt met de trailer `Co-Authored-By: Claude Sonnet 5.5 ` (tweede `-m`). + +## Bestandsstructuur + +``` +Design/tokens.json (wijzig) extra meetwaarden en opaciteiten +Design/preview.template.html (wijzig) leest die waarden uit de tokens +Design/build_tokens.py (wijzig) Swift-uitvoer en --check +Package.swift (wijzig) SstUI, SstUITests, SstGallery +Sources/SstUI/DesignTokens.swift (gegenereerd) +Sources/SstUI/DynamicColor.swift (nieuw) Color.dynamic / Color.fixed +Sources/SstUI/TextStyle.swift (nieuw) TextStyle, ShadowLayer, .textStyle(_:) +Sources/SstUI/Instruments.swift (nieuw) VUScale, NeedleSpring, SegmentGlyph, LEDBar +Sources/SstUI/Surfaces.swift (nieuw) PhysicalSurface, Panel/Well/Display/Paper, SeamDivider, SilkLabel, BrushedBackground +Sources/SstUI/Controls.swift (nieuw) KeyButtonStyle, LampView +Sources/SstUI/VUMeterView.swift (nieuw) +Sources/SstUI/CounterAndLEDs.swift (nieuw) SegmentCounterView, LEDBarView +Sources/SstGallery/GalleryApp.swift (nieuw) galerij + --snapshot +Tests/SstUITests/TokenIntegrityTests.swift, InstrumentsTests.swift +docs/design/tokens.md, tokens-preview.html (opnieuw gegenereerd) +``` + +--- + +### Task 1: Tokens uitbreiden en als Swift genereren + +**Files:** +- Modify: `Package.swift`, `Design/tokens.json`, `Design/preview.template.html`, `Design/build_tokens.py` +- Create: `Sources/SstUI/DynamicColor.swift`, `Sources/SstUI/TextStyle.swift`, `Sources/SstUI/DesignTokens.swift` (gegenereerd) +- Test: `Tests/SstUITests/TokenIntegrityTests.swift` + +**Interfaces:** +- Produces (gebruikt door alle latere taken en fasen): de enums `Palette`, `Opacity`, `FontFamily`, `Typography` (`TextStyle`), `Segment`, `Space`, `Layout`, `Size`, `Radius`, `Border`, `Shadows` (`[ShadowLayer]`; `Shadows.keyPressedOffset`), `Texture`, `MeterTokens`, `Motion`; `Color.dynamic(light:dark:)`, `Color.fixed(hex:)` (intern); `View.textStyle(_:)`; `ShadowLayer.color(_:)`. + +- [ ] **Step 1: Targets toevoegen** + +In `Package.swift`, voeg bij `targets` toe: + +```swift + .target(name: "SstUI"), +``` + +en bij de testtargets: + +```swift + .testTarget(name: "SstUITests", dependencies: ["SstUI"]), +``` + +Maak een tijdelijke `Sources/SstUI/Placeholder.swift` met de regel `import Foundation` (een target heeft minstens één bronbestand nodig; je verwijdert hem in stap 6). + +- [ ] **Step 2: Schrijf de falende tests** + +`Tests/SstUITests/TokenIntegrityTests.swift`: + +```swift +import Foundation +import Testing + +/// Root of the package: Tests/SstUITests/ → three levels up. +private let root = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() + +struct TokenIntegrityTests { + @Test func generatedSwiftMatchesTokensJSONAndContrastHolds() throws { + #expect( + FileManager.default.fileExists(atPath: root.appending(path: "Sources/SstUI/DesignTokens.swift").path), + "Sources/SstUI/DesignTokens.swift ontbreekt: run `python3 Design/build_tokens.py`.") + let process = Process() + process.executableURL = URL(fileURLWithPath: "/usr/bin/env") + process.arguments = ["python3", "Design/build_tokens.py", "--check"] + process.currentDirectoryURL = root + let pipe = Pipe() + process.standardOutput = pipe + process.standardError = pipe + try process.run() + process.waitUntilExit() + let output = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + #expect(process.terminationStatus == 0, "Run `python3 Design/build_tokens.py`.\n\(output.suffix(600))") + } + + /// Views take every colour, size and time from the tokens. A line may opt out with `// token-exempt: reason`. + @Test func viewsContainNoLooseDesignValues() throws { + let forbidden: [(name: String, pattern: String)] = [ + ("hex colour", #"0x[0-9A-Fa-f]{6}\b"#), + ("component colour", #"(NS)?Color\((red|white|hue):"#), + ("literal font size", #"\.system\(size:\s*\d"#), + ("literal corner radius", #"cornerRadius:\s*\d|\.cornerRadius\(\s*\d"#), + ("literal padding", #"\.padding\(\s*(\.\w+\s*,\s*)?\d"#), + ("literal frame size", #"(width|height|minWidth|minHeight|maxWidth|maxHeight|idealWidth|idealHeight):\s*[1-9]"#), + ("literal line width", #"lineWidth:\s*\d"#), + ("literal opacity", #"\.opacity\(\s*[0-9]"#), + ] + let skipped: Set = ["DesignTokens.swift", "DynamicColor.swift"] + var directories = [root.appending(path: "Sources/SstUI")] + let app = root.appending(path: "Sources/Sst") + if FileManager.default.fileExists(atPath: app.path) { directories.append(app) } + + var problems: [String] = [] + for directory in directories { + let files = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) + .filter { $0.pathExtension == "swift" && !skipped.contains($0.lastPathComponent) } + for file in files { + let lines = try String(contentsOf: file, encoding: .utf8).components(separatedBy: "\n") + for (index, line) in lines.enumerated() where !line.contains("token-exempt") { + for rule in forbidden where line.range(of: rule.pattern, options: .regularExpression) != nil { + problems.append("\(file.lastPathComponent):\(index + 1) \(rule.name): \(line.trimmingCharacters(in: .whitespaces))") + } + } + } + } + #expect(problems.isEmpty, "Loose design values:\n\(problems.joined(separator: "\n"))") + } +} +``` + +- [ ] **Step 3: Draai de tests, de eerste moet falen** + +Run: `swift test --filter TokenIntegrityTests` +Expected: `generatedSwiftMatchesTokensJSONAndContrastHolds` faalt met "Sources/SstUI/DesignTokens.swift ontbreekt"; `viewsContainNoLooseDesignValues` slaagt (er is nog geen code). + +- [ ] **Step 4: Tokens, preview en generator aanpassen** + +Maak tijdelijk `Design/apply_design_edits.py`: + +```python +#!/usr/bin/env python3 +"""One-off: extend Design/ for the Swift UI kit. Run once from the repo root; every replacement is asserted.""" +import json +import pathlib + +root = pathlib.Path(".") + + +def edit(path, replacements): + p = root / path + s = p.read_text() + for old, new in replacements: + assert old in s, f"{path}: pattern not found:\n{old}" + s = s.replace(old, new, 1) + p.write_text(s) + + +# 1. tokens.json: geometry for the meter and counter, extra opacities +edit("Design/tokens.json", [ + (''' "vu.ticks": { "value": "-20 -10 -7 -5 -3 0 +3", "use": "Genummerde schaalmarkeringen" },''', + ''' "vu.ticks": { "value": "-20 -10 -7 -5 -3 0 +3", "use": "Genummerde schaalmarkeringen" }, + "vu.pivotOffset": { "value": 22, "use": "Draaipunt van de wijzer, onder de onderrand van de wijzerplaat" }, + "vu.scaleRadius": { "value": 142, "use": "Straal van de schaalboog vanaf het draaipunt" }, + "vu.needleLength": { "value": 146, "use": "Lengte van de wijzer" }, + "vu.tickMajor": { "value": 11, "use": "Lengte van een genummerde streep" }, + "vu.tickMinor": { "value": 6, "use": "Lengte van een gewone streep" }, + "vu.labelInset": { "value": 24, "use": "Afstand van de schaalboog tot de cijfers" }, + "vu.redBandWidth": { "value": 5, "use": "Dikte van de rode band vanaf 0 VU" },'''), + (''' "slant": { "value": 0, "use": "Schuinte in graden; 0 = rechtop" }''', + ''' "slant": { "value": 0, "use": "Schuinte in graden; 0 = rechtop" }, + "advance": { "value": 26, "use": "Afstand van cijfer tot cijfer" }, + "colonWidth": { "value": 10, "use": "Breedte van een dubbele punt of punt" }'''), + (''' "unlit": { "value": 1.00, "use": "Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie" }''', + ''' "unlit": { "value": 1.00, "use": "Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie" }, + "pressedRecord": { "value": 0.85, "use": "Ingedrukte REC-toets: het rood dooft licht" }, + "specularOn": { "value": 0.60, "use": "Lichtvlekje op een brandend lampje" }, + "specularOff": { "value": 0.30, "use": "Lichtvlekje op een gedoofd lampje" }, + "caption": { "value": 0.75, "use": "Kleine opschriften op de wijzerplaat" }'''), +]) +json.loads((root / "Design/tokens.json").read_text()) # still valid JSON + +# 2. preview: read the meter and counter geometry from the tokens instead of fixed numbers +edit("Design/preview.template.html", [ + (" const R = 142;", " const R = VU['vu.scaleRadius'].value;"), + ("const pivot = { x: W / 2, y: H + 22 };", "const pivot = { x: W / 2, y: H + VU['vu.pivotOffset'].value };"), + ("y2=\"${pivot.y - 146}\"", "y2=\"${pivot.y - VU['vu.needleLength'].value}\""), + ("const [nx, ny] = pt(146, ang_);", "const [nx, ny] = pt(VU['vu.needleLength'].value, ang_);"), + ("pt(R - 24, a)", "pt(R - VU['vu.labelInset'].value, a)"), + ("pt(R - (major ? 11 : 6), a)", "pt(R - (major ? VU['vu.tickMajor'].value : VU['vu.tickMinor'].value), a)"), + ("stroke-width=\"5\" fill=\"none\"/>`;\n for (let v", "stroke-width=\"${VU['vu.redBandWidth'].value}\" fill=\"none\"/>`;\n for (let v"), + ("const adv = DW + 6;", "const adv = SEG.advance.value;"), + ("x += 10; }", "x += SEG.colonWidth.value; }"), +]) + +# 3. generator: Swift output and --check +marker = "# --- markdown ---------------------------------------------------------------" +swift_block = pathlib.Path("Design/swift_block.txt").read_text() +edit("Design/build_tokens.py", [(marker, swift_block + "\n" + marker)]) +print("Design/ updated") +``` + +en `Design/swift_block.txt` (de Swift-generator, wordt in `build_tokens.py` ingevoegd): + +```python +# --- swift ------------------------------------------------------------------ +def camel(name): + parts = name.replace("-", ".").split(".") + return parts[0] + "".join(p[:1].upper() + p[1:] for p in parts[1:]) + + +def num(v): + return repr(float(v)) if isinstance(v, float) else str(v) + + +def doc(use): + return " /// " + use.replace("\n", " ") + + +def swift_source(t): + o = [] + add = o.append + add("// GENERATED by Design/build_tokens.py from Design/tokens.json. Do not edit by hand.") + add("import SwiftUI\n") + + add("public enum Palette {") + for n, c in t["color"].items(): + add(doc(c["use"])) + add(f" public static let {camel(n)} = Color.dynamic(light: 0x{c['light'][1:].upper()}, dark: 0x{c['dark'][1:].upper()})") + add("}\n") + + add("public enum Opacity {") + for n, v in t["opacity"].items(): + add(doc(v["use"])) + add(f" public static let {camel(n)}: Double = {num(v['value'])}") + add("}\n") + + add("public enum FontFamily: String {") + for n, f in t["font"].items(): + add(f" case {n.replace('family.', '')} = \"{f['value']}\"") + add("}\n") + + add("public enum Typography {") + for n, s in t["type"].items(): + add(doc(s["use"])) + add(f" public static let {camel(n)} = TextStyle(family: .{s['family']}, size: {num(s['size'])}, weight: {s['weight']}, " + f"tracking: {num(s['tracking'])}, line: {num(s['line'])}, caps: {'true' if s['caps'] else 'false'}, " + f"tabular: {'true' if s.get('tabular') else 'false'})") + add("}\n") + + add("public enum Segment {") + for n, v in t["segment"].items(): + add(doc(v["use"])) + add(f" public static let {camel(n)}: CGFloat = {num(v['value'])}") + add("}\n") + + add("public enum Space {") + for n, v in t["space"].items(): + add(f" public static let s{n}: CGFloat = {v}") + add("}\n") + + for enum, group in (("Layout", "layout"), ("Size", "size"), ("Radius", "radius"), ("Border", "border")): + add(f"public enum {enum} {{") + for n, v in t[group].items(): + add(doc(v["use"])) + add(f" public static let {camel(n)}: CGFloat = {num(v['value'])}") + add("}\n") + + add("public enum Shadows {") + for n, s in t["shadow"].items(): + add(doc(s["use"])) + add(f" public static let {camel(n)}: [ShadowLayer] = [") + for l in s["layers"]: + add(f" ShadowLayer(inset: {'true' if l['inset'] else 'false'}, x: {l['x']}, y: {l['y']}, blur: {l['blur']}, spread: {l['spread']}, " + f"hex: 0x{l['color'][1:].upper()}, alphaLight: {num(l['alpha']['light'])}, alphaDark: {num(l['alpha']['dark'])}),") + add(" ]") + if "translateY" in s: + add(f" public static let {camel(n)}Offset: CGFloat = {s['translateY']}") + add("}\n") + + add("public enum Texture {") + for n, v in t["texture"].items(): + add(f" public static let {camel(n)}GrainLight: Double = {num(v['grain']['light'])}") + add(f" public static let {camel(n)}GrainDark: Double = {num(v['grain']['dark'])}") + add("}\n") + + add("public enum MeterTokens {") + for n, v in t["meter"].items(): + val = v["value"] + if n == "vu.ticks": + add(doc(v["use"])) + add(f" public static let vuTicks: [Int] = [{', '.join(str(int(x)) for x in val.split())}]") + elif n == "led.segmentSize": + w, h = val.split("x") + add(doc(v["use"])) + add(f" public static let ledSegmentWidth: CGFloat = {w}") + add(f" public static let ledSegmentHeight: CGFloat = {h}") + elif n == "peak.holdMs": + add(doc(v["use"])) + add(f" public static let peakHoldSeconds: Double = {num(val / 1000)}") + elif n == "led.segments": + add(doc(v["use"])) + add(f" public static let ledSegments: Int = {val}") + else: + add(doc(v["use"])) + add(f" public static let {camel(n)}: Double = {num(val)}") + add("}\n") + + add("public enum Motion {") + for n, m in t["motion"].items(): + if n == "reducedMotion": + continue + add(doc(m["use"])) + if m.get("unit") == "ms": + add(f" public static let {camel(n)}: Double = {num(m['value'] / 1000)}") + elif m.get("unit") in ("s", "ratio", "Hz"): + add(f" public static let {camel(n)}: Double = {num(m['value'])}") + elif isinstance(m["value"], str) and m["value"].startswith("cubic-bezier"): + pts = [x.strip() for x in m["value"][m["value"].index("(") + 1:-1].split(",")] + add(f" public static let {camel(n)}: (Double, Double, Double, Double) = ({', '.join(pts)})") + add("}") + return "\n".join(o) + "\n" + + +swift_text = swift_source(tokens) +swift_dir = ROOT / "Sources" / "SstUI" +swift_path = swift_dir / "DesignTokens.swift" + +if "--check" in sys.argv: + in_sync = swift_path.exists() and swift_path.read_text() == swift_text + print("DesignTokens.swift is " + ("in sync with tokens.json" if in_sync else "OUT OF DATE: run python3 Design/build_tokens.py")) + sys.exit(0 if (in_sync and not failed) else 1) + +if swift_dir.exists(): + swift_path.write_text(swift_text) + print("wrote Sources/SstUI/DesignTokens.swift") +``` + +Run vanuit de repo-root: `python3 Design/apply_design_edits.py && rm Design/apply_design_edits.py Design/swift_block.txt` +Expected: `Design/ updated`; beide tijdelijke bestanden zijn weg. + +Run: `python3 Design/build_tokens.py` +Expected: tabel met contrastwaarden zonder `FAIL`, daarna `wrote Sources/SstUI/DesignTokens.swift` en `wrote docs/design/tokens.md and docs/design/tokens-preview.html`. + +- [ ] **Step 5: Hulpbestanden schrijven** + +`Sources/SstUI/DynamicColor.swift`: + +```swift +import AppKit +import SwiftUI + +extension NSColor { + convenience init(hex: UInt32) { + self.init( + srgbRed: CGFloat((hex >> 16) & 0xFF) / 255, + green: CGFloat((hex >> 8) & 0xFF) / 255, + blue: CGFloat(hex & 0xFF) / 255, + alpha: 1) + } +} + +extension Color { + /// A colour that switches between the Aluminium (light) and Graphite (dark) value with the appearance. + static func dynamic(light: UInt32, dark: UInt32) -> Color { + Color(nsColor: NSColor(name: nil) { appearance in + appearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua ? NSColor(hex: dark) : NSColor(hex: light) + }) + } + + /// The same colour in both finishes (shadow bases). + static func fixed(hex: UInt32) -> Color { + Color(nsColor: NSColor(hex: hex)) + } +} +``` + +`Sources/SstUI/TextStyle.swift`: + +```swift +import SwiftUI + +public struct TextStyle: Equatable { + public let family: FontFamily + public let size: CGFloat + public let weight: Int + /// In em. + public let tracking: CGFloat + public let line: CGFloat + public let caps: Bool + public let tabular: Bool + + public init(family: FontFamily, size: CGFloat, weight: Int, tracking: CGFloat, line: CGFloat, caps: Bool, tabular: Bool) { + self.family = family + self.size = size + self.weight = weight + self.tracking = tracking + self.line = line + self.caps = caps + self.tabular = tabular + } + + var fontWeight: Font.Weight { + switch weight { + case ..<450: .regular + case 450..<550: .medium + case 550..<650: .semibold + default: .bold + } + } + + public var font: Font { + let base: Font + switch family { + case .mono: base = .system(size: size, weight: fontWeight, design: .monospaced) + case .text: base = .system(size: size, weight: fontWeight) + case .chrome: base = .custom(family.rawValue, size: size).weight(fontWeight) + } + return tabular ? base.monospacedDigit() : base + } +} + +public extension View { + /// Font, tracking, line spacing and capitalisation from a token text style. + func textStyle(_ style: TextStyle) -> some View { + self.font(style.font) + .tracking(style.tracking * style.size) + .lineSpacing(max(0, style.line - style.size * 1.2)) + .textCase(style.caps ? .uppercase : nil) + } +} + +/// One layer of a layered shadow token. Inset layers are drawn inside the shape. +public struct ShadowLayer: Equatable { + public let inset: Bool + public let x: CGFloat + public let y: CGFloat + public let blur: CGFloat + public let spread: CGFloat + public let hex: UInt32 + public let alphaLight: Double + public let alphaDark: Double + + public init(inset: Bool, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat, hex: UInt32, alphaLight: Double, alphaDark: Double) { + self.inset = inset + self.x = x + self.y = y + self.blur = blur + self.spread = spread + self.hex = hex + self.alphaLight = alphaLight + self.alphaDark = alphaDark + } + + public func color(_ scheme: ColorScheme) -> Color { + Color.fixed(hex: hex).opacity(scheme == .dark ? alphaDark : alphaLight) + } +} +``` + +- [ ] **Step 6: Placeholder weg, bouwen en testen** + +Run: `rm Sources/SstUI/Placeholder.swift && swift build && swift test --filter TokenIntegrityTests` +Expected: build zonder fouten of waarschuwingen; beide tests PASS. + +- [ ] **Step 7: Preview nog steeds in orde (handmatig, kort)** + +Run: `open docs/design/tokens-preview.html` +Expected: de meter, teller en LED-balk bewegen als eerder; geen lege vlakken. (De meetwaarden komen nu uit de tokens.) + +- [ ] **Step 8: Commit** + +```bash +git add Package.swift Design Sources/SstUI Tests/SstUITests docs/design +git commit -m "feat: generate Swift design tokens from tokens.json and add SstUI base" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 2: Instrument-logica — `VUScale`, `NeedleSpring`, `SegmentGlyph`, `LEDBar` + +**Files:** +- Create: `Sources/SstUI/Instruments.swift` +- Test: `Tests/SstUITests/InstrumentsTests.swift` + +**Interfaces:** +- Consumes: `MeterTokens`, `Motion`, `Segment` uit Task 1. +- Produces: + - `VUScale.vu(fromDbfs:)`, `fraction(forVU:)`, `fraction(forDbfs:)`, `angle(forFraction:)` (radialen vanaf verticaal) + - `NeedleSpring(response:damping:position:)` met `step(target:dt:) -> Double` en `position` + - `SegmentID` (a…g), `SegmentGlyph.lit(for:) -> Set`, `SegmentGlyph.polygon(_:) -> [CGPoint]` + - `LEDBar.tone(forSegment:segments:)` (`.green/.amber/.red`), `LEDBar.litCount(forFraction:segments:)` + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstUITests/InstrumentsTests.swift`: + +```swift +import CoreGraphics +import Testing +@testable import SstUI + +struct VUScaleTests { + @Test func zeroVUIsMinusTwentyDbfs() { + #expect(VUScale.vu(fromDbfs: -20) == 0) + #expect(VUScale.vu(fromDbfs: -40) == -20) + } + + @Test func fractionSpansTheScale() { + #expect(VUScale.fraction(forVU: -20) == 0) + #expect(abs(VUScale.fraction(forVU: 3) - 1) < 1e-9) + #expect(abs(VUScale.fraction(forVU: 0) - 20.0 / 23.0) < 1e-9) + } + + @Test func fractionIsClampedAtBothEnds() { + #expect(VUScale.fraction(forVU: -60) == 0) + #expect(VUScale.fraction(forVU: 30) == 1.03) + #expect(VUScale.fraction(forDbfs: -120) == 0) + } + + @Test func angleIsSymmetricAroundVertical() { + #expect(abs(VUScale.angle(forFraction: 0) + VUScale.angle(forFraction: 1)) < 1e-9) + #expect(abs(VUScale.angle(forFraction: 0.5)) < 1e-9) + #expect(abs(VUScale.angle(forFraction: 1) - 45 * .pi / 180) < 1e-9) + } +} + +struct NeedleSpringTests { + func simulate(_ spring: NeedleSpring, seconds: Double, target: Double = 1) -> (final: Double, peak: Double) { + var spring = spring + var peak = 0.0 + let dt = 1.0 / 240 + for _ in 0.. 1.003) + #expect(result.peak < 1.03) + #expect(abs(result.final - 1) < 0.001) + } + + @Test func criticalDampingNeverOvershoots() { + let result = simulate(NeedleSpring(damping: 1), seconds: 1) + #expect(result.peak <= 1.000001) + } + + @Test func largeFrameStepsStayStable() { + var spring = NeedleSpring() + for _ in 0..<40 { spring.step(target: 1, dt: 0.05) } + #expect(abs(spring.position - 1) < 0.001) + } + + @Test func restsAtTheStartWithoutATarget() { + var spring = NeedleSpring() + for _ in 0..<60 { spring.step(target: 0, dt: 1.0 / 60) } + #expect(spring.position == 0) + } +} + +struct SegmentGlyphTests { + @Test func everyDigitHasASegmentPattern() { + for digit in "0123456789" { #expect(!SegmentGlyph.lit(for: digit).isEmpty) } + #expect(SegmentGlyph.lit(for: "8").count == 7) + #expect(SegmentGlyph.lit(for: "1") == [.b, .c]) + #expect(SegmentGlyph.lit(for: ":").isEmpty) + } + + @Test func polygonsStayInsideTheDigitBoxAndDiffer() { + var seen: [[CGPoint]] = [] + for id in SegmentID.allCases { + let polygon = SegmentGlyph.polygon(id) + #expect(polygon.count == 6) + for point in polygon { + #expect(point.x >= 0 && point.x <= Segment.digitWidth) + #expect(point.y >= 0 && point.y <= Segment.digitHeight) + } + #expect(!seen.contains(polygon)) + seen.append(polygon) + } + #expect(seen.count == 7) + } +} + +struct LEDBarTests { + @Test func tonesFollowTheEightThreeOneSplit() { + let tones = (0..<12).map { LEDBar.tone(forSegment: $0) } + #expect(tones.prefix(8).allSatisfy { $0 == .green }) + #expect(tones[8...10].allSatisfy { $0 == .amber }) + #expect(tones[11] == .red) + } + + @Test func litCountScalesAndClamps() { + #expect(LEDBar.litCount(forFraction: 0) == 0) + #expect(LEDBar.litCount(forFraction: 0.5) == 6) + #expect(LEDBar.litCount(forFraction: 1) == 12) + #expect(LEDBar.litCount(forFraction: 1.03) == 12) + #expect(LEDBar.litCount(forFraction: -1) == 0) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "VUScaleTests|NeedleSpringTests|SegmentGlyphTests|LEDBarTests"` +Expected: compilatiefouten (`cannot find 'VUScale' in scope`). + +- [ ] **Step 3: Implementatie** + +`Sources/SstUI/Instruments.swift`: + +```swift +import CoreGraphics +import Foundation + +/// dBFS → VU → needle position and angle, all from the meter tokens. +public enum VUScale { + public static func vu(fromDbfs dbfs: Double) -> Double { + dbfs - MeterTokens.vuReferenceDbfs + } + + /// 0 = bottom of the scale, 1 = top. The needle may swing slightly past the end (up to 1.03). + public static func fraction(forVU vu: Double) -> Double { + min(1.03, max(0, (vu - MeterTokens.vuMin) / (MeterTokens.vuMax - MeterTokens.vuMin))) + } + + public static func fraction(forDbfs dbfs: Double) -> Double { + fraction(forVU: vu(fromDbfs: dbfs)) + } + + /// Radians from vertical; negative is left of vertical. + public static func angle(forFraction fraction: Double) -> Double { + (-MeterTokens.vuArcDegrees / 2 + MeterTokens.vuArcDegrees * fraction) * .pi / 180 + } +} + +/// Second-order needle: reaches ~99 % in `response` seconds, slight overshoot for damping < 1. +public struct NeedleSpring: Equatable { + public private(set) var position: Double + public private(set) var velocity: Double = 0 + private let omega: Double + private let zeta: Double + + public init(response: Double = Motion.needleResponse, damping: Double = Motion.needleDamping, position: Double = 0) { + self.zeta = damping + self.omega = 4.6 / (damping * response) + self.position = position + } + + /// Advances the simulation by `dt` seconds in small fixed sub-steps (stable for any frame time). + @discardableResult + public mutating func step(target: Double, dt: Double) -> Double { + var remaining = max(0, dt) + let h = 1.0 / 240.0 + while remaining > 1e-9 { + let s = min(h, remaining) + let acceleration = omega * omega * (target - position) - 2 * zeta * omega * velocity + velocity += acceleration * s + position += velocity * s + remaining -= s + } + return position + } +} + +public enum SegmentID: CaseIterable, Hashable { + case a, b, c, d, e, f, g +} + +/// Which segments of a seven-segment digit are lit, and where each segment sits (from the segment tokens). +public enum SegmentGlyph { + static let map: [Character: Set] = [ + "0": [.a, .b, .c, .d, .e, .f], "1": [.b, .c], "2": [.a, .b, .d, .e, .g], "3": [.a, .b, .c, .d, .g], + "4": [.b, .c, .f, .g], "5": [.a, .c, .d, .f, .g], "6": [.a, .c, .d, .e, .f, .g], "7": [.a, .b, .c], + "8": Set(SegmentID.allCases), "9": [.a, .b, .c, .d, .f, .g], + ] + + public static func lit(for character: Character) -> Set { + map[character] ?? [] + } + + /// Hexagonal bar for one segment, inside a `Segment.digitWidth` × `Segment.digitHeight` box. + public static func polygon(_ id: SegmentID) -> [CGPoint] { + let width = Segment.digitWidth, height = Segment.digitHeight + let h = Segment.thickness / 2, gap = Segment.gap + let left = h, right = width - h, mid = height / 2 + + func horizontal(_ x0: CGFloat, _ x1: CGFloat, _ cy: CGFloat) -> [CGPoint] { + [CGPoint(x: x0, y: cy), CGPoint(x: x0 + h, y: cy - h), CGPoint(x: x1 - h, y: cy - h), + CGPoint(x: x1, y: cy), CGPoint(x: x1 - h, y: cy + h), CGPoint(x: x0 + h, y: cy + h)] + } + func vertical(_ cx: CGFloat, _ y0: CGFloat, _ y1: CGFloat) -> [CGPoint] { + [CGPoint(x: cx, y: y0), CGPoint(x: cx + h, y: y0 + h), CGPoint(x: cx + h, y: y1 - h), + CGPoint(x: cx, y: y1), CGPoint(x: cx - h, y: y1 - h), CGPoint(x: cx - h, y: y0 + h)] + } + let x0 = left + h + gap, x1 = right - h - gap + switch id { + case .a: return horizontal(x0, x1, h) + case .g: return horizontal(x0, x1, mid) + case .d: return horizontal(x0, x1, height - h) + case .f: return vertical(left, h + h + gap, mid - h - gap) + case .b: return vertical(right, h + h + gap, mid - h - gap) + case .e: return vertical(left, mid + h + gap, height - h - h - gap) + case .c: return vertical(right, mid + h + gap, height - h - h - gap) + } + } +} + +public enum LEDBar { + public enum Tone { case green, amber, red } + + /// The last segment is red, the three before it amber, the rest green. + public static func tone(forSegment index: Int, segments: Int = MeterTokens.ledSegments) -> Tone { + if index >= segments - 1 { return .red } + if index >= segments - 4 { return .amber } + return .green + } + + public static func litCount(forFraction fraction: Double, segments: Int = MeterTokens.ledSegments) -> Int { + Int((min(1, max(0, fraction)) * Double(segments)).rounded()) + } +} +``` + +- [ ] **Step 4: Draai alle tests** + +Run: `swift test --filter SstUITests` +Expected: 13 instrumenttests + 2 integriteitstests PASS. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstUI Tests/SstUITests +git commit -m "feat: add instrument logic (VU scale, needle spring, segment glyphs, LED bar)" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 3: Oppervlakken en materiaal + +**Files:** +- Create: `Sources/SstUI/Surfaces.swift` + +**Interfaces:** +- Consumes: `Palette`, `Shadows`, `Radius`, `Border`, `Texture`, `Typography` (Task 1); `ShadowLayer` (Task 1). +- Produces (SwiftUI-views, alle `public`): `PhysicalSurface(fill:cornerRadius:shadows:edge:)`, `PanelBackground()`, `WellBackground()`, `DisplayBackground()`, `PaperBackground()`, `SeamDivider()`, `SilkLabel(_:style:)`, `BrushedBackground()`. + +Tekenwerk wordt visueel getoetst in Task 6; hier moet het compileren en door de token-scan komen. + +- [ ] **Step 1: Implementatie** + +`Sources/SstUI/Surfaces.swift`: + +```swift +import AppKit +import SwiftUI + +/// A shape with a fill, layered drop shadows, layered inset shadows and a hairline edge: a physical part. +public struct PhysicalSurface: View { + private let fill: AnyShapeStyle + private let cornerRadius: CGFloat + private let shadows: [ShadowLayer] + private let edge: Color? + @Environment(\.colorScheme) private var scheme + + public init(fill: AnyShapeStyle, cornerRadius: CGFloat, shadows: [ShadowLayer], edge: Color? = Palette.lineSeamDark) { + self.fill = fill + self.cornerRadius = cornerRadius + self.shadows = shadows + self.edge = edge + } + + public var body: some View { + let shape = RoundedRectangle(cornerRadius: cornerRadius, style: .circular) + ZStack { + shadows.filter { !$0.inset }.reduce(AnyView(shape.fill(fill))) { view, layer in + AnyView(view.shadow(color: layer.color(scheme), radius: layer.blur / 2, x: layer.x, y: layer.y)) + } + ForEach(Array(shadows.filter(\.inset).enumerated()), id: \.offset) { _, layer in + InsetShadow(layer: layer, cornerRadius: cornerRadius) + } + if let edge { + shape.strokeBorder(edge, lineWidth: Border.hairline) + } + } + } +} + +/// A shadow cast inwards from the edge of a rounded rectangle. +struct InsetShadow: View { + let layer: ShadowLayer + let cornerRadius: CGFloat + @Environment(\.colorScheme) private var scheme + + var body: some View { + Canvas { context, size in + let rect = CGRect(origin: .zero, size: size) + context.clip(to: Path(roundedRect: rect, cornerRadius: cornerRadius)) + context.drawLayer { inner in + inner.addFilter(.blur(radius: layer.blur / 2)) + let hole = rect.insetBy(dx: layer.spread, dy: layer.spread).offsetBy(dx: layer.x, dy: layer.y) + var frame = Path(rect.insetBy(dx: -size.width, dy: -size.height)) + frame.addPath(Path(roundedRect: hole, cornerRadius: max(0, cornerRadius - layer.spread))) + inner.fill(frame, with: .color(layer.color(scheme)), style: FillStyle(eoFill: true)) + } + } + .allowsHitTesting(false) + } +} + +/// Faceplate of matt plastic, sitting above the chassis. +public struct PanelBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfacePanel), cornerRadius: Radius.none, shadows: Shadows.panel) + } +} + +/// A recessed area (input fields, display windows). +public struct WellBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfaceWell), cornerRadius: Radius.none, shadows: Shadows.well) + } +} + +/// Dark display glass for the counter. +public struct DisplayBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfaceDisplay), cornerRadius: Radius.none, shadows: Shadows.well) + } +} + +/// Cassette-label cream, for lists of transcripts and dictionary entries. +public struct PaperBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfacePaper), cornerRadius: Radius.none, shadows: Shadows.well) + } +} + +/// An engraved groove: a dark line with a light line below it. +public struct SeamDivider: View { + public init() {} + public var body: some View { + VStack(spacing: 0) { + Rectangle().fill(Palette.lineSeamDark).frame(height: Border.hairline) + Rectangle().fill(Palette.lineSeamLight).frame(height: Border.hairline) + } + .accessibilityHidden(true) + } +} + +/// Small capitals silkscreen label. +public struct SilkLabel: View { + private let text: String + private let style: TextStyle + + public init(_ text: String, style: TextStyle = Typography.label) { + self.text = text + self.style = style + } + + public var body: some View { + Text(text).textStyle(style).foregroundStyle(Palette.inkTertiary) + } +} + +/// Brushed aluminium: a vertical gradient with a horizontal grain. +public struct BrushedBackground: View { + @Environment(\.colorScheme) private var scheme + public init() {} + + public var body: some View { + ZStack { + LinearGradient(colors: [Palette.surfaceMetalHigh, Palette.surfaceMetalLow], startPoint: .top, endPoint: .bottom) + Image(nsImage: scheme == .dark ? BrushedTexture.dark : BrushedTexture.light) + .resizable(resizingMode: .tile) + } + } +} + +enum BrushedTexture { + private static let side = 256 + private static let gain = 2.2 // makes the token grain value visible as streak opacity + + static let light = make(grain: Texture.brushedGrainLight) + static let dark = make(grain: Texture.brushedGrainDark) + + private struct SplitMix64: RandomNumberGenerator { + var state: UInt64 + mutating func next() -> UInt64 { + state &+= 0x9E37_79B9_7F4A_7C15 + var z = state + z = (z ^ (z >> 30)) &* 0xBF58_476D_1CE4_E5B9 + z = (z ^ (z >> 27)) &* 0x94D0_49BB_1331_11EB + return z ^ (z >> 31) + } + } + + /// A seeded grain: every row has its own brightness and pixels vary only slightly, which gives fine horizontal streaks. + private static func make(grain: Double) -> NSImage { + var rng = SplitMix64(state: 7) + let rows = (0.. 0.5 ? 1.0 : 0.0 + let index = (y * side + x) * 4 + let premultiplied = UInt8(shade * alpha * 255) + pixels[index] = premultiplied + pixels[index + 1] = premultiplied + pixels[index + 2] = premultiplied + pixels[index + 3] = UInt8(alpha * 255) + } + } + let provider = CGDataProvider(data: Data(pixels) as CFData)! + let image = CGImage( + width: side, height: side, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: side * 4, + space: CGColorSpaceCreateDeviceRGB(), + bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue), + provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)! + return NSImage(cgImage: image, size: NSSize(width: side, height: side)) + } +} +``` + +- [ ] **Step 2: Bouwen en scannen** + +Run: `swift build && swift test --filter TokenIntegrityTests` +Expected: build zonder fouten of waarschuwingen; beide tests PASS (geen losse waarden). + +- [ ] **Step 3: Commit** + +```bash +git add Sources/SstUI +git commit -m "feat: add physical surfaces, brushed metal and silkscreen label" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 4: Toets en lampje + +**Files:** +- Create: `Sources/SstUI/Controls.swift` + +**Interfaces:** +- Consumes: `PhysicalSurface` (Task 3), tokens (Task 1). +- Produces: `KeyButtonStyle(kind: .standard | .record, latched: Bool)` (`ButtonStyle`) en `LampView(isOn:tint: .record | .amber, large:)`. + +- [ ] **Step 1: Implementatie** + +`Sources/SstUI/Controls.swift`: + +```swift +import SwiftUI + +/// A physical key. `latched` keeps it looking pressed (REC while recording). +public struct KeyButtonStyle: ButtonStyle { + public enum Kind { case standard, record } + + private let kind: Kind + private let latched: Bool + + public init(kind: Kind = .standard, latched: Bool = false) { + self.kind = kind + self.latched = latched + } + + public func makeBody(configuration: Configuration) -> some View { + KeyBody(configuration: configuration, kind: kind, latched: latched) + } + + private struct KeyBody: View { + let configuration: Configuration + let kind: Kind + let latched: Bool + @Environment(\.isEnabled) private var isEnabled + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + private var pressed: Bool { configuration.isPressed || latched } + + private var face: AnyShapeStyle { + switch (kind, pressed) { + case (.standard, false): + AnyShapeStyle(LinearGradient(colors: [Palette.surfaceKeyHigh, Palette.surfaceKeyLow], startPoint: .top, endPoint: .bottom)) + case (.standard, true): + AnyShapeStyle(Palette.surfaceKeyPressed) + case (.record, false): + AnyShapeStyle(Palette.accentRecord) + case (.record, true): + AnyShapeStyle(Palette.accentRecord.opacity(Opacity.pressedRecord)) + } + } + + var body: some View { + configuration.label + .textStyle(Typography.control) + .foregroundStyle(kind == .record ? Palette.inkInverse : Palette.inkPrimary) + .padding(.horizontal, Space.s4) + .frame( + minWidth: kind == .record ? Size.keyRecWidth : Size.keyMinWidth, + minHeight: kind == .record ? Size.keyRecHeight : Size.controlHeight) + .background(PhysicalSurface( + fill: face, cornerRadius: Radius.sm, + shadows: pressed ? Shadows.keyPressed : Shadows.keyRaised)) + .offset(y: pressed ? Shadows.keyPressedOffset : 0) + .opacity(isEnabled ? 1 : Opacity.disabled) + .animation( + reduceMotion ? nil : .timingCurve( + Motion.easingMechanical.0, Motion.easingMechanical.1, Motion.easingMechanical.2, Motion.easingMechanical.3, + duration: pressed ? Motion.keyPress : Motion.keyRelease), + value: pressed) + } + } +} + +/// A round indicator lamp. The record lamp has a small glow; text never does. +public struct LampView: View { + public enum Tint { case record, amber } + + private let isOn: Bool + private let tint: Tint + private let large: Bool + @Environment(\.colorScheme) private var scheme + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + public init(isOn: Bool, tint: Tint = .record, large: Bool = false) { + self.isOn = isOn + self.tint = tint + self.large = large + } + + private var color: Color { + switch (tint, isOn) { + case (.record, true): Palette.accentRecord + case (.record, false): Palette.accentRecordUnlit + case (.amber, true): Palette.levelAmber + case (.amber, false): Palette.levelAmberUnlit + } + } + + public var body: some View { + let diameter = large ? Size.lampLarge : Size.lampDiameter + let glow = Shadows.lampOn[0] + Circle() + .fill(color) + .overlay( + Circle().fill(RadialGradient( + colors: [Color.white.opacity(isOn ? Opacity.specularOn : Opacity.specularOff), .clear], + center: UnitPoint(x: 0.35, y: 0.3), startRadius: 0, endRadius: diameter * 0.45))) + .overlay(Circle().strokeBorder(Palette.lineSeamDark, lineWidth: Border.hairline)) + .frame(width: diameter, height: diameter) + .shadow( + color: isOn && tint == .record ? glow.color(scheme) : .clear, + radius: glow.blur / 2) + .animation( + reduceMotion ? nil : .easeOut(duration: isOn ? Motion.lampOn : Motion.lampOff), + value: isOn) + .accessibilityHidden(true) + } +} +``` + +- [ ] **Step 2: Bouwen en scannen** + +Run: `swift build && swift test --filter SstUITests` +Expected: build zonder fouten of waarschuwingen; alle SstUI-tests PASS. + +- [ ] **Step 3: Commit** + +```bash +git add Sources/SstUI +git commit -m "feat: add physical key button style and lamp" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 5: VU-meter, segmentteller en LED-balk + +**Files:** +- Create: `Sources/SstUI/VUMeterView.swift`, `Sources/SstUI/CounterAndLEDs.swift` + +**Interfaces:** +- Consumes: `VUScale`, `NeedleSpring`, `SegmentGlyph`, `LEDBar` (Task 2); `DisplayBackground` (Task 3); tokens. +- Produces: + - `VUMeterView(levelDbfs: Double, isActive: Bool, brand: String = "SST · FIELD RECORDER", initialFraction: Double = 0)` — alleen animatie terwijl `isActive` (en 1,5 s erna) + - `SegmentCounterView(_ text: String)` — cijfers, `:` en `.` + - `LEDBarView(fraction: Double)` + +- [ ] **Step 1: Implementatie** + +`Sources/SstUI/VUMeterView.swift`: + +```swift +import SwiftUI + +/// Analogue VU meter with a physical needle. Pass the RMS level in dBFS; 0 VU = `MeterTokens.vuReferenceDbfs`. +/// The needle only animates while `isActive` (and briefly after, to settle), so an idle meter costs nothing. +public struct VUMeterView: View { + private let levelDbfs: Double + private let isActive: Bool + private let brand: String + + @State private var needle: NeedleState + @State private var awake = false + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + private final class NeedleState { + var spring: NeedleSpring + var dampingUsed = Motion.needleDamping + var last: TimeInterval? + var peakUntil: TimeInterval = 0 + + init(position: Double) { + spring = NeedleSpring(position: position) + } + } + + /// - Parameter initialFraction: where the needle starts (0 = bottom of the scale); useful for previews and snapshots. + public init(levelDbfs: Double, isActive: Bool, brand: String = "SST · FIELD RECORDER", initialFraction: Double = 0) { + self.levelDbfs = levelDbfs + self.isActive = isActive + self.brand = brand + _needle = State(initialValue: NeedleState(position: initialFraction)) + } + + public var body: some View { + TimelineView(.animation(minimumInterval: 1 / Motion.needleUpdateHz, paused: !(isActive || awake))) { timeline in + Canvas { context, size in + draw(&context, size: size, now: timeline.date.timeIntervalSinceReferenceDate) + } + } + .aspectRatio(MeterTokens.vuWidth / MeterTokens.vuHeight, contentMode: .fit) + .background(Palette.surfaceMeterFace) + .overlay( + RoundedRectangle(cornerRadius: Radius.bezel, style: .circular) + .strokeBorder(Palette.lineSeamDark, lineWidth: Border.bezel)) + .clipShape(RoundedRectangle(cornerRadius: Radius.bezel, style: .circular)) + .onChange(of: isActive) { _, active in + if active { + awake = true + } else { + Task { + try? await Task.sleep(for: .seconds(1.5)) + awake = false + } + } + } + .accessibilityElement() + .accessibilityLabel("Niveau") + .accessibilityValue(spokenValue) + } + + /// Rounded to steps of 3 VU so VoiceOver is not flooded with changes. + private var spokenValue: String { + guard isActive else { return "stil" } + let vu = (VUScale.vu(fromDbfs: levelDbfs) / 3).rounded() * 3 + return "\(Int(vu)) VU" + } + + // MARK: drawing (design coordinates: MeterTokens.vuWidth × vuHeight) + + private func draw(_ context: inout GraphicsContext, size: CGSize, now: TimeInterval) { + let width = MeterTokens.vuWidth, height = MeterTokens.vuHeight + let scale = min(size.width / width, size.height / height) + context.translateBy(x: (size.width - width * scale) / 2, y: (size.height - height * scale) / 2) + context.scaleBy(x: scale, y: scale) + context.clip(to: Path(CGRect(x: 0, y: 0, width: width, height: height))) + + // physics + let damping = reduceMotion ? 1.0 : Motion.needleDamping + if needle.dampingUsed != damping { + needle.spring = NeedleSpring(damping: damping, position: needle.spring.position) + needle.dampingUsed = damping + } + let dt = needle.last.map { min(0.05, now - $0) } ?? 0 + needle.last = now + let target = isActive ? VUScale.fraction(forDbfs: levelDbfs) : 0 + let position = needle.spring.step(target: target, dt: dt) + let vuNow = MeterTokens.vuMin + (MeterTokens.vuMax - MeterTokens.vuMin) * position + if vuNow > 0 && isActive { needle.peakUntil = now + MeterTokens.peakHoldSeconds } + + let pivot = CGPoint(x: width / 2, y: height + MeterTokens.vuPivotOffset) + func point(_ radius: CGFloat, _ fraction: Double) -> CGPoint { + let angle = VUScale.angle(forFraction: fraction) + return CGPoint(x: pivot.x + radius * CGFloat(sin(angle)), y: pivot.y - radius * CGFloat(cos(angle))) + } + func arc(_ radius: CGFloat, from: Double, to: Double) -> Path { + var path = Path() + let steps = 48 + for step in 0...steps { + let p = point(radius, from + (to - from) * Double(step) / Double(steps)) + step == 0 ? path.move(to: p) : path.addLine(to: p) + } + return path + } + + let radius = MeterTokens.vuScaleRadius + let redFrom = VUScale.fraction(forVU: 0) + context.stroke(arc(radius, from: 0, to: redFrom), with: .color(Palette.inkOnMeter), lineWidth: Border.hairline) + context.stroke(arc(radius - Border.hairline, from: redFrom, to: 1), with: .color(Palette.accentRecord), lineWidth: MeterTokens.vuRedBandWidth) + + for vu in Int(MeterTokens.vuMin)...Int(MeterTokens.vuMax) { + let major = MeterTokens.vuTicks.contains(vu) + let fraction = VUScale.fraction(forVU: Double(vu)) + let color = vu > 0 ? Palette.accentRecord : Palette.inkOnMeter + var tick = Path() + tick.move(to: point(radius, fraction)) + tick.addLine(to: point(radius - (major ? MeterTokens.vuTickMajor : MeterTokens.vuTickMinor), fraction)) + context.stroke(tick, with: .color(color), lineWidth: Border.hairline) + if major { + let label = Text(vu > 0 ? "+\(vu)" : "\(vu)").font(Typography.labelSmall.font).foregroundStyle(color) + context.draw(label, at: point(radius - MeterTokens.vuLabelInset, fraction), anchor: .center) + } + } + + context.draw( + Text("VU").font(Typography.heading.font).tracking(Typography.heading.tracking * Typography.heading.size).foregroundStyle(Palette.inkOnMeter), + at: CGPoint(x: width / 2, y: height - Space.s6), anchor: .center) + context.draw( + Text(brand).font(Typography.labelSmall.font).foregroundStyle(Palette.inkOnMeter.opacity(Opacity.caption)), + at: CGPoint(x: Space.s4, y: height - Space.s3), anchor: .leading) + + // peak lamp + let lampCenter = CGPoint(x: width - Space.s6, y: Space.s6) + let lampRadius = Size.lampDiameter / 2 + let peakLit = now < needle.peakUntil + context.fill(Path(ellipseIn: CGRect(x: lampCenter.x - lampRadius, y: lampCenter.y - lampRadius, width: lampRadius * 2, height: lampRadius * 2)), + with: .color(peakLit ? Palette.accentRecord : Palette.accentRecordUnlit)) + context.stroke(Path(ellipseIn: CGRect(x: lampCenter.x - lampRadius, y: lampCenter.y - lampRadius, width: lampRadius * 2, height: lampRadius * 2)), + with: .color(Palette.inkOnMeter), lineWidth: Border.hairline) + context.draw( + Text("PEAK").font(Typography.labelSmall.font).foregroundStyle(Palette.inkOnMeter), + at: CGPoint(x: lampCenter.x, y: lampCenter.y + lampRadius + Space.s3), anchor: .center) + + // needle + var needlePath = Path() + needlePath.move(to: pivot) + needlePath.addLine(to: point(MeterTokens.vuNeedleLength, max(-0.02, min(1.05, position)))) + context.stroke(needlePath, with: .color(Palette.inkOnMeter), lineWidth: MeterTokens.vuNeedleWidth) + } +} +``` + +`Sources/SstUI/CounterAndLEDs.swift`: + +```swift +import SwiftUI + +/// Seven-segment counter on dark glass. Digits, ":" and "." are drawn; unlit segments stay faintly visible. +public struct SegmentCounterView: View { + private let text: String + + public init(_ text: String) { + self.text = text + } + + private var contentWidth: CGFloat { + text.reduce(0) { total, character in + total + (character.isNumber ? Segment.advance : Segment.colonWidth) + } + } + + public var body: some View { + Canvas { context, _ in + var x: CGFloat = 0 + for character in text { + if character.isNumber { + var origin = context + origin.translateBy(x: x, y: 0) + let lit = SegmentGlyph.lit(for: character) + for id in SegmentID.allCases { + let polygon = SegmentGlyph.polygon(id) + var path = Path() + path.addLines(polygon) + path.closeSubpath() + origin.fill(path, with: .color(lit.contains(id) ? Palette.levelAmber : Palette.displaySegmentUnlit)) + } + x += Segment.advance + } else { + let dot = Segment.thickness + let inset = (Segment.colonWidth - dot) / 2 + let ys: [CGFloat] = character == ":" + ? [Segment.digitHeight * 0.28, Segment.digitHeight * 0.64] + : [Segment.digitHeight - dot] + for y in ys { + context.fill(Path(CGRect(x: x + inset, y: y, width: dot, height: dot)), with: .color(Palette.levelAmber)) + } + x += Segment.colonWidth + } + } + } + .frame(width: contentWidth, height: Segment.digitHeight) + .padding(.horizontal, Space.s4) + .padding(.vertical, Space.s3) + .background(DisplayBackground()) + .accessibilityElement() + .accessibilityLabel("Tijd") + .accessibilityValue(text) + } +} + +/// Twelve-segment level bar: green, amber, then one red segment. +public struct LEDBarView: View { + private let fraction: Double + + public init(fraction: Double) { + self.fraction = fraction + } + + public var body: some View { + let lit = LEDBar.litCount(forFraction: fraction) + HStack(spacing: MeterTokens.ledGap) { + ForEach(0.. Color { + switch LEDBar.tone(forSegment: index) { + case .green: lit ? Palette.levelGreen : Palette.levelGreenUnlit + case .amber: lit ? Palette.levelAmber : Palette.levelAmberUnlit + case .red: lit ? Palette.accentRecord : Palette.accentRecordUnlit + } + } +} +``` + +- [ ] **Step 2: Bouwen en scannen** + +Run: `swift build && swift test --filter SstUITests` +Expected: build zonder fouten of waarschuwingen; alle SstUI-tests PASS. + +- [ ] **Step 3: Commit** + +```bash +git add Sources/SstUI +git commit -m "feat: add VU meter, segment counter and LED bar views" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 6: Galerij en visuele controle + +**Files:** +- Modify: `Package.swift` +- Create: `Sources/SstGallery/GalleryApp.swift` + +**Interfaces:** +- Consumes: alle componenten uit Task 2–5. +- Produces: executable `SstGallery` (venster met beide afwerkingen, bewegende meter) en `SstGallery --snapshot ` (rendert de afwerkingen naar een PNG en stopt). + +- [ ] **Step 1: Target toevoegen** + +In `Package.swift`, voeg bij `targets` toe: + +```swift + .executableTarget(name: "SstGallery", dependencies: ["SstUI"]), +``` + +- [ ] **Step 2: Implementatie** + +`Sources/SstGallery/GalleryApp.swift`: + +```swift +import SwiftUI +import SstUI + +@main +struct GalleryApp: App { + init() { + // `SstGallery --snapshot out.png` renders both finishes to a PNG and exits (for checking without a screen grab). + let args = CommandLine.arguments + guard let flag = args.firstIndex(of: "--snapshot"), args.count > flag + 1 else { return } + let path = args[flag + 1] + Task { @MainActor in + let view = HStack(spacing: 0) { + Finish(scheme: .light, title: "Aluminium", frozen: true) + Finish(scheme: .dark, title: "Graphite", frozen: true) + } + .frame(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) + let renderer = ImageRenderer(content: view) + renderer.scale = 2 + if let tiff = renderer.nsImage?.tiffRepresentation, let png = NSBitmapImageRep(data: tiff)?.representation(using: .png, properties: [:]) { + try? png.write(to: URL(fileURLWithPath: path)) + } + exit(0) + } + } + + var body: some Scene { + WindowGroup("Sst componenten") { + HStack(spacing: 0) { + Finish(scheme: .light, title: "Aluminium") + Finish(scheme: .dark, title: "Graphite") + } + .frame(minWidth: Layout.windowMinWidth, minHeight: Layout.windowMinHeight) + } + .defaultSize(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) + } +} + +/// Fake speech level so the meter, LEDs and counter can be judged in motion. +private func simulatedDbfs(at t: Double) -> Double { + if t.truncatingRemainder(dividingBy: 9) > 6.2 { return -90 } + let syllable = floor(t * 4.2) + let r = abs(sin(syllable * 12.9898) * 43758.5453).truncatingRemainder(dividingBy: 1) + return r > 0.12 ? -38 + r * 22 : -90 +} + +struct Finish: View { + let scheme: ColorScheme + let title: String + var frozen = false + @State private var recording = true + @State private var started = Date() + @State private var query = "" + + var body: some View { + Group { + if frozen { + content(elapsed: 12.3, dbfs: -26) + } else { + TimelineView(.animation(minimumInterval: 1.0 / 30.0)) { timeline in + let elapsed = timeline.date.timeIntervalSince(started) + let dbfs = recording ? simulatedDbfs(at: elapsed) : -120 + content(elapsed: recording ? elapsed : 0, dbfs: dbfs) + } + } + } + .environment(\.colorScheme, scheme) + } + + private func content(elapsed: Double, dbfs: Double) -> some View { + ZStack { + BrushedBackground() + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel("Afwerking · \(title)") + VStack(alignment: .leading, spacing: Layout.panelGap) { + HStack(alignment: .top, spacing: Layout.panelGap) { + VUMeterView(levelDbfs: dbfs, isActive: recording, initialFraction: frozen ? VUScale.fraction(forDbfs: dbfs) : 0) + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel("Tijd", style: Typography.labelSmall) + SegmentCounterView(Self.clock(elapsed)) + SilkLabel("Niveau", style: Typography.labelSmall) + LEDBarView(fraction: VUScale.fraction(forDbfs: dbfs)) + } + } + SeamDivider() + HStack(spacing: Layout.panelGap) { + Button { recording.toggle(); started = Date() } label: { + HStack { LampView(isOn: recording); Text("Rec") } + } + .buttonStyle(KeyButtonStyle(kind: .record, latched: recording)) + Button("Stop") { recording = false }.buttonStyle(KeyButtonStyle()) + Button("Kopieer") {}.buttonStyle(KeyButtonStyle()) + Button("Uit") {}.buttonStyle(KeyButtonStyle()).disabled(true) + Spacer() + LampView(isOn: recording, large: true) + LampView(isOn: true, tint: .amber) + } + } + .padding(Layout.panelPadding) + .background(PanelBackground()) + + VStack(alignment: .leading, spacing: 0) { + row("14:02:11", "Ik wil de meeting verplaatsen naar tomorrow morning.", selected: false) + row("14:03:40", "Hij stuurt het door naar Claude Code vandaag.", selected: true) + } + .background(PaperBackground()) + + HStack { + SilkLabel("Zoek") + Group { + if frozen { + Text("claude").textStyle(Typography.body).frame(maxWidth: .infinity, alignment: .leading) + } else { + TextField("", text: $query).textFieldStyle(.plain).textStyle(Typography.body) + } + } + .padding(Space.s3).background(WellBackground()) + } + } + .padding(Layout.panelPadding) + } + } + + private func row(_ time: String, _ text: String, selected: Bool) -> some View { + HStack(alignment: .firstTextBaseline, spacing: Space.s4) { + Text(time).textStyle(Typography.counterSmall).foregroundStyle(Palette.inkSecondary) + Text(text).textStyle(Typography.reading).foregroundStyle(Palette.inkPrimary) + Spacer() + } + .padding(.horizontal, Space.s4).padding(.vertical, Space.s3) + .frame(minHeight: Size.rowHeight) + .background(selected ? Palette.surfaceSelection : Color.clear) + } + + private static func clock(_ seconds: Double) -> String { + let total = Int(seconds * 10) + return String(format: "%02d:%02d.%d", (total / 600) % 100, (total / 10) % 60, total % 10) + } +} +``` + +- [ ] **Step 3: Bouwen en een momentopname maken** + +Run: `swift build && .build/debug/SstGallery --snapshot /tmp/sst-gallery.png && open /tmp/sst-gallery.png` +Expected: een PNG met links Aluminium en rechts Graphite. Controleer visueel: geborsteld metaal is fijn en subtiel (geen grove scanlijnen); de toetsen lijken verhoogd, de REC-toets is rood met een lampje; de meter heeft een wijzer tussen −7 en −5 VU en een rode band vanaf 0; de teller toont amber segmenten met gedoofde segmenten erachter; het papier heeft een krijtstreep-achtige gedempte selectie; beide afwerkingen zijn goed leesbaar. + +- [ ] **Step 4: Levende controle samen met de gebruiker** + +Run: `swift run SstGallery` +Laat de gebruiker beoordelen: +1. De wijzer reageert met een korte, rustige overschrijding (niet stuiterend) en valt terug naar links bij stilte. +2. De piek-LED op de meter gaat aan boven 0 VU en blijft ongeveer 1 s branden. +3. REC/STOP: de REC-toets blijft ingedrukt zolang er wordt "opgenomen"; de lamp gaat snel aan en dooft traag. +4. Klik op een toets: die zakt 1 pt en wordt donkerder. +5. Zet in Systeeminstellingen → Toegankelijkheid → Beeldscherm "Verminder beweging" aan en herstart de galerij: geen overschrijding meer van de wijzer. +Noteer eventuele opmerkingen; kleine visuele bijstellingen horen bij deze taak, nieuwe tokens eerst voorleggen. + +- [ ] **Step 5: Alle tests en commit** + +Run: `swift test` +Expected: alle tests PASS. + +```bash +git add Package.swift Sources/SstGallery +git commit -m "feat: add component gallery with snapshot mode" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +## Self-review + +- **Spec-dekking:** tokens → Swift met parity- en contrasttest (Task 1), geen losse waarden (scan, Task 1), dynamische Aluminium/Graphite-kleuren, componentenlijst uit de spec (`KeyButtonStyle`, `LampView`, `PanelBackground`, `WellBackground`, `SeamDivider`, `SilkLabel`, `VUMeterView`, `SegmentCounterView`, `LEDBarView`, `PaperList` als `PaperBackground` + rijen in fase 5), geborstelde textuur gecachet (`BrushedTexture.light/dark` zijn statics), meter 60 Hz alleen actief, VoiceOver-labels, "Verminder beweging". `PaperList` zelf (de lijstweergave) komt in fase 5; hier alleen de achtergrond. +- **Placeholders:** geen; alle code is overgenomen uit een gecompileerde en geteste versie. +- **Typeconsistentie:** `Shadows.keyRaised/keyPressed/well/panel/lampOn`, `Shadows.keyPressedOffset`, `Motion.easingMechanical` (tuple), `MeterTokens.vu*`, `Opacity.pressedRecord/specularOn/specularOff/caption` zijn de namen die de generator uit `tokens.json` maakt en die de componenten gebruiken. +- **Risico's:** `NSSpellChecker`/AppKit-afhankelijkheid zit niet in deze fase. Tekenwerk is alleen visueel te beoordelen (Task 6 stap 3–4). `ImageRenderer` toont geen native tekstvelden; de galerij vervangt het zoekveld daarom in snapshotmodus. From c815e07b258c14096af6e2020f48f096599dd7f1 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:50:28 +0200 Subject: [PATCH 06/43] docs: add app plans 4 (shell), 5 (main window) and overview; update spec with verified model names Co-Authored-By: Claude Sonnet 5.5 --- .../plans/2026-10-01-app-0-overview.md | 38 + .../plans/2026-10-01-app-1-engine-hooks.md | 1 + .../plans/2026-10-01-app-4-shell.md | 1874 +++++++++++++++++ .../plans/2026-10-01-app-5-main-window.md | 1443 +++++++++++++ .../specs/2026-10-01-macos-app-design.md | 2 +- 5 files changed, 3357 insertions(+), 1 deletion(-) create mode 100644 docs/superpowers/plans/2026-10-01-app-0-overview.md create mode 100644 docs/superpowers/plans/2026-10-01-app-4-shell.md create mode 100644 docs/superpowers/plans/2026-10-01-app-5-main-window.md diff --git a/docs/superpowers/plans/2026-10-01-app-0-overview.md b/docs/superpowers/plans/2026-10-01-app-0-overview.md new file mode 100644 index 0000000..7b7650c --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-app-0-overview.md @@ -0,0 +1,38 @@ +# Sst als volwaardige Mac-app — overzicht van de uitvoeringsplannen + +**Spec:** `docs/superpowers/specs/2026-10-01-macos-app-design.md` · **Tokens:** `docs/design/tokens.md` (bron: `Design/tokens.json`) + +De spec is opgesplitst in vijf plannen. Elk levert op zichzelf werkende, geteste software op en bouwt voort op de vorige. Voer ze **in deze volgorde** uit, op één branch (`feat/macos-app`), bij voorkeur met `superpowers:subagent-driven-development`. + +| # | Plan | Levert | Tests daarna (per testtarget) | +|---|---|---|---| +| 1 | `2026-10-01-app-1-engine-hooks.md` | vier additieve raakpunten in de engine: woordenlijst voor de transcriber, niveau-uitvoer, correctie + resultaat-hook, instelbare sneltoets | SstCoreTests 41 · SstWhisperTests 4 | +| 2 | `2026-10-01-app-2-data.md` | `SstData`: woordenboekbestand, correctieronde, waarschuwingen, voorrangslijst, geschiedenis met zoeken | + SstDataTests 54 | +| 3 | `2026-10-01-app-3-ui-kit.md` | tokens als Swift (gegenereerd), componenten, galerij | + SstUITests 15 | +| 4 | `2026-10-01-app-4-shell.md` | echte Mac-app: Dock, menu's, Instellingen, menubalkitem, icoon, ondertekende bundel | SstCoreTests 48 · SstDataTests 56 · SstWhisperTests 11 | +| 5 | `2026-10-01-app-5-main-window.md` | hoofdvenster: deck, zijbalk, geschiedenis, woordenboek | SstDataTests 73 (totaal 147 tests) | + +## Hoe het plan is gecontroleerd + +De code in de plannen is niet geraden: elk plan is samengesteld uit bestanden die in een wegwerp-kopie van de repo zijn gecompileerd en getest (alle fasen samen: 147 tests groen, app bouwt, snapshots van het venster bekeken in beide afwerkingen, bundel gebouwd en ondertekend). Bewerkingen aan bestaande bestanden zijn scripts met asserts die bewezen dezelfde eindtoestand opleveren. Onderweg zijn zo ook fouten in de eerste versie van de plannen gevonden en hersteld (ontbrekende `import`, een te strenge "gewoon woord"-regel, een ontbrekend omgevingsobject, een niet-bestaand-map-bij-voorbeeldgegevens). + +## Gedeelde namen (de interface tussen de plannen) + +| Waar | Naam | +|---|---| +| `SstCore` | `Transcriber.transcribe(_:vocabulary:)`, `AppliedCorrection`, `CorrectionResult`, `TextCorrecting`, `DictationResult`, `DictationController(corrector:vocabularyProvider:)`, `onResult`, `HotkeyBinding`, `AudioRecorder.onLevel`, `AudioLevel`, `GatedInserter`, `SwappableTranscriber`, `LevelSource` | +| `SstData` | `DictionaryFile`, `DictionaryEntry`, `CorrectionEngine`, `EntryWarnings`, `VocabularyBuilder`, `HistoryStore`, `HistorySearch`, `DictionaryStore`, `DictionaryCorrector`, `AppSettings`, `HistoryViewModel`, `DictionaryViewModel`, `CorrectionMarkup`, `StopwatchFormat` | +| `SstWhisper` | `WhisperKitTranscriber(model:storage:)`, `ModelStorage`, `ModelCatalog`, `ModelOption` | +| `SstUI` | `Palette`, `Space`, `Radius`, `Border`, `Size`, `Layout`, `Opacity`, `Typography`, `Shadows`, `Motion`, `MeterTokens`, `Segment`; `PhysicalSurface`, `PanelBackground`, `WellBackground`, `DisplayBackground`, `PaperBackground`, `SeamDivider`, `SilkLabel`, `BrushedBackground`, `KeyButtonStyle`, `LampView`, `RockerToggleStyle`, `VUMeterView`, `SegmentCounterView`, `LEDBarView` | +| `Sst` | `AppModel`, `Strings`, `AppIdentity`, `PermissionMonitor`, scenes en views | + +## Uitvoeringsadvies + +- Taken met volledige code in het plan (fase 1–3, grote delen van 4 en 5) zijn transcriptie plus testen: een goedkoop model volstaat voor de implementer, een standaardmodel voor de reviewer. +- Visuele taken (fase 3 taak 6, fase 4 taak 7–8, fase 5 taak 3–6) hebben de gebruiker nodig voor het oordeel; de handmatige acceptatietaken zijn daarom bewust niet voor subagents. +- Fase 1 taak 5 stap 6 en fase 3 taak 6 vragen om een microfoon, Fn-toets of het scherm van de gebruiker. +- Na fase 4 moet het oude ad-hoc ondertekende `Sst` eenmalig zijn rechten opnieuw krijgen (`tccutil reset …`, door de gebruiker uit te voeren; staat in het plan). + +## Bewust buiten scope (zie spec) + +Overlay-pill, AI-cleanup, streaming, meerdere woordenboeken, import/export van de geschiedenis, notarisatie, DMG, updates, automatisch starten bij inloggen, toggle-modus, definitieve naam en merk. diff --git a/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md b/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md index 9716a51..484720b 100644 --- a/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md +++ b/docs/superpowers/plans/2026-10-01-app-1-engine-hooks.md @@ -62,6 +62,7 @@ Tests/SstWhisperTests/PromptTokensTests.swift (nieuw) `Tests/SstCoreTests/TranscriberVocabularyTests.swift`: ```swift +import Foundation import Testing @testable import SstCore diff --git a/docs/superpowers/plans/2026-10-01-app-4-shell.md b/docs/superpowers/plans/2026-10-01-app-4-shell.md new file mode 100644 index 0000000..48fd6dc --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-app-4-shell.md @@ -0,0 +1,1874 @@ +# App fase 4 — App-shell Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Van menubalk-hulpprogramma naar een echte Mac-app: Dock-icoon, app-menu, hoofdvenster (voorlopig een schil), Instellingen (⌘,) met sneltoets, model en privacy, een secundair menubalkitem, en een dubbelklikbaar `Sst.app` dat ondertekend is met jouw certificaat en in Programma's kan staan. De dicteer-keten gebruikt nu ook het woordenboek en bewaart de geschiedenis. + +**Architecture:** `AppModel` bezit alle services (instellingen, rechten, modelbeheer, woordenboek, geschiedenis, controller, sneltoets) en is de enige brug naar SwiftUI. Testbare lijmcode (`GatedInserter`, `SwappableTranscriber`, `LevelSource`, `AppSettings`, `ModelStorage`) staat in `SstCore`, `SstData` en `SstWhisper`. De views gebruiken alleen tokens; de token-scan uit fase 3 bewaakt dat ook voor `Sources/Sst`. + +**Tech Stack:** Swift 6.4 (language mode 5), SwiftUI (`Window`, `Settings`, `MenuBarExtra`, `Commands`), AppKit, Combine, WhisperKit, `codesign`/`iconutil`. + +**Spec:** `docs/superpowers/specs/2026-10-01-macos-app-design.md` (secties "App-shell", "Instellingen", "Rechten en eerste start") + +**Voorwaarde:** fase 1, 2 en 3 zijn afgerond (`DictationController` met corrector/`onResult`, `HotkeyBinding`, `SstData`, `SstUI` met tokens en componenten). + +## Global Constraints + +- Echte app: Dock-icoon, geen `LSUIElement`; bundle-id blijft `local.sst.app`; niet gesandboxed. +- Sluit je het hoofdvenster, dan draait de app door (Dock-icoon en menubalkitem blijven; dicteren werkt). ⌘Q sluit af. Klik op het Dock-icoon opent het venster weer. +- Menubalkitem is secundair: statusregel, sneltoets, "Open Sst", "Instellingen…", "Stop Sst". Geen geschiedenis, geen meter. +- Sneltoetsen alleen: Fn (standaard), Rechter Option, Rechter Command, Rechter Control, Linker Control; alleen vasthouden. Vastleggen door erop te drukken. +- Modellen (exacte WhisperKit-varianten, groottes gemeten bij Hugging Face): `large-v3-v20240930_turbo` (standaard, 1638 MB), `large-v3-v20240930_turbo_632MB` (646 MB), `small` (486 MB). Wisselen laadt het nieuwe model en zet dicteren uit tot het klaar is; mislukt het, dan blijft het vorige actief. +- Tekst wordt niet geplakt wanneer Sst zelf voorop staat of de opname via het venster/⌥⌘R is gestart; hij gaat dan alleen naar de geschiedenis. +- Rechten worden opnieuw gelezen zodra de app actief wordt en elke 2 s zolang een weergave met de lampjes zichtbaar is. +- Ondertekening met de eerste "Apple Development"-identiteit (of `SST_SIGN_IDENTITY`); geen identiteit in de repo; zonder identiteit ad-hoc met een duidelijke waarschuwing. +- UI-teksten Nederlands, allemaal in `Strings.swift`; alleen tokens in views. +- Elke commit eindigt met de trailer `Co-Authored-By: Claude Sonnet 5.5 ` (tweede `-m`). + +## Bestandsstructuur + +``` +Package.swift (wijzig) Sst-afhankelijkheden, SstIcon +Sources/SstCore/AppGlue.swift (nieuw) GatedInserter, SwappableTranscriber, LevelSource +Sources/SstData/AppSettings.swift (nieuw) +Sources/SstWhisper/ModelStorage.swift (nieuw) ModelOption, ModelCatalog, ModelStorage +Sources/SstWhisper/WhisperKitTranscriber.swift (vervang) gebruikt ModelStorage +Sources/SstUI/Controls.swift (aanvullen) RockerToggleStyle +Sources/Sst/AppIdentity.swift, Strings.swift, PermissionMonitor.swift, AppModel.swift (nieuw) +Sources/Sst/SstApp.swift, MainWindow.swift, AppCommands.swift, MenuBarContent.swift, SettingsView.swift (nieuw) +Sources/Sst/AppModel.swift (oud), MenuContent.swift (verwijderd) +Sources/SstIcon/IconMaker.swift (nieuw) rendert het icoon uit de tokens +scripts/make-icon.sh, scripts/make-app.sh (nieuw/vervang) +Design/AppIcon.icns (gegenereerd, gecommit) +Tests/SstCoreTests/AppGlueTests.swift, Tests/SstDataTests/AppSettingsTests.swift, +Tests/SstWhisperTests/ModelStorageTests.swift (nieuw) +``` + +--- + +### Task 1: Lijmcode in `SstCore` + +**Files:** +- Create: `Sources/SstCore/AppGlue.swift` +- Test: `Tests/SstCoreTests/AppGlueTests.swift` + +**Interfaces:** +- Produces: + - `GatedInserter(inner: TextInserting, shouldInsert: @escaping () -> Bool)` — vraagt `shouldInsert()` op het moment van invoegen + - `SwappableTranscriber(_ initial: Transcriber? = nil)` met `swap(to:)`, `hasTranscriber`, beide `transcribe`-varianten; werpt `SwapError.noTranscriber` zonder model + - `LevelSource` met `dbfs`, `update(_:)`, `reset()` (thread-safe; start op `AudioLevel.silenceFloorDbfs`) + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstCoreTests/AppGlueTests.swift`: + +```swift +import Foundation +import Testing +@testable import SstCore + +struct GatedInserterTests { + @Test func insertsWhenAllowed() async throws { + let inner = FakeInserter() + try await GatedInserter(inner: inner, shouldInsert: { true }).insert("hallo") + #expect(inner.inserted == ["hallo"]) + } + + @Test func staysSilentWhenNotAllowed() async throws { + let inner = FakeInserter() + try await GatedInserter(inner: inner, shouldInsert: { false }).insert("hallo") + #expect(inner.inserted.isEmpty) + } + + @Test func askedAtInsertionTimeNotAtCreation() async throws { + let inner = FakeInserter() + var allowed = false + let gated = GatedInserter(inner: inner, shouldInsert: { allowed }) + allowed = true + try await gated.insert("nu wel") + #expect(inner.inserted == ["nu wel"]) + } +} + +struct SwappableTranscriberTests { + @Test func throwsWithoutATranscriber() async { + let swappable = SwappableTranscriber() + #expect(!swappable.hasTranscriber) + await #expect(throws: SwappableTranscriber.SwapError.self) { + _ = try await swappable.transcribe([0]) + } + } + + @Test func delegatesIncludingTheVocabulary() async throws { + let inner = FakeVocabularyTranscriber() + inner.result = "klaar" + let swappable = SwappableTranscriber(inner) + #expect(try await swappable.transcribe([0], vocabulary: ["Anthropic"]) == "klaar") + #expect(inner.vocabularies == [["Anthropic"]]) + #expect(try await swappable.transcribe([0]) == "klaar") + #expect(inner.vocabularies == [["Anthropic"], []]) + } + + @Test func swapReplacesTheTranscriber() async throws { + let first = FakeVocabularyTranscriber(), second = FakeVocabularyTranscriber() + first.result = "een"; second.result = "twee" + let swappable = SwappableTranscriber(first) + #expect(try await swappable.transcribe([0]) == "een") + swappable.swap(to: second) + #expect(try await swappable.transcribe([0]) == "twee") + swappable.swap(to: nil) + #expect(!swappable.hasTranscriber) + } +} + +struct LevelSourceTests { + @Test func startsSilentAndKeepsTheLatestValue() { + let source = LevelSource() + #expect(source.dbfs == AudioLevel.silenceFloorDbfs) + source.update(-23) + source.update(-18) + #expect(source.dbfs == -18) + source.reset() + #expect(source.dbfs == AudioLevel.silenceFloorDbfs) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "GatedInserterTests|SwappableTranscriberTests|LevelSourceTests"` +Expected: compilatiefouten (`cannot find 'GatedInserter' in scope`). + +- [ ] **Step 3: Implementatie** + +`Sources/SstCore/AppGlue.swift`: + +```swift +import Foundation + +/// Inserts text only while `shouldInsert()` says so (not when Sst itself is in front, nor for window recordings). +public struct GatedInserter: TextInserting { + private let inner: TextInserting + private let shouldInsert: () -> Bool + + public init(inner: TextInserting, shouldInsert: @escaping () -> Bool) { + self.inner = inner + self.shouldInsert = shouldInsert + } + + public func insert(_ text: String) async throws { + guard shouldInsert() else { return } + try await inner.insert(text) + } +} + +/// Lets the app replace the speech model (for example after a model switch) without rebuilding the controller. +public final class SwappableTranscriber: Transcriber, @unchecked Sendable { + public enum SwapError: Error, LocalizedError { + case noTranscriber + + public var errorDescription: String? { "Spraakmodel is nog niet geladen." } + } + + private let lock = NSLock() + private var current: Transcriber? + + public init(_ initial: Transcriber? = nil) { + current = initial + } + + public func swap(to transcriber: Transcriber?) { + lock.withLock { current = transcriber } + } + + public var hasTranscriber: Bool { + lock.withLock { current != nil } + } + + public func transcribe(_ samples: [Float]) async throws -> String { + try await transcribe(samples, vocabulary: []) + } + + public func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + guard let transcriber = lock.withLock({ current }) else { throw SwapError.noTranscriber } + return try await transcriber.transcribe(samples, vocabulary: vocabulary) + } +} + +/// The latest input level, written from the audio thread and read by the meter once per frame. +public final class LevelSource: @unchecked Sendable { + private let lock = NSLock() + private var value = AudioLevel.silenceFloorDbfs + + public init() {} + + public var dbfs: Float { lock.withLock { value } } + + public func update(_ dbfs: Float) { + lock.withLock { value = dbfs } + } + + public func reset() { + update(AudioLevel.silenceFloorDbfs) + } +} +``` + +- [ ] **Step 4: Draai de tests** + +Run: `swift test --filter "GatedInserterTests|SwappableTranscriberTests|LevelSourceTests"` +Expected: 7 tests PASS. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstCore Tests/SstCoreTests +git commit -m "feat: add GatedInserter, SwappableTranscriber and LevelSource" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 2: `AppSettings` + +**Files:** +- Create: `Sources/SstData/AppSettings.swift` +- Test: `Tests/SstDataTests/AppSettingsTests.swift` + +**Interfaces:** +- Produces: `final class AppSettings: ObservableObject` met `init(defaults: UserDefaults = .standard)`, `@Published` `hotkeyID` ("fn"), `modelVariant` ("large-v3-v20240930_turbo"), `keepHistory` (true), `usePriming` (true), `useCorrection` (true); elke wijziging wordt direct in `UserDefaults` bewaard. `AppSettings.defaultModelVariant` is bewust gelijk aan `ModelCatalog.defaultVariant` (SstData kent SstWhisper niet). + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstDataTests/AppSettingsTests.swift`: + +```swift +import Foundation +import Testing +@testable import SstData + +struct AppSettingsTests { + func freshDefaults() -> UserDefaults { + let name = "sst-tests-\(UUID().uuidString)" + let defaults = UserDefaults(suiteName: name)! + defaults.removePersistentDomain(forName: name) + return defaults + } + + @Test func defaultsMatchTheSpec() { + let settings = AppSettings(defaults: freshDefaults()) + #expect(settings.hotkeyID == "fn") + #expect(settings.modelVariant == "large-v3-v20240930_turbo") + #expect(settings.keepHistory) + #expect(settings.usePriming) + #expect(settings.useCorrection) + } + + @Test func changesArePersisted() { + let defaults = freshDefaults() + let settings = AppSettings(defaults: defaults) + settings.hotkeyID = "rightOption" + settings.modelVariant = "small" + settings.keepHistory = false + settings.usePriming = false + settings.useCorrection = false + + let reloaded = AppSettings(defaults: defaults) + #expect(reloaded.hotkeyID == "rightOption") + #expect(reloaded.modelVariant == "small") + #expect(!reloaded.keepHistory) + #expect(!reloaded.usePriming) + #expect(!reloaded.useCorrection) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter AppSettingsTests` +Expected: compilatiefout `cannot find 'AppSettings' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstData/AppSettings.swift`: + +```swift +import Combine +import Foundation + +/// User preferences, persisted in UserDefaults as soon as a value changes. +public final class AppSettings: ObservableObject { + public static let defaultModelVariant = "large-v3-v20240930_turbo" + + enum Key { + static let hotkey = "hotkeyID" + static let model = "modelVariant" + static let keepHistory = "keepHistory" + static let usePriming = "usePriming" + static let useCorrection = "useCorrection" + } + + private let defaults: UserDefaults + + /// `HotkeyBinding.id` of the key that has to be held. + @Published public var hotkeyID: String { + didSet { defaults.set(hotkeyID, forKey: Key.hotkey) } + } + @Published public var modelVariant: String { + didSet { defaults.set(modelVariant, forKey: Key.model) } + } + @Published public var keepHistory: Bool { + didSet { defaults.set(keepHistory, forKey: Key.keepHistory) } + } + /// Give the speech engine the dictionary words as context. + @Published public var usePriming: Bool { + didSet { defaults.set(usePriming, forKey: Key.usePriming) } + } + /// Apply the dictionary corrections after transcription. + @Published public var useCorrection: Bool { + didSet { defaults.set(useCorrection, forKey: Key.useCorrection) } + } + + public init(defaults: UserDefaults = .standard) { + self.defaults = defaults + hotkeyID = defaults.string(forKey: Key.hotkey) ?? "fn" + modelVariant = defaults.string(forKey: Key.model) ?? Self.defaultModelVariant + keepHistory = defaults.object(forKey: Key.keepHistory) as? Bool ?? true + usePriming = defaults.object(forKey: Key.usePriming) as? Bool ?? true + useCorrection = defaults.object(forKey: Key.useCorrection) as? Bool ?? true + } +} +``` + +- [ ] **Step 4: Draai de tests** + +Run: `swift test --filter AppSettingsTests` +Expected: 2 tests PASS. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstData Tests/SstDataTests +git commit -m "feat: add AppSettings" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 3: `ModelStorage`, `ModelCatalog` en de transcriber + +**Files:** +- Create: `Sources/SstWhisper/ModelStorage.swift` +- Replace: `Sources/SstWhisper/WhisperKitTranscriber.swift` +- Test: `Tests/SstWhisperTests/ModelStorageTests.swift` + +**Interfaces:** +- Consumes: bestaande `WhisperKitTranscriber` (fase 1: `transcribe(_:vocabulary:)`, `promptTokens`). +- Produces: + - `ModelOption` (`id`, `title`, `detail`, `approxSizeMB`), `ModelCatalog.all`, `ModelCatalog.defaultVariant` + - `ModelStorage(base:)` met `base`, `folder(for:) -> URL?`, `isInstalled(_:)`, `delete(_:) throws`, `defaultBase` (`~/Library/Application Support/Sst/models`); varianten worden exact gematcht (`openai_whisper-`) + - `WhisperKitTranscriber(model:storage:)` met `variant`, `load()`, `onProgress` + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstWhisperTests/ModelStorageTests.swift`: + +```swift +import Foundation +import Testing +@testable import SstWhisper + +struct ModelStorageTests { + func makeStorage() -> ModelStorage { + ModelStorage(base: FileManager.default.temporaryDirectory.appending(path: "sst-models-\(UUID().uuidString)")) + } + + /// Creates `//.mlmodelc/coremldata.bin` for the given models. + func install(_ storage: ModelStorage, folder: String, models: [String] = ModelStorage.requiredModels) throws { + for model in models { + let dir = storage.repoDirectory.appending(path: "\(folder)/\(model).mlmodelc", directoryHint: .isDirectory) + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + try Data([1]).write(to: dir.appending(path: "coremldata.bin")) + } + } + + @Test func emptyStorageHasNothingInstalled() { + let storage = makeStorage() + #expect(storage.folder(for: "small") == nil) + #expect(!storage.isInstalled("small")) + } + + @Test func completeInstallIsFound() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-large-v3-v20240930_turbo") + #expect(storage.isInstalled("large-v3-v20240930_turbo")) + #expect(storage.folder(for: "large-v3-v20240930_turbo")?.lastPathComponent == "openai_whisper-large-v3-v20240930_turbo") + } + + @Test func incompleteInstallDoesNotCount() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-small", models: ["MelSpectrogram", "AudioEncoder"]) + #expect(!storage.isInstalled("small")) + } + + @Test func variantsAreMatchedExactly() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-large-v3-v20240930_turbo_632MB") + #expect(!storage.isInstalled("large-v3-v20240930_turbo")) + #expect(storage.isInstalled("large-v3-v20240930_turbo_632MB")) + } + + @Test func deleteRemovesOnlyThatVariant() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-small") + try install(storage, folder: "openai_whisper-base") + try storage.delete("small") + #expect(!storage.isInstalled("small")) + #expect(storage.isInstalled("base")) + } + + @Test func deletingSomethingNotInstalledIsHarmless() throws { + try makeStorage().delete("small") + } + + @Test func catalogStartsWithTheDefaultAndHasUniqueIds() { + #expect(ModelCatalog.all.first?.id == ModelCatalog.defaultVariant) + #expect(Set(ModelCatalog.all.map(\.id)).count == ModelCatalog.all.count) + #expect(ModelCatalog.all.allSatisfy { $0.approxSizeMB > 0 }) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter ModelStorageTests` +Expected: compilatiefout `cannot find 'ModelStorage' in scope`. + +- [ ] **Step 3: Implementatie** + +`Sources/SstWhisper/ModelStorage.swift`: + +```swift +import Foundation + +/// A speech model the user can pick in Settings. +public struct ModelOption: Identifiable, Equatable, Sendable { + public let id: String // WhisperKit variant name + public let title: String + public let detail: String + public let approxSizeMB: Int + + public init(id: String, title: String, detail: String, approxSizeMB: Int) { + self.id = id + self.title = title + self.detail = detail + self.approxSizeMB = approxSizeMB + } +} + +public enum ModelCatalog { + public static let defaultVariant = "large-v3-v20240930_turbo" + + /// Sizes measured from the Hugging Face repository `argmaxinc/whisperkit-coreml`. + public static let all: [ModelOption] = [ + ModelOption(id: defaultVariant, title: "Whisper Large v3 Turbo", detail: "Aanbevolen · nauwkeurigst", approxSizeMB: 1638), + ModelOption(id: "large-v3-v20240930_turbo_632MB", title: "Whisper Large v3 Turbo (compact)", detail: "Kleiner, iets minder nauwkeurig", approxSizeMB: 646), + ModelOption(id: "small", title: "Whisper Small", detail: "Snel en licht, minder nauwkeurig", approxSizeMB: 486), + ] +} + +/// Where models live on disk: `/models/argmaxinc/whisperkit-coreml/openai_whisper-`. +public struct ModelStorage: Sendable { + public static let repo = "argmaxinc/whisperkit-coreml" + public static let requiredModels = ["MelSpectrogram", "AudioEncoder", "TextDecoder"] + + public let base: URL + + public init(base: URL = ModelStorage.defaultBase) { + self.base = base + } + + /// Application Support, so models are neither in ~/Documents (privacy prompt, iCloud) nor re-fetched. + public static var defaultBase: URL { + FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + .appending(path: "Sst/models", directoryHint: .isDirectory) + } + + var repoDirectory: URL { + base.appending(path: "models/\(Self.repo)", directoryHint: .isDirectory) + } + + private func folders(for variant: String) -> [URL] { + let names = (try? FileManager.default.contentsOfDirectory(atPath: repoDirectory.path)) ?? [] + return names + .filter { $0 == variant || $0 == "openai_whisper-\(variant)" } + .map { repoDirectory.appending(path: $0, directoryHint: .isDirectory) } + } + + /// The folder of a completely downloaded variant, or nil. + public func folder(for variant: String) -> URL? { + folders(for: variant).first { folder in + Self.requiredModels.allSatisfy { + FileManager.default.fileExists(atPath: folder.appending(path: "\($0).mlmodelc/coremldata.bin").path) + } + } + } + + public func isInstalled(_ variant: String) -> Bool { + folder(for: variant) != nil + } + + public func delete(_ variant: String) throws { + for folder in folders(for: variant) { try FileManager.default.removeItem(at: folder) } + } +} +``` + +Vervang `Sources/SstWhisper/WhisperKitTranscriber.swift` volledig door: + +```swift +import Foundation +import SstCore +import WhisperKit + +public final class WhisperKitTranscriber: Transcriber { + public enum TranscriberError: Error, LocalizedError { + case notLoaded + + public var errorDescription: String? { "Spraakmodel is nog niet geladen." } + } + + private let modelName: String + private let storage: ModelStorage + private var pipeline: WhisperKit? + + /// Download progress (0...1) during the one-time model download. May be called from any thread. + public var onProgress: ((Double) -> Void)? + + public init(model: String = ModelCatalog.defaultVariant, storage: ModelStorage = ModelStorage()) { + self.modelName = model + self.storage = storage + } + + public var variant: String { modelName } + + private func loadPipeline(from folder: URL) async throws -> WhisperKit { + try await WhisperKit(WhisperKitConfig( + downloadBase: storage.base, + modelFolder: folder.path, + tokenizerFolder: storage.base, + prewarm: true, + load: true, + download: false + )) + } + + /// Loads the model into memory. Downloads it (≈1.5 GB) only if it is not stored locally yet; + /// once downloaded, launching needs no network. + public func load() async throws { + guard pipeline == nil else { return } + try FileManager.default.createDirectory(at: storage.base, withIntermediateDirectories: true) + + if let folder = storage.folder(for: modelName), + let loaded = try? await loadPipeline(from: folder) { + pipeline = loaded + return + } + let folder = try await WhisperKit.download( + variant: modelName, + downloadBase: storage.base, + from: ModelStorage.repo + ) { [onProgress] progress in + onProgress?(progress.fractionCompleted) + } + pipeline = try await loadPipeline(from: folder) + } + + public func transcribe(_ samples: [Float]) async throws -> String { + try await transcribe(samples, vocabulary: []) + } + + /// Terms are added one by one while the encoded prompt stays within `maxTokens`. + /// Returns nil when nothing fits or the list is empty. + static func promptTokens( + vocabulary: [String], + maxTokens: Int = 64, + specialTokenBegin: Int, + encode: (String) -> [Int] + ) -> [Int]? { + var kept: [String] = [] + var tokens: [Int] = [] + for term in vocabulary { + let candidate = kept + [term] + let encoded = encode(" " + candidate.joined(separator: ", ")).filter { $0 < specialTokenBegin } + if encoded.count > maxTokens { break } + kept = candidate + tokens = encoded + } + return tokens.isEmpty ? nil : tokens + } + + public func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + guard let pipeline else { throw TranscriberError.notLoaded } + // language nil + detectLanguage: Whisper picks NL or EN per utterance. + var options = DecodingOptions( + task: .transcribe, + language: nil, + detectLanguage: true, + skipSpecialTokens: true, + withoutTimestamps: true + ) + if !vocabulary.isEmpty, let tokenizer = pipeline.tokenizer { + options.promptTokens = Self.promptTokens( + vocabulary: vocabulary, + specialTokenBegin: tokenizer.specialTokens.specialTokenBegin, + encode: { tokenizer.encode(text: $0) } + ) + } + let results = try await pipeline.transcribe(audioArray: samples, decodeOptions: options) + return results + .map(\.text) + .joined(separator: " ") + .trimmingCharacters(in: .whitespacesAndNewlines) + } +} +``` + +- [ ] **Step 4: Draai alle tests** + +Run: `swift test` +Expected: alle tests PASS (7 nieuwe `ModelStorageTests`; `PromptTokensTests` uit fase 1 blijft groen). + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstWhisper Tests/SstWhisperTests +git commit -m "feat: add ModelStorage and ModelCatalog; transcriber uses ModelStorage" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 4: `RockerToggleStyle` in `SstUI` + +**Files:** +- Modify: `Sources/SstUI/Controls.swift` (onderaan aanvullen) + +**Interfaces:** +- Consumes: `WellBackground`, `PhysicalSurface`, tokens (`Size.switchWidth/Height`, `Shadows.keyRaised`, `Motion.durationFast`). +- Produces: `RockerToggleStyle()` (`ToggleStyle`): label links, tuimelschakelaar rechts; toegankelijkheidswaarde "aan"/"uit". + +- [ ] **Step 1: Implementatie** + +Voeg onderaan `Sources/SstUI/Controls.swift` toe (een lege regel ertussen): + +```swift +/// A two-position rocker switch: a key cap sliding in a recessed track. +public struct RockerToggleStyle: ToggleStyle { + public init() {} + + public func makeBody(configuration: Configuration) -> some View { + HStack(spacing: Space.s4) { + configuration.label + Button { + configuration.isOn.toggle() + } label: { + RockerTrack(isOn: configuration.isOn) + } + .buttonStyle(.plain) + .accessibilityValue(configuration.isOn ? "aan" : "uit") + } + } + + private struct RockerTrack: View { + let isOn: Bool + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + var body: some View { + ZStack(alignment: isOn ? .trailing : .leading) { + WellBackground() + PhysicalSurface( + fill: AnyShapeStyle(LinearGradient(colors: [Palette.surfaceKeyHigh, Palette.surfaceKeyLow], startPoint: .top, endPoint: .bottom)), + cornerRadius: Radius.sm, shadows: Shadows.keyRaised) + .frame(width: Size.switchWidth / 2, height: Size.switchHeight - Space.s2) + .padding(Space.s1) + } + .frame(width: Size.switchWidth, height: Size.switchHeight) + .animation(reduceMotion ? nil : .easeOut(duration: Motion.durationFast), value: isOn) + } + } +} +``` + +- [ ] **Step 2: Bouwen en scannen** + +Run: `swift build && swift test --filter SstUITests` +Expected: build zonder fouten of waarschuwingen; alle SstUI-tests PASS (geen losse waarden). + +- [ ] **Step 3: Commit** + +```bash +git add Sources/SstUI +git commit -m "feat: add rocker toggle style" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 5: App-kern — identiteit, teksten, rechten en `AppModel` + +**Files:** +- Create: `Sources/Sst/AppIdentity.swift`, `Sources/Sst/Strings.swift`, `Sources/Sst/PermissionMonitor.swift` +- Replace: `Sources/Sst/AppModel.swift` +- Modify: `Package.swift` + +**Interfaces:** +- Consumes: Task 1–3, fase 1 (`DictationController` met `corrector`, `vocabularyProvider`, `onResult`; `HotkeyMonitor.binding`; `AudioRecorder.onLevel`), fase 2 (`DictionaryStore`, `DictionaryCorrector`, `HistoryStore`, `VocabularyBuilder`, `SystemWordKnowledge`). +- Produces (gebruikt door de views en door fase 5): + - `AppIdentity.displayName`, `.brandCaption`; `AppPaths.support/dictionary/history` + - `Strings` (alle UI-teksten) + - `PermissionMonitor` met `microphone/accessibility/inputMonitoring`, `allGranted`, `isGranted(_:)`, `refresh()`, `requestMissing()`, `openSettings(for:)`, `startPolling()/stopPolling()`; `PermissionMonitor.Kind` + - `AppModel` (`@MainActor ObservableObject`): `settings`, `permissions`, `levelSource`, `history`, `dictionary`, `storage`, `state`, `modelStatus` (`.loading/.downloading/.ready/.failed`), `activeVariant`, `installedVariants`, `recordingStartedAt`, `lastDuration`, `lastError`, `section` (`.transcripts/.dictionary`), `searchFocusTick`, `confirmingClearHistory`, `isRecording`, `modelReady`, `hotkeyBinding`, `recordingBlocker`, `statusLine`, `menuIconName`, `title(for:)`, `retryHotkey()`, `toggleWindowRecording()`, `startWindowRecording()`, `stopWindowRecording()`, `refreshInstalled()`, `activateModel(_:)`, `deleteModel(_:)`, `show(_:)`, `focusSearch()`, `clearHistory()` + +- [ ] **Step 1: Package.swift** + +Vervang in `Package.swift` de regel + +```swift + .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper"]), +``` + +door: + +```swift + .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper", "SstData", "SstUI"]), + .executableTarget(name: "SstIcon", dependencies: ["SstUI"]), +``` + +- [ ] **Step 2: Oude app-bestanden opruimen** + +Run: `git rm Sources/Sst/MenuContent.swift` +(`Sources/Sst/AppModel.swift` en `SstApp.swift` worden in deze en de volgende taak vervangen.) + +- [ ] **Step 3: Bestanden schrijven** + +`Sources/Sst/AppIdentity.swift`: + +```swift +import Foundation + +/// The one place that knows the app's name, so renaming it later is a one-file change. +enum AppIdentity { + static let displayName = "Sst" + static let supportFolderName = "Sst" + static let brandCaption = "SST · FIELD RECORDER" +} + +enum AppPaths { + static var support: URL { + FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + .appending(path: AppIdentity.supportFolderName, directoryHint: .isDirectory) + } + static var dictionary: URL { support.appending(path: "dictionary.txt") } + static var history: URL { support.appending(path: "history.json") } +} +``` + +`Sources/Sst/Strings.swift`: + +```swift +import Foundation + +/// All user-facing text, in one place (Dutch), so translating later is a single-file job. +enum Strings { + // sections + static let transcripts = "Transcripties" + static let dictionary = "Woordenboek" + static let controls = "Controles" + + // deck + static let record = "Rec" + static let stop = "Stop" + static let time = "Tijd" + static let level = "Niveau" + + // menus + static let recordingMenu = "Opname" + static let startRecording = "Start opname" + static let stopRecording = "Stop opname" + static let search = "Zoek" + static let clearHistory = "Wis geschiedenis…" + static let openApp = "Open Sst" + static let settings = "Instellingen…" + static let quit = "Stop Sst" + + // status + static let statusIdle = "Klaar" + static let statusListening = "Luistert…" + static let statusTranscribing = "Transcriberen…" + static let statusInserting = "Invoegen…" + static func modelLoading(_ title: String) -> String { "Model laden: \(title)…" } + static func modelDownloading(_ percent: Int) -> String { "Model downloaden… \(percent)%" } + static let modelFailed = "Model laden mislukt" + static func holdToDictate(_ keyName: String) -> String { "Houd \(keyName) ingedrukt om te dicteren" } + + // permissions + static let microphone = "Microfoon" + static let accessibility = "Accessibility" + static let inputMonitoring = "Input Monitoring" + static let modelLamp = "Model" + static let microphoneMissing = "Microfoon-toegang ontbreekt" + static let openSystemSettings = "Open Instellingen" + + // settings + static let tabHotkey = "Sneltoets" + static let tabModel = "Model" + static let tabPrivacy = "Privacy" + static let captureHotkey = "Leg nieuwe toets vast" + static let capturing = "Druk op de toets die je wilt gebruiken…" + static let hotkeyRejected = "Die toets kan niet. Kies Fn, Rechter Option, Rechter Command of Control." + static let globeHint = "Zet bij Fn in Systeeminstellingen → Toetsenbord ‘Druk op 🌐 voor’ op ‘Doe niets’." + static let useModel = "Gebruik" + static let download = "Download" + static let remove = "Verwijder" + static let showInFinder = "Toon in Finder" + static let keepHistory = "Transcripties bewaren" + static let clearHistoryConfirm = "Alle transcripties wissen?" + static let clearHistoryDetail = "Dit kan niet ongedaan worden gemaakt. Audio wordt nooit bewaard." + static let cancel = "Annuleer" + static let erase = "Wis" +} +``` + +`Sources/Sst/PermissionMonitor.swift`: + +```swift +import AppKit +import ApplicationServices +import AVFoundation +import Combine + +/// Reads the three macOS permissions and keeps them current: on every app activation and while polling is on. +@MainActor +final class PermissionMonitor: ObservableObject { + enum Kind: CaseIterable, Identifiable { + case microphone, accessibility, inputMonitoring + + var id: Self { self } + + var title: String { + switch self { + case .microphone: Strings.microphone + case .accessibility: Strings.accessibility + case .inputMonitoring: Strings.inputMonitoring + } + } + + fileprivate var pane: String { + switch self { + case .microphone: "Privacy_Microphone" + case .accessibility: "Privacy_Accessibility" + case .inputMonitoring: "Privacy_ListenEvent" + } + } + } + + @Published private(set) var microphone = false + @Published private(set) var accessibility = false + @Published private(set) var inputMonitoring = false + + private var timer: Timer? + private var activation: AnyCancellable? + + init() { + refresh() + activation = NotificationCenter.default + .publisher(for: NSApplication.didBecomeActiveNotification) + .sink { [weak self] _ in MainActor.assumeIsolated { self?.refresh() } } + } + + var allGranted: Bool { microphone && accessibility && inputMonitoring } + + func isGranted(_ kind: Kind) -> Bool { + switch kind { + case .microphone: microphone + case .accessibility: accessibility + case .inputMonitoring: inputMonitoring + } + } + + func refresh() { + microphone = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized + accessibility = AXIsProcessTrusted() + inputMonitoring = CGPreflightListenEventAccess() + } + + /// Asks macOS for every permission that is still missing (each shows its own system prompt). + func requestMissing() async { + if !microphone { _ = await AVCaptureDevice.requestAccess(for: .audio) } + if !inputMonitoring { _ = CGRequestListenEventAccess() } + if !accessibility { _ = AXIsProcessTrustedWithOptions(["AXTrustedCheckOptionPrompt": true] as CFDictionary) } + refresh() + } + + func openSettings(for kind: Kind) { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?\(kind.pane)") { + NSWorkspace.shared.open(url) + } + } + + /// Polls while a view that shows the lamps is visible, so granting a permission shows up without a click. + func startPolling(every interval: TimeInterval = 2) { + guard timer == nil else { return } + timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in + MainActor.assumeIsolated { self?.refresh() } + } + } + + func stopPolling() { + timer?.invalidate() + timer = nil + } +} +``` + +`Sources/Sst/AppModel.swift` (vervang volledig): + +```swift +import AppKit +import AVFoundation +import Combine +import SstCore +import SstData +import SstWhisper +import SwiftUI + +/// Decides at insertion time whether dictated text may be pasted. +/// Not when Sst itself is in front (it would land in Sst's own search field) and not for recordings started in the window. +final class InsertionPolicy { + var windowSession = false + + func allowed() -> Bool { + !windowSession && NSWorkspace.shared.frontmostApplication?.bundleIdentifier != Bundle.main.bundleIdentifier + } +} + +@MainActor +final class AppModel: ObservableObject { + enum Section: String, CaseIterable, Identifiable { + case transcripts, dictionary + var id: String { rawValue } + } + + enum ModelStatus: Equatable { + case loading(String) + case downloading(String, Double) + case ready + case failed(String) + } + + // Services + let settings: AppSettings + let permissions = PermissionMonitor() + let levelSource = LevelSource() + let history: HistoryStore + let dictionary: DictionaryStore + let storage = ModelStorage() + + // Observable state + @Published private(set) var state: DictationState = .idle + @Published private(set) var modelStatus: ModelStatus = .loading(ModelCatalog.defaultVariant) + @Published private(set) var activeVariant: String? + @Published private(set) var installedVariants: Set = [] + @Published private(set) var recordingStartedAt: Date? + @Published private(set) var lastDuration: TimeInterval? + /// Bumped whenever the history or dictionary changes, so views that cache derived data can refresh. + @Published private(set) var historyRevision = 0 + @Published private(set) var dictionaryRevision = 0 + @Published private(set) var searchFocusTick = 0 + @Published var lastError: String? + @Published var section: Section = .transcripts + @Published var confirmingClearHistory = false + + private let transcriber = SwappableTranscriber() + private let corrector: DictionaryCorrector + private let hotkey = HotkeyMonitor() + private let policy: InsertionPolicy + private let controller: DictationController + private var modelTask: Task? + private var loadingVariant: String? + private var cancellables = Set() + + init() { + let settings = AppSettings() + let policy = InsertionPolicy() + let knowledge = SystemWordKnowledge() + let corrector = DictionaryCorrector(knowledge: knowledge) + let dictionary = DictionaryStore(fileURL: AppPaths.dictionary) + let history = HistoryStore(fileURL: AppPaths.history) + let levelSource = self.levelSource + let recorder = AudioRecorder() + recorder.onLevel = { levelSource.update($0) } + + self.settings = settings + self.policy = policy + self.corrector = corrector + self.dictionary = dictionary + self.history = history + self.controller = DictationController( + recorder: recorder, + transcriber: transcriber, + cleaner: PassthroughCleaner(), + inserter: GatedInserter( + inner: TextInserter(pasteboard: SystemPasteboard(), paster: CGEventPaster()), + shouldInsert: { policy.allowed() }), + corrector: corrector, + vocabularyProvider: { settings.usePriming ? VocabularyBuilder.build(from: dictionary.entries) : [] }) + + wire() + Task { await start() } + } + + // MARK: derived + + var isRecording: Bool { state == .recording } + var hotkeyBinding: HotkeyBinding { HotkeyBinding.binding(id: settings.hotkeyID) ?? .fn } + var modelReady: Bool { modelStatus == .ready } + + /// Why recording is not possible right now, or nil when it is. + var recordingBlocker: String? { + if !permissions.allGranted { return Strings.openSystemSettings } + switch modelStatus { + case .ready: return nil + case .loading(let variant): return Strings.modelLoading(title(for: variant)) + case .downloading(_, let fraction): return Strings.modelDownloading(Int(fraction * 100)) + case .failed: return Strings.modelFailed + } + } + + var statusLine: String { + if let blocker = recordingBlocker, state == .idle { return blocker } + switch state { + case .idle: return Strings.holdToDictate(hotkeyBinding.displayName) + case .recording: return Strings.statusListening + case .transcribing: return Strings.statusTranscribing + case .inserting: return Strings.statusInserting + } + } + + var menuIconName: String { + switch state { + case .idle: + if lastError != nil { return "exclamationmark.triangle" } + return modelReady ? "mic" : "mic.slash" + case .recording: return "mic.fill" + case .transcribing, .inserting: return "waveform" + } + } + + func title(for variant: String) -> String { + ModelCatalog.all.first { $0.id == variant }?.title ?? variant + } + + // MARK: lifecycle + + private func wire() { + controller.onStateChange = { [weak self] state in + guard let self else { return } + self.state = state + switch state { + case .recording: + self.lastError = nil + self.recordingStartedAt = Date() + self.levelSource.reset() + case .idle: + if let started = self.recordingStartedAt { self.lastDuration = Date().timeIntervalSince(started) } + self.recordingStartedAt = nil + self.levelSource.reset() + default: break + } + } + controller.onError = { [weak self] in self?.lastError = $0 } + controller.onResult = { [weak self] result in + guard let self, self.settings.keepHistory else { return } + self.history.add(HistoryEntry(result: result)) + } + hotkey.onEvent = { [weak self] event in self?.handleHotkey(event) } + hotkey.binding = hotkeyBinding + + settings.$hotkeyID + .sink { [weak self] _ in + guard let self else { return } + self.hotkey.binding = self.hotkeyBinding + self.objectWillChange.send() + } + .store(in: &cancellables) + settings.$useCorrection + .sink { [corrector] in corrector.setEnabled($0) } + .store(in: &cancellables) + permissions.objectWillChange + .sink { [weak self] _ in self?.objectWillChange.send() } + .store(in: &cancellables) + + dictionary.onChange = { [weak self] in + guard let self else { return } + self.corrector.update(entries: self.dictionary.entries) + self.dictionaryRevision += 1 + } + history.onChange = { [weak self] in self?.historyRevision += 1 } + NotificationCenter.default.addObserver(forName: NSApplication.willTerminateNotification, object: nil, queue: .main) { [history] _ in + MainActor.assumeIsolated { history.flush() } + } + } + + private func start() async { + do { try dictionary.load() } catch { lastError = error.localizedDescription } + dictionary.startWatching() + if case .recoveredCorrupt = history.load() { + lastError = "Het geschiedenisbestand was beschadigd en is opzij gezet." + } + refreshInstalled() + await permissions.requestMissing() + retryHotkey() + activateModel(settings.modelVariant) + } + + func retryHotkey() { + permissions.refresh() + do { + try hotkey.start() + } catch { + lastError = error.localizedDescription + } + } + + // MARK: recording + + private func handleHotkey(_ event: HotkeyEvent) { + guard modelReady else { return } + if event == .pressed, AVCaptureDevice.authorizationStatus(for: .audio) != .authorized { + lastError = Strings.microphoneMissing + return + } + Task { await controller.handle(event) } + } + + /// Start/stop from the window or ⌥⌘R. The result goes to the history only; nothing is pasted. + func toggleWindowRecording() { + if isRecording { + stopWindowRecording() + } else { + startWindowRecording() + } + } + + func startWindowRecording() { + guard recordingBlocker == nil, state == .idle else { return } + policy.windowSession = true + Task { await controller.handle(.pressed) } + } + + func stopWindowRecording() { + guard policy.windowSession else { return } + Task { + await controller.handle(.released) + policy.windowSession = false + } + } + + // MARK: model + + func refreshInstalled() { + installedVariants = Set(ModelCatalog.all.map(\.id).filter { storage.isInstalled($0) }) + } + + /// Downloads (if needed) and loads `variant`. Dictation is off meanwhile; if it fails the previous model stays. + func activateModel(_ variant: String) { + modelTask?.cancel() + let previous = activeVariant + loadingVariant = variant + modelStatus = .loading(variant) + modelTask = Task { [weak self] in + guard let self else { return } + let candidate = WhisperKitTranscriber(model: variant, storage: storage) + candidate.onProgress = { [weak self] fraction in + Task { @MainActor in + guard let self, self.loadingVariant == variant else { return } + self.modelStatus = fraction < 1 ? .downloading(variant, fraction) : .loading(variant) + } + } + do { + try await candidate.load() + guard !Task.isCancelled else { return } + transcriber.swap(to: candidate) + activeVariant = variant + settings.modelVariant = variant + modelStatus = .ready + } catch { + guard !Task.isCancelled else { return } + lastError = error.localizedDescription + modelStatus = previous == nil ? .failed(error.localizedDescription) : .ready + } + if loadingVariant == variant { loadingVariant = nil } + refreshInstalled() + } + } + + func deleteModel(_ variant: String) { + guard variant != activeVariant else { return } + do { + try storage.delete(variant) + } catch { + lastError = error.localizedDescription + } + refreshInstalled() + } + + // MARK: navigation and history + + func show(_ section: Section) { + self.section = section + NSApp.activate(ignoringOtherApps: true) + } + + func focusSearch() { + searchFocusTick += 1 + } + + func clearHistory() { + history.removeAll() + history.flush() + } +} +``` + +- [ ] **Step 4: Bouwen** + +De app bouwt pas na Task 6 (de scenes ontbreken nog); controleer hier alleen de bibliotheken: + +Run: `swift build --target SstCore --target SstData --target SstWhisper --target SstUI` +Expected: build zonder fouten. + +- [ ] **Step 5: Commit (samen met Task 6)** + +Er wordt pas gecommit na Task 6, want de app-target bouwt pas dan. + +--- + +### Task 6: Scenes, menu's, instellingen en menubalkitem + +**Files:** +- Create: `Sources/Sst/SstApp.swift`, `Sources/Sst/MainWindow.swift`, `Sources/Sst/AppCommands.swift`, `Sources/Sst/MenuBarContent.swift`, `Sources/Sst/SettingsView.swift` +- Test: de token-scan (`Tests/SstUITests/TokenIntegrityTests.swift`) bewaakt ook `Sources/Sst` + +**Interfaces:** +- Consumes: Task 5 (`AppModel`, `Strings`), `SstUI`. +- Produces: scenes `Window("Sst", id: "main")`, `Settings`, `MenuBarExtra`; menu "Opname" (⌥⌘R start/stop, ⌘1 Transcripties, ⌘2 Woordenboek, ⌘F zoek) en "Wis geschiedenis…" in Bewerken; `SettingsView` met drie toetsen-tabs; `MainWindow` is voorlopig een schil (fase 5 vervangt hem). + +- [ ] **Step 1: Bestanden schrijven** + +`Sources/Sst/SstApp.swift`: + +```swift +import SwiftUI +import SstUI + +@main +struct SstApp: App { + @StateObject private var model = AppModel() + + var body: some Scene { + Window(AppIdentity.displayName, id: "main") { + MainWindow() + .environmentObject(model) + .environmentObject(model.settings) + .frame(minWidth: Layout.windowMinWidth, minHeight: Layout.windowMinHeight) + } + .defaultSize(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) + .commands { AppCommands(model: model) } + + Settings { + SettingsView() + .environmentObject(model) + .environmentObject(model.settings) + } + + MenuBarExtra { + MenuBarContent() + .environmentObject(model) + } label: { + Image(systemName: model.menuIconName) + } + } +} +``` + +`Sources/Sst/MainWindow.swift`: + +```swift +import SwiftUI +import SstUI + +/// Placeholder shell; phase 5 replaces the content with the deck, history and dictionary. +struct MainWindow: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + ZStack { + BrushedBackground().ignoresSafeArea() + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(AppIdentity.brandCaption) + Text(model.statusLine) + .textStyle(Typography.reading) + .foregroundStyle(Palette.inkPrimary) + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, alignment: .leading) + .background(PanelBackground()) + } + .padding(Layout.panelPadding) + } + } +} +``` + +`Sources/Sst/AppCommands.swift`: + +```swift +import SwiftUI + +struct AppCommands: Commands { + @ObservedObject var model: AppModel + @Environment(\.openWindow) private var openWindow + + var body: some Commands { + CommandGroup(after: .pasteboard) { + Divider() + Button(Strings.clearHistory) { + openWindow(id: "main") + model.confirmingClearHistory = true + } + } + CommandMenu(Strings.recordingMenu) { + Button(model.isRecording ? Strings.stopRecording : Strings.startRecording) { + model.toggleWindowRecording() + } + .keyboardShortcut("r", modifiers: [.command, .option]) + Divider() + Button(Strings.transcripts) { show(.transcripts) } + .keyboardShortcut("1", modifiers: .command) + Button(Strings.dictionary) { show(.dictionary) } + .keyboardShortcut("2", modifiers: .command) + Button(Strings.search) { + show(.transcripts) + model.focusSearch() + } + .keyboardShortcut("f", modifiers: .command) + } + } + + private func show(_ section: AppModel.Section) { + openWindow(id: "main") + model.show(section) + } +} +``` + +`Sources/Sst/MenuBarContent.swift`: + +```swift +import SwiftUI + +/// The secondary interface: status and shortcut while working in another app. +struct MenuBarContent: View { + @EnvironmentObject private var model: AppModel + @Environment(\.openWindow) private var openWindow + + var body: some View { + Text(model.statusLine) + if let error = model.lastError { + Text(error) + } + Divider() + Button(Strings.openApp) { + openWindow(id: "main") + model.show(model.section) + } + SettingsLink { Text(Strings.settings) } + Divider() + Button(Strings.quit) { NSApplication.shared.terminate(nil) } + .keyboardShortcut("q") + } +} +``` + +`Sources/Sst/SettingsView.swift`: + +```swift +import AppKit +import SstCore +import SstData +import SstUI +import SstWhisper +import SwiftUI + +struct SettingsView: View { + private enum Tab: CaseIterable { + case hotkey, model, privacy + + var title: String { + switch self { + case .hotkey: Strings.tabHotkey + case .model: Strings.tabModel + case .privacy: Strings.tabPrivacy + } + } + } + + @State private var tab: Tab = .hotkey + + var body: some View { + ZStack { + BrushedBackground() + VStack(alignment: .leading, spacing: Layout.panelGap) { + HStack(spacing: Space.s3) { + ForEach(Tab.allCases, id: \.self) { candidate in + Button(candidate.title) { tab = candidate } + .buttonStyle(KeyButtonStyle(latched: tab == candidate)) + } + } + Group { + switch tab { + case .hotkey: HotkeySettingsView() + case .model: ModelSettingsView() + case .privacy: PrivacySettingsView() + } + } + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, alignment: .topLeading) + .background(PanelBackground()) + } + .padding(Layout.panelPadding) + } + .frame(width: Layout.settingsWidth) + .fixedSize(horizontal: false, vertical: true) + } +} + +// MARK: hotkey + +extension HotkeyBinding { + /// The matching flag in an AppKit event, used while capturing a new key. + fileprivate var eventFlag: NSEvent.ModifierFlags { + switch id { + case "fn": .function + case "rightOption": .option + case "rightCommand": .command + default: .control + } + } +} + +@MainActor +private final class HotkeyCapture: ObservableObject { + @Published var isCapturing = false + @Published var message: String? + private var monitor: Any? + + func start(onCapture: @escaping (HotkeyBinding) -> Void) { + stop() + isCapturing = true + message = Strings.capturing + monitor = NSEvent.addLocalMonitorForEvents(matching: [.flagsChanged, .keyDown]) { [weak self] event in + guard let self else { return event } + if event.type == .keyDown { + if event.keyCode == 53 { self.stop() } // Escape cancels + return nil + } + guard let binding = HotkeyBinding.binding(keyCode: Int(event.keyCode)) else { + self.message = Strings.hotkeyRejected + return nil + } + if event.modifierFlags.contains(binding.eventFlag) { + self.stop() + onCapture(binding) + } + return nil + } + } + + func stop() { + if let monitor { NSEvent.removeMonitor(monitor) } + monitor = nil + isCapturing = false + message = nil + } +} + +private struct HotkeySettingsView: View { + @EnvironmentObject private var settings: AppSettings + @StateObject private var capture = HotkeyCapture() + + private var current: HotkeyBinding { HotkeyBinding.binding(id: settings.hotkeyID) ?? .fn } + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.tabHotkey, style: Typography.heading) + HStack(spacing: Layout.panelGap) { + Text(current.displayName) + .textStyle(Typography.heading) + .foregroundStyle(Palette.inkPrimary) + .padding(.horizontal, Space.s4) + .frame(minHeight: Size.controlHeight) + .background(WellBackground()) + Button(Strings.captureHotkey) { + capture.start { settings.hotkeyID = $0.id } + } + .buttonStyle(KeyButtonStyle(latched: capture.isCapturing)) + } + if let message = capture.message { + Text(message).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + } + SeamDivider() + Text(HotkeyBinding.allowed.map(\.displayName).joined(separator: " · ")) + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + if current == .fn { + Text(Strings.globeHint).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + } + } + .onDisappear { capture.stop() } + } +} + +// MARK: model + +private struct ModelSettingsView: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.tabModel, style: Typography.heading) + ForEach(ModelCatalog.all) { option in + row(option) + SeamDivider() + } + HStack { + Text(model.storage.base.path) + .textStyle(Typography.code) + .foregroundStyle(Palette.inkSecondary) + .lineLimit(1) + .truncationMode(.middle) + Spacer() + Button(Strings.showInFinder) { + NSWorkspace.shared.activateFileViewerSelecting([model.storage.base]) + } + .buttonStyle(KeyButtonStyle()) + } + } + .onAppear { model.refreshInstalled() } + } + + private func row(_ option: ModelOption) -> some View { + let isActive = model.activeVariant == option.id + let installed = model.installedVariants.contains(option.id) + let progress: Double? = { + if case .downloading(option.id, let fraction) = model.modelStatus { return fraction } + return nil + }() + return HStack(alignment: .center, spacing: Layout.panelGap) { + LampView(isOn: isActive, tint: .amber) + VStack(alignment: .leading, spacing: Space.s1) { + Text(option.title).textStyle(Typography.body).foregroundStyle(Palette.inkPrimary) + Text("\(option.detail) · \(option.approxSizeMB) MB") + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + if let progress { + LEDBarView(fraction: progress) + } else if case .loading(option.id) = model.modelStatus { + SilkLabel(Strings.modelLoading(option.title), style: Typography.labelSmall) + } + } + Spacer() + if installed && !isActive { + Button(Strings.useModel) { model.activateModel(option.id) }.buttonStyle(KeyButtonStyle()) + Button(Strings.remove) { model.deleteModel(option.id) }.buttonStyle(KeyButtonStyle()) + } else if !installed { + Button(Strings.download) { model.activateModel(option.id) }.buttonStyle(KeyButtonStyle()) + } + } + } +} + +// MARK: privacy + +private struct PrivacySettingsView: View { + @EnvironmentObject private var model: AppModel + @EnvironmentObject private var settings: AppSettings + @State private var confirming = false + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.tabPrivacy, style: Typography.heading) + Toggle(isOn: $settings.keepHistory) { + Text(Strings.keepHistory).textStyle(Typography.body).foregroundStyle(Palette.inkPrimary) + } + .toggleStyle(RockerToggleStyle()) + SeamDivider() + Button(Strings.clearHistory) { confirming = true } + .buttonStyle(KeyButtonStyle()) + } + .confirmationDialog(Strings.clearHistoryConfirm, isPresented: $confirming) { + Button(Strings.erase, role: .destructive) { model.clearHistory() } + Button(Strings.cancel, role: .cancel) {} + } message: { + Text(Strings.clearHistoryDetail) + } + } +} +``` + +- [ ] **Step 2: Bouwen en alle tests** + +Run: `swift build --product Sst && swift test` +Expected: build zonder fouten of waarschuwingen; alle tests PASS, inclusief `viewsContainNoLooseDesignValues` over `Sources/Sst`. + +- [ ] **Step 3: Commit (Task 5 + 6 samen)** + +```bash +git add Package.swift Sources/Sst +git commit -m "feat: turn Sst into a regular Mac app with settings, menus and menu bar item" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 7: Icoon, bundel en ondertekening + +**Files:** +- Create: `Sources/SstIcon/IconMaker.swift`, `scripts/make-icon.sh`, `Design/AppIcon.icns` (gegenereerd) +- Replace: `scripts/make-app.sh` + +**Interfaces:** +- Produces: `build/Sst.app` — een gewone Dock-app (geen `LSUIElement`), met icoon, versie 0.2.0, categorie productiviteit, Nederlandse microfoon-uitleg, ondertekend met je Apple Development-identiteit; `scripts/make-app.sh --install` kopieert naar `/Applications`. + +- [ ] **Step 1: Icoon-generator** + +`Sources/SstIcon/IconMaker.swift`: + +```swift +import AppKit +import SstUI +import SwiftUI + +/// The app icon: a brushed-aluminium plate with a VU meter and the red record lamp, drawn with the design tokens. +/// Canvas is 256 pt; the macOS icon grid puts the plate at 206 pt with a 46 pt corner radius. +struct IconView: View { + static let canvas: CGFloat = 256 + private let plate: CGFloat = 206 + private let corner: CGFloat = 46 + + var body: some View { + ZStack { + BrushedBackground() + .frame(width: plate, height: plate) + .clipShape(RoundedRectangle(cornerRadius: corner, style: .continuous)) + .overlay( + RoundedRectangle(cornerRadius: corner, style: .continuous) + .strokeBorder(Palette.lineSeamDark, lineWidth: Border.hairline)) + .shadow(color: .black.opacity(Opacity.mark), radius: Space.s3, y: Space.s2) + + VStack(spacing: Space.s5) { + VUMeterView(levelDbfs: -26, isActive: true, brand: "", initialFraction: 0.62) + .frame(width: 150) + HStack(spacing: Space.s4) { + LampView(isOn: true, large: true).scaleEffect(1.5) + SilkLabel("REC", style: Typography.heading) + } + } + .offset(y: Space.s2) + } + .frame(width: Self.canvas, height: Self.canvas) + .environment(\.colorScheme, .light) + } +} + +@main +@MainActor +struct IconMaker { + static func main() { + guard CommandLine.arguments.count > 1 else { + print("usage: SstIcon ") + exit(2) + } + let folder = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true) + try? FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true) + + // (file name, pixel size) as iconutil expects them + let outputs: [(String, CGFloat)] = [ + ("icon_16x16", 16), ("icon_16x16@2x", 32), ("icon_32x32", 32), ("icon_32x32@2x", 64), + ("icon_128x128", 128), ("icon_128x128@2x", 256), ("icon_256x256", 256), ("icon_256x256@2x", 512), + ("icon_512x512", 512), ("icon_512x512@2x", 1024), + ] + for (name, pixels) in outputs { + let renderer = ImageRenderer(content: IconView()) + renderer.scale = pixels / IconView.canvas + guard let tiff = renderer.nsImage?.tiffRepresentation, + let png = NSBitmapImageRep(data: tiff)?.representation(using: .png, properties: [:]) + else { + print("kon \(name) niet renderen") + exit(1) + } + try? png.write(to: folder.appending(path: "\(name).png")) + } + print("iconset geschreven naar \(folder.path)") + } +} +``` + +`scripts/make-icon.sh`: + +```bash +#!/usr/bin/env bash +# Renders the app icon from the design tokens and stores it as Design/AppIcon.icns (committed). +set -euo pipefail +cd "$(dirname "$0")/.." + +swift build --product SstIcon +rm -rf build/AppIcon.iconset +.build/debug/SstIcon build/AppIcon.iconset +iconutil -c icns build/AppIcon.iconset -o Design/AppIcon.icns +echo "Icoon geschreven: Design/AppIcon.icns" +``` + +- [ ] **Step 2: Icoon genereren en bekijken** + +Run: `chmod +x scripts/make-icon.sh && ./scripts/make-icon.sh && open build/AppIcon.iconset/icon_512x512@2x.png` +Expected: `Icoon geschreven: Design/AppIcon.icns`. Het icoon toont een afgeronde aluminium plaat met een VU-meter (cream wijzerplaat, rode band, wijzer rond −5 VU) en een rood lampje met "REC". + +- [ ] **Step 3: Bundelscript** + +Vervang `scripts/make-app.sh` volledig door: + +```bash +#!/usr/bin/env bash +# Builds build/Sst.app (a normal Dock app, no LSUIElement). Pass --install to copy it to /Applications. +set -euo pipefail +cd "$(dirname "$0")/.." + +APP="build/Sst.app" +IDENTIFIER="local.sst.app" +VERSION="0.2.0" + +swift build -c release --product Sst +BIN=".build/release/Sst" + +rm -rf "$APP" +mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources" +cp "$BIN" "$APP/Contents/MacOS/Sst" +# SwiftPM resource bundles (if any dependency ships them) live next to the binary. +for b in .build/release/*.bundle; do + [ -e "$b" ] && cp -R "$b" "$APP/Contents/Resources/" +done +if [ -f Design/AppIcon.icns ]; then + cp Design/AppIcon.icns "$APP/Contents/Resources/AppIcon.icns" +else + echo "Let op: Design/AppIcon.icns ontbreekt; run scripts/make-icon.sh." +fi + +cat > "$APP/Contents/Info.plist" < + + + + CFBundleIdentifier$IDENTIFIER + CFBundleNameSst + CFBundleDisplayNameSst + CFBundleExecutableSst + CFBundlePackageTypeAPPL + CFBundleShortVersionString$VERSION + CFBundleVersion1 + CFBundleIconFileAppIcon + LSMinimumSystemVersion14.0 + LSApplicationCategoryTypepublic.app-category.productivity + NSHighResolutionCapable + NSMicrophoneUsageDescription + Sst neemt je stem op zolang je de sneltoets ingedrukt houdt, om die lokaal naar tekst om te zetten. + + +PLIST + +# A stable signing identity keeps the macOS permissions (Microphone, Accessibility, Input Monitoring) across rebuilds. +IDENTITY="${SST_SIGN_IDENTITY:-$(security find-identity -v -p codesigning | sed -n 's/.*"\(Apple Development: [^"]*\)".*/\1/p' | head -1)}" +if [ -n "$IDENTITY" ]; then + codesign --force --sign "$IDENTITY" --identifier "$IDENTIFIER" "$APP" + echo "Ondertekend met: $IDENTITY" +else + codesign --force --sign - --identifier "$IDENTIFIER" "$APP" + echo "WAARSCHUWING: geen Apple Development-certificaat gevonden; ad-hoc ondertekend (rechten gaan na elke herbouw verloren)." +fi +codesign --verify --strict "$APP" +echo "Gebouwd: $APP" +echo "Eenmalig na het wisselen van ondertekening: tccutil reset Accessibility $IDENTIFIER; tccutil reset ListenEvent $IDENTIFIER; tccutil reset Microphone $IDENTIFIER (en geef de rechten opnieuw)." + +if [ "${1:-}" = "--install" ]; then + rm -rf /Applications/Sst.app + cp -R "$APP" /Applications/Sst.app + echo "Geïnstalleerd: /Applications/Sst.app" +fi +``` + +- [ ] **Step 4: Bundel bouwen en controleren** + +Run: +```bash +chmod +x scripts/make-app.sh && ./scripts/make-app.sh +plutil -lint build/Sst.app/Contents/Info.plist +/usr/libexec/PlistBuddy -c "Print :LSUIElement" build/Sst.app/Contents/Info.plist +codesign -dv build/Sst.app 2>&1 | grep -E "Identifier|TeamIdentifier" +ls build/Sst.app/Contents/Resources +``` +Expected: `Ondertekend met: Apple Development: …`; `plutil … OK`; PlistBuddy meldt `:LSUIElement … Does Not Exist`; `Identifier=local.sst.app` en een `TeamIdentifier`; `AppIcon.icns` staat in Resources. + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstIcon scripts Design/AppIcon.icns +git commit -m "feat: app icon generator, signed app bundle script with install option" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 8: Handmatige acceptatie met de gebruiker + +Dit vraagt rechten, het toetsenbord en het scherm van de gebruiker; subagents voeren dit niet uit. + +- [ ] **Step 1: Voorbereiden** + +1. Sluit de oude `Sst` (de menubalk-app) als die nog draait: `pkill -f "Sst.app/Contents/MacOS/Sst"`. +2. De ondertekening is veranderd van ad-hoc naar je certificaat, dus eenmalig: `tccutil reset Accessibility local.sst.app; tccutil reset ListenEvent local.sst.app; tccutil reset Microphone local.sst.app` (door de gebruiker zelf uit te voeren). +3. `open build/Sst.app` en sta Microfoon, Accessibility en Input Monitoring toe (bij Input Monitoring zo nodig de app herstarten). + +- [ ] **Step 2: Controleer samen** + +1. **Dock en menu:** Sst heeft een Dock-icoon met het nieuwe icoon; het menu "Sst" toont Instellingen… (⌘,), Verberg en Stop Sst; er is een menu "Opname". +2. **Venster:** het hoofdvenster opent, is vrij te vergroten en niet kleiner te maken dan 800×560; de statusregel zegt "Houd Fn ingedrukt om te dicteren" zodra het model geladen is (of "Geef eerst de rechten onder Controles" zolang rechten ontbreken). +3. **Doorlopen na sluiten:** sluit het venster (⌘W). Dock-icoon en menubalkitem blijven; dicteer in TextEdit met Fn: de tekst verschijnt. Klik op het Dock-icoon: het venster komt terug. +4. **⌘Q** sluit Sst af (Dock-icoon weg, menubalkitem weg). +5. **Instellingen (⌘,):** drie toetsen Sneltoets / Model / Privacy. Sneltoets: kies "Leg nieuwe toets vast" en druk op Rechter Option; dicteer in TextEdit met Rechter Option (Fn doet dan niets). Zet terug op Fn. Druk bij het vastleggen op Shift of A: de melding legt uit dat dat niet kan. +6. **Model:** drie modellen; het standaardmodel heeft een brandend lampje. (Optioneel: download het compacte model, kijk naar de voortgangsbalk, schakel erheen en terug.) +7. **Voorop-regel:** zet Sst voorop (klik in het venster) en dicteer met Fn: er wordt niets geplakt. +8. **Menubalkitem:** toont de statusregel; "Open Sst" opent het venster; "Instellingen…" opent Instellingen; "Stop Sst" sluit af. +9. **Rechten:** trek Microfoon in via Systeeminstellingen; binnen enkele seconden of bij terugkeren naar Sst meldt de statusregel de ontbrekende rechten; geef ze terug. +10. **Installeren:** `./scripts/make-app.sh --install`, start `/Applications/Sst.app` vanuit Launchpad of Finder: werkt, en de rechten blijven na een herbouw behouden (herhaal `make-app.sh` en start opnieuw: geen nieuwe vragen). + +Noteer afwijkingen; kleine visuele bijstellingen horen bij deze taak. + +- [ ] **Step 3: Alle tests en afronden** + +Run: `swift test` +Expected: alle tests PASS. + +--- + +## Self-review + +- **Spec-dekking:** Dock-app zonder `LSUIElement`, vrij schaalbaar venster met minimumgrootte, Instellingen (⌘,) met sneltoets/model/privacy, secundair menubalkitem, menu's (Opname, Wis geschiedenis), doorlopen na sluiten, bundel met icoon, stabiele ondertekening en `--install`, rechten opnieuw lezen bij activeren en polling, voorop-regel en window-opname alleen opslaan, woordenboek en geschiedenis aan de keten gekoppeld (`corrector`, `vocabularyProvider`, `onResult`). Hoofdvenster-inhoud (deck, geschiedenis, woordenboek, zijbalk, CONTROLES) komt in fase 5; hier is `MainWindow` een schil. +- **Placeholders:** geen; alle code is overgenomen uit een gecompileerde en geteste versie (130 tests groen in de toestand na deze fase). +- **Typeconsistentie:** `AppModel.Section`, `ModelStatus`, `PermissionMonitor.Kind`, `Strings.*` en de tokenformaten komen overeen met fase 3 en worden in fase 5 hergebruikt. +- **Afwijkingen van de spec (bewust):** de modelvarianten en groottes zijn nu bij de bron geverifieerd; `AppSettings.defaultModelVariant` dupliceert `ModelCatalog.defaultVariant` omdat `SstData` niet van `SstWhisper` afhangt. +- **Risico's:** het exacte gedrag van `Window`-heropenen via het Dock en `SettingsLink` is alleen handmatig te controleren (Task 8 stap 3 en 8); valt het Dock-heropenen tegen, voeg dan een `NSApplicationDelegateAdaptor` toe met `applicationShouldHandleReopen` en meld dit als afwijking. diff --git a/docs/superpowers/plans/2026-10-01-app-5-main-window.md b/docs/superpowers/plans/2026-10-01-app-5-main-window.md new file mode 100644 index 0000000..6e52271 --- /dev/null +++ b/docs/superpowers/plans/2026-10-01-app-5-main-window.md @@ -0,0 +1,1443 @@ +# App fase 5 — Hoofdvenster Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Het hoofdvenster van de app: vast deck met levende VU-meter, teller, LED-balk en REC/STOP; zijbalk met navigatie en CONTROLES; doorzoekbare transcriptiegeschiedenis met kopieerknop en zichtbare woordenboek-correcties; en het woordenboek met toevoegen, bewerken, verwijderen, zoeken, waarschuwingen en ongedaan maken. + +**Architecture:** Testbare logica (`HistoryViewModel`, `DictionaryViewModel`, `CorrectionMarkup`, `StopwatchFormat`) staat in `SstData`; de views in `Sources/Sst` zijn dun en gebruiken uitsluitend tokens en componenten uit `SstUI`. Een debug-snapshotmodus (`Sst --snapshot`) rendert het venster met voorbeeldgegevens in beide afwerkingen, zodat de weergaven te beoordelen zijn zonder rechten of model. + +**Tech Stack:** Swift 6.4 (language mode 5), SwiftUI, Swift Testing. + +**Spec:** `docs/superpowers/specs/2026-10-01-macos-app-design.md` (secties "Hoofdvenster", "Woordenboek — het ontwerp in detail", "Geschiedenis", "Toegankelijkheid") + +**Voorwaarde:** fase 1 t/m 4 zijn afgerond (de app bouwt en draait met een schil als `MainWindow`). + +## Global Constraints + +- Indeling: zijbalk `Layout.sidebarWidth` met navigatie (Transcripties, Woordenboek) en CONTROLES (Microfoon, Accessibility, Input Monitoring, Model); rechts het deck (altijd zichtbaar) en daaronder de gekozen weergave. +- Deck: VU-meter 0 VU = −20 dBFS; teller toont opnametijd (in rust de duur van de laatste opname); LED-balk op hetzelfde signaal; ververst 60 Hz **alleen tijdens het opnemen**; REC blijft ingedrukt zolang er wordt opgenomen; REC uitgeschakeld met reden zolang rechten of model ontbreken; STOP beëindigt elke lopende opname (ook één die met de sneltoets begon). +- Geschiedenis: nieuwste eerst, per dag gegroepeerd (Nederlandse datums), rij = tijd + tekst + kopieerknop; correcties met amber markering en samenvatting "N correcties · gehoord → geschreven"; geselecteerde rij toont de oorspronkelijke tekst; zoeken (⌘F) hoofdletter- en diakriet-ongevoelig; rechtsklik: kopieer, kopieer origineel, verwijder; ⌫ verwijdert de selectie. +- Woordenboek: kolommen GEHOORD → SCHRIJF; term toont "—" links; `+` voegt toe in een editor met live waarschuwingen; dubbelklik bewerkt; ⌫ verwijdert; ⌘Z of "Herstel" maakt ongedaan; zoeken; "Open bestand"; schakelaars Voorrang en Correctie; lampje per rij: rood = fout, amber = waarschuwing, gedoofd = geen of alleen een notitie. +- Alleen tokens in views (token-scan over `Sources/Sst`); UI-teksten in `Strings.swift`. +- Elke commit eindigt met de trailer `Co-Authored-By: Claude Sonnet 5.5 ` (tweede `-m`). + +## Bestandsstructuur + +``` +Sources/SstData/ViewSupport.swift (nieuw) StopwatchFormat, TextSegment, CorrectionMarkup, HistoryViewModel, DictionaryViewModel +Sources/Sst/AppModel.swift (wijzig via script) lijst-viewmodellen, preview-modus, STOP-regel +Sources/Sst/Strings.swift (wijzig via script) teksten voor lijsten en woordenboek +Sources/Sst/SstApp.swift (wijzig via script) @main verhuist naar main.swift +Sources/Sst/main.swift (nieuw) ingang; debug-snapshot +Sources/Sst/Snapshot.swift (nieuw, alleen DEBUG) +Sources/Sst/ListSupport.swift (nieuw) SnapshotMode, ScrollList, SearchField +Sources/Sst/DeckView.swift, Sidebar.swift, MainWindow.swift (vervang), HistoryView.swift, DictionaryView.swift (nieuw) +Tests/SstDataTests/ViewSupportTests.swift (nieuw) +``` + +--- + +### Task 1: Logica achter de weergaven in `SstData` + +**Files:** +- Create: `Sources/SstData/ViewSupport.swift` +- Test: `Tests/SstDataTests/ViewSupportTests.swift` + +**Interfaces:** +- Consumes: fase 2 (`HistoryStore`, `HistorySearch`, `HistoryGrouping`, `DictionaryStore`, `DictionaryFile`, `EntryWarnings`, `WordKnowledge`), fase 1 (`AppliedCorrection`). +- Produces: + - `StopwatchFormat.string(_ seconds: Double) -> String` ("mm:ss.t", minuten lopen rond bij 100) + - `TextSegment(text:correction:)`, `CorrectionMarkup.segments(finalText:corrections:) -> [TextSegment]` (UTF-16-offsets; ongeldige of overlappende correcties worden overgeslagen) + - `@MainActor HistoryViewModel(store:calendar:)` met `query`, `selection`, `days`, `totalCount`, `selectedEntry`, `refresh()`, `delete(id:)` + - `@MainActor DictionaryViewModel(store:knowledge:)` met `rows` (`Row`: `id`, `entry`, `warnings`, `worst`), `visibleRows`, `query`, `canUndo`, `errorMessage`, `reload()`, `warnings(for:editing:)`, `add(_:) -> UUID?`, `update(id:to:)`, `remove(id:)`, `undo()` + +- [ ] **Step 1: Schrijf de falende tests** + +`Tests/SstDataTests/ViewSupportTests.swift`: + +```swift +import Foundation +import Testing +import SstCore +@testable import SstData + +struct StopwatchFormatTests { + @Test func formatsMinutesSecondsAndTenths() { + #expect(StopwatchFormat.string(0) == "00:00.0") + #expect(StopwatchFormat.string(7.46) == "00:07.4") + #expect(StopwatchFormat.string(65.2) == "01:05.2") + #expect(StopwatchFormat.string(-3) == "00:00.0") + #expect(StopwatchFormat.string(6039.9) == "00:39.9") // minutes wrap at 100 + } +} + +struct CorrectionMarkupTests { + func correction(_ start: Int, _ length: Int, heard: String = "x", wrote: String = "y") -> AppliedCorrection { + AppliedCorrection(heard: heard, wrote: wrote, rule: heard, start: start, length: length) + } + + @Test func noCorrectionsGivesOnePlainSegment() { + #expect(CorrectionMarkup.segments(finalText: "hallo wereld", corrections: []) == [TextSegment(text: "hallo wereld")]) + } + + @Test func emptyTextGivesNoSegments() { + #expect(CorrectionMarkup.segments(finalText: "", corrections: [correction(0, 1)]).isEmpty) + } + + @Test func splitsAroundACorrectionInTheMiddle() { + let c = correction(11, 11, heard: "cloud code", wrote: "Claude Code") + let segments = CorrectionMarkup.segments(finalText: "ik gebruik Claude Code vandaag", corrections: [c]) + #expect(segments == [ + TextSegment(text: "ik gebruik "), + TextSegment(text: "Claude Code", correction: c), + TextSegment(text: " vandaag"), + ]) + } + + @Test func handlesCorrectionsAtTheStartAndEnd() { + let first = correction(0, 3), last = correction(8, 3) + let segments = CorrectionMarkup.segments(finalText: "abc def ghi", corrections: [last, first]) + #expect(segments.map(\.text) == ["abc", " def ", "ghi"]) + #expect(segments[0].correction == first && segments[2].correction == last && segments[1].correction == nil) + } + + @Test func skipsOutOfRangeAndOverlappingCorrections() { + let ok = correction(0, 3), overlapping = correction(2, 3), outside = correction(50, 4), empty = correction(4, 0) + let segments = CorrectionMarkup.segments(finalText: "abc def", corrections: [ok, overlapping, outside, empty]) + #expect(segments.map(\.text) == ["abc", " def"]) + } + + @Test func offsetsAreUTF16SoEmojiDoNotShiftThem() { + // "😀 " is 3 UTF-16 units; the corrected word starts at 3. + let c = correction(3, 6, heard: "wereld", wrote: "wereld") + let segments = CorrectionMarkup.segments(finalText: "😀 wereld", corrections: [c]) + #expect(segments.map(\.text) == ["😀 ", "wereld"]) + } +} + +@MainActor +struct HistoryViewModelTests { + func store(_ texts: [String]) -> HistoryStore { + let store = HistoryStore(fileURL: FileManager.default.temporaryDirectory.appending(path: "sst-vm-\(UUID().uuidString)/h.json")) + for (index, text) in texts.enumerated() { + store.add(HistoryEntry(date: Date(timeIntervalSince1970: 1_700_000_000 + Double(index)), duration: 1, rawText: text, finalText: text)) + } + return store + } + + @Test func listsAllEntriesNewestFirstAndFiltersByQuery() { + let model = HistoryViewModel(store: store(["eerste", "tweede zin", "derde"])) + #expect(model.totalCount == 3) + #expect(model.days.flatMap(\.entries).map(\.finalText) == ["derde", "tweede zin", "eerste"]) + model.query = "tweede" + #expect(model.days.flatMap(\.entries).map(\.finalText) == ["tweede zin"]) + #expect(model.totalCount == 3) + } + + @Test func selectionIsClearedWhenItDisappearsFromTheFilter() { + let history = store(["alpha", "beta"]) + let model = HistoryViewModel(store: history) + model.selection = history.entries.first { $0.finalText == "alpha" }?.id + #expect(model.selectedEntry?.finalText == "alpha") + model.query = "beta" + #expect(model.selection == nil) + } + + @Test func deleteRemovesFromStoreAndList() { + let history = store(["a", "b"]) + let model = HistoryViewModel(store: history) + model.delete(id: history.entries[0].id) + #expect(history.entries.count == 1) + #expect(model.totalCount == 1) + } + + @Test func refreshPicksUpNewEntries() { + let history = store(["a"]) + let model = HistoryViewModel(store: history) + history.add(HistoryEntry(date: .now, duration: 1, rawText: "nieuw", finalText: "nieuw")) + model.refresh() + #expect(model.totalCount == 2) + } +} + +@MainActor +struct DictionaryViewModelTests { + func makeModel(_ text: String = "") throws -> (DictionaryViewModel, DictionaryStore) { + let url = FileManager.default.temporaryDirectory.appending(path: "sst-dict-\(UUID().uuidString)/dictionary.txt") + try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true) + try text.write(to: url, atomically: true, encoding: .utf8) + let store = DictionaryStore(fileURL: url) + try store.load() + return (DictionaryViewModel(store: store, knowledge: FakeKnowledge()), store) + } + + @Test func rowsCarryWarnings() throws { + let (model, _) = try makeModel("Supabase\ncloud => Claude\n") + #expect(model.rows.count == 2) + #expect(model.rows[0].warnings.isEmpty) + #expect(model.rows[1].worst == .warning) + } + + @Test func duplicateIsFlaggedOnTheLaterEntryOnly() throws { + let (model, _) = try makeModel("cloud code => Claude Code\nCloud-Code => Other\n") + #expect(model.rows[0].worst != .error) + #expect(model.rows[1].worst == .error) + } + + @Test func addUpdateAndRemoveGoThroughTheStore() throws { + let (model, store) = try makeModel() + let id = model.add(.term("Vercel"))! + #expect(store.entries == [term("Vercel")]) + model.update(id: id, to: .correction(heard: "versel", wrote: "Vercel")) + #expect(store.entries == [pair("versel", "Vercel")]) + model.remove(id: id) + #expect(store.entries.isEmpty) + #expect(model.rows.isEmpty) + } + + @Test func undoRestoresTheLastChangeStepByStep() throws { + let (model, store) = try makeModel("Supabase\n") + #expect(!model.canUndo) + let id = model.add(.term("Vercel"))! + model.remove(id: id) + #expect(store.entries == [term("Supabase")]) + model.undo() + #expect(store.entries == [term("Supabase"), term("Vercel")]) + model.undo() + #expect(store.entries == [term("Supabase")]) + #expect(!model.canUndo) + model.undo() // nothing left: harmless + #expect(store.entries == [term("Supabase")]) + } + + @Test func searchMatchesBothSidesIgnoringCaseAndDiacritics() throws { + let (model, _) = try makeModel("cloud code => Claude Code\nCafé\nVercel\n") + model.query = "CLAUDE" + #expect(model.visibleRows.count == 1) + model.query = "cafe" + #expect(model.visibleRows.count == 1) + model.query = "" + #expect(model.visibleRows.count == 3) + } + + @Test func editorWarningsIgnoreTheEntryBeingEdited() throws { + let (model, store) = try makeModel("cloud code => Claude Code\n") + let id = store.entries[0].id + let editingItself = model.warnings(for: .correction(heard: "cloud code", wrote: "Claude Code"), editing: id) + #expect(!editingItself.contains { $0.level == .error }) + let asNew = model.warnings(for: .correction(heard: "cloud code", wrote: "Other"), editing: nil) + #expect(asNew.contains { $0.level == .error }) + } +} +``` + +- [ ] **Step 2: Draai de tests, moeten falen** + +Run: `swift test --filter "StopwatchFormatTests|CorrectionMarkupTests|HistoryViewModelTests|DictionaryViewModelTests"` +Expected: compilatiefouten (`cannot find 'StopwatchFormat' in scope`). + +- [ ] **Step 3: Implementatie** + +`Sources/SstData/ViewSupport.swift`: + +```swift +import Combine +import Foundation +import SstCore + +/// "mm:ss.t" for the counter. +public enum StopwatchFormat { + public static func string(_ seconds: Double) -> String { + let tenths = Int(max(0, seconds) * 10) + return String(format: "%02d:%02d.%d", (tenths / 600) % 100, (tenths / 10) % 60, tenths % 10) + } +} + +public struct TextSegment: Equatable, Sendable { + public let text: String + /// Set when this piece was written by the dictionary correction. + public let correction: AppliedCorrection? + + public init(text: String, correction: AppliedCorrection? = nil) { + self.text = text + self.correction = correction + } +} + +public enum CorrectionMarkup { + /// Splits `finalText` into plain and corrected pieces. Offsets are UTF-16; out-of-range or overlapping ones are skipped. + public static func segments(finalText: String, corrections: [AppliedCorrection]) -> [TextSegment] { + let ns = finalText as NSString + guard ns.length > 0 else { return [] } + var result: [TextSegment] = [] + var cursor = 0 + for correction in corrections.sorted(by: { $0.start < $1.start }) { + let end = correction.start + correction.length + guard correction.length > 0, correction.start >= cursor, end <= ns.length else { continue } + if correction.start > cursor { + result.append(TextSegment(text: ns.substring(with: NSRange(location: cursor, length: correction.start - cursor)))) + } + result.append(TextSegment(text: ns.substring(with: NSRange(location: correction.start, length: correction.length)), correction: correction)) + cursor = end + } + if cursor < ns.length { + result.append(TextSegment(text: ns.substring(from: cursor))) + } + return result + } +} + +/// The searchable, day-grouped list behind the transcripts view. +@MainActor +public final class HistoryViewModel: ObservableObject { + private let store: HistoryStore + private let calendar: Calendar + + @Published public var query = "" { + didSet { refresh() } + } + @Published public var selection: UUID? + @Published public private(set) var days: [HistoryDay] = [] + @Published public private(set) var totalCount = 0 + + public init(store: HistoryStore, calendar: Calendar = .current) { + self.store = store + self.calendar = calendar + refresh() + } + + public func refresh() { + let visible = HistorySearch.filter(store.entries, query: query) + days = HistoryGrouping.byDay(visible, calendar: calendar) + totalCount = store.entries.count + if let selection, !visible.contains(where: { $0.id == selection }) { self.selection = nil } + } + + public var selectedEntry: HistoryEntry? { + store.entries.first { $0.id == selection } + } + + public func delete(id: UUID) { + store.remove(id: id) + refresh() + } +} + +/// Dictionary rows with warnings, search, editing and undo. +@MainActor +public final class DictionaryViewModel: ObservableObject { + public struct Row: Identifiable, Equatable { + public let id: UUID + public let entry: DictionaryEntry + public let warnings: [EntryWarning] + + public var worst: EntryWarning.Level? { warnings.map(\.level).max() } + } + + private let store: DictionaryStore + private let knowledge: WordKnowledge + private var undoStack: [DictionaryFile] = [] + + @Published public var query = "" { + didSet { applyFilter() } + } + @Published public private(set) var rows: [Row] = [] + @Published public private(set) var visibleRows: [Row] = [] + @Published public private(set) var canUndo = false + @Published public var errorMessage: String? + + public init(store: DictionaryStore, knowledge: WordKnowledge) { + self.store = store + self.knowledge = knowledge + reload() + } + + /// Rebuilds the rows from the store (call after the store changed). + public func reload() { + let entries = store.entries + rows = entries.enumerated().map { index, entry in + // Only earlier entries count as "already there", so a duplicate is flagged on the later one. + Row(id: entry.id, entry: entry, + warnings: EntryWarnings.check(entry.kind, among: Array(entries[.. [EntryWarning] { + EntryWarnings.check(kind, among: store.entries.filter { $0.id != id }, knowledge: knowledge) + } + + @discardableResult + public func add(_ kind: DictionaryEntry.Kind) -> UUID? { + var created: UUID? + mutate { created = $0.append(kind) } + return created + } + + public func update(id: UUID, to kind: DictionaryEntry.Kind) { + mutate { $0.update(id: id, kind: kind) } + } + + public func remove(id: UUID) { + mutate { $0.remove(id: id) } + } + + public func undo() { + guard let snapshot = undoStack.popLast() else { return } + canUndo = !undoStack.isEmpty + do { + try store.apply { $0 = snapshot } + } catch { + errorMessage = error.localizedDescription + } + reload() + } + + private func mutate(_ change: (inout DictionaryFile) -> Void) { + let snapshot = store.file + do { + try store.apply(change) + undoStack.append(snapshot) + canUndo = true + } catch { + errorMessage = error.localizedDescription + } + reload() + } +} +``` + +- [ ] **Step 4: Draai de tests** + +Run: `swift test --filter SstDataTests` +Expected: alle SstData-tests PASS (17 nieuwe). + +- [ ] **Step 5: Commit** + +```bash +git add Sources/SstData Tests/SstDataTests +git commit -m "feat: add view-model logic for history and dictionary, correction markup, stopwatch format" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 2: Fundament — modelkoppeling, teksten, lijsthulpen en snapshotmodus + +**Files:** +- Modify (via script): `Sources/Sst/AppModel.swift`, `Sources/Sst/Strings.swift`, `Sources/Sst/SstApp.swift` +- Create: `Sources/Sst/ListSupport.swift`, `Sources/Sst/main.swift`, `Sources/Sst/Snapshot.swift` + +**Interfaces:** +- Consumes: fase 4 (`AppModel`, `Strings`, `SstApp`, `MainWindow`-schil). +- Produces: + - `AppModel.historyList: HistoryViewModel`, `AppModel.dictionaryList: DictionaryViewModel` (verversen zelf bij wijzigingen van de stores), `AppModel.openDictionaryFile()`, `AppModel(preview: true)` + - `Strings` voor lijsten en woordenboek, `Strings.open`, `Strings.permissionsMissing` + - `SnapshotMode.isActive`, `ScrollList { }`, `SearchField(text:focused:)` + - `Sst --snapshot ` (alleen DEBUG): schrijft `-transcripts.png` en `-dictionary.png` (Aluminium links, Graphite rechts) + +- [ ] **Step 1: Bewerkingsscript uitvoeren** + +Maak tijdelijk `apply_plan5_edits.py` in de repo-root: + +```python +#!/usr/bin/env python3 +"""One-off for phase 5: extends AppModel, Strings and SstApp from their phase-4 state. Run from the repo root; every replacement is asserted.""" +from pathlib import Path + + +def edit(path, replacements): + p = Path(path) + s = p.read_text() + for old, new in replacements: + assert old in s, f"{path}: pattern not found:\n{old}" + s = s.replace(old, new, 1) + p.write_text(s) + + +# --- AppModel ----------------------------------------------------------------------------------------------- +edit("Sources/Sst/AppModel.swift", [ + # the two list view models + (" let storage = ModelStorage()\n", + " let storage = ModelStorage()\n let historyList: HistoryViewModel\n let dictionaryList: DictionaryViewModel\n"), + # preview mode: `init()` becomes a convenience init + (''' init() { + let settings = AppSettings() + let policy = InsertionPolicy() + let knowledge = SystemWordKnowledge() + let corrector = DictionaryCorrector(knowledge: knowledge) + let dictionary = DictionaryStore(fileURL: AppPaths.dictionary) + let history = HistoryStore(fileURL: AppPaths.history) +''', ''' convenience init() { + self.init(preview: false) + } + + /// `preview: true` is for snapshots: sample data in a temporary folder, no permission prompts, no model, no hotkey. + init(preview: Bool) { + let settings = preview ? AppSettings(defaults: UserDefaults(suiteName: "sst-preview")!) : AppSettings() + let policy = InsertionPolicy() + let knowledge = SystemWordKnowledge() + let corrector = DictionaryCorrector(knowledge: knowledge) + let folder = FileManager.default.temporaryDirectory.appending(path: "sst-preview-\\(UUID().uuidString)", directoryHint: .isDirectory) + let dictionary = DictionaryStore(fileURL: preview ? folder.appending(path: "dictionary.txt") : AppPaths.dictionary) + let history = HistoryStore(fileURL: preview ? folder.appending(path: "history.json") : AppPaths.history) +'''), + (''' self.dictionary = dictionary + self.history = history + self.controller''', ''' self.dictionary = dictionary + self.history = history + self.historyList = HistoryViewModel(store: history) + self.dictionaryList = DictionaryViewModel(store: dictionary, knowledge: knowledge) + self.controller'''), + (''' wire() + Task { await start() } + }''', ''' wire() + if preview { + loadPreviewData() + } else { + Task { await start() } + } + }'''), + # the views refresh when the stores change + (''' self.corrector.update(entries: self.dictionary.entries) + self.dictionaryRevision += 1''', ''' self.corrector.update(entries: self.dictionary.entries) + self.dictionaryList.reload() + self.dictionaryRevision += 1'''), + (" history.onChange = { [weak self] in self?.historyRevision += 1 }", + " history.onChange = { [weak self] in\n self?.historyRevision += 1\n self?.historyList.refresh()\n }"), + # STOP also ends a recording that the hotkey started + (''' func stopWindowRecording() { + guard policy.windowSession else { return }''', ''' func stopWindowRecording() { + guard isRecording else { return }'''), + ("if !permissions.allGranted { return Strings.openSystemSettings }", + "if !permissions.allGranted { return Strings.permissionsMissing }"), +]) +model = Path("Sources/Sst/AppModel.swift") +text = model.read_text().rstrip("\n") +assert text.endswith("}") +model.write_text(text[:-1] + ''' + func openDictionaryFile() { + NSWorkspace.shared.open(dictionary.fileURL) + } + + // MARK: preview + + private func loadPreviewData() { + try? dictionary.load() + try? dictionary.apply { file in + file.append(.term("Anthropic")) + file.append(.term("Supabase")) + file.append(.correction(heard: "cloud code", wrote: "Claude Code")) + file.append(.correction(heard: "cloud", wrote: "Claude")) + } + let now = Date() + history.add(HistoryEntry(date: now.addingTimeInterval(-86_400 * 2), duration: 5.1, + rawText: "een oudere notitie over de planning", finalText: "Een oudere notitie over de planning.")) + history.add(HistoryEntry(date: now.addingTimeInterval(-3_600), duration: 7.2, + rawText: "ik wil de meeting verplaatsen naar tomorrow morning", + finalText: "Ik wil de meeting verplaatsen naar tomorrow morning.")) + history.add(HistoryEntry( + date: now.addingTimeInterval(-300), duration: 4.4, + rawText: "ik gebruik cloud code met supabase", finalText: "Ik gebruik Claude Code met Supabase.", + corrections: [ + AppliedCorrection(heard: "cloud code", wrote: "Claude Code", rule: "cloud code", start: 11, length: 11), + AppliedCorrection(heard: "supabase", wrote: "Supabase", rule: "Supabase", start: 27, length: 8), + ])) + activeVariant = ModelCatalog.defaultVariant + modelStatus = .ready + lastDuration = 4.4 + historyList.selection = history.entries.first?.id + } +} +''') + +# --- Strings ------------------------------------------------------------------------------------------------ +edit("Sources/Sst/Strings.swift", [ + (' static let openSystemSettings = "Open Instellingen"\n', + ' static let openSystemSettings = "Open Instellingen"\n static let open = "Open"\n static let permissionsMissing = "Geef eerst de rechten onder Controles"\n'), + (" // deck\n", ''' // lists + static let copy = "Kopieer" + static let copyOriginal = "Kopieer origineel" + static let delete = "Verwijder" + static let original = "Origineel" + static let noTranscripts = "Nog geen transcripties. Houd de sneltoets ingedrukt en praat." + static let noResults = "Geen resultaten" + static let historyOff = "De geschiedenis staat uit (Instellingen → Privacy)." + static func corrections(_ count: Int) -> String { count == 1 ? "1 correctie" : "\\(count) correcties" } + static func entries(_ count: Int) -> String { count == 1 ? "1 invoer" : "\\(count) invoeren" } + + // dictionary + static let heard = "Gehoord" + static let wrote = "Schrijf" + static let term = "Term" + static let correction = "Correctie" + static let add = "+" + static let openFile = "Open bestand" + static let priming = "Voorrang" + static let correcting = "Correctie" + static let undo = "Herstel" + static let save = "Bewaar" + static let editEntry = "Invoer" + static let emptyDictionary = "Nog geen invoeren. Voeg een woord of een correctie toe met +." + + // deck +'''), +]) + +# --- SstApp: the entry point moves to main.swift (needed for the debug snapshot mode) ------------------------- +edit("Sources/Sst/SstApp.swift", [("@main\nstruct SstApp: App {", "struct SstApp: App {")]) +print("phase 5 edits applied") +``` + +Run: `python3 apply_plan5_edits.py && rm apply_plan5_edits.py` +Expected: `phase 5 edits applied`; het script is weg. + +- [ ] **Step 2: Nieuwe bestanden** + +`Sources/Sst/ListSupport.swift`: + +```swift +import SstUI +import SwiftUI + +/// Debug snapshots (`Sst --snapshot`) are drawn by `ImageRenderer`, which cannot draw scroll views or text fields. +/// While rendering one, the views below fall back to plain stacks and text. +@MainActor +enum SnapshotMode { + static var isActive = false +} + +/// A scrolling, lazily built list (a plain stack while a snapshot is rendered). +struct ScrollList: View { + @ViewBuilder var content: Content + + var body: some View { + if SnapshotMode.isActive { + VStack(alignment: .leading, spacing: 0) { content } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } else { + ScrollView { + LazyVStack(alignment: .leading, spacing: 0, pinnedViews: .sectionHeaders) { content } + } + } + } +} + +/// The recessed "ZOEK" field. +struct SearchField: View { + @Binding var text: String + var focused: FocusState.Binding + + var body: some View { + HStack(spacing: Space.s4) { + SilkLabel(Strings.search) + Group { + if SnapshotMode.isActive { + Text(text).frame(maxWidth: .infinity, alignment: .leading) + } else { + TextField("", text: $text) + .textFieldStyle(.plain) + .focused(focused) + } + } + .textStyle(Typography.body) + .foregroundStyle(Palette.inkPrimary) + .padding(Space.s3) + .background(WellBackground()) + } + } +} +``` + +`Sources/Sst/main.swift`: + +```swift +import AppKit + +#if DEBUG +// `Sst --snapshot ` renders the main window with sample data to -transcripts.png and +// -dictionary.png (Aluminium left, Graphite right) and exits. No prompts, no model, no hotkey. +if let flag = CommandLine.arguments.firstIndex(of: "--snapshot"), CommandLine.arguments.count > flag + 1 { + MainActor.assumeIsolated { Snapshot.run(prefix: CommandLine.arguments[flag + 1]) } + exit(0) +} +#endif + +SstApp.main() +``` + +`Sources/Sst/Snapshot.swift`: + +```swift +#if DEBUG +import AppKit +import SstUI +import SwiftUI + +@MainActor +enum Snapshot { + static func run(prefix: String) { + _ = NSApplication.shared + SnapshotMode.isActive = true + let model = AppModel(preview: true) + for section in AppModel.Section.allCases { + model.section = section + let view = HStack(spacing: 0) { + MainWindow().environment(\.colorScheme, .light) + MainWindow().environment(\.colorScheme, .dark) + } + .environmentObject(model) + .environmentObject(model.settings) + .frame(width: Layout.windowDefaultWidth * 2, height: Layout.windowDefaultHeight) + + let renderer = ImageRenderer(content: view) + renderer.scale = 1 + if let tiff = renderer.nsImage?.tiffRepresentation, + let png = NSBitmapImageRep(data: tiff)?.representation(using: .png, properties: [:]) { + try? png.write(to: URL(fileURLWithPath: "\(prefix)-\(section.rawValue).png")) + } + } + } +} +#endif +``` + +- [ ] **Step 3: Bouwen, testen en de snapshot-infrastructuur controleren** + +Run: `swift build --product Sst && swift test && .build/debug/Sst --snapshot /tmp/sst-main && ls /tmp/sst-main-*.png` +Expected: build zonder fouten of waarschuwingen; alle tests PASS (incl. token-scan); twee PNG's. Ze tonen nog de schil van het venster; de echte weergaven volgen in Task 3 t/m 5. + +- [ ] **Step 4: Commit** + +```bash +git add Sources/Sst +git commit -m "feat: wire list view models into AppModel, add strings, list helpers and debug snapshot mode" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 3: Deck, zijbalk met CONTROLES en het venster + +**Files:** +- Create: `Sources/Sst/DeckView.swift`, `Sources/Sst/Sidebar.swift`, `Sources/Sst/HistoryView.swift` (tijdelijk), `Sources/Sst/DictionaryView.swift` (tijdelijk) +- Replace: `Sources/Sst/MainWindow.swift` + +**Interfaces:** +- Consumes: Task 2; `SstUI` (`VUMeterView`, `SegmentCounterView`, `LEDBarView`, `KeyButtonStyle`, `LampView`, panelen); `LevelSource`, `StopwatchFormat`. +- Produces: `DeckView`, `Sidebar`, `ControlsBlock`, `MainWindow` (zijbalk + deck + gekozen weergave, met het wis-geschiedenis-dialoog), en tijdelijke lege `HistoryView(list:)` en `DictionaryView(list:)` die Task 4 en 5 vervangen. + +- [ ] **Step 1: Bestanden schrijven** + +`Sources/Sst/DeckView.swift`: + +```swift +import SstData +import SstUI +import SwiftUI + +/// The transport deck: VU meter, counter, LED bar and the REC/STOP keys. Always visible at the top. +struct DeckView: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + HStack(alignment: .top, spacing: Layout.panelGap) { + instruments + Spacer(minLength: 0) + controls + } + .padding(Layout.panelPadding) + .background(PanelBackground()) + } + + private var instruments: some View { + TimelineView(.animation(minimumInterval: 1 / Motion.needleUpdateHz, paused: !model.isRecording)) { context in + let dbfs = Double(model.levelSource.dbfs) + HStack(alignment: .top, spacing: Layout.panelGap) { + VUMeterView(levelDbfs: dbfs, isActive: model.isRecording, brand: AppIdentity.brandCaption) + .frame(width: MeterTokens.vuWidth) + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.time, style: Typography.labelSmall) + SegmentCounterView(StopwatchFormat.string(elapsed(at: context.date))) + SilkLabel(Strings.level, style: Typography.labelSmall) + LEDBarView(fraction: model.isRecording ? VUScale.fraction(forDbfs: dbfs) : 0) + } + } + } + } + + private func elapsed(at date: Date) -> TimeInterval { + if let started = model.recordingStartedAt { return date.timeIntervalSince(started) } + return model.lastDuration ?? 0 + } + + private var controls: some View { + VStack(alignment: .trailing, spacing: Layout.panelGap) { + HStack(spacing: Layout.panelGap) { + Button { model.toggleWindowRecording() } label: { + HStack(spacing: Space.s3) { + LampView(isOn: model.isRecording) + Text(Strings.record) + } + } + .buttonStyle(KeyButtonStyle(kind: .record, latched: model.isRecording)) + .disabled(!model.isRecording && model.recordingBlocker != nil) + .help(model.recordingBlocker ?? "") + + Button(Strings.stop) { model.stopWindowRecording() } + .buttonStyle(KeyButtonStyle()) + .disabled(!model.isRecording) + } + if let error = model.lastError { + HStack(spacing: Space.s3) { + LampView(isOn: true, tint: .amber) + Text(error) + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + .multilineTextAlignment(.trailing) + } + } else { + Text(model.statusLine) + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + .multilineTextAlignment(.trailing) + } + } + .frame(maxWidth: Layout.sidebarWidth, alignment: .trailing) + } +} +``` + +`Sources/Sst/Sidebar.swift`: + +```swift +import SstUI +import SwiftUI + +struct Sidebar: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(AppIdentity.brandCaption) + ForEach(AppModel.Section.allCases) { section in + Button { + model.section = section + } label: { + Text(title(for: section)).frame(maxWidth: .infinity) + } + .buttonStyle(KeyButtonStyle(latched: model.section == section)) + } + Spacer() + ControlsBlock() + } + .padding(Layout.panelPadding) + .frame(width: Layout.sidebarWidth) + } + + private func title(for section: AppModel.Section) -> String { + switch section { + case .transcripts: Strings.transcripts + case .dictionary: Strings.dictionary + } + } +} + +/// Four lamps: the three macOS permissions and the speech model. +struct ControlsBlock: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + VStack(alignment: .leading, spacing: Space.s3) { + SilkLabel(Strings.controls) + SeamDivider() + ForEach(PermissionMonitor.Kind.allCases) { kind in + let granted = model.permissions.isGranted(kind) + HStack(spacing: Space.s3) { + LampView(isOn: granted, tint: .amber) + Text(kind.title).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + Spacer() + if !granted { + Button(Strings.open) { model.permissions.openSettings(for: kind) } + .buttonStyle(KeyButtonStyle()) + .help(Strings.openSystemSettings) + } + } + } + HStack(spacing: Space.s3) { + LampView(isOn: model.modelReady, tint: .amber) + Text(Strings.modelLamp).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + Spacer() + } + } + .onAppear { model.permissions.startPolling() } + .onDisappear { model.permissions.stopPolling() } + } +} +``` + +`Sources/Sst/MainWindow.swift` (vervang volledig): + +```swift +import SstUI +import SwiftUI + +struct MainWindow: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + ZStack { + BrushedBackground().ignoresSafeArea() + HStack(spacing: 0) { + Sidebar() + VStack(spacing: Layout.panelGap) { + DeckView() + switch model.section { + case .transcripts: HistoryView(list: model.historyList) + case .dictionary: DictionaryView(list: model.dictionaryList) + } + } + .padding(Layout.panelPadding) + } + } + .confirmationDialog(Strings.clearHistoryConfirm, isPresented: $model.confirmingClearHistory) { + Button(Strings.erase, role: .destructive) { model.clearHistory() } + Button(Strings.cancel, role: .cancel) {} + } message: { + Text(Strings.clearHistoryDetail) + } + } +} +``` + +Tijdelijk `Sources/Sst/HistoryView.swift`: + +```swift +import SstData +import SstUI +import SwiftUI + +// Temporary: replaced in Task 4. +struct HistoryView: View { + @ObservedObject var list: HistoryViewModel + var body: some View { PaperBackground() } +} +``` + +Tijdelijk `Sources/Sst/DictionaryView.swift`: + +```swift +import SstData +import SstUI +import SwiftUI + +// Temporary: replaced in Task 5. +struct DictionaryView: View { + @ObservedObject var list: DictionaryViewModel + var body: some View { PaperBackground() } +} +``` + +- [ ] **Step 2: Bouwen, testen en bekijken** + +Run: `swift build --product Sst && swift test && .build/debug/Sst --snapshot /tmp/sst-main && open /tmp/sst-main-transcripts.png` +Expected: build zonder fouten of waarschuwingen; alle tests PASS. De afbeelding (Aluminium links, Graphite rechts) toont links de zijbalk met twee navigatietoetsen (de gekozen is ingedrukt) en onderaan CONTROLES met vier lampjes en korte "Open"-toetsen (geen afgebroken tekst); rechts het deck met VU-meter, teller, LED-balk, REC (uitgeschakeld) en STOP, met de melding "Geef eerst de rechten onder Controles"; daaronder een leeg papieren vlak. + +- [ ] **Step 3: Commit** + +```bash +git add Sources/Sst +git commit -m "feat: add deck, sidebar with controls and the main window layout" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 4: Transcriptiegeschiedenis + +**Files:** +- Replace: `Sources/Sst/HistoryView.swift` + +**Interfaces:** +- Consumes: Task 1 (`HistoryViewModel`, `CorrectionMarkup`), Task 2 (`ScrollList`, `SearchField`, `Strings`). +- Produces: `HistoryView(list:)` — zoekveld (focus via `AppModel.searchFocusTick`), lijst per dag, rij met kopieerknop, correctiemarkering en -samenvatting, contextmenu, ⌫-verwijderen, lege staten. + +- [ ] **Step 1: Implementatie** + +`Sources/Sst/HistoryView.swift` (vervang volledig): + +```swift +import AppKit +import SstCore +import SstData +import SstUI +import SwiftUI + +private let dutch = Locale(identifier: "nl_NL") + +struct HistoryView: View { + @EnvironmentObject private var model: AppModel + @ObservedObject var list: HistoryViewModel + @FocusState private var searchFocused: Bool + + var body: some View { + VStack(spacing: Layout.panelGap) { + SearchField(text: $list.query, focused: $searchFocused) + ZStack { + PaperBackground() + content + } + } + .onChange(of: model.searchFocusTick) { _, _ in searchFocused = true } + } + + @ViewBuilder + private var content: some View { + if list.days.isEmpty { + Text(emptyMessage) + .textStyle(Typography.body) + .foregroundStyle(Palette.inkSecondary) + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } else { + ScrollList { + ForEach(list.days) { day in + Section { + ForEach(day.entries) { entry in + HistoryRow(entry: entry, isSelected: list.selection == entry.id, list: list) + } + } header: { + SilkLabel(day.day.formatted(Date.FormatStyle(date: .complete, time: .omitted).locale(dutch))) + .padding(.horizontal, Space.s4) + .padding(.vertical, Space.s3) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Palette.surfacePaper) + } + } + } + .focusable() + .onDeleteCommand { + if let id = list.selection { list.delete(id: id) } + } + } + } + + private var emptyMessage: String { + if list.totalCount > 0 { return Strings.noResults } + return model.settings.keepHistory ? Strings.noTranscripts : Strings.historyOff + } +} + +private struct HistoryRow: View { + let entry: HistoryEntry + let isSelected: Bool + @ObservedObject var list: HistoryViewModel + + var body: some View { + HStack(alignment: .firstTextBaseline, spacing: Space.s4) { + Text(entry.date.formatted(Date.FormatStyle().hour().minute().second().locale(dutch))) + .textStyle(Typography.counterSmall) + .foregroundStyle(Palette.inkSecondary) + VStack(alignment: .leading, spacing: Space.s2) { + Text(markedText) + .textStyle(Typography.reading) + .foregroundStyle(Palette.inkPrimary) + .textSelection(.enabled) + if !entry.corrections.isEmpty { + SilkLabel(correctionSummary, style: Typography.labelSmall) + } + if isSelected && entry.rawText != entry.finalText { + Text("\(Strings.original): \(entry.rawText)") + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + } + } + Spacer(minLength: Space.s4) + Button(Strings.copy) { copy(entry.finalText) } + .buttonStyle(KeyButtonStyle()) + } + .padding(.horizontal, Space.s4) + .padding(.vertical, Space.s3) + .frame(minHeight: Size.rowHeight) + .background(isSelected ? Palette.surfaceSelection : Color.clear) + .contentShape(Rectangle()) + .onTapGesture { list.selection = entry.id } + .contextMenu { + Button(Strings.copy) { copy(entry.finalText) } + Button(Strings.copyOriginal) { copy(entry.rawText) } + Divider() + Button(Strings.delete, role: .destructive) { list.delete(id: entry.id) } + } + } + + /// The final text with every dictionary replacement highlighted. + private var markedText: AttributedString { + var result = AttributedString() + for segment in CorrectionMarkup.segments(finalText: entry.finalText, corrections: entry.corrections) { + var piece = AttributedString(segment.text) + if segment.correction != nil { + piece.backgroundColor = Palette.levelAmber.opacity(Opacity.mark) + } + result += piece + } + return result + } + + private var correctionSummary: String { + let changes = entry.corrections.map { "\($0.heard) → \($0.wrote)" }.joined(separator: ", ") + return "\(Strings.corrections(entry.corrections.count)) · \(changes)" + } + + private func copy(_ text: String) { + NSPasteboard.general.clearContents() + NSPasteboard.general.setString(text, forType: .string) + } +} +``` + +- [ ] **Step 2: Bouwen, testen en bekijken** + +Run: `swift build --product Sst && swift test && .build/debug/Sst --snapshot /tmp/sst-main && open /tmp/sst-main-transcripts.png` +Expected: build zonder fouten of waarschuwingen; alle tests PASS. De afbeelding toont drie voorbeeldtranscripties onder twee dagkoppen in het Nederlands ("DONDERDAG 1 OKTOBER 2026"); de nieuwste (geselecteerde) rij heeft "Claude Code" en "Supabase" met amber markering, de regel "2 CORRECTIES · CLOUD CODE → CLAUDE CODE, SUPABASE → SUPABASE" en de oorspronkelijke tekst; elke rij heeft een KOPIEER-toets; tijden staan in mono. + +- [ ] **Step 3: Commit** + +```bash +git add Sources/Sst +git commit -m "feat: add transcript history view with corrections, search and copy" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 5: Woordenboek met editor + +**Files:** +- Replace: `Sources/Sst/DictionaryView.swift` + +**Interfaces:** +- Consumes: Task 1 (`DictionaryViewModel`), Task 2 (`ScrollList`, `SearchField`, `Strings`, `AppModel.openDictionaryFile()`), fase 4 (`RockerToggleStyle`, `AppSettings`). +- Produces: `DictionaryView(list:)` met zoekveld, `+`, "Open bestand", schakelaars Voorrang/Correctie, tabel, voettekst met telling en "Herstel", en de editor (sheet) met live waarschuwingen. + +- [ ] **Step 1: Implementatie** + +`Sources/Sst/DictionaryView.swift` (vervang volledig): + +```swift +import SstData +import SstUI +import SwiftUI + +struct DictionaryView: View { + enum EditorTarget: Identifiable, Equatable { + case new + case existing(UUID) + + var id: String { + switch self { + case .new: "new" + case .existing(let id): id.uuidString + } + } + } + + @EnvironmentObject private var model: AppModel + @EnvironmentObject private var settings: AppSettings + @ObservedObject var list: DictionaryViewModel + @State private var selection: UUID? + @State private var editing: EditorTarget? + @FocusState private var searchFocused: Bool + + var body: some View { + VStack(spacing: Layout.panelGap) { + header + ZStack { + PaperBackground() + table + } + footer + } + .sheet(item: $editing) { target in + EntryEditor(list: list, target: target) { editing = nil } + } + } + + private var header: some View { + VStack(spacing: Layout.panelGap) { + HStack(spacing: Space.s4) { + SearchField(text: $list.query, focused: $searchFocused) + Button(Strings.add) { editing = .new }.buttonStyle(KeyButtonStyle()) + Button(Strings.openFile) { model.openDictionaryFile() }.buttonStyle(KeyButtonStyle()) + } + HStack(spacing: Layout.panelGap) { + Toggle(isOn: $settings.usePriming) { + SilkLabel(Strings.priming) + } + .toggleStyle(RockerToggleStyle()) + Toggle(isOn: $settings.useCorrection) { + SilkLabel(Strings.correcting) + } + .toggleStyle(RockerToggleStyle()) + Spacer() + } + } + } + + private var table: some View { + VStack(spacing: 0) { + HStack(spacing: Space.s4) { + Color.clear.frame(width: Size.iconMedium, height: Size.iconMedium) + SilkLabel(Strings.heard).frame(maxWidth: .infinity, alignment: .leading) + SilkLabel(Strings.wrote).frame(maxWidth: .infinity, alignment: .leading) + } + .padding(.horizontal, Space.s4) + .padding(.vertical, Space.s3) + if list.visibleRows.isEmpty { + Text(list.rows.isEmpty ? Strings.emptyDictionary : Strings.noResults) + .textStyle(Typography.body) + .foregroundStyle(Palette.inkSecondary) + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + } else { + ScrollList { + ForEach(list.visibleRows) { row in + DictionaryRow(row: row, isSelected: selection == row.id) + .onTapGesture(count: 2) { editing = .existing(row.id) } + .onTapGesture { selection = row.id } + } + } + } + } + .focusable() + .onDeleteCommand { + if let id = selection { list.remove(id: id) } + } + .onKeyPress(KeyEquivalent("z"), phases: .down) { press in + guard press.modifiers == .command, list.canUndo else { return .ignored } + list.undo() + return .handled + } + } + + private var footer: some View { + HStack(spacing: Layout.panelGap) { + SilkLabel(Strings.entries(list.rows.count), style: Typography.labelSmall) + if let message = list.errorMessage { + Text(message).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + } + Spacer() + Button(Strings.undo) { list.undo() } + .buttonStyle(KeyButtonStyle()) + .disabled(!list.canUndo) + } + } +} + +private struct DictionaryRow: View { + let row: DictionaryViewModel.Row + let isSelected: Bool + + var body: some View { + HStack(spacing: Space.s4) { + lamp + .frame(width: Size.iconMedium, height: Size.iconMedium) + if let heard = heard { + Text(heard).textStyle(Typography.code).foregroundStyle(Palette.inkPrimary) + .frame(maxWidth: .infinity, alignment: .leading) + } else { + Text("—").textStyle(Typography.code).foregroundStyle(Palette.inkSecondary) + .frame(maxWidth: .infinity, alignment: .leading) + } + Text(row.entry.wrote).textStyle(Typography.code).foregroundStyle(Palette.inkPrimary) + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding(.horizontal, Space.s4) + .padding(.vertical, Space.s3) + .frame(minHeight: Size.rowHeight) + .background(isSelected ? Palette.surfaceSelection : Color.clear) + .contentShape(Rectangle()) + .help(row.warnings.map(\.message).joined(separator: "\n")) + } + + private var heard: String? { + if case .correction(let heard, _) = row.entry.kind { return heard } + return nil + } + + @ViewBuilder + private var lamp: some View { + switch row.worst { + case .error: LampView(isOn: true) + case .warning: LampView(isOn: true, tint: .amber) + default: LampView(isOn: false, tint: .amber) + } + } +} + +/// Add or edit one entry, with live warnings. +private struct EntryEditor: View { + @ObservedObject var list: DictionaryViewModel + let target: DictionaryView.EditorTarget + let onClose: () -> Void + + @State private var isCorrection = false + @State private var heard = "" + @State private var wrote = "" + + private var editingID: UUID? { + if case .existing(let id) = target { return id } + return nil + } + + private var kind: DictionaryEntry.Kind { + let write = wrote.trimmingCharacters(in: .whitespaces) + return isCorrection ? .correction(heard: heard.trimmingCharacters(in: .whitespaces), wrote: write) : .term(write) + } + + private var warnings: [EntryWarning] { + let emptySource = isCorrection ? heard.trimmingCharacters(in: .whitespaces).isEmpty : wrote.trimmingCharacters(in: .whitespaces).isEmpty + guard !emptySource, !wrote.trimmingCharacters(in: .whitespaces).isEmpty else { return [] } + return list.warnings(for: kind, editing: editingID) + } + + private var canSave: Bool { + let hasText = !wrote.trimmingCharacters(in: .whitespaces).isEmpty && (!isCorrection || !heard.trimmingCharacters(in: .whitespaces).isEmpty) + return hasText && !warnings.contains { $0.level == .error } + } + + var body: some View { + ZStack { + BrushedBackground().ignoresSafeArea() + VStack(alignment: .leading, spacing: Layout.panelGap) { + HStack(spacing: Space.s3) { + Button(Strings.term) { isCorrection = false }.buttonStyle(KeyButtonStyle(latched: !isCorrection)) + Button(Strings.correction) { isCorrection = true }.buttonStyle(KeyButtonStyle(latched: isCorrection)) + } + VStack(alignment: .leading, spacing: Layout.panelGap) { + if isCorrection { field(Strings.heard, text: $heard) } + field(Strings.wrote, text: $wrote) + ForEach(Array(warnings.enumerated()), id: \.offset) { _, warning in + HStack(alignment: .firstTextBaseline, spacing: Space.s3) { + LampView(isOn: warning.level != .note, tint: warning.level == .error ? .record : .amber) + Text(warning.message).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + } + } + } + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, alignment: .leading) + .background(PanelBackground()) + HStack(spacing: Space.s3) { + Spacer() + Button(Strings.cancel, action: onClose).buttonStyle(KeyButtonStyle()) + Button(Strings.save, action: save).buttonStyle(KeyButtonStyle(kind: .record)).disabled(!canSave) + } + } + .padding(Layout.panelPadding) + } + .frame(width: Layout.settingsWidth) + .onAppear(perform: load) + } + + private func field(_ title: String, text: Binding) -> some View { + HStack(spacing: Space.s4) { + SilkLabel(title).frame(width: Size.keyRecWidth, alignment: .leading) + TextField("", text: text) + .textFieldStyle(.plain) + .textStyle(Typography.code) + .foregroundStyle(Palette.inkPrimary) + .padding(Space.s3) + .background(WellBackground()) + } + } + + private func load() { + guard let id = editingID, let row = list.rows.first(where: { $0.id == id }) else { return } + switch row.entry.kind { + case .term(let term): (isCorrection, heard, wrote) = (false, "", term) + case .correction(let h, let w): (isCorrection, heard, wrote) = (true, h, w) + } + } + + private func save() { + if let id = editingID { + list.update(id: id, to: kind) + } else { + list.add(kind) + } + onClose() + } +} +``` + +- [ ] **Step 2: Bouwen, testen en bekijken** + +Run: `swift build --product Sst && swift test && .build/debug/Sst --snapshot /tmp/sst-main && open /tmp/sst-main-dictionary.png` +Expected: build zonder fouten of waarschuwingen; alle tests PASS. De afbeelding toont het woordenboek met vier voorbeeldinvoeren: twee termen ("—" links), "cloud code → Claude Code" en "cloud → Claude"; alleen de laatste heeft een brandend amber lampje (gewoon woord). Zoekveld, `+` en OPEN BESTAND staan boven; twee tuimelschakelaars (VOORRANG, CORRECTIE); onderaan "4 INVOEREN" en een HERSTEL-toets (uitgeschakeld). + +- [ ] **Step 3: Commit** + +```bash +git add Sources/Sst +git commit -m "feat: add dictionary view with editor, warnings and undo" -m "Co-Authored-By: Claude Sonnet 5.5 " +``` + +--- + +### Task 6: Handmatige acceptatie met de gebruiker + +Dit vraagt rechten, het toetsenbord en het scherm van de gebruiker; subagents voeren dit niet uit. + +- [ ] **Step 1: Voorbereiden** + +Run: `./scripts/make-app.sh && open build/Sst.app` (rechten en model zijn al in orde uit fase 4). Alle vier CONTROLES-lampjes branden binnen enkele seconden. + +- [ ] **Step 2: Deck en opnemen** + +1. Klik REC: de toets blijft ingedrukt, het lampje brandt, de teller loopt, de wijzer volgt je stem en zakt terug bij stilte; de piek-LED gaat aan boven 0 VU. Klik STOP. +2. Het resultaat staat in Transcripties; er is **niets geplakt** (opname vanuit het venster). +3. Houd Fn ingedrukt in TextEdit en spreek: de tekst wordt geplakt én staat in de lijst. Het deck beweegt ook dan. +4. Start een opname met de sneltoets en klik STOP in het venster: de opname eindigt en wordt verwerkt. +5. ⌥⌘R start en stopt een opname. + +- [ ] **Step 3: Geschiedenis** + +1. Zoek met ⌘F (veld krijgt focus); zoek "cafe" vindt "Café"; meerdere woorden in willekeurige volgorde. +2. KOPIEER zet de tekst op het klembord; rechtsklik: kopieer origineel, verwijder; selecteer een rij en druk ⌫. +3. Zet in Instellingen → Privacy "Transcripties bewaren" uit, dicteer: er komt geen nieuwe rij; de lege staat verwijst naar Instellingen. Zet terug aan. "Wis geschiedenis…" vraagt bevestiging en wist alles. + +- [ ] **Step 4: Woordenboek** + +1. Voeg met `+` de correctie `cloud code => Claude Code` toe: de editor toont een gele melding (alle delen zijn gewone woorden); bewaar. +2. Zeg "ik gebruik cloud code": de tekst bevat "Claude Code"; de rij in Transcripties toont de markering en de samenvatting. Zeg ook "CloudCode" en "cloud-code": beide worden "Claude Code". Zeg "Cloudflare" en "the cloud": blijven ongewijzigd. +3. Voeg `cloud => Claude` toe: waarschuwing (gewoon woord), lampje brandt amber. Verwijder met ⌫ en herstel met ⌘Z. +4. Voeg de term `Supabase` toe en zeg het woord: de schrijfwijze is goed. +5. Zet VOORRANG uit, dan CORRECTIE uit: elk mechanisme heeft zichtbaar effect (zonder CORRECTIE blijft "cloud code" staan). +6. Open het bestand met OPEN BESTAND, voeg in de editor een regel toe en bewaar: de lijst in Sst werkt binnen een seconde bij. Wijzig een regel in Sst: het bestand verandert, met behoud van `#`-regels. +7. Meet de snelheid: zeg 8 seconden spraak met en zonder VOORRANG; noteer beide tijden (doel: onder 1,5 s). +8. Zeg niets (hou de sneltoets 2 s ingedrukt) met een gevuld woordenboek: er wordt geen tekst ingevoegd. + +- [ ] **Step 5: Toegankelijkheid en afronden** + +1. Alle bediening is met het toetsenbord bereikbaar; de focusring is zichtbaar. +2. VoiceOver leest "Niveau" en "Tijd" voor; de lampjes zijn verborgen voor VoiceOver. +3. Zet "Verminder beweging" aan: geen overschrijding van de wijzer. +4. Draai `swift test`: alle tests PASS. +5. Noteer afwijkingen; kleine visuele bijstellingen horen bij deze taak. + +--- + +## Self-review + +- **Spec-dekking:** deck met meter/teller/LED/REC/STOP en uitschakelreden, zijbalk met CONTROLES, geschiedenis (dagen, markering, samenvatting, origineel bij selectie, zoeken, kopieerknop, contextmenu, ⌫, lege staten, geschiedenis-uit-melding), woordenboek (tabel, `+`, editor met live waarschuwingen, dubbelklik, ⌫, ⌘Z/Herstel, zoeken, bestand openen, twee schakelaars, lampje per rij), tekst-niet-plakken-voorop en alleen-opslaan (in fase 4), handmatige lijst uit de spec. Niet in scope: overlay, cleanup. +- **Placeholders:** geen; de twee tijdelijke views in Task 3 zijn volledig en worden in Task 4 en 5 vervangen. +- **Typeconsistentie:** `HistoryViewModel`/`DictionaryViewModel` (Task 1) worden door `AppModel` (Task 2) en de views (Task 3–5) met dezelfde namen gebruikt; `Strings`-sleutels in de views bestaan na het script in Task 2; `AppSettings` wordt aan het hoofdvenster meegegeven in `SstApp` (fase 4). +- **Risico's:** `ImageRenderer` tekent geen scrollende lijsten of tekstvelden; `SnapshotMode` vervangt ze alleen tijdens snapshots. Gedrag van ⌘Z en ⌫ op een focusbare lijst en de sheet-editor is alleen handmatig te controleren (Task 6 stap 4). diff --git a/docs/superpowers/specs/2026-10-01-macos-app-design.md b/docs/superpowers/specs/2026-10-01-macos-app-design.md index 6f468c9..fcf351c 100644 --- a/docs/superpowers/specs/2026-10-01-macos-app-design.md +++ b/docs/superpowers/specs/2026-10-01-macos-app-design.md @@ -130,7 +130,7 @@ Tabel op papier-oppervlak met kolommen GEHOORD → SCHRIJF. Een regel zonder pij Venster `layout.settings.width` breed, drie tabbladen als zeefdruk-toetsen. - **Sneltoets:** de gekozen toets wordt vastgelegd door erop te drukken (de app is dan actief en luistert met een lokale event-monitor). Alleen de toegestane modifiers uit beslissing 8; andere toetsen worden afgewezen met uitleg. Bij Fn staat een hint over de Globe-instelling van macOS. -- **Model:** vaste lijst van varianten (op dit moment: `large-v3-v20240930_turbo` standaard, de 632 MB-variant en `small`; exacte namen worden bij de bouw tegen WhisperKit gecontroleerd). Per model: status (aanwezig, gekozen, niet gedownload), download- of verwijderknop en voortgang als LED-balk. Wisselen laadt het nieuwe model en zet dicteren uit tot het klaar is; mislukt het laden, dan blijft het vorige model actief met een duidelijke melding. Toont de opslaglocatie met "Toon in Finder". +- **Model:** vaste lijst van drie varianten, namen en groottes gecontroleerd bij Hugging Face (`argmaxinc/whisperkit-coreml`): `large-v3-v20240930_turbo` (standaard, 1638 MB), `large-v3-v20240930_turbo_632MB` (646 MB) en `small` (486 MB). Per model: status (aanwezig, gekozen, niet gedownload), download- of verwijderknop en voortgang als LED-balk. Wisselen laadt het nieuwe model en zet dicteren uit tot het klaar is; mislukt het laden, dan blijft het vorige model actief met een duidelijke melding. Toont de opslaglocatie met "Toon in Finder". - **Privacy:** schakelaar "Transcripties bewaren" en knop "Wis geschiedenis…" (met bevestiging). ## Woordenboek — het ontwerp in detail From aec4aa7a5b7d506c175ff731c1c4afd6a7b20bfb Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:52:33 +0200 Subject: [PATCH 07/43] feat: add vocabulary-aware Transcriber and shared correction/result types Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstCore/Models.swift | 57 +++++++++++++++++++ .../TranscriberVocabularyTests.swift | 21 +++++++ 2 files changed, 78 insertions(+) create mode 100644 Tests/SstCoreTests/TranscriberVocabularyTests.swift diff --git a/Sources/SstCore/Models.swift b/Sources/SstCore/Models.swift index 36444a2..9cdac52 100644 --- a/Sources/SstCore/Models.swift +++ b/Sources/SstCore/Models.swift @@ -24,6 +24,63 @@ public protocol AudioRecording { public protocol Transcriber { func transcribe(_ samples: [Float]) async throws -> String + /// Biases decoding towards `vocabulary`. Engines without bias support ignore the list. + func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String +} + +public extension Transcriber { + func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + try await transcribe(samples) + } +} + +/// One replacement made by the dictionary correction pass. +/// `start`/`length` are UTF-16 offsets into the corrected text. +public struct AppliedCorrection: Equatable, Codable, Sendable { + public var heard: String + public var wrote: String + public var rule: String + public var start: Int + public var length: Int + + public init(heard: String, wrote: String, rule: String, start: Int, length: Int) { + self.heard = heard + self.wrote = wrote + self.rule = rule + self.start = start + self.length = length + } +} + +public struct CorrectionResult: Equatable, Sendable { + public var text: String + public var corrections: [AppliedCorrection] + + public init(text: String, corrections: [AppliedCorrection]) { + self.text = text + self.corrections = corrections + } +} + +public protocol TextCorrecting { + func correct(_ text: String) -> CorrectionResult +} + +/// A finished dictation, emitted just before the text is inserted. +public struct DictationResult: Equatable, Sendable { + public var date: Date + public var duration: TimeInterval + public var rawText: String + public var finalText: String + public var corrections: [AppliedCorrection] + + public init(date: Date, duration: TimeInterval, rawText: String, finalText: String, corrections: [AppliedCorrection]) { + self.date = date + self.duration = duration + self.rawText = rawText + self.finalText = finalText + self.corrections = corrections + } } public protocol Cleaner { diff --git a/Tests/SstCoreTests/TranscriberVocabularyTests.swift b/Tests/SstCoreTests/TranscriberVocabularyTests.swift new file mode 100644 index 0000000..e066b40 --- /dev/null +++ b/Tests/SstCoreTests/TranscriberVocabularyTests.swift @@ -0,0 +1,21 @@ +import Foundation +import Testing +@testable import SstCore + +private struct PlainTranscriber: Transcriber { + func transcribe(_ samples: [Float]) async throws -> String { "plain \(samples.count)" } +} + +struct TranscriberVocabularyTests { + @Test func vocabularyVariantFallsBackToPlainTranscribe() async throws { + let result = try await PlainTranscriber().transcribe([0, 0, 0], vocabulary: ["Anthropic"]) + #expect(result == "plain 3") + } + + @Test func appliedCorrectionRoundTripsThroughJSON() throws { + let original = AppliedCorrection(heard: "cloud code", wrote: "Claude Code", rule: "cloud code", start: 12, length: 11) + let data = try JSONEncoder().encode(original) + let decoded = try JSONDecoder().decode(AppliedCorrection.self, from: data) + #expect(decoded == original) + } +} From 7fbc182fa7c524d77eb7d470d61a20a2c7386465 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:54:49 +0200 Subject: [PATCH 08/43] feat: controller supports vocabulary bias, prompt-echo guard, corrector and result hook Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstCore/DictationController.swift | 39 +++++- Sources/SstCore/PromptEcho.swift | 21 +++ .../DictationControllerHooksTests.swift | 124 ++++++++++++++++++ Tests/SstCoreTests/Fakes.swift | 28 ++++ Tests/SstCoreTests/PromptEchoTests.swift | 31 +++++ 5 files changed, 236 insertions(+), 7 deletions(-) create mode 100644 Sources/SstCore/PromptEcho.swift create mode 100644 Tests/SstCoreTests/DictationControllerHooksTests.swift create mode 100644 Tests/SstCoreTests/PromptEchoTests.swift diff --git a/Sources/SstCore/DictationController.swift b/Sources/SstCore/DictationController.swift index 5db278c..3c6c2f6 100644 --- a/Sources/SstCore/DictationController.swift +++ b/Sources/SstCore/DictationController.swift @@ -7,6 +7,8 @@ public final class DictationController { } public var onStateChange: ((DictationState) -> Void)? public var onError: ((String) -> Void)? + /// Called with every finished transcription, just before it is inserted. + public var onResult: ((DictationResult) -> Void)? private let recorder: AudioRecording private let transcriber: Transcriber @@ -14,16 +16,25 @@ public final class DictationController { private let inserter: TextInserting private let minimumSamples: Int private let silenceThreshold: Float + private let corrector: TextCorrecting? + private let vocabularyProvider: () -> [String] + private let biasMinimumRMS: Float /// - Parameter minimumSamples: 16 kHz samples; default is 0.3 s. /// - Parameter silenceThreshold: RMS below which a recording counts as silence and is skipped. + /// - Parameter corrector: optional dictionary correction applied to the transcript. + /// - Parameter vocabularyProvider: words to bias the transcriber towards. + /// - Parameter biasMinimumRMS: below this RMS no vocabulary is passed (guards against invented text on quiet audio). public init( recorder: AudioRecording, transcriber: Transcriber, cleaner: Cleaner, inserter: TextInserting, minimumSamples: Int = 4_800, - silenceThreshold: Float = 0.005 + silenceThreshold: Float = 0.005, + corrector: TextCorrecting? = nil, + vocabularyProvider: @escaping () -> [String] = { [] }, + biasMinimumRMS: Float = 0.015 ) { self.recorder = recorder self.transcriber = transcriber @@ -31,6 +42,9 @@ public final class DictationController { self.inserter = inserter self.minimumSamples = minimumSamples self.silenceThreshold = silenceThreshold + self.corrector = corrector + self.vocabularyProvider = vocabularyProvider + self.biasMinimumRMS = biasMinimumRMS } public func handle(_ event: HotkeyEvent) async { @@ -49,24 +63,35 @@ public final class DictationController { return } let energy = samples.reduce(0) { $0 + $1 * $1 } - guard (energy / Float(samples.count)).squareRoot() >= silenceThreshold else { + let rms = (energy / Float(samples.count)).squareRoot() + guard rms >= silenceThreshold else { state = .idle return } - await process(samples) + await process(samples, rms: rms) default: break } } - private func process(_ samples: [Float]) async { + private func process(_ samples: [Float], rms: Float) async { defer { state = .idle } do { state = .transcribing - let raw = try await transcriber.transcribe(samples) + let vocabulary = rms >= biasMinimumRMS ? vocabularyProvider() : [] + let raw = try await transcriber.transcribe(samples, vocabulary: vocabulary) let text = raw.trimmingCharacters(in: .whitespacesAndNewlines) - guard !text.isEmpty else { return } - let cleaned = try await cleaner.clean(text, context: CleaningContext()) + guard !text.isEmpty, !PromptEcho.isEcho(text, vocabulary: vocabulary) else { return } + let corrected = corrector?.correct(text) ?? CorrectionResult(text: text, corrections: []) + let cleaned = try await cleaner.clean(corrected.text, context: CleaningContext()) + onResult?(DictationResult( + date: Date(), + duration: Double(samples.count) / 16_000, + rawText: text, + finalText: cleaned, + // Offsets only stay valid if the cleaner left the corrected text alone. + corrections: cleaned == corrected.text ? corrected.corrections : [] + )) state = .inserting try await inserter.insert(cleaned) } catch { diff --git a/Sources/SstCore/PromptEcho.swift b/Sources/SstCore/PromptEcho.swift new file mode 100644 index 0000000..8c11184 --- /dev/null +++ b/Sources/SstCore/PromptEcho.swift @@ -0,0 +1,21 @@ +import Foundation + +/// Whisper sometimes answers a vocabulary prompt with the prompt itself when there is little speech. +public enum PromptEcho { + static func normalize(_ text: String) -> String { + String(text.lowercased().map { $0.isLetter || $0.isNumber ? $0 : " " }) + .split(separator: " ") + .joined(separator: " ") + } + + /// True when `text` is nothing but the first k ≥ 2 terms of the prompt. + /// A single term on its own is valid speech ("Anthropic"). + public static func isEcho(_ text: String, vocabulary: [String]) -> Bool { + guard vocabulary.count >= 2 else { return false } + let normalized = normalize(text) + guard !normalized.isEmpty else { return false } + return (2...vocabulary.count).contains { k in + normalize(vocabulary.prefix(k).joined(separator: " ")) == normalized + } + } +} diff --git a/Tests/SstCoreTests/DictationControllerHooksTests.swift b/Tests/SstCoreTests/DictationControllerHooksTests.swift new file mode 100644 index 0000000..fda62e6 --- /dev/null +++ b/Tests/SstCoreTests/DictationControllerHooksTests.swift @@ -0,0 +1,124 @@ +import Foundation +import Testing +@testable import SstCore + +@MainActor +struct DictationControllerHooksTests { + let recorder = FakeRecorder() + let transcriber = FakeVocabularyTranscriber() + let inserter = FakeInserter() + static let loud = [Float](repeating: 0.1, count: 16_000) + static let quiet = [Float](repeating: 0.01, count: 16_000) + + func makeController( + cleaner: Cleaner = FakeCleaner(), + corrector: TextCorrecting? = nil, + vocabulary: [String] = [] + ) -> DictationController { + DictationController( + recorder: recorder, + transcriber: transcriber, + cleaner: cleaner, + inserter: inserter, + corrector: corrector, + vocabularyProvider: { vocabulary } + ) + } + + func dictate(_ controller: DictationController) async { + await controller.handle(.pressed) + await controller.handle(.released) + } + + @Test func loudRecordingPassesVocabularyToTranscriber() async { + recorder.samples = Self.loud + await dictate(makeController(vocabulary: ["Anthropic", "Vercel"])) + #expect(transcriber.vocabularies == [["Anthropic", "Vercel"]]) + } + + @Test func quietRecordingSkipsVocabularyButStillTranscribes() async { + recorder.samples = Self.quiet // RMS 0.01: above silence (0.005), below bias minimum (0.015) + await dictate(makeController(vocabulary: ["Anthropic", "Vercel"])) + #expect(transcriber.vocabularies == [[]]) + #expect(inserter.inserted == ["[hallo wereld]"]) + } + + @Test func promptEchoIsDiscarded() async { + recorder.samples = Self.loud + transcriber.result = "Anthropic, Vercel." + let controller = makeController(vocabulary: ["Anthropic", "Vercel", "Supabase"]) + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + + await dictate(controller) + + #expect(inserter.inserted.isEmpty) + #expect(results.isEmpty) + #expect(controller.state == .idle) + } + + @Test func singleVocabularyTermIsKept() async { + recorder.samples = Self.loud + transcriber.result = "Anthropic" + await dictate(makeController(vocabulary: ["Anthropic", "Vercel", "Supabase"])) + #expect(inserter.inserted == ["[Anthropic]"]) + } + + @Test func correctorRunsBeforeCleanerAndCorrectionsSurviveAPassthroughCleaner() async { + recorder.samples = Self.loud + transcriber.result = "hallo" + let controller = makeController(cleaner: PassthroughCleaner(), corrector: UppercaseCorrector()) + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + + await dictate(controller) + + #expect(inserter.inserted == ["HALLO"]) + #expect(results.count == 1) + #expect(results[0].rawText == "hallo") + #expect(results[0].finalText == "HALLO") + #expect(results[0].corrections.count == 1) + } + + @Test func correctionsAreDroppedWhenTheCleanerChangesTheText() async { + recorder.samples = Self.loud + transcriber.result = "hallo" + let controller = makeController(cleaner: FakeCleaner(), corrector: UppercaseCorrector()) + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + + await dictate(controller) + + #expect(results[0].finalText == "[HALLO]") + #expect(results[0].corrections.isEmpty) + } + + @Test func resultIsReportedEvenWhenInsertionFails() async { + recorder.samples = Self.loud + inserter.error = TestError() + let controller = makeController() + var results: [DictationResult] = [] + var errors: [String] = [] + controller.onResult = { results.append($0) } + controller.onError = { errors.append($0) } + + await dictate(controller) + + #expect(results.count == 1) + #expect(errors.count == 1) + } + + @Test func resultCarriesDurationAndDate() async { + recorder.samples = Self.loud // 16 000 samples = 1 s + let controller = makeController() + var results: [DictationResult] = [] + controller.onResult = { results.append($0) } + let before = Date() + + await dictate(controller) + + #expect(results[0].duration == 1.0) + #expect(results[0].date >= before) + #expect(results[0].corrections.isEmpty) + } +} diff --git a/Tests/SstCoreTests/Fakes.swift b/Tests/SstCoreTests/Fakes.swift index f8b2cf7..81b4df2 100644 --- a/Tests/SstCoreTests/Fakes.swift +++ b/Tests/SstCoreTests/Fakes.swift @@ -41,3 +41,31 @@ final class FakeInserter: TextInserting { inserted.append(text) } } + +final class FakeVocabularyTranscriber: Transcriber { + var result = "hallo wereld" + private(set) var vocabularies: [[String]] = [] + + func transcribe(_ samples: [Float]) async throws -> String { result } + + func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + vocabularies.append(vocabulary) + return result + } +} + +struct UppercaseCorrector: TextCorrecting { + func correct(_ text: String) -> CorrectionResult { + let upper = text.uppercased() + return CorrectionResult( + text: upper, + corrections: [AppliedCorrection(heard: text, wrote: upper, rule: "upper", start: 0, length: upper.utf16.count)] + ) + } +} + +final class PassthroughCleaner: Cleaner { + func clean(_ text: String, context: CleaningContext) async throws -> String { + text + } +} diff --git a/Tests/SstCoreTests/PromptEchoTests.swift b/Tests/SstCoreTests/PromptEchoTests.swift new file mode 100644 index 0000000..84ff80c --- /dev/null +++ b/Tests/SstCoreTests/PromptEchoTests.swift @@ -0,0 +1,31 @@ +import Testing +@testable import SstCore + +struct PromptEchoTests { + let vocabulary = ["Anthropic", "Vercel", "Supabase"] + + @Test func firstTwoTermsAreAnEcho() { + #expect(PromptEcho.isEcho("Anthropic, Vercel.", vocabulary: vocabulary)) + } + + @Test func wholeListIsAnEcho() { + #expect(PromptEcho.isEcho("anthropic vercel supabase", vocabulary: vocabulary)) + } + + @Test func singleTermIsRealSpeech() { + #expect(!PromptEcho.isEcho("Anthropic", vocabulary: vocabulary)) + } + + @Test func termsOutOfOrderAreNotAnEcho() { + #expect(!PromptEcho.isEcho("Vercel, Anthropic", vocabulary: vocabulary)) + } + + @Test func sentenceContainingTermsIsNotAnEcho() { + #expect(!PromptEcho.isEcho("Ik gebruik Anthropic en Vercel", vocabulary: vocabulary)) + } + + @Test func tooShortVocabularyNeverEchoes() { + #expect(!PromptEcho.isEcho("Anthropic", vocabulary: ["Anthropic"])) + #expect(!PromptEcho.isEcho("", vocabulary: vocabulary)) + } +} From 7eb3a156d47a189d71cc08b24491729867d9a565 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:56:14 +0200 Subject: [PATCH 09/43] fix: remove shadowing PassthroughCleaner fake from test module Co-Authored-By: Claude Sonnet 5.5 --- Tests/SstCoreTests/Fakes.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Tests/SstCoreTests/Fakes.swift b/Tests/SstCoreTests/Fakes.swift index 81b4df2..c6afd8a 100644 --- a/Tests/SstCoreTests/Fakes.swift +++ b/Tests/SstCoreTests/Fakes.swift @@ -63,9 +63,3 @@ struct UppercaseCorrector: TextCorrecting { ) } } - -final class PassthroughCleaner: Cleaner { - func clean(_ text: String, context: CleaningContext) async throws -> String { - text - } -} From 2c4b096850a6a2a8559b0dc59d08f7c61652d2c7 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 14:58:31 +0200 Subject: [PATCH 10/43] feat: configurable hotkey binding and ModifierKeyTracker Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstCore/HotkeyBinding.swift | 27 ++++++++++++++++ Sources/SstCore/HotkeyMonitor.swift | 17 ++++++---- Sources/SstCore/ModifierKeyTracker.swift | 12 +++++++ Tests/SstCoreTests/HotkeyBindingTests.swift | 32 +++++++++++++++++++ .../ModifierKeyTrackerTests.swift | 18 +++++++++++ 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 Sources/SstCore/HotkeyBinding.swift create mode 100644 Sources/SstCore/ModifierKeyTracker.swift create mode 100644 Tests/SstCoreTests/HotkeyBindingTests.swift create mode 100644 Tests/SstCoreTests/ModifierKeyTrackerTests.swift diff --git a/Sources/SstCore/HotkeyBinding.swift b/Sources/SstCore/HotkeyBinding.swift new file mode 100644 index 0000000..140b710 --- /dev/null +++ b/Sources/SstCore/HotkeyBinding.swift @@ -0,0 +1,27 @@ +import CoreGraphics + +/// A modifier key that can be held to dictate. Left/right keys share a flag, so the key code decides. +public struct HotkeyBinding: Hashable, Sendable, Identifiable { + public let id: String + public let displayName: String + public let keyCode: Int + public let flagRawValue: UInt64 + + public var flag: CGEventFlags { CGEventFlags(rawValue: flagRawValue) } + + public static let fn = HotkeyBinding( + id: "fn", displayName: "Fn", keyCode: 63, flagRawValue: CGEventFlags.maskSecondaryFn.rawValue) + public static let rightOption = HotkeyBinding( + id: "rightOption", displayName: "Rechter Option", keyCode: 61, flagRawValue: CGEventFlags.maskAlternate.rawValue) + public static let rightCommand = HotkeyBinding( + id: "rightCommand", displayName: "Rechter Command", keyCode: 54, flagRawValue: CGEventFlags.maskCommand.rawValue) + public static let rightControl = HotkeyBinding( + id: "rightControl", displayName: "Rechter Control", keyCode: 62, flagRawValue: CGEventFlags.maskControl.rawValue) + public static let leftControl = HotkeyBinding( + id: "leftControl", displayName: "Linker Control", keyCode: 59, flagRawValue: CGEventFlags.maskControl.rawValue) + + public static let allowed: [HotkeyBinding] = [.fn, .rightOption, .rightCommand, .rightControl, .leftControl] + + public static func binding(id: String) -> HotkeyBinding? { allowed.first { $0.id == id } } + public static func binding(keyCode: Int) -> HotkeyBinding? { allowed.first { $0.keyCode == keyCode } } +} diff --git a/Sources/SstCore/HotkeyMonitor.swift b/Sources/SstCore/HotkeyMonitor.swift index 07d4f22..44b1b6f 100644 --- a/Sources/SstCore/HotkeyMonitor.swift +++ b/Sources/SstCore/HotkeyMonitor.swift @@ -6,13 +6,18 @@ public final class HotkeyMonitor { case tapUnavailable public var errorDescription: String? { - "Kan de Fn-toets niet volgen: geef Input Monitoring-rechten in Systeeminstellingen en probeer opnieuw (soms is een herstart van de app nodig)." + "Kan de sneltoets niet volgen: geef Input Monitoring-rechten in Systeeminstellingen en probeer opnieuw (soms is een herstart van de app nodig)." } } public var onEvent: ((HotkeyEvent) -> Void)? - private var tracker = FnKeyTracker() + /// The key that has to be held. Changing it resets the press tracking. + public var binding: HotkeyBinding = .fn { + didSet { tracker = ModifierKeyTracker() } + } + + private var tracker = ModifierKeyTracker() private var tap: CFMachPort? private var source: CFRunLoopSource? @@ -56,10 +61,10 @@ public final class HotkeyMonitor { case .tapDisabledByTimeout, .tapDisabledByUserInput: if let tap { CGEvent.tapEnable(tap: tap, enable: true) } case .flagsChanged: - // .maskSecondaryFn is also set while arrows/F-keys are held; only react to the Fn key itself. - guard event.getIntegerValueField(.keyboardEventKeycode) == 63 else { return } - let fnDown = event.flags.contains(.maskSecondaryFn) - if let hotkeyEvent = tracker.process(fnDown: fnDown) { + // Flags are shared between left and right keys and with arrows/F-keys: only react to the bound key itself. + guard event.getIntegerValueField(.keyboardEventKeycode) == Int64(binding.keyCode) else { return } + let isDown = event.flags.contains(binding.flag) + if let hotkeyEvent = tracker.process(isDown: isDown) { onEvent?(hotkeyEvent) } default: diff --git a/Sources/SstCore/ModifierKeyTracker.swift b/Sources/SstCore/ModifierKeyTracker.swift new file mode 100644 index 0000000..e83880c --- /dev/null +++ b/Sources/SstCore/ModifierKeyTracker.swift @@ -0,0 +1,12 @@ +public struct ModifierKeyTracker { + private var isDown = false + + public init() {} + + /// Feed the current state of the bound modifier flag; returns an event only on a transition. + public mutating func process(isDown: Bool) -> HotkeyEvent? { + guard isDown != self.isDown else { return nil } + self.isDown = isDown + return isDown ? .pressed : .released + } +} diff --git a/Tests/SstCoreTests/HotkeyBindingTests.swift b/Tests/SstCoreTests/HotkeyBindingTests.swift new file mode 100644 index 0000000..0e09999 --- /dev/null +++ b/Tests/SstCoreTests/HotkeyBindingTests.swift @@ -0,0 +1,32 @@ +import Testing +@testable import SstCore + +struct HotkeyBindingTests { + @Test func allowedBindingsMatchTheSpec() { + #expect(HotkeyBinding.allowed.map(\.id) == ["fn", "rightOption", "rightCommand", "rightControl", "leftControl"]) + #expect(HotkeyBinding.allowed.map(\.keyCode) == [63, 61, 54, 62, 59]) + } + + @Test func idsAndKeyCodesAreUnique() { + #expect(Set(HotkeyBinding.allowed.map(\.id)).count == HotkeyBinding.allowed.count) + #expect(Set(HotkeyBinding.allowed.map(\.keyCode)).count == HotkeyBinding.allowed.count) + } + + @Test func lookupByKeyCodeAndId() { + #expect(HotkeyBinding.binding(keyCode: 61) == .rightOption) + #expect(HotkeyBinding.binding(id: "leftControl") == .leftControl) + } + + @Test func unsupportedKeysAreRejected() { + #expect(HotkeyBinding.binding(keyCode: 56) == nil) // left shift + #expect(HotkeyBinding.binding(keyCode: 57) == nil) // caps lock + #expect(HotkeyBinding.binding(id: "nope") == nil) + } + + @Test func everyBindingHasAFlagAndName() { + for binding in HotkeyBinding.allowed { + #expect(binding.flagRawValue != 0) + #expect(!binding.displayName.isEmpty) + } + } +} diff --git a/Tests/SstCoreTests/ModifierKeyTrackerTests.swift b/Tests/SstCoreTests/ModifierKeyTrackerTests.swift new file mode 100644 index 0000000..377b04a --- /dev/null +++ b/Tests/SstCoreTests/ModifierKeyTrackerTests.swift @@ -0,0 +1,18 @@ +import Testing +@testable import SstCore + +struct ModifierKeyTrackerTests { + @Test func emitsPressedThenReleasedOnlyOnTransitions() { + var tracker = ModifierKeyTracker() + #expect(tracker.process(isDown: false) == nil) + #expect(tracker.process(isDown: true) == .pressed) + #expect(tracker.process(isDown: true) == nil) + #expect(tracker.process(isDown: false) == .released) + #expect(tracker.process(isDown: false) == nil) + } + + @Test func freshTrackerStartsReleased() { + var tracker = ModifierKeyTracker() + #expect(tracker.process(isDown: true) == .pressed) + } +} From c8e16145e16d8e9494df56e0c2be69541cb6995e Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:00:40 +0200 Subject: [PATCH 11/43] feat: AudioLevel helper and AudioRecorder.onLevel Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstCore/AudioLevel.swift | 24 ++++++++++++++++++ Sources/SstCore/AudioRecorder.swift | 9 ++++++- Sources/SstProbe/RecordProbe.swift | 17 +++++++++++-- Tests/SstCoreTests/AudioLevelTests.swift | 31 ++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 Sources/SstCore/AudioLevel.swift create mode 100644 Tests/SstCoreTests/AudioLevelTests.swift diff --git a/Sources/SstCore/AudioLevel.swift b/Sources/SstCore/AudioLevel.swift new file mode 100644 index 0000000..804e1f0 --- /dev/null +++ b/Sources/SstCore/AudioLevel.swift @@ -0,0 +1,24 @@ +import Foundation + +public enum AudioLevel { + /// Reported for empty or silent buffers (log of zero would be -infinity). + public static let silenceFloorDbfs: Float = -120 + + public static func rmsDbfs(_ samples: [Float]) -> Float { + samples.withUnsafeBufferPointer { rmsDbfs($0) } + } + + public static func rmsDbfs(_ samples: UnsafeBufferPointer) -> Float { + guard !samples.isEmpty else { return silenceFloorDbfs } + var sum: Float = 0 + for sample in samples { sum += sample * sample } + let rms = (sum / Float(samples.count)).squareRoot() + guard rms > 0 else { return silenceFloorDbfs } + return max(silenceFloorDbfs, 20 * log10(rms)) + } + + /// VU reading for a dBFS level, where `referenceDbfs` is 0 VU. + public static func vu(fromDbfs dbfs: Float, referenceDbfs: Float = -20) -> Float { + dbfs - referenceDbfs + } +} diff --git a/Sources/SstCore/AudioRecorder.swift b/Sources/SstCore/AudioRecorder.swift index b537465..1e31c4a 100644 --- a/Sources/SstCore/AudioRecorder.swift +++ b/Sources/SstCore/AudioRecorder.swift @@ -12,6 +12,10 @@ public final class AudioRecorder: AudioRecording { private let engine = AVAudioEngine() private let lock = NSLock() private var buffer: [Float] = [] + + /// RMS level (dBFS) of every delivered input buffer. Called on the audio thread; set before `start()`. + public var onLevel: ((Float) -> Void)? + private let targetFormat = AVAudioFormat( commonFormat: .pcmFormatFloat32, sampleRate: 16_000, @@ -32,8 +36,11 @@ public final class AudioRecorder: AudioRecording { let ratio = targetFormat.sampleRate / inputFormat.sampleRate input.removeTap(onBus: 0) - input.installTap(onBus: 0, bufferSize: 4096, format: inputFormat) { [weak self] pcm, _ in + input.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { [weak self] pcm, _ in guard let self else { return } + if let onLevel = self.onLevel, let channel = pcm.floatChannelData?[0] { + onLevel(AudioLevel.rmsDbfs(UnsafeBufferPointer(start: channel, count: Int(pcm.frameLength)))) + } let capacity = AVAudioFrameCount(Double(pcm.frameLength) * ratio) + 1 guard let out = AVAudioPCMBuffer(pcmFormat: self.targetFormat, frameCapacity: capacity) else { return } diff --git a/Sources/SstProbe/RecordProbe.swift b/Sources/SstProbe/RecordProbe.swift index e8e3c3f..0566a31 100644 --- a/Sources/SstProbe/RecordProbe.swift +++ b/Sources/SstProbe/RecordProbe.swift @@ -2,12 +2,13 @@ import AVFoundation import SstCore /// Records `seconds` of microphone audio; returns nil if access is denied or recording fails. -func recordSamples(seconds: Double) async -> [Float]? { +func recordSamples(seconds: Double, onLevel: ((Float) -> Void)? = nil) async -> [Float]? { guard await AVCaptureDevice.requestAccess(for: .audio) else { print("Microfoon-toegang geweigerd (Systeeminstellingen → Privacy → Microfoon).") return nil } let recorder = AudioRecorder() + recorder.onLevel = onLevel do { try recorder.start() } catch { print("start mislukt:", error.localizedDescription) return nil @@ -18,7 +19,19 @@ func recordSamples(seconds: Double) async -> [Float]? { } func runRecordProbe() async { - guard let samples = await recordSamples(seconds: 3) else { return } + final class Levels: @unchecked Sendable { + let lock = NSLock() + var values: [Float] = [] + func add(_ v: Float) { lock.withLock { values.append(v) } } + } + let levels = Levels() + guard let samples = await recordSamples(seconds: 3, onLevel: { levels.add($0) }) else { return } let peak = samples.map { abs($0) }.max() ?? 0 print("samples: \(samples.count) (verwacht ≈ 48000), piek: \(peak)") + let values = levels.lock.withLock { levels.values } + if let lo = values.min(), let hi = values.max() { + print(String(format: "niveau-meldingen: %d (≈ %.0f per s), bereik %.1f … %.1f dBFS", values.count, Double(values.count) / 3, lo, hi)) + } else { + print("geen niveau-meldingen ontvangen") + } } diff --git a/Tests/SstCoreTests/AudioLevelTests.swift b/Tests/SstCoreTests/AudioLevelTests.swift new file mode 100644 index 0000000..6561bf4 --- /dev/null +++ b/Tests/SstCoreTests/AudioLevelTests.swift @@ -0,0 +1,31 @@ +import Testing +@testable import SstCore + +struct AudioLevelTests { + @Test func constantTenthIsMinusTwentyDbfs() { + let level = AudioLevel.rmsDbfs([Float](repeating: 0.1, count: 1_000)) + #expect(abs(level - (-20)) < 0.01) + } + + @Test func fullScaleIsZeroDbfs() { + let level = AudioLevel.rmsDbfs([Float](repeating: 1.0, count: 100)) + #expect(abs(level) < 0.01) + } + + @Test func negativeSamplesCountLikePositiveOnes() { + let level = AudioLevel.rmsDbfs([Float](repeating: -0.1, count: 1_000)) + #expect(abs(level - (-20)) < 0.01) + } + + @Test func emptyAndSilentBuffersHitTheFloor() { + #expect(AudioLevel.rmsDbfs([]) == AudioLevel.silenceFloorDbfs) + #expect(AudioLevel.rmsDbfs([Float](repeating: 0, count: 64)) == AudioLevel.silenceFloorDbfs) + } + + @Test func vuIsRelativeToTheReference() { + #expect(AudioLevel.vu(fromDbfs: -20) == 0) + #expect(AudioLevel.vu(fromDbfs: -40) == -20) + #expect(AudioLevel.vu(fromDbfs: -17) == 3) + #expect(AudioLevel.vu(fromDbfs: -30, referenceDbfs: -18) == -12) + } +} From af1acdc0fa34167edab0cf72bf9bd6a2c54d2727 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:03:04 +0200 Subject: [PATCH 12/43] feat: WhisperKit transcriber biases decoding with vocabulary prompt tokens Co-Authored-By: Claude Sonnet 5.5 --- Package.swift | 1 + Sources/SstProbe/TranscribeProbe.swift | 5 ++- .../SstWhisper/WhisperKitTranscriber.swift | 33 ++++++++++++++++++- Tests/SstWhisperTests/PromptTokensTests.swift | 32 ++++++++++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Tests/SstWhisperTests/PromptTokensTests.swift diff --git a/Package.swift b/Package.swift index 127d1c3..c8c391c 100644 --- a/Package.swift +++ b/Package.swift @@ -19,6 +19,7 @@ let package = Package( .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper"]), .executableTarget(name: "SstProbe", dependencies: ["SstCore", "SstWhisper"]), .testTarget(name: "SstCoreTests", dependencies: ["SstCore"]), + .testTarget(name: "SstWhisperTests", dependencies: ["SstWhisper"]), ], swiftLanguageModes: [.v5] ) diff --git a/Sources/SstProbe/TranscribeProbe.swift b/Sources/SstProbe/TranscribeProbe.swift index f678751..914862c 100644 --- a/Sources/SstProbe/TranscribeProbe.swift +++ b/Sources/SstProbe/TranscribeProbe.swift @@ -25,8 +25,11 @@ func runTranscribeProbe() async { } print("RMS per seconde:", perSecond.joined(separator: " ")) do { + let vocabulary = (ProcessInfo.processInfo.environment["SST_VOCAB"] ?? "") + .split(separator: ",").map { $0.trimmingCharacters(in: .whitespaces) }.filter { !$0.isEmpty } + if !vocabulary.isEmpty { print("voorrang: \(vocabulary.joined(separator: ", "))") } let started = Date.now - let text = try await transcriber.transcribe(samples) + let text = try await transcriber.transcribe(samples, vocabulary: vocabulary) let elapsed = Date.now.timeIntervalSince(started) print(String(format: "(%.2f s) ", elapsed) + text) } catch { diff --git a/Sources/SstWhisper/WhisperKitTranscriber.swift b/Sources/SstWhisper/WhisperKitTranscriber.swift index 6bf4916..b64781f 100644 --- a/Sources/SstWhisper/WhisperKitTranscriber.swift +++ b/Sources/SstWhisper/WhisperKitTranscriber.swift @@ -77,15 +77,46 @@ public final class WhisperKitTranscriber: Transcriber { } public func transcribe(_ samples: [Float]) async throws -> String { + try await transcribe(samples, vocabulary: []) + } + + /// Terms are added one by one while the encoded prompt stays within `maxTokens`. + /// Returns nil when nothing fits or the list is empty. + static func promptTokens( + vocabulary: [String], + maxTokens: Int = 64, + specialTokenBegin: Int, + encode: (String) -> [Int] + ) -> [Int]? { + var kept: [String] = [] + var tokens: [Int] = [] + for term in vocabulary { + let candidate = kept + [term] + let encoded = encode(" " + candidate.joined(separator: ", ")).filter { $0 < specialTokenBegin } + if encoded.count > maxTokens { break } + kept = candidate + tokens = encoded + } + return tokens.isEmpty ? nil : tokens + } + + public func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { guard let pipeline else { throw TranscriberError.notLoaded } // language nil + detectLanguage: Whisper picks NL or EN per utterance. - let options = DecodingOptions( + var options = DecodingOptions( task: .transcribe, language: nil, detectLanguage: true, skipSpecialTokens: true, withoutTimestamps: true ) + if !vocabulary.isEmpty, let tokenizer = pipeline.tokenizer { + options.promptTokens = Self.promptTokens( + vocabulary: vocabulary, + specialTokenBegin: tokenizer.specialTokens.specialTokenBegin, + encode: { tokenizer.encode(text: $0) } + ) + } let results = try await pipeline.transcribe(audioArray: samples, decodeOptions: options) return results .map(\.text) diff --git a/Tests/SstWhisperTests/PromptTokensTests.swift b/Tests/SstWhisperTests/PromptTokensTests.swift new file mode 100644 index 0000000..1ac2169 --- /dev/null +++ b/Tests/SstWhisperTests/PromptTokensTests.swift @@ -0,0 +1,32 @@ +import Testing +@testable import SstWhisper + +struct PromptTokensTests { + /// Fake tokenizer: one token (id 7) per word, plus one special token (id 2000) that must be filtered out. + let encode: (String) -> [Int] = { text in + text.split(whereSeparator: { !$0.isLetter }).map { _ in 7 } + [2000] + } + + @Test func allTermsFitAndSpecialTokensAreFiltered() { + let tokens = WhisperKitTranscriber.promptTokens( + vocabulary: ["Anthropic", "Vercel", "Supabase"], maxTokens: 64, specialTokenBegin: 1000, encode: encode) + #expect(tokens == [7, 7, 7]) + } + + @Test func termsThatDoNotFitAreDroppedFromTheEnd() { + let vocab = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] + let tokens = WhisperKitTranscriber.promptTokens( + vocabulary: vocab, maxTokens: 4, specialTokenBegin: 1000, encode: encode) + #expect(tokens?.count == 4) + } + + @Test func emptyVocabularyGivesNoPrompt() { + #expect(WhisperKitTranscriber.promptTokens( + vocabulary: [], maxTokens: 64, specialTokenBegin: 1000, encode: encode) == nil) + } + + @Test func firstTermAloneTooLongGivesNoPrompt() { + #expect(WhisperKitTranscriber.promptTokens( + vocabulary: ["one two three"], maxTokens: 2, specialTokenBegin: 1000, encode: encode) == nil) + } +} From a5fa0686f06b4b837813660a2f25e06a5d57d81f Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:06:22 +0200 Subject: [PATCH 13/43] feat: add SstData with DictionaryFile and word knowledge Co-Authored-By: Claude Sonnet 5.5 --- Package.swift | 2 + Sources/SstData/DictionaryFile.swift | 110 ++++++++++++++++++ Sources/SstData/WordKnowledge.swift | 72 ++++++++++++ Tests/SstDataTests/DictionaryFileTests.swift | 52 +++++++++ Tests/SstDataTests/Fakes.swift | 13 +++ .../SystemWordKnowledgeTests.swift | 24 ++++ 6 files changed, 273 insertions(+) create mode 100644 Sources/SstData/DictionaryFile.swift create mode 100644 Sources/SstData/WordKnowledge.swift create mode 100644 Tests/SstDataTests/DictionaryFileTests.swift create mode 100644 Tests/SstDataTests/Fakes.swift create mode 100644 Tests/SstDataTests/SystemWordKnowledgeTests.swift diff --git a/Package.swift b/Package.swift index c8c391c..3ef7517 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,7 @@ let package = Package( ], targets: [ .target(name: "SstCore"), + .target(name: "SstData", dependencies: ["SstCore"]), .target( name: "SstWhisper", dependencies: [ @@ -19,6 +20,7 @@ let package = Package( .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper"]), .executableTarget(name: "SstProbe", dependencies: ["SstCore", "SstWhisper"]), .testTarget(name: "SstCoreTests", dependencies: ["SstCore"]), + .testTarget(name: "SstDataTests", dependencies: ["SstData", "SstCore"]), .testTarget(name: "SstWhisperTests", dependencies: ["SstWhisper"]), ], swiftLanguageModes: [.v5] diff --git a/Sources/SstData/DictionaryFile.swift b/Sources/SstData/DictionaryFile.swift new file mode 100644 index 0000000..2185841 --- /dev/null +++ b/Sources/SstData/DictionaryFile.swift @@ -0,0 +1,110 @@ +import Foundation + +public struct DictionaryEntry: Identifiable, Equatable, Sendable { + public enum Kind: Equatable, Sendable { + case term(String) + case correction(heard: String, wrote: String) + } + + public let id: UUID + public var kind: Kind + + public init(id: UUID = UUID(), kind: Kind) { + self.id = id + self.kind = kind + } + + /// Equality ignores `id`: two entries with the same content are the same entry. + public static func == (lhs: DictionaryEntry, rhs: DictionaryEntry) -> Bool { lhs.kind == rhs.kind } + + /// The text the correction pass listens for (a term listens for itself). + public var source: String { + switch kind { + case .term(let term): term + case .correction(let heard, _): heard + } + } + + /// The text that ends up in the transcript. + public var wrote: String { + switch kind { + case .term(let term): term + case .correction(_, let wrote): wrote + } + } +} + +public enum DictionaryLine: Equatable, Sendable { + case entry(DictionaryEntry) + case comment(String) + case blank + case invalid(String) +} + +/// The dictionary as a list of lines, so comments, blank lines and order survive an edit. +public struct DictionaryFile: Equatable, Sendable { + public var lines: [DictionaryLine] + + public init(lines: [DictionaryLine] = []) { + self.lines = lines + } + + public init(parsing text: String) { + var raw = text.replacingOccurrences(of: "\r\n", with: "\n") + .split(separator: "\n", omittingEmptySubsequences: false) + .map(String.init) + if raw.last == "" { raw.removeLast() } + lines = raw.map(Self.parseLine) + } + + static func parseLine(_ line: String) -> DictionaryLine { + let trimmed = line.trimmingCharacters(in: .whitespaces) + if trimmed.isEmpty { return .blank } + if trimmed.hasPrefix("#") { return .comment(line) } + if let arrow = trimmed.range(of: "=>") { + let heard = trimmed[.. String { + let out = lines.map { line -> String in + switch line { + case .entry(let entry): + switch entry.kind { + case .term(let term): return term + case .correction(let heard, let wrote): return "\(heard) => \(wrote)" + } + case .comment(let text), .invalid(let text): return text + case .blank: return "" + } + } + return out.isEmpty ? "" : out.joined(separator: "\n") + "\n" + } + + public var entries: [DictionaryEntry] { + lines.compactMap { line in + if case .entry(let entry) = line { return entry } + return nil + } + } + + @discardableResult + public mutating func append(_ kind: DictionaryEntry.Kind) -> UUID { + let entry = DictionaryEntry(kind: kind) + lines.append(.entry(entry)) + return entry.id + } + + public mutating func update(id: UUID, kind: DictionaryEntry.Kind) { + guard let index = lines.firstIndex(where: { if case .entry(let e) = $0 { return e.id == id } else { return false } }) else { return } + lines[index] = .entry(DictionaryEntry(id: id, kind: kind)) + } + + public mutating func remove(id: UUID) { + lines.removeAll { if case .entry(let e) = $0 { return e.id == id } else { return false } } + } +} diff --git a/Sources/SstData/WordKnowledge.swift b/Sources/SstData/WordKnowledge.swift new file mode 100644 index 0000000..b2e0e62 --- /dev/null +++ b/Sources/SstData/WordKnowledge.swift @@ -0,0 +1,72 @@ +import AppKit +import Foundation +import NaturalLanguage + +public protocol WordKnowledge { + /// A common word in Dutch or English (case-insensitive). Rare words, names and product names are not. + func isWord(_ word: String) -> Bool + /// A very frequent function word ("in", "de", "the", "of"). + func isFunctionWord(_ word: String) -> Bool +} + +/// Splitting and normalising the left-hand side of an entry, shared by the engine and the warnings. +enum EntryKey { + static func parts(_ source: String) -> [String] { + source.split(whereSeparator: { $0.isWhitespace || "-‐‑–".contains($0) }).map(String.init) + } + + /// Spacing, hyphens and case do not matter: "Cloud-Code", "cloud code" and "cloudcode" are the same source. + static func key(_ source: String) -> String { + parts(source).joined().lowercased() + } +} + +/// The most frequent Dutch and English function words. +enum CommonWords { + static let english: Set = [ + "a", "about", "after", "all", "also", "an", "and", "any", "are", "as", "at", "be", "because", "been", "but", + "by", "can", "could", "did", "do", "does", "for", "from", "had", "has", "have", "he", "her", "here", "him", + "his", "how", "i", "if", "in", "into", "is", "it", "its", "just", "me", "more", "most", "my", "no", "not", + "now", "of", "on", "one", "only", "or", "other", "our", "out", "over", "she", "so", "some", "such", "than", + "that", "the", "their", "them", "then", "there", "these", "they", "this", "those", "to", "up", "us", "very", + "was", "we", "were", "what", "when", "which", "who", "will", "with", "would", "you", "your", + ] + static let dutch: Set = [ + "aan", "al", "als", "ben", "bij", "dan", "dat", "de", "den", "der", "deze", "die", "dit", "doen", "door", + "dus", "een", "en", "er", "ga", "had", "heb", "heeft", "hem", "het", "hier", "hij", "hoe", "ik", "in", "is", + "ja", "je", "jij", "kan", "kon", "maar", "me", "mij", "mijn", "moet", "na", "naar", "niet", "nog", "nu", + "of", "om", "ons", "onze", "ook", "op", "over", "te", "ten", "ter", "toch", "toen", "tot", "u", "uit", "van", + "veel", "voor", "was", "wat", "we", "wel", "werd", "wij", "wil", "worden", "wordt", "zal", "ze", "zei", + "zich", "zij", "zijn", "zo", "zou", + ] + static let all: Set = english.union(dutch) +} + +/// English: the on-device word list of `NaturalLanguage` (common words only, so "cloud" and "reef" are in, "anthropic" and "vercel" are not). +/// Dutch has no such list, so the spell checker decides. Function words come from the built-in list. +public final class SystemWordKnowledge: WordKnowledge, @unchecked Sendable { + private let englishWords = NLEmbedding.wordEmbedding(for: .english) + + public init() {} + + public func isWord(_ word: String) -> Bool { + let lowered = word.lowercased() + guard !lowered.isEmpty else { return false } + if let englishWords { + if englishWords.contains(lowered) { return true } + } else if spellChecks(lowered, language: "en") { + return true + } + return spellChecks(lowered, language: "nl") + } + + public func isFunctionWord(_ word: String) -> Bool { + CommonWords.all.contains(word.lowercased()) + } + + private func spellChecks(_ word: String, language: String) -> Bool { + NSSpellChecker.shared.checkSpelling( + of: word, startingAt: 0, language: language, wrap: false, + inSpellDocumentWithTag: 0, wordCount: nil).location == NSNotFound + } +} diff --git a/Tests/SstDataTests/DictionaryFileTests.swift b/Tests/SstDataTests/DictionaryFileTests.swift new file mode 100644 index 0000000..6d0fcd6 --- /dev/null +++ b/Tests/SstDataTests/DictionaryFileTests.swift @@ -0,0 +1,52 @@ +import Testing +@testable import SstData + +struct DictionaryFileTests { + let sample = "# Opmerking\nAnthropic\n\ncloud code => Claude Code\n" + + @Test func parsesTermsPairsCommentsAndBlanks() { + let file = DictionaryFile(parsing: sample) + #expect(file.lines.count == 4) + #expect(file.entries == [term("Anthropic"), pair("cloud code", "Claude Code")]) + #expect(file.lines[0] == .comment("# Opmerking")) + #expect(file.lines[2] == .blank) + } + + @Test func roundTripKeepsCommentsBlankLinesAndOrder() { + #expect(DictionaryFile(parsing: sample).serialized() == sample) + } + + @Test func normalisesSpacingAroundTheArrow() { + #expect(DictionaryFile(parsing: "a=>b\n").serialized() == "a => b\n") + #expect(DictionaryFile(parsing: " cloud code => Claude Code \n").entries == [pair("cloud code", "Claude Code")]) + } + + @Test func unreadableLinesAreKeptAsInvalid() { + let file = DictionaryFile(parsing: "oops =>\n=> nothing\nok\n") + #expect(file.lines[0] == .invalid("oops =>")) + #expect(file.lines[1] == .invalid("=> nothing")) + #expect(file.serialized() == "oops =>\n=> nothing\nok\n") + } + + @Test func handlesWindowsLineEndingsAndMissingFinalNewline() { + let file = DictionaryFile(parsing: "a\r\nb => c") + #expect(file.entries == [term("a"), pair("b", "c")]) + #expect(file.serialized() == "a\nb => c\n") + } + + @Test func emptyTextIsAValidEmptyFile() { + #expect(DictionaryFile(parsing: "").entries.isEmpty) + #expect(DictionaryFile(parsing: "").serialized() == "") + } + + @Test func appendUpdateAndRemove() { + var file = DictionaryFile(parsing: "# c\n") + let id = file.append(.term("Vercel")) + #expect(file.entries == [term("Vercel")]) + file.update(id: id, kind: .correction(heard: "versel", wrote: "Vercel")) + #expect(file.entries == [pair("versel", "Vercel")]) + file.remove(id: id) + #expect(file.entries.isEmpty) + #expect(file.serialized() == "# c\n") + } +} diff --git a/Tests/SstDataTests/Fakes.swift b/Tests/SstDataTests/Fakes.swift new file mode 100644 index 0000000..b93dedd --- /dev/null +++ b/Tests/SstDataTests/Fakes.swift @@ -0,0 +1,13 @@ +import Foundation +@testable import SstData + +/// Deterministic stand-in for the macOS spell checker. +struct FakeKnowledge: WordKnowledge { + var words: Set = ["cloud", "code", "some", "thing", "something", "reef", "slack", "in", "de", "the", "of"] + var functionWords: Set = ["in", "de", "the", "of"] + func isWord(_ word: String) -> Bool { words.contains(word.lowercased()) } + func isFunctionWord(_ word: String) -> Bool { functionWords.contains(word.lowercased()) } +} + +func term(_ text: String) -> DictionaryEntry { DictionaryEntry(kind: .term(text)) } +func pair(_ heard: String, _ wrote: String) -> DictionaryEntry { DictionaryEntry(kind: .correction(heard: heard, wrote: wrote)) } diff --git a/Tests/SstDataTests/SystemWordKnowledgeTests.swift b/Tests/SstDataTests/SystemWordKnowledgeTests.swift new file mode 100644 index 0000000..4a42015 --- /dev/null +++ b/Tests/SstDataTests/SystemWordKnowledgeTests.swift @@ -0,0 +1,24 @@ +import Testing +@testable import SstData + +struct SystemWordKnowledgeTests { + let knowledge = SystemWordKnowledge() + + @Test func recognisesEnglishAndDutchWords() { + #expect(knowledge.isWord("cloud")) + #expect(knowledge.isWord("Cloud")) + #expect(knowledge.isWord("vandaag")) + } + + @Test func doesNotTreatProductNamesAsWords() { + #expect(!knowledge.isWord("vercel")) + #expect(!knowledge.isWord("supabase")) + #expect(!knowledge.isWord("anthropic")) + } + + @Test func functionWordsComeFromTheBuiltInList() { + #expect(knowledge.isFunctionWord("de")) + #expect(knowledge.isFunctionWord("The")) + #expect(!knowledge.isFunctionWord("vercel")) + } +} From 93bf59e090a40269ea1f167dd2a0be57b32c3aea Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:09:27 +0200 Subject: [PATCH 14/43] feat: add CorrectionEngine (whole-word, longest-first, single pass) Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstData/CorrectionEngine.swift | 79 +++++++++++++++ .../SstDataTests/CorrectionEngineTests.swift | 98 +++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 Sources/SstData/CorrectionEngine.swift create mode 100644 Tests/SstDataTests/CorrectionEngineTests.swift diff --git a/Sources/SstData/CorrectionEngine.swift b/Sources/SstData/CorrectionEngine.swift new file mode 100644 index 0000000..9e11266 --- /dev/null +++ b/Sources/SstData/CorrectionEngine.swift @@ -0,0 +1,79 @@ +import Foundation +import SstCore + +/// Whole-word, case-insensitive, single-pass replacement. Longest match wins, replaced text is never rescanned. +public struct CorrectionEngine: TextCorrecting { + private struct Rule { + let source: String + let wrote: String + } + + private let rules: [Rule] + private let regex: NSRegularExpression? + + public init(entries: [DictionaryEntry], knowledge: WordKnowledge) { + var seen = Set() + var built: [(rule: Rule, order: Int)] = [] + for (order, entry) in entries.enumerated() { + let source = entry.source.trimmingCharacters(in: .whitespacesAndNewlines) + let key = EntryKey.key(source) + guard !key.isEmpty, !seen.contains(key) else { continue } + // A single-word term that is also a real word only biases the model; capitalising it everywhere would corrupt normal text. + if case .term(let term) = entry.kind, EntryKey.parts(term).count == 1, knowledge.isWord(term) { continue } + seen.insert(key) + built.append((Rule(source: source, wrote: entry.wrote), order)) + } + let ordered = built.sorted { a, b in + let la = a.rule.source.filter(\.isLetter).count + let lb = b.rule.source.filter(\.isLetter).count + return la != lb ? la > lb : a.order < b.order + }.map(\.rule) + rules = ordered + if ordered.isEmpty { + regex = nil + } else { + let alternatives = ordered.map { "(\(Self.pattern($0.source)))" }.joined(separator: "|") + regex = try? NSRegularExpression( + pattern: "(? String { + EntryKey.parts(source) + .map { NSRegularExpression.escapedPattern(for: $0) } + .joined(separator: "[\\s\\-‐‑–]*") + } + + public func correct(_ text: String) -> CorrectionResult { + guard let regex, !text.isEmpty else { return CorrectionResult(text: text, corrections: []) } + let ns = text as NSString + var output = "" + var outputLength = 0 + var last = 0 + var applied: [AppliedCorrection] = [] + + for match in regex.matches(in: text, range: NSRange(location: 0, length: ns.length)) { + let gap = ns.substring(with: NSRange(location: last, length: match.range.location - last)) + output += gap + outputLength += gap.utf16.count + + let heard = ns.substring(with: match.range) + let group = (1.. CorrectionEngine { + CorrectionEngine(entries: entries, knowledge: knowledge) + } + + @Test func replacesAPairAndReportsIt() { + let result = engine([pair("cloud code", "Claude Code")]).correct("ik gebruik cloud code vandaag") + #expect(result.text == "ik gebruik Claude Code vandaag") + #expect(result.corrections.count == 1) + let c = result.corrections[0] + #expect(c.heard == "cloud code" && c.wrote == "Claude Code" && c.rule == "cloud code") + #expect((result.text as NSString).substring(with: NSRange(location: c.start, length: c.length)) == "Claude Code") + } + + @Test func findsGluedHyphenatedAndSpacedVariants() { + let e = engine([pair("cloud code", "Claude Code")]) + for variant in ["CloudCode", "Cloud-Code", "CLOUD CODE", "cloud\u{2011}code", "cloud code"] { + #expect(e.correct(variant).text == "Claude Code", "variant \(variant)") + } + } + + @Test func neverTouchesRealWordsThatMerelyStartTheSame() { + let e = engine([pair("cloud code", "Claude Code")]) + #expect(e.correct("Cloudflare is handig").text == "Cloudflare is handig") + #expect(e.correct("cloud codex").text == "cloud codex") + #expect(e.correct("de cloud en de code").text == "de cloud en de code") + #expect(e.correct("the cloud").corrections.isEmpty) + } + + @Test func respectsPunctuationBoundaries() { + let e = engine([pair("cloud code", "Claude Code")]) + #expect(e.correct("(cloud code)").text == "(Claude Code)") + #expect(e.correct("cloud code.").text == "Claude Code.") + #expect(e.correct("Cloud Code, Cloud Code").text == "Claude Code, Claude Code") + } + + @Test func longestMatchWins() { + let e = engine([pair("cloud code", "Claude Code"), pair("cloud code review", "Claude Code Review")]) + #expect(e.correct("cloud code review nu").text == "Claude Code Review nu") + #expect(e.correct("cloud-code-review").text == "Claude Code Review") + } + + @Test func replacedTextIsNeverRescanned() { + let e = engine([pair("a", "b"), pair("b", "c")]) + #expect(e.correct("a b").text == "b c") + } + + @Test func termsFixCasingAndSpacing() { + let e = engine([term("Anthropic"), term("Claude Code")]) + #expect(e.correct("anthropic ANTHROPIC").text == "Anthropic Anthropic") + #expect(e.correct("claudecode en claude-code").text == "Claude Code en Claude Code") + } + + @Test func termAlreadyRightIsNotReportedAsACorrection() { + let result = engine([term("Anthropic")]).correct("Anthropic bouwt Claude") + #expect(result.text == "Anthropic bouwt Claude") + #expect(result.corrections.isEmpty) + } + + @Test func singleWordTermThatIsARealWordGetsNoCasingRule() { + let e = engine([term("Reef"), term("Slack")]) + #expect(e.correct("het reef en slack").text == "het reef en slack") + } + + @Test func firstEntryWinsOnDuplicateSources() { + let e = engine([pair("cloud code", "Claude Code"), pair("Cloud-Code", "Other")]) + #expect(e.correct("cloudcode").text == "Claude Code") + } + + @Test func replacementIsLiteral() { + let e = engine([pair("price", "US$1 \\0 $1")]) + #expect(e.correct("the price").text == "the US$1 \\0 $1") + } + + @Test func handlesDiacritics() { + let e = engine([term("Café")]) + #expect(e.correct("een CAFÉ om de hoek").text == "een Café om de hoek") + } + + @Test func offsetsAreRelativeToTheCorrectedText() { + let result = engine([pair("a", "xxx"), pair("b", "y")]).correct("a b a") + #expect(result.text == "xxx y xxx") + #expect(result.corrections.map(\.start) == [0, 4, 6]) + #expect(result.corrections.map(\.length) == [3, 1, 3]) + } + + @Test func emptyDictionaryAndEmptyTextAreNoOps() { + #expect(engine([]).correct("hallo").text == "hallo") + #expect(engine([pair("a", "b")]).correct("").text == "") + } +} From fe24292279430f14792a93070fcdc9736e1f1d01 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:11:48 +0200 Subject: [PATCH 15/43] feat: add EntryWarnings and VocabularyBuilder Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstData/EntryWarnings.swift | 67 +++++++++++++++++++ Sources/SstData/VocabularyBuilder.swift | 19 ++++++ Tests/SstDataTests/EntryWarningsTests.swift | 60 +++++++++++++++++ .../SstDataTests/VocabularyBuilderTests.swift | 19 ++++++ 4 files changed, 165 insertions(+) create mode 100644 Sources/SstData/EntryWarnings.swift create mode 100644 Sources/SstData/VocabularyBuilder.swift create mode 100644 Tests/SstDataTests/EntryWarningsTests.swift create mode 100644 Tests/SstDataTests/VocabularyBuilderTests.swift diff --git a/Sources/SstData/EntryWarnings.swift b/Sources/SstData/EntryWarnings.swift new file mode 100644 index 0000000..b8b559f --- /dev/null +++ b/Sources/SstData/EntryWarnings.swift @@ -0,0 +1,67 @@ +import Foundation + +public struct EntryWarning: Equatable, Sendable { + public enum Level: Int, Comparable, Sendable { + case note = 0, warning, error + public static func < (a: Level, b: Level) -> Bool { a.rawValue < b.rawValue } + } + + public let level: Level + public let message: String + + public init(level: Level, message: String) { + self.level = level + self.message = message + } +} + +/// Flags dictionary entries that are likely to corrupt normal text. +public enum EntryWarnings { + /// - Parameter others: all entries except the one being checked. + public static func check(_ kind: DictionaryEntry.Kind, among others: [DictionaryEntry], knowledge: WordKnowledge) -> [EntryWarning] { + let source: String + let isCorrection: Bool + switch kind { + case .term(let term): (source, isCorrection) = (term, false) + case .correction(let heard, _): (source, isCorrection) = (heard, true) + } + + let parts = EntryKey.parts(source) + guard !parts.isEmpty else { return [EntryWarning(level: .error, message: "Leeg: vul een woord in.")] } + + var result: [EntryWarning] = [] + if others.contains(where: { EntryKey.key($0.source) == EntryKey.key(source) }) { + result.append(EntryWarning(level: .error, message: "Dubbele invoer: deze bron staat er al, de eerste telt.")) + } + + if !isCorrection { + if parts.count == 1, knowledge.isWord(source) { + result.append(EntryWarning(level: .note, message: "Gewoon woord: krijgt alleen voorrang bij het model, geen automatische hoofdletterregel.")) + } + return result.sorted { $0.level > $1.level } + } + + if parts.count == 1, knowledge.isWord(parts[0]) { + result.append(EntryWarning(level: .warning, message: "'\(parts[0])' is een gewoon woord en wordt overal in je tekst vervangen.")) + } + if source.filter(\.isLetter).count < 3 { + result.append(EntryWarning(level: .warning, message: "Erg kort (minder dan 3 letters): veel kans op ongewenste vervangingen.")) + } + if parts.count > 1 { + var flagged = false + if let common = parts.first(where: { knowledge.isFunctionWord($0) }) { + result.append(EntryWarning(level: .warning, message: "Bevat het veelvoorkomende woord '\(common)'; dit kan gewone zinnen raken.")) + flagged = true + } + let glued = parts.joined() + if knowledge.isWord(glued) { + result.append(EntryWarning(level: .warning, message: "Aaneengeschreven ('\(glued)') is een bestaand woord en wordt ook vervangen.")) + flagged = true + } + if !flagged, parts.allSatisfy({ knowledge.isWord($0) }) { + result.append(EntryWarning(level: .note, message: "Alle delen zijn gewone woorden: alleen de hele combinatie wordt vervangen.")) + } + } + return result.sorted { $0.level > $1.level } + } +} diff --git a/Sources/SstData/VocabularyBuilder.swift b/Sources/SstData/VocabularyBuilder.swift new file mode 100644 index 0000000..43a2dc0 --- /dev/null +++ b/Sources/SstData/VocabularyBuilder.swift @@ -0,0 +1,19 @@ +import Foundation + +/// The short word list handed to the speech engine as context. +public enum VocabularyBuilder { + /// Terms first (file order), then the unique right-hand sides of correction pairs; at most `maxTerms` words. + public static func build(from entries: [DictionaryEntry], maxTerms: Int = 24) -> [String] { + var out: [String] = [] + var seen = Set() + func add(_ word: String) { + let key = word.lowercased() + guard !word.isEmpty, !seen.contains(key) else { return } + seen.insert(key) + out.append(word) + } + for entry in entries { if case .term(let term) = entry.kind { add(term) } } + for entry in entries { if case .correction(_, let wrote) = entry.kind { add(wrote) } } + return Array(out.prefix(maxTerms)) + } +} diff --git a/Tests/SstDataTests/EntryWarningsTests.swift b/Tests/SstDataTests/EntryWarningsTests.swift new file mode 100644 index 0000000..d37338a --- /dev/null +++ b/Tests/SstDataTests/EntryWarningsTests.swift @@ -0,0 +1,60 @@ +import Testing +@testable import SstData + +struct EntryWarningsTests { + let knowledge = FakeKnowledge() + + func check(_ kind: DictionaryEntry.Kind, others: [DictionaryEntry] = []) -> [EntryWarning] { + EntryWarnings.check(kind, among: others, knowledge: knowledge) + } + + @Test func singleCommonWordIsAWarning() { + let w = check(.correction(heard: "cloud", wrote: "Claude")) + #expect(w.contains { $0.level == .warning && $0.message.contains("cloud") }) + } + + @Test func veryShortSourceIsAWarning() { + #expect(check(.correction(heard: "ab", wrote: "AB")).contains { $0.level == .warning }) + } + + @Test func functionWordInsideAPhraseIsAWarning() { + #expect(check(.correction(heard: "in de", wrote: "indeed")).contains { $0.level == .warning }) + } + + @Test func gluedFormThatIsARealWordIsAWarning() { + let w = check(.correction(heard: "some thing", wrote: "Something")) + #expect(w.contains { $0.level == .warning && $0.message.contains("something") }) + } + + @Test func phraseOfCommonWordsOnlyGetsANote() { + let w = check(.correction(heard: "cloud code", wrote: "Claude Code")) + #expect(w.count == 1) + #expect(w[0].level == .note) + } + + @Test func distinctiveEntriesGetNoWarnings() { + #expect(check(.correction(heard: "versel", wrote: "Vercel")).isEmpty) + #expect(check(.term("Supabase")).isEmpty) + } + + @Test func duplicateSourceIsAnErrorEvenWithDifferentSpelling() { + let others = [pair("cloud code", "Claude Code")] + let w = check(.correction(heard: "Cloud-Code", wrote: "X"), others: others) + #expect(w.first?.level == .error) + } + + @Test func termThatIsARealWordGetsANoteAboutBiasOnly() { + let w = check(.term("Reef")) + #expect(w.count == 1) + #expect(w[0].level == .note) + } + + @Test func emptySourceIsAnError() { + #expect(check(.term(" ")).first?.level == .error) + } + + @Test func warningsAreSortedMostSevereFirst() { + let w = check(.correction(heard: "cloud", wrote: "Claude"), others: [pair("cloud", "x")]) + #expect(w.map(\.level) == w.map(\.level).sorted(by: >)) + } +} diff --git a/Tests/SstDataTests/VocabularyBuilderTests.swift b/Tests/SstDataTests/VocabularyBuilderTests.swift new file mode 100644 index 0000000..f9cd1ca --- /dev/null +++ b/Tests/SstDataTests/VocabularyBuilderTests.swift @@ -0,0 +1,19 @@ +import Testing +@testable import SstData + +struct VocabularyBuilderTests { + @Test func termsComeFirstThenCorrectionTargetsWithoutDuplicates() { + let entries = [pair("cloud code", "Claude Code"), term("Anthropic"), term("anthropic"), pair("versel", "Vercel"), term("Supabase")] + #expect(VocabularyBuilder.build(from: entries) == ["Anthropic", "Supabase", "Claude Code", "Vercel"]) + } + + @Test func listIsCapped() { + let entries = (1...40).map { term("Woord\($0)") } + #expect(VocabularyBuilder.build(from: entries).count == 24) + #expect(VocabularyBuilder.build(from: entries, maxTerms: 5).count == 5) + } + + @Test func emptyDictionaryGivesNoVocabulary() { + #expect(VocabularyBuilder.build(from: []).isEmpty) + } +} From 57eb455edb99dcbcaea1abaaf8b817e778ae47fe Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:13:58 +0200 Subject: [PATCH 16/43] feat: add history store, search and day grouping Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstData/History.swift | 164 ++++++++++++++++++++++++++ Tests/SstDataTests/HistoryTests.swift | 119 +++++++++++++++++++ 2 files changed, 283 insertions(+) create mode 100644 Sources/SstData/History.swift create mode 100644 Tests/SstDataTests/HistoryTests.swift diff --git a/Sources/SstData/History.swift b/Sources/SstData/History.swift new file mode 100644 index 0000000..d97caf3 --- /dev/null +++ b/Sources/SstData/History.swift @@ -0,0 +1,164 @@ +import Foundation +import SstCore + +public struct HistoryEntry: Codable, Equatable, Identifiable, Sendable { + public var id: UUID + public var date: Date + public var duration: TimeInterval + public var rawText: String + public var finalText: String + public var corrections: [AppliedCorrection] + + public init( + id: UUID = UUID(), date: Date, duration: TimeInterval, + rawText: String, finalText: String, corrections: [AppliedCorrection] = [] + ) { + self.id = id + self.date = date + self.duration = duration + self.rawText = rawText + self.finalText = finalText + self.corrections = corrections + } + + public init(result: DictationResult) { + self.init( + date: result.date, duration: result.duration, rawText: result.rawText, + finalText: result.finalText, corrections: result.corrections) + } +} + +/// Newest first, stored as one JSON file; saves are debounced. +@MainActor +public final class HistoryStore { + public enum LoadOutcome: Equatable { + case empty + case loaded + case recoveredCorrupt(backup: URL) + } + + private struct Document: Codable { + var version: Int + var entries: [HistoryEntry] + } + + public private(set) var entries: [HistoryEntry] = [] + public private(set) var lastError: String? + public var onChange: (() -> Void)? + + private let fileURL: URL + private var saveTask: Task? + + public init(fileURL: URL) { + self.fileURL = fileURL + } + + private static func makeEncoder() -> JSONEncoder { + let encoder = JSONEncoder() + encoder.dateEncodingStrategy = .iso8601 + encoder.outputFormatting = [.prettyPrinted, .sortedKeys] + return encoder + } + + private static func makeDecoder() -> JSONDecoder { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + return decoder + } + + @discardableResult + public func load() -> LoadOutcome { + guard let data = try? Data(contentsOf: fileURL) else { + entries = [] + return .empty + } + do { + entries = try Self.makeDecoder().decode(Document.self, from: data).entries + return .loaded + } catch { + let stamp = ISO8601DateFormatter().string(from: Date()).replacingOccurrences(of: ":", with: "-") + let backup = fileURL.deletingLastPathComponent().appending(path: "history.corrupt-\(stamp).json") + try? FileManager.default.moveItem(at: fileURL, to: backup) + entries = [] + return .recoveredCorrupt(backup: backup) + } + } + + public func add(_ entry: HistoryEntry) { + entries.insert(entry, at: 0) + changed() + } + + public func remove(id: UUID) { + entries.removeAll { $0.id == id } + changed() + } + + public func removeAll() { + entries = [] + changed() + } + + /// Writes immediately (also call this when the app quits). + public func flush() { + saveTask?.cancel() + saveTask = nil + do { + try FileManager.default.createDirectory( + at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true) + let data = try Self.makeEncoder().encode(Document(version: 1, entries: entries)) + try data.write(to: fileURL, options: .atomic) + lastError = nil + } catch { + lastError = error.localizedDescription + } + } + + private func changed() { + onChange?() + saveTask?.cancel() + saveTask = Task { [weak self] in + try? await Task.sleep(for: .milliseconds(300)) + guard !Task.isCancelled else { return } + self?.flush() + } + } +} + +public enum HistorySearch { + static func fold(_ text: String) -> String { + text.folding(options: [.caseInsensitive, .diacriticInsensitive], locale: nil) + } + + /// Every query word must occur in the final or the original text (case- and diacritic-insensitive). + public static func filter(_ entries: [HistoryEntry], query: String) -> [HistoryEntry] { + let words = fold(query).split(whereSeparator: \.isWhitespace).map(String.init) + guard !words.isEmpty else { return entries } + return entries.filter { entry in + let haystack = fold(entry.finalText + " " + entry.rawText) + return words.allSatisfy { haystack.contains($0) } + } + } +} + +public struct HistoryDay: Identifiable, Equatable, Sendable { + public let day: Date + public let entries: [HistoryEntry] + public var id: Date { day } +} + +public enum HistoryGrouping { + /// Groups newest-first entries by calendar day, keeping their order. + public static func byDay(_ entries: [HistoryEntry], calendar: Calendar = .current) -> [HistoryDay] { + var days: [HistoryDay] = [] + for entry in entries { + let start = calendar.startOfDay(for: entry.date) + if let last = days.last, last.day == start { + days[days.count - 1] = HistoryDay(day: start, entries: last.entries + [entry]) + } else { + days.append(HistoryDay(day: start, entries: [entry])) + } + } + return days + } +} diff --git a/Tests/SstDataTests/HistoryTests.swift b/Tests/SstDataTests/HistoryTests.swift new file mode 100644 index 0000000..13c06e2 --- /dev/null +++ b/Tests/SstDataTests/HistoryTests.swift @@ -0,0 +1,119 @@ +import Foundation +import Testing +import SstCore +@testable import SstData + +@MainActor +struct HistoryStoreTests { + func tempFile() -> URL { + FileManager.default.temporaryDirectory.appending(path: "sst-tests-\(UUID().uuidString)/history.json") + } + + func entry(_ text: String, at seconds: TimeInterval) -> HistoryEntry { + HistoryEntry(date: Date(timeIntervalSince1970: 1_700_000_000 + seconds), duration: 2, rawText: text, finalText: text) + } + + @Test func newestEntryComesFirstAndSurvivesARoundTrip() { + let url = tempFile() + let store = HistoryStore(fileURL: url) + store.add(entry("eerste", at: 0)) + store.add(HistoryEntry( + date: Date(timeIntervalSince1970: 1_700_000_100), duration: 3, rawText: "cloud code", finalText: "Claude Code", + corrections: [AppliedCorrection(heard: "cloud code", wrote: "Claude Code", rule: "cloud code", start: 0, length: 11)])) + store.flush() + + let reloaded = HistoryStore(fileURL: url) + #expect(reloaded.load() == .loaded) + #expect(reloaded.entries.map(\.finalText) == ["Claude Code", "eerste"]) + #expect(reloaded.entries == store.entries) + } + + @Test func missingFileIsEmpty() { + let store = HistoryStore(fileURL: tempFile()) + #expect(store.load() == .empty) + #expect(store.entries.isEmpty) + } + + @Test func removeAndRemoveAll() { + let store = HistoryStore(fileURL: tempFile()) + let a = entry("a", at: 0), b = entry("b", at: 1) + store.add(a); store.add(b) + store.remove(id: a.id) + #expect(store.entries.map(\.finalText) == ["b"]) + store.removeAll() + #expect(store.entries.isEmpty) + } + + @Test func corruptFileIsMovedAsideAndStoreStartsFresh() throws { + let url = tempFile() + try FileManager.default.createDirectory(at: url.deletingLastPathComponent(), withIntermediateDirectories: true) + try "dit is geen json".write(to: url, atomically: true, encoding: .utf8) + let store = HistoryStore(fileURL: url) + guard case .recoveredCorrupt(let backup) = store.load() else { + Issue.record("expected recoveredCorrupt"); return + } + #expect(FileManager.default.fileExists(atPath: backup.path)) + #expect(!FileManager.default.fileExists(atPath: url.path)) + #expect(store.entries.isEmpty) + } + + @Test func onChangeFiresForEdits() { + let store = HistoryStore(fileURL: tempFile()) + var calls = 0 + store.onChange = { calls += 1 } + store.add(entry("a", at: 0)) + store.removeAll() + #expect(calls == 2) + } + + @Test func entryFromDictationResultKeepsEverything() { + let result = DictationResult( + date: Date(timeIntervalSince1970: 1_700_000_000), duration: 4.5, rawText: "raw", finalText: "final", + corrections: [AppliedCorrection(heard: "a", wrote: "b", rule: "a", start: 0, length: 1)]) + let e = HistoryEntry(result: result) + #expect(e.rawText == "raw" && e.finalText == "final" && e.duration == 4.5 && e.corrections.count == 1) + } +} + +struct HistorySearchTests { + let entries = [ + HistoryEntry(date: .now, duration: 1, rawText: "ik ga naar het café", finalText: "Ik ga naar het Café"), + HistoryEntry(date: .now, duration: 1, rawText: "cloud code is handig", finalText: "Claude Code is handig"), + ] + + @Test func emptyQueryReturnsEverything() { + #expect(HistorySearch.filter(entries, query: " ").count == 2) + } + + @Test func ignoresCaseAndDiacritics() { + #expect(HistorySearch.filter(entries, query: "CAFE").count == 1) + #expect(HistorySearch.filter(entries, query: "café").count == 1) + } + + @Test func allWordsMustMatchInAnyOrder() { + #expect(HistorySearch.filter(entries, query: "handig claude").count == 1) + #expect(HistorySearch.filter(entries, query: "handig café").isEmpty) + } + + @Test func searchesTheOriginalTextToo() { + #expect(HistorySearch.filter(entries, query: "cloud").count == 1) + } +} + +struct HistoryGroupingTests { + @Test func groupsByDayKeepingOrder() { + var calendar = Calendar(identifier: .gregorian) + calendar.timeZone = TimeZone(identifier: "UTC")! + func at(_ day: Int, _ hour: Int) -> Date { + calendar.date(from: DateComponents(year: 2026, month: 10, day: day, hour: hour))! + } + let entries = [at(2, 15), at(2, 9), at(1, 20)].map { + HistoryEntry(date: $0, duration: 1, rawText: "x", finalText: "x") + } + let days = HistoryGrouping.byDay(entries, calendar: calendar) + #expect(days.count == 2) + #expect(days[0].entries.count == 2) + #expect(days[1].entries.count == 1) + #expect(days[0].day > days[1].day) + } +} From bf731dab37680c1ad2b256dd2eb0fa4dd4a1bf9f Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:16:21 +0200 Subject: [PATCH 17/43] feat: add DictionaryStore with file watching and DictionaryCorrector Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstData/DictionaryStore.swift | 116 ++++++++++++++++++ Tests/SstDataTests/DictionaryStoreTests.swift | 82 +++++++++++++ 2 files changed, 198 insertions(+) create mode 100644 Sources/SstData/DictionaryStore.swift create mode 100644 Tests/SstDataTests/DictionaryStoreTests.swift diff --git a/Sources/SstData/DictionaryStore.swift b/Sources/SstData/DictionaryStore.swift new file mode 100644 index 0000000..20072c1 --- /dev/null +++ b/Sources/SstData/DictionaryStore.swift @@ -0,0 +1,116 @@ +import Foundation +import SstCore + +/// Owns `dictionary.txt`: reads, writes atomically, and notices edits made in another editor. +@MainActor +public final class DictionaryStore { + public static let header = """ + # Sst woordenboek + # Eén invoer per regel. Een los woord is een term (voorrang bij het model). + # 'gehoord => schrijf' is een correctie, bijvoorbeeld: cloud code => Claude Code + + """ + + public let fileURL: URL + public private(set) var file = DictionaryFile() + public var onChange: (() -> Void)? + + private var lastText = "" + private var watcher: DispatchSourceFileSystemObject? + private var debounce: Task? + + public init(fileURL: URL) { + self.fileURL = fileURL + } + + public var entries: [DictionaryEntry] { file.entries } + + /// Reads the file; creates it with a short header when it does not exist yet. + public func load() throws { + try FileManager.default.createDirectory( + at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true) + if !FileManager.default.fileExists(atPath: fileURL.path) { + try Self.header.write(to: fileURL, atomically: true, encoding: .utf8) + } + let text = try String(contentsOf: fileURL, encoding: .utf8) + lastText = text + file = DictionaryFile(parsing: text) + onChange?() + } + + /// Applies an edit and writes the file straight away. + public func apply(_ mutate: (inout DictionaryFile) -> Void) throws { + var updated = file + mutate(&updated) + let text = updated.serialized() + try text.write(to: fileURL, atomically: true, encoding: .utf8) + file = updated + lastText = text + onChange?() + } + + /// Re-reads the file when its text differs from what we last wrote or read. + public func reloadIfChanged() { + guard let text = try? String(contentsOf: fileURL, encoding: .utf8), text != lastText else { return } + lastText = text + file = DictionaryFile(parsing: text) + onChange?() + } + + /// Watches the folder (editors replace files instead of editing them in place). + public func startWatching() { + guard watcher == nil else { return } + let descriptor = open(fileURL.deletingLastPathComponent().path, O_EVTONLY) + guard descriptor >= 0 else { return } + let source = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: descriptor, eventMask: .write, queue: .main) + source.setEventHandler { [weak self] in + MainActor.assumeIsolated { self?.scheduleReload() } + } + source.setCancelHandler { close(descriptor) } + source.resume() + watcher = source + } + + public func stopWatching() { + watcher?.cancel() + watcher = nil + debounce?.cancel() + } + + private func scheduleReload() { + debounce?.cancel() + debounce = Task { [weak self] in + try? await Task.sleep(for: .milliseconds(200)) + guard !Task.isCancelled else { return } + self?.reloadIfChanged() + } + } +} + +/// Bridges the dictionary to the controller: thread-safe, can be switched off, rebuilt when the dictionary changes. +public final class DictionaryCorrector: TextCorrecting, @unchecked Sendable { + private let lock = NSLock() + private let knowledge: WordKnowledge + private var engine: CorrectionEngine + private var enabled = true + + public init(knowledge: WordKnowledge) { + self.knowledge = knowledge + self.engine = CorrectionEngine(entries: [], knowledge: knowledge) + } + + public func update(entries: [DictionaryEntry]) { + let rebuilt = CorrectionEngine(entries: entries, knowledge: knowledge) + lock.withLock { engine = rebuilt } + } + + public func setEnabled(_ value: Bool) { + lock.withLock { enabled = value } + } + + public func correct(_ text: String) -> CorrectionResult { + let (current, isOn) = lock.withLock { (engine, enabled) } + return isOn ? current.correct(text) : CorrectionResult(text: text, corrections: []) + } +} diff --git a/Tests/SstDataTests/DictionaryStoreTests.swift b/Tests/SstDataTests/DictionaryStoreTests.swift new file mode 100644 index 0000000..f614813 --- /dev/null +++ b/Tests/SstDataTests/DictionaryStoreTests.swift @@ -0,0 +1,82 @@ +import Foundation +import Testing +import SstCore +@testable import SstData + +@MainActor +struct DictionaryStoreTests { + func tempFile() -> URL { + FileManager.default.temporaryDirectory.appending(path: "sst-tests-\(UUID().uuidString)/dictionary.txt") + } + + @Test func loadCreatesAFileWithAHeaderWhenMissing() throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + #expect(FileManager.default.fileExists(atPath: url.path)) + #expect(store.entries.isEmpty) + #expect(try String(contentsOf: url, encoding: .utf8).hasPrefix("# Sst woordenboek")) + } + + @Test func applyWritesAtomicallyAndKeepsComments() throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + try store.apply { $0.append(.correction(heard: "cloud code", wrote: "Claude Code")) } + let text = try String(contentsOf: url, encoding: .utf8) + #expect(text.hasPrefix("# Sst woordenboek")) + #expect(text.hasSuffix("cloud code => Claude Code\n")) + #expect(store.entries == [pair("cloud code", "Claude Code")]) + } + + @Test func externalEditIsPickedUp() throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + var changes = 0 + store.onChange = { changes += 1 } + try "Vercel\n".write(to: url, atomically: true, encoding: .utf8) + store.reloadIfChanged() + #expect(store.entries == [term("Vercel")]) + #expect(changes == 1) + store.reloadIfChanged() // unchanged text: no second notification + #expect(changes == 1) + } + + @Test func ourOwnWriteDoesNotTriggerAReload() throws { + let store = DictionaryStore(fileURL: tempFile()) + try store.load() + try store.apply { $0.append(.term("Supabase")) } + var changes = 0 + store.onChange = { changes += 1 } + store.reloadIfChanged() + #expect(changes == 0) + } + + @Test func watcherNoticesAnEditorSavingTheFile() async throws { + let url = tempFile() + let store = DictionaryStore(fileURL: url) + try store.load() + var changed = false + store.onChange = { changed = true } + store.startWatching() + try "Anthropic\n".write(to: url, atomically: true, encoding: .utf8) + for _ in 0..<40 where !changed { try await Task.sleep(for: .milliseconds(100)) } + store.stopWatching() + #expect(changed) + #expect(store.entries == [term("Anthropic")]) + } +} + +struct DictionaryCorrectorTests { + @Test func correctsWhenEnabledAndPassesThroughWhenDisabled() { + let corrector = DictionaryCorrector(knowledge: FakeKnowledge()) + corrector.update(entries: [pair("cloud code", "Claude Code")]) + #expect(corrector.correct("cloud code").text == "Claude Code") + corrector.setEnabled(false) + #expect(corrector.correct("cloud code").text == "cloud code") + corrector.setEnabled(true) + corrector.update(entries: []) + #expect(corrector.correct("cloud code").text == "cloud code") + } +} From bd6bc3b890d6aa36e4de66733608ff42cf3c3630 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:19:06 +0200 Subject: [PATCH 18/43] feat: generate Swift design tokens from tokens.json and add SstUI base Co-Authored-By: Claude Sonnet 5.5 --- Design/build_tokens.py | 132 +++++++++ Design/preview.template.html | 18 +- Design/tokens.json | 17 +- Package.swift | 2 + Sources/SstUI/DesignTokens.swift | 316 +++++++++++++++++++++ Sources/SstUI/DynamicColor.swift | 26 ++ Sources/SstUI/TextStyle.swift | 78 +++++ Tests/SstUITests/TokenIntegrityTests.swift | 58 ++++ docs/design/tokens-preview.html | 20 +- docs/design/tokens.md | 13 + 10 files changed, 659 insertions(+), 21 deletions(-) create mode 100644 Sources/SstUI/DesignTokens.swift create mode 100644 Sources/SstUI/DynamicColor.swift create mode 100644 Sources/SstUI/TextStyle.swift create mode 100644 Tests/SstUITests/TokenIntegrityTests.swift diff --git a/Design/build_tokens.py b/Design/build_tokens.py index 8ca01e9..c81edd0 100644 --- a/Design/build_tokens.py +++ b/Design/build_tokens.py @@ -58,6 +58,138 @@ print(f"{'text':16} {'on':20} {'finish':6} ratio") for fg, bg, theme, r, ok in rows: print(f"{fg:16} {bg:20} {theme:6} {r:5.2f} {'ok' if ok else 'FAIL'}") +# --- swift ------------------------------------------------------------------ +def camel(name): + parts = name.replace("-", ".").split(".") + return parts[0] + "".join(p[:1].upper() + p[1:] for p in parts[1:]) + + +def num(v): + return repr(float(v)) if isinstance(v, float) else str(v) + + +def doc(use): + return " /// " + use.replace("\n", " ") + + +def swift_source(t): + o = [] + add = o.append + add("// GENERATED by Design/build_tokens.py from Design/tokens.json. Do not edit by hand.") + add("import SwiftUI\n") + + add("public enum Palette {") + for n, c in t["color"].items(): + add(doc(c["use"])) + add(f" public static let {camel(n)} = Color.dynamic(light: 0x{c['light'][1:].upper()}, dark: 0x{c['dark'][1:].upper()})") + add("}\n") + + add("public enum Opacity {") + for n, v in t["opacity"].items(): + add(doc(v["use"])) + add(f" public static let {camel(n)}: Double = {num(v['value'])}") + add("}\n") + + add("public enum FontFamily: String {") + for n, f in t["font"].items(): + add(f" case {n.replace('family.', '')} = \"{f['value']}\"") + add("}\n") + + add("public enum Typography {") + for n, s in t["type"].items(): + add(doc(s["use"])) + add(f" public static let {camel(n)} = TextStyle(family: .{s['family']}, size: {num(s['size'])}, weight: {s['weight']}, " + f"tracking: {num(s['tracking'])}, line: {num(s['line'])}, caps: {'true' if s['caps'] else 'false'}, " + f"tabular: {'true' if s.get('tabular') else 'false'})") + add("}\n") + + add("public enum Segment {") + for n, v in t["segment"].items(): + add(doc(v["use"])) + add(f" public static let {camel(n)}: CGFloat = {num(v['value'])}") + add("}\n") + + add("public enum Space {") + for n, v in t["space"].items(): + add(f" public static let s{n}: CGFloat = {v}") + add("}\n") + + for enum, group in (("Layout", "layout"), ("Size", "size"), ("Radius", "radius"), ("Border", "border")): + add(f"public enum {enum} {{") + for n, v in t[group].items(): + add(doc(v["use"])) + add(f" public static let {camel(n)}: CGFloat = {num(v['value'])}") + add("}\n") + + add("public enum Shadows {") + for n, s in t["shadow"].items(): + add(doc(s["use"])) + add(f" public static let {camel(n)}: [ShadowLayer] = [") + for l in s["layers"]: + add(f" ShadowLayer(inset: {'true' if l['inset'] else 'false'}, x: {l['x']}, y: {l['y']}, blur: {l['blur']}, spread: {l['spread']}, " + f"hex: 0x{l['color'][1:].upper()}, alphaLight: {num(l['alpha']['light'])}, alphaDark: {num(l['alpha']['dark'])}),") + add(" ]") + if "translateY" in s: + add(f" public static let {camel(n)}Offset: CGFloat = {s['translateY']}") + add("}\n") + + add("public enum Texture {") + for n, v in t["texture"].items(): + add(f" public static let {camel(n)}GrainLight: Double = {num(v['grain']['light'])}") + add(f" public static let {camel(n)}GrainDark: Double = {num(v['grain']['dark'])}") + add("}\n") + + add("public enum MeterTokens {") + for n, v in t["meter"].items(): + val = v["value"] + if n == "vu.ticks": + add(doc(v["use"])) + add(f" public static let vuTicks: [Int] = [{', '.join(str(int(x)) for x in val.split())}]") + elif n == "led.segmentSize": + w, h = val.split("x") + add(doc(v["use"])) + add(f" public static let ledSegmentWidth: CGFloat = {w}") + add(f" public static let ledSegmentHeight: CGFloat = {h}") + elif n == "peak.holdMs": + add(doc(v["use"])) + add(f" public static let peakHoldSeconds: Double = {num(val / 1000)}") + elif n == "led.segments": + add(doc(v["use"])) + add(f" public static let ledSegments: Int = {val}") + else: + add(doc(v["use"])) + add(f" public static let {camel(n)}: Double = {num(val)}") + add("}\n") + + add("public enum Motion {") + for n, m in t["motion"].items(): + if n == "reducedMotion": + continue + add(doc(m["use"])) + if m.get("unit") == "ms": + add(f" public static let {camel(n)}: Double = {num(m['value'] / 1000)}") + elif m.get("unit") in ("s", "ratio", "Hz"): + add(f" public static let {camel(n)}: Double = {num(m['value'])}") + elif isinstance(m["value"], str) and m["value"].startswith("cubic-bezier"): + pts = [x.strip() for x in m["value"][m["value"].index("(") + 1:-1].split(",")] + add(f" public static let {camel(n)}: (Double, Double, Double, Double) = ({', '.join(pts)})") + add("}") + return "\n".join(o) + "\n" + + +swift_text = swift_source(tokens) +swift_dir = ROOT / "Sources" / "SstUI" +swift_path = swift_dir / "DesignTokens.swift" + +if "--check" in sys.argv: + in_sync = swift_path.exists() and swift_path.read_text() == swift_text + print("DesignTokens.swift is " + ("in sync with tokens.json" if in_sync else "OUT OF DATE: run python3 Design/build_tokens.py")) + sys.exit(0 if (in_sync and not failed) else 1) + +if swift_dir.exists(): + swift_path.write_text(swift_text) + print("wrote Sources/SstUI/DesignTokens.swift") + # --- markdown --------------------------------------------------------------- md = [] add = md.append diff --git a/Design/preview.template.html b/Design/preview.template.html index 4bac882..7c5ceb6 100644 --- a/Design/preview.template.html +++ b/Design/preview.template.html @@ -264,29 +264,29 @@ $('#rad').innerHTML = Object.entries(T.radius).map(([n, r]) => `
`; s += ``; const redFrom = frac(0); const arcPath = (r0, f0, f1) => { const [x0, y0] = pt(r0, ang(f0)), [x1, y1] = pt(r0, ang(f1)); return `M${f2(x0)} ${f2(y0)} A${r0} ${r0} 0 0 1 ${f2(x1)} ${f2(y1)}`; }; s += ``; - s += ``; + s += ``; for (let v = vuMin; v <= vuMax; v++) { const major = [-20, -10, -7, -5, -3, 0, 3].includes(v), a = ang(frac(v)); - const [x0, y0] = pt(R, a), [x1, y1] = pt(R - (major ? 11 : 6), a); + const [x0, y0] = pt(R, a), [x1, y1] = pt(R - (major ? VU['vu.tickMajor'].value : VU['vu.tickMinor'].value), a); s += ``; - if (major) { const [tx, ty] = pt(R - 24, a); s += `${v > 0 ? '+' + v : v}`; } + if (major) { const [tx, ty] = pt(R - VU['vu.labelInset'].value, a); s += `${v > 0 ? '+' + v : v}`; } } s += `VU`; s += `SST · FIELD RECORDER`; - s += ``; + s += ``; s += `PEAK`; s += ``; return s; @@ -304,9 +304,9 @@ function digit(ch, ox, lit) { return `` + Object.entries(P).map(([k, pts]) => ``).join('') + ``; } function counterSvg(text) { - const adv = DW + 6; let x = 0, out = ''; + const adv = SEG.advance.value; let x = 0, out = ''; for (const ch of text) { - if (ch === ':' || ch === '.') { out += `` + (ch === ':' ? `` : ''); x += 10; } + if (ch === ':' || ch === '.') { out += `` + (ch === ':' ? `` : ''); x += SEG.colonWidth.value; } else { out += digit(ch, x, true); x += adv; } } return `${out}`; @@ -350,7 +350,7 @@ function frame(now) { const a = wn * wn * (tgt - r.x) - 2 * zeta * wn * r.v; r.v += a * dt; r.x += r.v * dt; const ang_ = ang(Math.max(-0.02, Math.min(1.05, r.x))); - const [nx, ny] = pt(146, ang_); + const [nx, ny] = pt(VU['vu.needleLength'].value, ang_); r.needle.setAttribute('x2', f2(nx)); r.needle.setAttribute('y2', f2(ny)); const vuNow = vuMin + (vuMax - vuMin) * r.x; if (vuNow > 0 && r.running) r.peakUntil = now + VU['peak.holdMs'].value; diff --git a/Design/tokens.json b/Design/tokens.json index 0563c1d..a8f350f 100644 --- a/Design/tokens.json +++ b/Design/tokens.json @@ -45,7 +45,11 @@ "opacity": { "mark": { "value": 0.30, "use": "Amber markering achter een gecorrigeerd woord" }, "disabled": { "value": 0.45, "use": "Uitgeschakelde bediening" }, - "unlit": { "value": 1.00, "use": "Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie" } + "unlit": { "value": 1.00, "use": "Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie" }, + "pressedRecord": { "value": 0.85, "use": "Ingedrukte REC-toets: het rood dooft licht" }, + "specularOn": { "value": 0.60, "use": "Lichtvlekje op een brandend lampje" }, + "specularOff": { "value": 0.30, "use": "Lichtvlekje op een gedoofd lampje" }, + "caption": { "value": 0.75, "use": "Kleine opschriften op de wijzerplaat" } }, "font": { @@ -72,7 +76,9 @@ "digitWidth": { "value": 20, "use": "Breedte van een segmentcijfer" }, "thickness": { "value": 4, "use": "Dikte van één segment" }, "gap": { "value": 1, "use": "Ruimte tussen segmenten" }, - "slant": { "value": 0, "use": "Schuinte in graden; 0 = rechtop" } + "slant": { "value": 0, "use": "Schuinte in graden; 0 = rechtop" }, + "advance": { "value": 26, "use": "Afstand van cijfer tot cijfer" }, + "colonWidth": { "value": 10, "use": "Breedte van een dubbele punt of punt" } }, "space": { @@ -179,6 +185,13 @@ "vu.height": { "value": 150, "use": "Hoogte van de meter" }, "vu.needleWidth": { "value": 1.5, "use": "Dikte van de wijzer" }, "vu.ticks": { "value": "-20 -10 -7 -5 -3 0 +3", "use": "Genummerde schaalmarkeringen" }, + "vu.pivotOffset": { "value": 22, "use": "Draaipunt van de wijzer, onder de onderrand van de wijzerplaat" }, + "vu.scaleRadius": { "value": 142, "use": "Straal van de schaalboog vanaf het draaipunt" }, + "vu.needleLength": { "value": 146, "use": "Lengte van de wijzer" }, + "vu.tickMajor": { "value": 11, "use": "Lengte van een genummerde streep" }, + "vu.tickMinor": { "value": 6, "use": "Lengte van een gewone streep" }, + "vu.labelInset": { "value": 24, "use": "Afstand van de schaalboog tot de cijfers" }, + "vu.redBandWidth": { "value": 5, "use": "Dikte van de rode band vanaf 0 VU" }, "led.segments": { "value": 12, "use": "Aantal segmenten: 8 groen, 3 amber, 1 rood" }, "led.segmentSize": { "value": "6x14", "use": "Segment in pt (breedte x hoogte)" }, "led.gap": { "value": 2, "use": "Ruimte tussen segmenten" }, diff --git a/Package.swift b/Package.swift index 3ef7517..f7272f3 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,7 @@ let package = Package( ], targets: [ .target(name: "SstCore"), + .target(name: "SstUI"), .target(name: "SstData", dependencies: ["SstCore"]), .target( name: "SstWhisper", @@ -22,6 +23,7 @@ let package = Package( .testTarget(name: "SstCoreTests", dependencies: ["SstCore"]), .testTarget(name: "SstDataTests", dependencies: ["SstData", "SstCore"]), .testTarget(name: "SstWhisperTests", dependencies: ["SstWhisper"]), + .testTarget(name: "SstUITests", dependencies: ["SstUI"]), ], swiftLanguageModes: [.v5] ) diff --git a/Sources/SstUI/DesignTokens.swift b/Sources/SstUI/DesignTokens.swift new file mode 100644 index 0000000..e8a4e1d --- /dev/null +++ b/Sources/SstUI/DesignTokens.swift @@ -0,0 +1,316 @@ +// GENERATED by Design/build_tokens.py from Design/tokens.json. Do not edit by hand. +import SwiftUI + +public enum Palette { + /// Venster-achtergrond; het 'huis' van het apparaat + public static let surfaceChassis = Color.dynamic(light: 0xCBC8C0, dark: 0x1C1B19) + /// Geborsteld metaal, bovenkant van de verloop + public static let surfaceMetalHigh = Color.dynamic(light: 0xE3E1DB, dark: 0x3B3A36) + /// Geborsteld metaal, onderkant van de verloop + public static let surfaceMetalLow = Color.dynamic(light: 0xBDBAB2, dark: 0x262522) + /// Frontplaat van mat plastic; zijbalk en bedieningspanelen + public static let surfacePanel = Color.dynamic(light: 0xE2DFD5, dark: 0x2B2A27) + /// Verzonken vlak: invoervelden, display-vensters + public static let surfaceWell = Color.dynamic(light: 0xB4B1A8, dark: 0x131211) + /// Cassette-label crème: lijsten met transcripties en woordenboek + public static let surfacePaper = Color.dynamic(light: 0xEEE9D9, dark: 0x232220) + /// Toetsdop, bovenkant + public static let surfaceKeyHigh = Color.dynamic(light: 0xE6E3DA, dark: 0x44433F) + /// Toetsdop, onderkant + public static let surfaceKeyLow = Color.dynamic(light: 0xCBC8BF, dark: 0x33322F) + /// Ingedrukte toets + public static let surfaceKeyPressed = Color.dynamic(light: 0xBEBBB2, dark: 0x2A2927) + /// Wijzerplaat van de VU-meter (blijft crème in beide afwerkingen) + public static let surfaceMeterFace = Color.dynamic(light: 0xEFE6C8, dark: 0xD8CEA6) + /// Geselecteerde rij in een lijst + public static let surfaceSelection = Color.dynamic(light: 0xD4CDB2, dark: 0x3A3828) + /// Displayglas van de teller; donker in beide afwerkingen + public static let surfaceDisplay = Color.dynamic(light: 0x1B1A18, dark: 0x0E0D0C) + /// Gedoofd segment op het displayglas + public static let displaySegmentUnlit = Color.dynamic(light: 0x3A3320, dark: 0x2A2415) + /// Hoofdtekst en wijzer + public static let inkPrimary = Color.dynamic(light: 0x1E1D1B, dark: 0xECE8DC) + /// Secundaire tekst + public static let inkSecondary = Color.dynamic(light: 0x403D38, dark: 0xC4BFB2) + /// Zeefdruk-labels en hulptekst + public static let inkTertiary = Color.dynamic(light: 0x4D4A44, dark: 0xABA79B) + /// Uitgeschakeld; nooit voor essentiële tekst + public static let inkDisabled = Color.dynamic(light: 0x8F8B82, dark: 0x6B685F) + /// Tekst op rode of donkere vulling; crème in beide afwerkingen + public static let inkInverse = Color.dynamic(light: 0xF4EFE3, dark: 0xF4EFE3) + /// Schaal, cijfers en wijzer op de wijzerplaat; vast donker in beide afwerkingen + public static let inkOnMeter = Color.dynamic(light: 0x1E1D1B, dark: 0x1E1D1B) + /// ENIGE accent: opnamelampje, REC-toets, piek-LED. Nooit voor lopende tekst + public static let accentRecord = Color.dynamic(light: 0xC8281E, dark: 0xCC2A20) + /// Lampje uit + public static let accentRecordUnlit = Color.dynamic(light: 0x7A2E28, dark: 0x4A1F1B) + /// Niveau-segment, brandend + public static let levelGreen = Color.dynamic(light: 0x5E9E48, dark: 0x6DB556) + /// Niveau-segment, waarschuwing, gecorrigeerd woord + public static let levelAmber = Color.dynamic(light: 0xD9A023, dark: 0xE5B032) + /// Groen segment, uit + public static let levelGreenUnlit = Color.dynamic(light: 0xA8B79C, dark: 0x26331F) + /// Amber segment, uit + public static let levelAmberUnlit = Color.dynamic(light: 0xCDBA8A, dark: 0x3D3418) + /// Naad tussen panelen, donkere helft + public static let lineSeamDark = Color.dynamic(light: 0x8C8981, dark: 0x0A0A09) + /// Naad tussen panelen, lichte helft + public static let lineSeamLight = Color.dynamic(light: 0xF3F1EB, dark: 0x45433F) + /// Fijne scheidingslijn, zeefdruk-kader + public static let lineHairline = Color.dynamic(light: 0xA9A69D, dark: 0x3A3834) + /// Toetsenbord-focus + public static let lineFocus = Color.dynamic(light: 0x1E1D1B, dark: 0xECE8DC) +} + +public enum Opacity { + /// Amber markering achter een gecorrigeerd woord + public static let mark: Double = 0.3 + /// Uitgeschakelde bediening + public static let disabled: Double = 0.45 + /// Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie + public static let unlit: Double = 1.0 + /// Ingedrukte REC-toets: het rood dooft licht + public static let pressedRecord: Double = 0.85 + /// Lichtvlekje op een brandend lampje + public static let specularOn: Double = 0.6 + /// Lichtvlekje op een gedoofd lampje + public static let specularOff: Double = 0.3 + /// Kleine opschriften op de wijzerplaat + public static let caption: Double = 0.75 +} + +public enum FontFamily: String { + case chrome = "Helvetica Neue" + case text = "SF Pro Text" + case mono = "SF Mono" +} + +public enum Typography { + /// Zeefdruk-label + public static let label = TextStyle(family: .chrome, size: 10, weight: 500, tracking: 0.09, line: 12, caps: true, tabular: false) + /// Schaalmarkeringen en eenheden + public static let labelSmall = TextStyle(family: .chrome, size: 9, weight: 500, tracking: 0.1, line: 11, caps: true, tabular: false) + /// Opschrift op toetsen + public static let control = TextStyle(family: .chrome, size: 11, weight: 500, tracking: 0.06, line: 14, caps: true, tabular: false) + /// Paneelkop + public static let heading = TextStyle(family: .chrome, size: 12, weight: 500, tracking: 0.12, line: 16, caps: true, tabular: false) + /// Standaardtekst + public static let body = TextStyle(family: .text, size: 13, weight: 400, tracking: 0, line: 18, caps: false, tabular: false) + /// Transcript + public static let reading = TextStyle(family: .text, size: 15, weight: 400, tracking: 0, line: 22, caps: false, tabular: false) + /// Bijschriften + public static let meta = TextStyle(family: .text, size: 11, weight: 400, tracking: 0, line: 14, caps: false, tabular: false) + /// Teller + public static let counter = TextStyle(family: .mono, size: 22, weight: 500, tracking: 0, line: 26, caps: false, tabular: true) + /// Tijdstip in een rij + public static let counterSmall = TextStyle(family: .mono, size: 12, weight: 500, tracking: 0, line: 16, caps: false, tabular: true) + /// Woordenboekregels + public static let code = TextStyle(family: .mono, size: 12, weight: 400, tracking: 0, line: 18, caps: false, tabular: false) +} + +public enum Segment { + /// Hoogte van een getekend segmentcijfer + public static let digitHeight: CGFloat = 36 + /// Breedte van een segmentcijfer + public static let digitWidth: CGFloat = 20 + /// Dikte van één segment + public static let thickness: CGFloat = 4 + /// Ruimte tussen segmenten + public static let gap: CGFloat = 1 + /// Schuinte in graden; 0 = rechtop + public static let slant: CGFloat = 0 + /// Afstand van cijfer tot cijfer + public static let advance: CGFloat = 26 + /// Breedte van een dubbele punt of punt + public static let colonWidth: CGFloat = 10 +} + +public enum Space { + public static let s0: CGFloat = 0 + public static let s1: CGFloat = 2 + public static let s2: CGFloat = 4 + public static let s3: CGFloat = 8 + public static let s4: CGFloat = 12 + public static let s5: CGFloat = 16 + public static let s6: CGFloat = 24 + public static let s7: CGFloat = 32 + public static let s8: CGFloat = 48 +} + +public enum Layout { + /// Minimale vensterbreedte + public static let windowMinWidth: CGFloat = 800 + /// Minimale vensterhoogte + public static let windowMinHeight: CGFloat = 560 + /// Standaard vensterbreedte + public static let windowDefaultWidth: CGFloat = 1080 + /// Standaard vensterhoogte + public static let windowDefaultHeight: CGFloat = 700 + /// Breedte zijbalk + public static let sidebarWidth: CGFloat = 232 + /// Breedte instellingenvenster + public static let settingsWidth: CGFloat = 560 + /// Binnenmarge van een paneel (= space.5) + public static let panelPadding: CGFloat = 16 + /// Afstand tussen panelen (= space.4) + public static let panelGap: CGFloat = 12 +} + +public enum Size { + /// Standaardhoogte van toets of veld + public static let controlHeight: CGFloat = 28 + /// Minimale breedte van een toets + public static let keyMinWidth: CGFloat = 56 + /// REC-toets, breedte + public static let keyRecWidth: CGFloat = 64 + /// REC-toets, hoogte + public static let keyRecHeight: CGFloat = 44 + /// Standaard lampje + public static let lampDiameter: CGFloat = 10 + /// Opnamelampje + public static let lampLarge: CGFloat = 14 + /// Tuimelschakelaar, breedte + public static let switchWidth: CGFloat = 40 + /// Tuimelschakelaar, hoogte + public static let switchHeight: CGFloat = 22 + /// Rij in transcriptielijst + public static let rowHeight: CGFloat = 40 + /// Klein pictogram + public static let iconSmall: CGFloat = 16 + /// Normaal pictogram + public static let iconMedium: CGFloat = 20 + /// Minimaal klikdoel + public static let hitMin: CGFloat = 24 +} + +public enum Radius { + /// Panelen en velden: scherpe randen + public static let none: CGFloat = 0 + /// Toetsranden: bijna scherp + public static let sharp: CGFloat = 1 + /// Toetsen, velden + public static let sm: CGFloat = 2 + /// Grote panelen + public static let md: CGFloat = 4 + /// Rand van de VU-meter + public static let bezel: CGFloat = 6 + /// Lampjes: rond + public static let lamp: CGFloat = 999 +} + +public enum Border { + /// Fijne lijn + public static let hairline: CGFloat = 1 + /// Naad: 1 donkere lijn boven, 1 lichte lijn eronder, leest als gegraveerde groef + public static let seam: CGFloat = 2 + /// Rand om de VU-meter + public static let bezel: CGFloat = 3 + /// Focusring, 2 pt offset + public static let focus: CGFloat = 2 +} + +public enum Shadows { + /// Toets in rust: lichtvang boven, donkere onderrand, valschaduw + public static let keyRaised: [ShadowLayer] = [ + ShadowLayer(inset: true, x: 0, y: 1, blur: 0, spread: 0, hex: 0xFFFFFF, alphaLight: 0.65, alphaDark: 0.1), + ShadowLayer(inset: true, x: 0, y: -1, blur: 0, spread: 0, hex: 0x000000, alphaLight: 0.25, alphaDark: 0.55), + ShadowLayer(inset: false, x: 0, y: 2, blur: 2, spread: 0, hex: 0x000000, alphaLight: 0.35, alphaDark: 0.6), + ] + /// Ingedrukte toets: dop 1 pt omlaag, geen valschaduw + public static let keyPressed: [ShadowLayer] = [ + ShadowLayer(inset: true, x: 0, y: 2, blur: 4, spread: 0, hex: 0x000000, alphaLight: 0.5, alphaDark: 0.7), + ShadowLayer(inset: true, x: 0, y: 0, blur: 0, spread: 1, hex: 0x000000, alphaLight: 0.35, alphaDark: 0.6), + ] + public static let keyPressedOffset: CGFloat = 1 + /// Verzonken vlak met lichtvang op de onderlip + public static let well: [ShadowLayer] = [ + ShadowLayer(inset: true, x: 0, y: 1, blur: 3, spread: 0, hex: 0x000000, alphaLight: 0.4, alphaDark: 0.7), + ShadowLayer(inset: false, x: 0, y: 1, blur: 0, spread: 0, hex: 0xFFFFFF, alphaLight: 0.55, alphaDark: 0.07), + ] + /// Frontplaat boven het chassis + public static let panel: [ShadowLayer] = [ + ShadowLayer(inset: true, x: 0, y: 1, blur: 0, spread: 0, hex: 0xFFFFFF, alphaLight: 0.6, alphaDark: 0.08), + ShadowLayer(inset: false, x: 0, y: 3, blur: 8, spread: 0, hex: 0x000000, alphaLight: 0.22, alphaDark: 0.55), + ] + /// Brandend lampje: kleine gloed, alleen voor lampjes en nooit voor tekst + public static let lampOn: [ShadowLayer] = [ + ShadowLayer(inset: false, x: 0, y: 0, blur: 6, spread: 1, hex: 0xCC2A20, alphaLight: 0.45, alphaDark: 0.55), + ShadowLayer(inset: true, x: 0, y: -1, blur: 1, spread: 0, hex: 0x000000, alphaLight: 0.35, alphaDark: 0.45), + ] +} + +public enum Texture { + public static let brushedGrainLight: Double = 0.035 + public static let brushedGrainDark: Double = 0.05 +} + +public enum MeterTokens { + /// 0 VU = -20 dBFS (RMS) + public static let vuReferenceDbfs: Double = -20 + /// Onderkant van de schaal in VU + public static let vuMin: Double = -20 + /// Bovenkant van de schaal in VU; vanaf 0 VU rood + public static let vuMax: Double = 3 + /// Hoek tussen minimale en maximale stand + public static let vuArcDegrees: Double = 90 + /// Breedte van de meter + public static let vuWidth: Double = 260 + /// Hoogte van de meter + public static let vuHeight: Double = 150 + /// Dikte van de wijzer + public static let vuNeedleWidth: Double = 1.5 + /// Genummerde schaalmarkeringen + public static let vuTicks: [Int] = [-20, -10, -7, -5, -3, 0, 3] + /// Draaipunt van de wijzer, onder de onderrand van de wijzerplaat + public static let vuPivotOffset: Double = 22 + /// Straal van de schaalboog vanaf het draaipunt + public static let vuScaleRadius: Double = 142 + /// Lengte van de wijzer + public static let vuNeedleLength: Double = 146 + /// Lengte van een genummerde streep + public static let vuTickMajor: Double = 11 + /// Lengte van een gewone streep + public static let vuTickMinor: Double = 6 + /// Afstand van de schaalboog tot de cijfers + public static let vuLabelInset: Double = 24 + /// Dikte van de rode band vanaf 0 VU + public static let vuRedBandWidth: Double = 5 + /// Aantal segmenten: 8 groen, 3 amber, 1 rood + public static let ledSegments: Int = 12 + /// Segment in pt (breedte x hoogte) + public static let ledSegmentWidth: CGFloat = 6 + public static let ledSegmentHeight: CGFloat = 14 + /// Ruimte tussen segmenten + public static let ledGap: Double = 2 + /// Piek-LED blijft zo lang branden + public static let peakHoldSeconds: Double = 1.0 +} + +public enum Motion { + /// Geen overgang + public static let durationInstant: Double = 0.0 + /// Toets in/uit + public static let durationFast: Double = 0.06 + /// Standaardovergang + public static let durationBase: Double = 0.12 + /// Paneel of venster + public static let durationSlow: Double = 0.22 + /// Toetsen: direct, zonder veer + public static let easingMechanical: (Double, Double, Double, Double) = (0.2, 0, 0, 1) + /// Overige overgangen + public static let easingStandard: (Double, Double, Double, Double) = (0.4, 0, 0.2, 1) + /// Toets indrukken + public static let keyPress: Double = 0.05 + /// Toets loslaten + public static let keyRelease: Double = 0.09 + /// Lampje aan + public static let lampOn: Double = 0.03 + /// Lampje uit: dooft langzaam, als een gloeidraad + public static let lampOff: Double = 0.2 + /// VU-ballistiek: 300 ms om de stand te bereiken + public static let needleResponse: Double = 0.3 + /// Veerdemping; geeft ongeveer 1,5 % overschot + public static let needleDamping: Double = 0.8 + /// Verversing van de wijzer + public static let needleUpdateHz: Double = 60 +} diff --git a/Sources/SstUI/DynamicColor.swift b/Sources/SstUI/DynamicColor.swift new file mode 100644 index 0000000..c31a376 --- /dev/null +++ b/Sources/SstUI/DynamicColor.swift @@ -0,0 +1,26 @@ +import AppKit +import SwiftUI + +extension NSColor { + convenience init(hex: UInt32) { + self.init( + srgbRed: CGFloat((hex >> 16) & 0xFF) / 255, + green: CGFloat((hex >> 8) & 0xFF) / 255, + blue: CGFloat(hex & 0xFF) / 255, + alpha: 1) + } +} + +extension Color { + /// A colour that switches between the Aluminium (light) and Graphite (dark) value with the appearance. + static func dynamic(light: UInt32, dark: UInt32) -> Color { + Color(nsColor: NSColor(name: nil) { appearance in + appearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua ? NSColor(hex: dark) : NSColor(hex: light) + }) + } + + /// The same colour in both finishes (shadow bases). + static func fixed(hex: UInt32) -> Color { + Color(nsColor: NSColor(hex: hex)) + } +} diff --git a/Sources/SstUI/TextStyle.swift b/Sources/SstUI/TextStyle.swift new file mode 100644 index 0000000..8896cab --- /dev/null +++ b/Sources/SstUI/TextStyle.swift @@ -0,0 +1,78 @@ +import SwiftUI + +public struct TextStyle: Equatable { + public let family: FontFamily + public let size: CGFloat + public let weight: Int + /// In em. + public let tracking: CGFloat + public let line: CGFloat + public let caps: Bool + public let tabular: Bool + + public init(family: FontFamily, size: CGFloat, weight: Int, tracking: CGFloat, line: CGFloat, caps: Bool, tabular: Bool) { + self.family = family + self.size = size + self.weight = weight + self.tracking = tracking + self.line = line + self.caps = caps + self.tabular = tabular + } + + var fontWeight: Font.Weight { + switch weight { + case ..<450: .regular + case 450..<550: .medium + case 550..<650: .semibold + default: .bold + } + } + + public var font: Font { + let base: Font + switch family { + case .mono: base = .system(size: size, weight: fontWeight, design: .monospaced) + case .text: base = .system(size: size, weight: fontWeight) + case .chrome: base = .custom(family.rawValue, size: size).weight(fontWeight) + } + return tabular ? base.monospacedDigit() : base + } +} + +public extension View { + /// Font, tracking, line spacing and capitalisation from a token text style. + func textStyle(_ style: TextStyle) -> some View { + self.font(style.font) + .tracking(style.tracking * style.size) + .lineSpacing(max(0, style.line - style.size * 1.2)) + .textCase(style.caps ? .uppercase : nil) + } +} + +/// One layer of a layered shadow token. Inset layers are drawn inside the shape. +public struct ShadowLayer: Equatable { + public let inset: Bool + public let x: CGFloat + public let y: CGFloat + public let blur: CGFloat + public let spread: CGFloat + public let hex: UInt32 + public let alphaLight: Double + public let alphaDark: Double + + public init(inset: Bool, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat, hex: UInt32, alphaLight: Double, alphaDark: Double) { + self.inset = inset + self.x = x + self.y = y + self.blur = blur + self.spread = spread + self.hex = hex + self.alphaLight = alphaLight + self.alphaDark = alphaDark + } + + public func color(_ scheme: ColorScheme) -> Color { + Color.fixed(hex: hex).opacity(scheme == .dark ? alphaDark : alphaLight) + } +} diff --git a/Tests/SstUITests/TokenIntegrityTests.swift b/Tests/SstUITests/TokenIntegrityTests.swift new file mode 100644 index 0000000..4e4f769 --- /dev/null +++ b/Tests/SstUITests/TokenIntegrityTests.swift @@ -0,0 +1,58 @@ +import Foundation +import Testing + +/// Root of the package: Tests/SstUITests/ → three levels up. +private let root = URL(fileURLWithPath: #filePath) + .deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent() + +struct TokenIntegrityTests { + @Test func generatedSwiftMatchesTokensJSONAndContrastHolds() throws { + #expect( + FileManager.default.fileExists(atPath: root.appending(path: "Sources/SstUI/DesignTokens.swift").path), + "Sources/SstUI/DesignTokens.swift ontbreekt: run `python3 Design/build_tokens.py`.") + let process = Process() + process.executableURL = URL(fileURLWithPath: "/usr/bin/env") + process.arguments = ["python3", "Design/build_tokens.py", "--check"] + process.currentDirectoryURL = root + let pipe = Pipe() + process.standardOutput = pipe + process.standardError = pipe + try process.run() + process.waitUntilExit() + let output = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? "" + #expect(process.terminationStatus == 0, "Run `python3 Design/build_tokens.py`.\n\(output.suffix(600))") + } + + /// Views take every colour, size and time from the tokens. A line may opt out with `// token-exempt: reason`. + @Test func viewsContainNoLooseDesignValues() throws { + let forbidden: [(name: String, pattern: String)] = [ + ("hex colour", #"0x[0-9A-Fa-f]{6}\b"#), + ("component colour", #"(NS)?Color\((red|white|hue):"#), + ("literal font size", #"\.system\(size:\s*\d"#), + ("literal corner radius", #"cornerRadius:\s*\d|\.cornerRadius\(\s*\d"#), + ("literal padding", #"\.padding\(\s*(\.\w+\s*,\s*)?\d"#), + ("literal frame size", #"(width|height|minWidth|minHeight|maxWidth|maxHeight|idealWidth|idealHeight):\s*[1-9]"#), + ("literal line width", #"lineWidth:\s*\d"#), + ("literal opacity", #"\.opacity\(\s*[0-9]"#), + ] + let skipped: Set = ["DesignTokens.swift", "DynamicColor.swift"] + var directories = [root.appending(path: "Sources/SstUI")] + let app = root.appending(path: "Sources/Sst") + if FileManager.default.fileExists(atPath: app.path) { directories.append(app) } + + var problems: [String] = [] + for directory in directories { + let files = try FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) + .filter { $0.pathExtension == "swift" && !skipped.contains($0.lastPathComponent) } + for file in files { + let lines = try String(contentsOf: file, encoding: .utf8).components(separatedBy: "\n") + for (index, line) in lines.enumerated() where !line.contains("token-exempt") { + for rule in forbidden where line.range(of: rule.pattern, options: .regularExpression) != nil { + problems.append("\(file.lastPathComponent):\(index + 1) \(rule.name): \(line.trimmingCharacters(in: .whitespaces))") + } + } + } + } + #expect(problems.isEmpty, "Loose design values:\n\(problems.joined(separator: "\n"))") + } +} diff --git a/docs/design/tokens-preview.html b/docs/design/tokens-preview.html index de04656..c9f9b10 100644 --- a/docs/design/tokens-preview.html +++ b/docs/design/tokens-preview.html @@ -106,7 +106,7 @@ - +
@@ -264,29 +264,29 @@ $('#rad').innerHTML = Object.entries(T.radius).map(([n, r]) => `
`; s += ``; const redFrom = frac(0); const arcPath = (r0, f0, f1) => { const [x0, y0] = pt(r0, ang(f0)), [x1, y1] = pt(r0, ang(f1)); return `M${f2(x0)} ${f2(y0)} A${r0} ${r0} 0 0 1 ${f2(x1)} ${f2(y1)}`; }; s += ``; - s += ``; + s += ``; for (let v = vuMin; v <= vuMax; v++) { const major = [-20, -10, -7, -5, -3, 0, 3].includes(v), a = ang(frac(v)); - const [x0, y0] = pt(R, a), [x1, y1] = pt(R - (major ? 11 : 6), a); + const [x0, y0] = pt(R, a), [x1, y1] = pt(R - (major ? VU['vu.tickMajor'].value : VU['vu.tickMinor'].value), a); s += ``; - if (major) { const [tx, ty] = pt(R - 24, a); s += `${v > 0 ? '+' + v : v}`; } + if (major) { const [tx, ty] = pt(R - VU['vu.labelInset'].value, a); s += `${v > 0 ? '+' + v : v}`; } } s += `VU`; s += `SST · FIELD RECORDER`; - s += ``; + s += ``; s += `PEAK`; s += ``; return s; @@ -304,9 +304,9 @@ function digit(ch, ox, lit) { return `` + Object.entries(P).map(([k, pts]) => ``).join('') + ``; } function counterSvg(text) { - const adv = DW + 6; let x = 0, out = ''; + const adv = SEG.advance.value; let x = 0, out = ''; for (const ch of text) { - if (ch === ':' || ch === '.') { out += `` + (ch === ':' ? `` : ''); x += 10; } + if (ch === ':' || ch === '.') { out += `` + (ch === ':' ? `` : ''); x += SEG.colonWidth.value; } else { out += digit(ch, x, true); x += adv; } } return `${out}`; @@ -350,7 +350,7 @@ function frame(now) { const a = wn * wn * (tgt - r.x) - 2 * zeta * wn * r.v; r.v += a * dt; r.x += r.v * dt; const ang_ = ang(Math.max(-0.02, Math.min(1.05, r.x))); - const [nx, ny] = pt(146, ang_); + const [nx, ny] = pt(VU['vu.needleLength'].value, ang_); r.needle.setAttribute('x2', f2(nx)); r.needle.setAttribute('y2', f2(ny)); const vuNow = vuMin + (vuMax - vuMin) * r.x; if (vuNow > 0 && r.running) r.peakUntil = now + VU['peak.holdMs'].value; diff --git a/docs/design/tokens.md b/docs/design/tokens.md index 0a93212..6f0937c 100644 --- a/docs/design/tokens.md +++ b/docs/design/tokens.md @@ -92,6 +92,10 @@ Verwijs ernaar als `color.`. Kolommen: ALUMINIUM (licht) en GRAPHITE (donk | `opacity.mark` | 0.3 | Amber markering achter een gecorrigeerd woord | | `opacity.disabled` | 0.45 | Uitgeschakelde bediening | | `opacity.unlit` | 1.0 | Gedoofde lampjes gebruiken eigen 'unlit'-kleuren, geen transparantie | +| `opacity.pressedRecord` | 0.85 | Ingedrukte REC-toets: het rood dooft licht | +| `opacity.specularOn` | 0.6 | Lichtvlekje op een brandend lampje | +| `opacity.specularOff` | 0.3 | Lichtvlekje op een gedoofd lampje | +| `opacity.caption` | 0.75 | Kleine opschriften op de wijzerplaat | ## Lettertypen @@ -125,6 +129,8 @@ Verwijs ernaar als `color.`. Kolommen: ALUMINIUM (licht) en GRAPHITE (donk | `segment.thickness` | 4 | Dikte van één segment | | `segment.gap` | 1 | Ruimte tussen segmenten | | `segment.slant` | 0 | Schuinte in graden; 0 = rechtop | +| `segment.advance` | 26 | Afstand van cijfer tot cijfer | +| `segment.colonWidth` | 10 | Breedte van een dubbele punt of punt | ## Ruimte @@ -254,6 +260,13 @@ Brandend lampje: kleine gloed, alleen voor lampjes en nooit voor tekst | `meter.vu.height` | 150 | Hoogte van de meter | | `meter.vu.needleWidth` | 1.5 | Dikte van de wijzer | | `meter.vu.ticks` | -20 -10 -7 -5 -3 0 +3 | Genummerde schaalmarkeringen | +| `meter.vu.pivotOffset` | 22 | Draaipunt van de wijzer, onder de onderrand van de wijzerplaat | +| `meter.vu.scaleRadius` | 142 | Straal van de schaalboog vanaf het draaipunt | +| `meter.vu.needleLength` | 146 | Lengte van de wijzer | +| `meter.vu.tickMajor` | 11 | Lengte van een genummerde streep | +| `meter.vu.tickMinor` | 6 | Lengte van een gewone streep | +| `meter.vu.labelInset` | 24 | Afstand van de schaalboog tot de cijfers | +| `meter.vu.redBandWidth` | 5 | Dikte van de rode band vanaf 0 VU | | `meter.led.segments` | 12 | Aantal segmenten: 8 groen, 3 amber, 1 rood | | `meter.led.segmentSize` | 6x14 | Segment in pt (breedte x hoogte) | | `meter.led.gap` | 2 | Ruimte tussen segmenten | From 9a0effa88160e80765112e91badd18dccebb52c0 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:21:10 +0200 Subject: [PATCH 19/43] feat: add instrument logic (VU scale, needle spring, segment glyphs, LED bar) Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstUI/Instruments.swift | 110 ++++++++++++++++++++++++ Tests/SstUITests/InstrumentsTests.swift | 108 +++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 Sources/SstUI/Instruments.swift create mode 100644 Tests/SstUITests/InstrumentsTests.swift diff --git a/Sources/SstUI/Instruments.swift b/Sources/SstUI/Instruments.swift new file mode 100644 index 0000000..d5e2913 --- /dev/null +++ b/Sources/SstUI/Instruments.swift @@ -0,0 +1,110 @@ +import CoreGraphics +import Foundation + +/// dBFS → VU → needle position and angle, all from the meter tokens. +public enum VUScale { + public static func vu(fromDbfs dbfs: Double) -> Double { + dbfs - MeterTokens.vuReferenceDbfs + } + + /// 0 = bottom of the scale, 1 = top. The needle may swing slightly past the end (up to 1.03). + public static func fraction(forVU vu: Double) -> Double { + min(1.03, max(0, (vu - MeterTokens.vuMin) / (MeterTokens.vuMax - MeterTokens.vuMin))) + } + + public static func fraction(forDbfs dbfs: Double) -> Double { + fraction(forVU: vu(fromDbfs: dbfs)) + } + + /// Radians from vertical; negative is left of vertical. + public static func angle(forFraction fraction: Double) -> Double { + (-MeterTokens.vuArcDegrees / 2 + MeterTokens.vuArcDegrees * fraction) * .pi / 180 + } +} + +/// Second-order needle: reaches ~99 % in `response` seconds, slight overshoot for damping < 1. +public struct NeedleSpring: Equatable { + public private(set) var position: Double + public private(set) var velocity: Double = 0 + private let omega: Double + private let zeta: Double + + public init(response: Double = Motion.needleResponse, damping: Double = Motion.needleDamping, position: Double = 0) { + self.zeta = damping + self.omega = 4.6 / (damping * response) + self.position = position + } + + /// Advances the simulation by `dt` seconds in small fixed sub-steps (stable for any frame time). + @discardableResult + public mutating func step(target: Double, dt: Double) -> Double { + var remaining = max(0, dt) + let h = 1.0 / 240.0 + while remaining > 1e-9 { + let s = min(h, remaining) + let acceleration = omega * omega * (target - position) - 2 * zeta * omega * velocity + velocity += acceleration * s + position += velocity * s + remaining -= s + } + return position + } +} + +public enum SegmentID: CaseIterable, Hashable { + case a, b, c, d, e, f, g +} + +/// Which segments of a seven-segment digit are lit, and where each segment sits (from the segment tokens). +public enum SegmentGlyph { + static let map: [Character: Set] = [ + "0": [.a, .b, .c, .d, .e, .f], "1": [.b, .c], "2": [.a, .b, .d, .e, .g], "3": [.a, .b, .c, .d, .g], + "4": [.b, .c, .f, .g], "5": [.a, .c, .d, .f, .g], "6": [.a, .c, .d, .e, .f, .g], "7": [.a, .b, .c], + "8": Set(SegmentID.allCases), "9": [.a, .b, .c, .d, .f, .g], + ] + + public static func lit(for character: Character) -> Set { + map[character] ?? [] + } + + /// Hexagonal bar for one segment, inside a `Segment.digitWidth` × `Segment.digitHeight` box. + public static func polygon(_ id: SegmentID) -> [CGPoint] { + let width = Segment.digitWidth, height = Segment.digitHeight + let h = Segment.thickness / 2, gap = Segment.gap + let left = h, right = width - h, mid = height / 2 + + func horizontal(_ x0: CGFloat, _ x1: CGFloat, _ cy: CGFloat) -> [CGPoint] { + [CGPoint(x: x0, y: cy), CGPoint(x: x0 + h, y: cy - h), CGPoint(x: x1 - h, y: cy - h), + CGPoint(x: x1, y: cy), CGPoint(x: x1 - h, y: cy + h), CGPoint(x: x0 + h, y: cy + h)] + } + func vertical(_ cx: CGFloat, _ y0: CGFloat, _ y1: CGFloat) -> [CGPoint] { + [CGPoint(x: cx, y: y0), CGPoint(x: cx + h, y: y0 + h), CGPoint(x: cx + h, y: y1 - h), + CGPoint(x: cx, y: y1), CGPoint(x: cx - h, y: y1 - h), CGPoint(x: cx - h, y: y0 + h)] + } + let x0 = left + h + gap, x1 = right - h - gap + switch id { + case .a: return horizontal(x0, x1, h) + case .g: return horizontal(x0, x1, mid) + case .d: return horizontal(x0, x1, height - h) + case .f: return vertical(left, h + h + gap, mid - h - gap) + case .b: return vertical(right, h + h + gap, mid - h - gap) + case .e: return vertical(left, mid + h + gap, height - h - h - gap) + case .c: return vertical(right, mid + h + gap, height - h - h - gap) + } + } +} + +public enum LEDBar { + public enum Tone { case green, amber, red } + + /// The last segment is red, the three before it amber, the rest green. + public static func tone(forSegment index: Int, segments: Int = MeterTokens.ledSegments) -> Tone { + if index >= segments - 1 { return .red } + if index >= segments - 4 { return .amber } + return .green + } + + public static func litCount(forFraction fraction: Double, segments: Int = MeterTokens.ledSegments) -> Int { + Int((min(1, max(0, fraction)) * Double(segments)).rounded()) + } +} diff --git a/Tests/SstUITests/InstrumentsTests.swift b/Tests/SstUITests/InstrumentsTests.swift new file mode 100644 index 0000000..145eb0e --- /dev/null +++ b/Tests/SstUITests/InstrumentsTests.swift @@ -0,0 +1,108 @@ +import CoreGraphics +import Testing +@testable import SstUI + +struct VUScaleTests { + @Test func zeroVUIsMinusTwentyDbfs() { + #expect(VUScale.vu(fromDbfs: -20) == 0) + #expect(VUScale.vu(fromDbfs: -40) == -20) + } + + @Test func fractionSpansTheScale() { + #expect(VUScale.fraction(forVU: -20) == 0) + #expect(abs(VUScale.fraction(forVU: 3) - 1) < 1e-9) + #expect(abs(VUScale.fraction(forVU: 0) - 20.0 / 23.0) < 1e-9) + } + + @Test func fractionIsClampedAtBothEnds() { + #expect(VUScale.fraction(forVU: -60) == 0) + #expect(VUScale.fraction(forVU: 30) == 1.03) + #expect(VUScale.fraction(forDbfs: -120) == 0) + } + + @Test func angleIsSymmetricAroundVertical() { + #expect(abs(VUScale.angle(forFraction: 0) + VUScale.angle(forFraction: 1)) < 1e-9) + #expect(abs(VUScale.angle(forFraction: 0.5)) < 1e-9) + #expect(abs(VUScale.angle(forFraction: 1) - 45 * .pi / 180) < 1e-9) + } +} + +struct NeedleSpringTests { + func simulate(_ spring: NeedleSpring, seconds: Double, target: Double = 1) -> (final: Double, peak: Double) { + var spring = spring + var peak = 0.0 + let dt = 1.0 / 240 + for _ in 0.. 1.003) + #expect(result.peak < 1.03) + #expect(abs(result.final - 1) < 0.001) + } + + @Test func criticalDampingNeverOvershoots() { + let result = simulate(NeedleSpring(damping: 1), seconds: 1) + #expect(result.peak <= 1.000001) + } + + @Test func largeFrameStepsStayStable() { + var spring = NeedleSpring() + for _ in 0..<40 { spring.step(target: 1, dt: 0.05) } + #expect(abs(spring.position - 1) < 0.001) + } + + @Test func restsAtTheStartWithoutATarget() { + var spring = NeedleSpring() + for _ in 0..<60 { spring.step(target: 0, dt: 1.0 / 60) } + #expect(spring.position == 0) + } +} + +struct SegmentGlyphTests { + @Test func everyDigitHasASegmentPattern() { + for digit in "0123456789" { #expect(!SegmentGlyph.lit(for: digit).isEmpty) } + #expect(SegmentGlyph.lit(for: "8").count == 7) + #expect(SegmentGlyph.lit(for: "1") == [.b, .c]) + #expect(SegmentGlyph.lit(for: ":").isEmpty) + } + + @Test func polygonsStayInsideTheDigitBoxAndDiffer() { + var seen: [[CGPoint]] = [] + for id in SegmentID.allCases { + let polygon = SegmentGlyph.polygon(id) + #expect(polygon.count == 6) + for point in polygon { + #expect(point.x >= 0 && point.x <= Segment.digitWidth) + #expect(point.y >= 0 && point.y <= Segment.digitHeight) + } + #expect(!seen.contains(polygon)) + seen.append(polygon) + } + #expect(seen.count == 7) + } +} + +struct LEDBarTests { + @Test func tonesFollowTheEightThreeOneSplit() { + let tones = (0..<12).map { LEDBar.tone(forSegment: $0) } + #expect(tones.prefix(8).allSatisfy { $0 == .green }) + #expect(tones[8...10].allSatisfy { $0 == .amber }) + #expect(tones[11] == .red) + } + + @Test func litCountScalesAndClamps() { + #expect(LEDBar.litCount(forFraction: 0) == 0) + #expect(LEDBar.litCount(forFraction: 0.5) == 6) + #expect(LEDBar.litCount(forFraction: 1) == 12) + #expect(LEDBar.litCount(forFraction: 1.03) == 12) + #expect(LEDBar.litCount(forFraction: -1) == 0) + } +} From 9ac93bc868f0bd43a5f1d380f858de83517eef98 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:23:30 +0200 Subject: [PATCH 20/43] feat: add physical surfaces, brushed metal and silkscreen label Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstUI/Surfaces.swift | 175 +++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 Sources/SstUI/Surfaces.swift diff --git a/Sources/SstUI/Surfaces.swift b/Sources/SstUI/Surfaces.swift new file mode 100644 index 0000000..3b03615 --- /dev/null +++ b/Sources/SstUI/Surfaces.swift @@ -0,0 +1,175 @@ +import AppKit +import SwiftUI + +/// A shape with a fill, layered drop shadows, layered inset shadows and a hairline edge: a physical part. +public struct PhysicalSurface: View { + private let fill: AnyShapeStyle + private let cornerRadius: CGFloat + private let shadows: [ShadowLayer] + private let edge: Color? + @Environment(\.colorScheme) private var scheme + + public init(fill: AnyShapeStyle, cornerRadius: CGFloat, shadows: [ShadowLayer], edge: Color? = Palette.lineSeamDark) { + self.fill = fill + self.cornerRadius = cornerRadius + self.shadows = shadows + self.edge = edge + } + + public var body: some View { + let shape = RoundedRectangle(cornerRadius: cornerRadius, style: .circular) + ZStack { + shadows.filter { !$0.inset }.reduce(AnyView(shape.fill(fill))) { view, layer in + AnyView(view.shadow(color: layer.color(scheme), radius: layer.blur / 2, x: layer.x, y: layer.y)) + } + ForEach(Array(shadows.filter(\.inset).enumerated()), id: \.offset) { _, layer in + InsetShadow(layer: layer, cornerRadius: cornerRadius) + } + if let edge { + shape.strokeBorder(edge, lineWidth: Border.hairline) + } + } + } +} + +/// A shadow cast inwards from the edge of a rounded rectangle. +struct InsetShadow: View { + let layer: ShadowLayer + let cornerRadius: CGFloat + @Environment(\.colorScheme) private var scheme + + var body: some View { + Canvas { context, size in + let rect = CGRect(origin: .zero, size: size) + context.clip(to: Path(roundedRect: rect, cornerRadius: cornerRadius)) + context.drawLayer { inner in + inner.addFilter(.blur(radius: layer.blur / 2)) + let hole = rect.insetBy(dx: layer.spread, dy: layer.spread).offsetBy(dx: layer.x, dy: layer.y) + var frame = Path(rect.insetBy(dx: -size.width, dy: -size.height)) + frame.addPath(Path(roundedRect: hole, cornerRadius: max(0, cornerRadius - layer.spread))) + inner.fill(frame, with: .color(layer.color(scheme)), style: FillStyle(eoFill: true)) + } + } + .allowsHitTesting(false) + } +} + +/// Faceplate of matt plastic, sitting above the chassis. +public struct PanelBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfacePanel), cornerRadius: Radius.none, shadows: Shadows.panel) + } +} + +/// A recessed area (input fields, display windows). +public struct WellBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfaceWell), cornerRadius: Radius.none, shadows: Shadows.well) + } +} + +/// Dark display glass for the counter. +public struct DisplayBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfaceDisplay), cornerRadius: Radius.none, shadows: Shadows.well) + } +} + +/// Cassette-label cream, for lists of transcripts and dictionary entries. +public struct PaperBackground: View { + public init() {} + public var body: some View { + PhysicalSurface(fill: AnyShapeStyle(Palette.surfacePaper), cornerRadius: Radius.none, shadows: Shadows.well) + } +} + +/// An engraved groove: a dark line with a light line below it. +public struct SeamDivider: View { + public init() {} + public var body: some View { + VStack(spacing: 0) { + Rectangle().fill(Palette.lineSeamDark).frame(height: Border.hairline) + Rectangle().fill(Palette.lineSeamLight).frame(height: Border.hairline) + } + .accessibilityHidden(true) + } +} + +/// Small capitals silkscreen label. +public struct SilkLabel: View { + private let text: String + private let style: TextStyle + + public init(_ text: String, style: TextStyle = Typography.label) { + self.text = text + self.style = style + } + + public var body: some View { + Text(text).textStyle(style).foregroundStyle(Palette.inkTertiary) + } +} + +/// Brushed aluminium: a vertical gradient with a horizontal grain. +public struct BrushedBackground: View { + @Environment(\.colorScheme) private var scheme + public init() {} + + public var body: some View { + ZStack { + LinearGradient(colors: [Palette.surfaceMetalHigh, Palette.surfaceMetalLow], startPoint: .top, endPoint: .bottom) + Image(nsImage: scheme == .dark ? BrushedTexture.dark : BrushedTexture.light) + .resizable(resizingMode: .tile) + } + } +} + +enum BrushedTexture { + private static let side = 256 + private static let gain = 2.2 // makes the token grain value visible as streak opacity + + static let light = make(grain: Texture.brushedGrainLight) + static let dark = make(grain: Texture.brushedGrainDark) + + private struct SplitMix64: RandomNumberGenerator { + var state: UInt64 + mutating func next() -> UInt64 { + state &+= 0x9E37_79B9_7F4A_7C15 + var z = state + z = (z ^ (z >> 30)) &* 0xBF58_476D_1CE4_E5B9 + z = (z ^ (z >> 27)) &* 0x94D0_49BB_1331_11EB + return z ^ (z >> 31) + } + } + + /// A seeded grain: every row has its own brightness and pixels vary only slightly, which gives fine horizontal streaks. + private static func make(grain: Double) -> NSImage { + var rng = SplitMix64(state: 7) + let rows = (0.. 0.5 ? 1.0 : 0.0 + let index = (y * side + x) * 4 + let premultiplied = UInt8(shade * alpha * 255) + pixels[index] = premultiplied + pixels[index + 1] = premultiplied + pixels[index + 2] = premultiplied + pixels[index + 3] = UInt8(alpha * 255) + } + } + let provider = CGDataProvider(data: Data(pixels) as CFData)! + let image = CGImage( + width: side, height: side, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: side * 4, + space: CGColorSpaceCreateDeviceRGB(), + bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue), + provider: provider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)! + return NSImage(cgImage: image, size: NSSize(width: side, height: side)) + } +} From 529934b1776e2feb17ef418869e997e9610bc382 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:23:56 +0200 Subject: [PATCH 21/43] feat: add physical key button style and lamp Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstUI/Controls.swift | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 Sources/SstUI/Controls.swift diff --git a/Sources/SstUI/Controls.swift b/Sources/SstUI/Controls.swift new file mode 100644 index 0000000..72c2241 --- /dev/null +++ b/Sources/SstUI/Controls.swift @@ -0,0 +1,107 @@ +import SwiftUI + +/// A physical key. `latched` keeps it looking pressed (REC while recording). +public struct KeyButtonStyle: ButtonStyle { + public enum Kind { case standard, record } + + private let kind: Kind + private let latched: Bool + + public init(kind: Kind = .standard, latched: Bool = false) { + self.kind = kind + self.latched = latched + } + + public func makeBody(configuration: Configuration) -> some View { + KeyBody(configuration: configuration, kind: kind, latched: latched) + } + + private struct KeyBody: View { + let configuration: Configuration + let kind: Kind + let latched: Bool + @Environment(\.isEnabled) private var isEnabled + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + private var pressed: Bool { configuration.isPressed || latched } + + private var face: AnyShapeStyle { + switch (kind, pressed) { + case (.standard, false): + AnyShapeStyle(LinearGradient(colors: [Palette.surfaceKeyHigh, Palette.surfaceKeyLow], startPoint: .top, endPoint: .bottom)) + case (.standard, true): + AnyShapeStyle(Palette.surfaceKeyPressed) + case (.record, false): + AnyShapeStyle(Palette.accentRecord) + case (.record, true): + AnyShapeStyle(Palette.accentRecord.opacity(Opacity.pressedRecord)) + } + } + + var body: some View { + configuration.label + .textStyle(Typography.control) + .foregroundStyle(kind == .record ? Palette.inkInverse : Palette.inkPrimary) + .padding(.horizontal, Space.s4) + .frame( + minWidth: kind == .record ? Size.keyRecWidth : Size.keyMinWidth, + minHeight: kind == .record ? Size.keyRecHeight : Size.controlHeight) + .background(PhysicalSurface( + fill: face, cornerRadius: Radius.sm, + shadows: pressed ? Shadows.keyPressed : Shadows.keyRaised)) + .offset(y: pressed ? Shadows.keyPressedOffset : 0) + .opacity(isEnabled ? 1 : Opacity.disabled) + .animation( + reduceMotion ? nil : .timingCurve( + Motion.easingMechanical.0, Motion.easingMechanical.1, Motion.easingMechanical.2, Motion.easingMechanical.3, + duration: pressed ? Motion.keyPress : Motion.keyRelease), + value: pressed) + } + } +} + +/// A round indicator lamp. The record lamp has a small glow; text never does. +public struct LampView: View { + public enum Tint { case record, amber } + + private let isOn: Bool + private let tint: Tint + private let large: Bool + @Environment(\.colorScheme) private var scheme + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + public init(isOn: Bool, tint: Tint = .record, large: Bool = false) { + self.isOn = isOn + self.tint = tint + self.large = large + } + + private var color: Color { + switch (tint, isOn) { + case (.record, true): Palette.accentRecord + case (.record, false): Palette.accentRecordUnlit + case (.amber, true): Palette.levelAmber + case (.amber, false): Palette.levelAmberUnlit + } + } + + public var body: some View { + let diameter = large ? Size.lampLarge : Size.lampDiameter + let glow = Shadows.lampOn[0] + Circle() + .fill(color) + .overlay( + Circle().fill(RadialGradient( + colors: [Color.white.opacity(isOn ? Opacity.specularOn : Opacity.specularOff), .clear], + center: UnitPoint(x: 0.35, y: 0.3), startRadius: 0, endRadius: diameter * 0.45))) + .overlay(Circle().strokeBorder(Palette.lineSeamDark, lineWidth: Border.hairline)) + .frame(width: diameter, height: diameter) + .shadow( + color: isOn && tint == .record ? glow.color(scheme) : .clear, + radius: glow.blur / 2) + .animation( + reduceMotion ? nil : .easeOut(duration: isOn ? Motion.lampOn : Motion.lampOff), + value: isOn) + .accessibilityHidden(true) + } +} From 114548f63bfa7065e20b0b4166ae6015aa6a0399 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:24:43 +0200 Subject: [PATCH 22/43] feat: add VU meter, segment counter and LED bar views Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstUI/CounterAndLEDs.swift | 83 ++++++++++++++++ Sources/SstUI/VUMeterView.swift | 148 +++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 Sources/SstUI/CounterAndLEDs.swift create mode 100644 Sources/SstUI/VUMeterView.swift diff --git a/Sources/SstUI/CounterAndLEDs.swift b/Sources/SstUI/CounterAndLEDs.swift new file mode 100644 index 0000000..926fa5b --- /dev/null +++ b/Sources/SstUI/CounterAndLEDs.swift @@ -0,0 +1,83 @@ +import SwiftUI + +/// Seven-segment counter on dark glass. Digits, ":" and "." are drawn; unlit segments stay faintly visible. +public struct SegmentCounterView: View { + private let text: String + + public init(_ text: String) { + self.text = text + } + + private var contentWidth: CGFloat { + text.reduce(0) { total, character in + total + (character.isNumber ? Segment.advance : Segment.colonWidth) + } + } + + public var body: some View { + Canvas { context, _ in + var x: CGFloat = 0 + for character in text { + if character.isNumber { + var origin = context + origin.translateBy(x: x, y: 0) + let lit = SegmentGlyph.lit(for: character) + for id in SegmentID.allCases { + let polygon = SegmentGlyph.polygon(id) + var path = Path() + path.addLines(polygon) + path.closeSubpath() + origin.fill(path, with: .color(lit.contains(id) ? Palette.levelAmber : Palette.displaySegmentUnlit)) + } + x += Segment.advance + } else { + let dot = Segment.thickness + let inset = (Segment.colonWidth - dot) / 2 + let ys: [CGFloat] = character == ":" + ? [Segment.digitHeight * 0.28, Segment.digitHeight * 0.64] + : [Segment.digitHeight - dot] + for y in ys { + context.fill(Path(CGRect(x: x + inset, y: y, width: dot, height: dot)), with: .color(Palette.levelAmber)) + } + x += Segment.colonWidth + } + } + } + .frame(width: contentWidth, height: Segment.digitHeight) + .padding(.horizontal, Space.s4) + .padding(.vertical, Space.s3) + .background(DisplayBackground()) + .accessibilityElement() + .accessibilityLabel("Tijd") + .accessibilityValue(text) + } +} + +/// Twelve-segment level bar: green, amber, then one red segment. +public struct LEDBarView: View { + private let fraction: Double + + public init(fraction: Double) { + self.fraction = fraction + } + + public var body: some View { + let lit = LEDBar.litCount(forFraction: fraction) + HStack(spacing: MeterTokens.ledGap) { + ForEach(0.. Color { + switch LEDBar.tone(forSegment: index) { + case .green: lit ? Palette.levelGreen : Palette.levelGreenUnlit + case .amber: lit ? Palette.levelAmber : Palette.levelAmberUnlit + case .red: lit ? Palette.accentRecord : Palette.accentRecordUnlit + } + } +} diff --git a/Sources/SstUI/VUMeterView.swift b/Sources/SstUI/VUMeterView.swift new file mode 100644 index 0000000..c3dcb3c --- /dev/null +++ b/Sources/SstUI/VUMeterView.swift @@ -0,0 +1,148 @@ +import SwiftUI + +/// Analogue VU meter with a physical needle. Pass the RMS level in dBFS; 0 VU = `MeterTokens.vuReferenceDbfs`. +/// The needle only animates while `isActive` (and briefly after, to settle), so an idle meter costs nothing. +public struct VUMeterView: View { + private let levelDbfs: Double + private let isActive: Bool + private let brand: String + + @State private var needle: NeedleState + @State private var awake = false + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + private final class NeedleState { + var spring: NeedleSpring + var dampingUsed = Motion.needleDamping + var last: TimeInterval? + var peakUntil: TimeInterval = 0 + + init(position: Double) { + spring = NeedleSpring(position: position) + } + } + + /// - Parameter initialFraction: where the needle starts (0 = bottom of the scale); useful for previews and snapshots. + public init(levelDbfs: Double, isActive: Bool, brand: String = "SST · FIELD RECORDER", initialFraction: Double = 0) { + self.levelDbfs = levelDbfs + self.isActive = isActive + self.brand = brand + _needle = State(initialValue: NeedleState(position: initialFraction)) + } + + public var body: some View { + TimelineView(.animation(minimumInterval: 1 / Motion.needleUpdateHz, paused: !(isActive || awake))) { timeline in + Canvas { context, size in + draw(&context, size: size, now: timeline.date.timeIntervalSinceReferenceDate) + } + } + .aspectRatio(MeterTokens.vuWidth / MeterTokens.vuHeight, contentMode: .fit) + .background(Palette.surfaceMeterFace) + .overlay( + RoundedRectangle(cornerRadius: Radius.bezel, style: .circular) + .strokeBorder(Palette.lineSeamDark, lineWidth: Border.bezel)) + .clipShape(RoundedRectangle(cornerRadius: Radius.bezel, style: .circular)) + .onChange(of: isActive) { _, active in + if active { + awake = true + } else { + Task { + try? await Task.sleep(for: .seconds(1.5)) + awake = false + } + } + } + .accessibilityElement() + .accessibilityLabel("Niveau") + .accessibilityValue(spokenValue) + } + + /// Rounded to steps of 3 VU so VoiceOver is not flooded with changes. + private var spokenValue: String { + guard isActive else { return "stil" } + let vu = (VUScale.vu(fromDbfs: levelDbfs) / 3).rounded() * 3 + return "\(Int(vu)) VU" + } + + // MARK: drawing (design coordinates: MeterTokens.vuWidth × vuHeight) + + private func draw(_ context: inout GraphicsContext, size: CGSize, now: TimeInterval) { + let width = MeterTokens.vuWidth, height = MeterTokens.vuHeight + let scale = min(size.width / width, size.height / height) + context.translateBy(x: (size.width - width * scale) / 2, y: (size.height - height * scale) / 2) + context.scaleBy(x: scale, y: scale) + context.clip(to: Path(CGRect(x: 0, y: 0, width: width, height: height))) + + // physics + let damping = reduceMotion ? 1.0 : Motion.needleDamping + if needle.dampingUsed != damping { + needle.spring = NeedleSpring(damping: damping, position: needle.spring.position) + needle.dampingUsed = damping + } + let dt = needle.last.map { min(0.05, now - $0) } ?? 0 + needle.last = now + let target = isActive ? VUScale.fraction(forDbfs: levelDbfs) : 0 + let position = needle.spring.step(target: target, dt: dt) + let vuNow = MeterTokens.vuMin + (MeterTokens.vuMax - MeterTokens.vuMin) * position + if vuNow > 0 && isActive { needle.peakUntil = now + MeterTokens.peakHoldSeconds } + + let pivot = CGPoint(x: width / 2, y: height + MeterTokens.vuPivotOffset) + func point(_ radius: CGFloat, _ fraction: Double) -> CGPoint { + let angle = VUScale.angle(forFraction: fraction) + return CGPoint(x: pivot.x + radius * CGFloat(sin(angle)), y: pivot.y - radius * CGFloat(cos(angle))) + } + func arc(_ radius: CGFloat, from: Double, to: Double) -> Path { + var path = Path() + let steps = 48 + for step in 0...steps { + let p = point(radius, from + (to - from) * Double(step) / Double(steps)) + step == 0 ? path.move(to: p) : path.addLine(to: p) + } + return path + } + + let radius = MeterTokens.vuScaleRadius + let redFrom = VUScale.fraction(forVU: 0) + context.stroke(arc(radius, from: 0, to: redFrom), with: .color(Palette.inkOnMeter), lineWidth: Border.hairline) + context.stroke(arc(radius - Border.hairline, from: redFrom, to: 1), with: .color(Palette.accentRecord), lineWidth: MeterTokens.vuRedBandWidth) + + for vu in Int(MeterTokens.vuMin)...Int(MeterTokens.vuMax) { + let major = MeterTokens.vuTicks.contains(vu) + let fraction = VUScale.fraction(forVU: Double(vu)) + let color = vu > 0 ? Palette.accentRecord : Palette.inkOnMeter + var tick = Path() + tick.move(to: point(radius, fraction)) + tick.addLine(to: point(radius - (major ? MeterTokens.vuTickMajor : MeterTokens.vuTickMinor), fraction)) + context.stroke(tick, with: .color(color), lineWidth: Border.hairline) + if major { + let label = Text(vu > 0 ? "+\(vu)" : "\(vu)").font(Typography.labelSmall.font).foregroundStyle(color) + context.draw(label, at: point(radius - MeterTokens.vuLabelInset, fraction), anchor: .center) + } + } + + context.draw( + Text("VU").font(Typography.heading.font).tracking(Typography.heading.tracking * Typography.heading.size).foregroundStyle(Palette.inkOnMeter), + at: CGPoint(x: width / 2, y: height - Space.s6), anchor: .center) + context.draw( + Text(brand).font(Typography.labelSmall.font).foregroundStyle(Palette.inkOnMeter.opacity(Opacity.caption)), + at: CGPoint(x: Space.s4, y: height - Space.s3), anchor: .leading) + + // peak lamp + let lampCenter = CGPoint(x: width - Space.s6, y: Space.s6) + let lampRadius = Size.lampDiameter / 2 + let peakLit = now < needle.peakUntil + context.fill(Path(ellipseIn: CGRect(x: lampCenter.x - lampRadius, y: lampCenter.y - lampRadius, width: lampRadius * 2, height: lampRadius * 2)), + with: .color(peakLit ? Palette.accentRecord : Palette.accentRecordUnlit)) + context.stroke(Path(ellipseIn: CGRect(x: lampCenter.x - lampRadius, y: lampCenter.y - lampRadius, width: lampRadius * 2, height: lampRadius * 2)), + with: .color(Palette.inkOnMeter), lineWidth: Border.hairline) + context.draw( + Text("PEAK").font(Typography.labelSmall.font).foregroundStyle(Palette.inkOnMeter), + at: CGPoint(x: lampCenter.x, y: lampCenter.y + lampRadius + Space.s3), anchor: .center) + + // needle + var needlePath = Path() + needlePath.move(to: pivot) + needlePath.addLine(to: point(MeterTokens.vuNeedleLength, max(-0.02, min(1.05, position)))) + context.stroke(needlePath, with: .color(Palette.inkOnMeter), lineWidth: MeterTokens.vuNeedleWidth) + } +} From d0f78f5165602dd4fea4428a2a7b0379a0c41125 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:27:29 +0200 Subject: [PATCH 23/43] fix: add VUScale.spokenLevel with proper clamping and non-finite handling Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstUI/Instruments.swift | 8 ++++++++ Sources/SstUI/VUMeterView.swift | 3 +-- Tests/SstUITests/InstrumentsTests.swift | 23 +++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Sources/SstUI/Instruments.swift b/Sources/SstUI/Instruments.swift index d5e2913..c77fee9 100644 --- a/Sources/SstUI/Instruments.swift +++ b/Sources/SstUI/Instruments.swift @@ -20,6 +20,14 @@ public enum VUScale { public static func angle(forFraction fraction: Double) -> Double { (-MeterTokens.vuArcDegrees / 2 + MeterTokens.vuArcDegrees * fraction) * .pi / 180 } + + /// Level as VoiceOver should say it: rounded to steps of 3 VU, clamped to the scale, "stil" for non-finite input. + public static func spokenLevel(fromDbfs dbfs: Double) -> String { + guard dbfs.isFinite else { return "stil" } + let stepped = (vu(fromDbfs: dbfs) / 3).rounded() * 3 + let clamped = min(MeterTokens.vuMax, max(MeterTokens.vuMin, stepped)) + return "\(Int(clamped)) VU" + } } /// Second-order needle: reaches ~99 % in `response` seconds, slight overshoot for damping < 1. diff --git a/Sources/SstUI/VUMeterView.swift b/Sources/SstUI/VUMeterView.swift index c3dcb3c..2ae62cf 100644 --- a/Sources/SstUI/VUMeterView.swift +++ b/Sources/SstUI/VUMeterView.swift @@ -60,8 +60,7 @@ public struct VUMeterView: View { /// Rounded to steps of 3 VU so VoiceOver is not flooded with changes. private var spokenValue: String { guard isActive else { return "stil" } - let vu = (VUScale.vu(fromDbfs: levelDbfs) / 3).rounded() * 3 - return "\(Int(vu)) VU" + return VUScale.spokenLevel(fromDbfs: levelDbfs) } // MARK: drawing (design coordinates: MeterTokens.vuWidth × vuHeight) diff --git a/Tests/SstUITests/InstrumentsTests.swift b/Tests/SstUITests/InstrumentsTests.swift index 145eb0e..00984c0 100644 --- a/Tests/SstUITests/InstrumentsTests.swift +++ b/Tests/SstUITests/InstrumentsTests.swift @@ -106,3 +106,26 @@ struct LEDBarTests { #expect(LEDBar.litCount(forFraction: -1) == 0) } } + +struct SpokenLevelTests { + @Test func nonFiniteInputReturnsStil() { + #expect(VUScale.spokenLevel(fromDbfs: -.infinity) == "stil") + #expect(VUScale.spokenLevel(fromDbfs: .nan) == "stil") + } + + @Test func referenceLevel() { + #expect(VUScale.spokenLevel(fromDbfs: -20) == "0 VU") + } + + @Test func negativeLevel() { + #expect(VUScale.spokenLevel(fromDbfs: -30) == "-9 VU") + } + + @Test func clampedToMinimum() { + #expect(VUScale.spokenLevel(fromDbfs: -120) == "-20 VU") + } + + @Test func clampedToMaximum() { + #expect(VUScale.spokenLevel(fromDbfs: 10) == "3 VU") + } +} From 9ac8df7b9d1ed7f0c708bc49ce92ae4969a439fd Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:29:33 +0200 Subject: [PATCH 24/43] feat: add component gallery with snapshot mode Co-Authored-By: Claude Sonnet 5.5 --- Package.swift | 1 + Sources/SstGallery/GalleryApp.swift | 138 ++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 Sources/SstGallery/GalleryApp.swift diff --git a/Package.swift b/Package.swift index f7272f3..098a8bb 100644 --- a/Package.swift +++ b/Package.swift @@ -20,6 +20,7 @@ let package = Package( ), .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper"]), .executableTarget(name: "SstProbe", dependencies: ["SstCore", "SstWhisper"]), + .executableTarget(name: "SstGallery", dependencies: ["SstUI"]), .testTarget(name: "SstCoreTests", dependencies: ["SstCore"]), .testTarget(name: "SstDataTests", dependencies: ["SstData", "SstCore"]), .testTarget(name: "SstWhisperTests", dependencies: ["SstWhisper"]), diff --git a/Sources/SstGallery/GalleryApp.swift b/Sources/SstGallery/GalleryApp.swift new file mode 100644 index 0000000..cd9f0da --- /dev/null +++ b/Sources/SstGallery/GalleryApp.swift @@ -0,0 +1,138 @@ +import SwiftUI +import SstUI + +@main +struct GalleryApp: App { + init() { + // `SstGallery --snapshot out.png` renders both finishes to a PNG and exits (for checking without a screen grab). + let args = CommandLine.arguments + guard let flag = args.firstIndex(of: "--snapshot"), args.count > flag + 1 else { return } + let path = args[flag + 1] + Task { @MainActor in + let view = HStack(spacing: 0) { + Finish(scheme: .light, title: "Aluminium", frozen: true) + Finish(scheme: .dark, title: "Graphite", frozen: true) + } + .frame(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) + let renderer = ImageRenderer(content: view) + renderer.scale = 2 + if let tiff = renderer.nsImage?.tiffRepresentation, let png = NSBitmapImageRep(data: tiff)?.representation(using: .png, properties: [:]) { + try? png.write(to: URL(fileURLWithPath: path)) + } + exit(0) + } + } + + var body: some Scene { + WindowGroup("Sst componenten") { + HStack(spacing: 0) { + Finish(scheme: .light, title: "Aluminium") + Finish(scheme: .dark, title: "Graphite") + } + .frame(minWidth: Layout.windowMinWidth, minHeight: Layout.windowMinHeight) + } + .defaultSize(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) + } +} + +/// Fake speech level so the meter, LEDs and counter can be judged in motion. +private func simulatedDbfs(at t: Double) -> Double { + if t.truncatingRemainder(dividingBy: 9) > 6.2 { return -90 } + let syllable = floor(t * 4.2) + let r = abs(sin(syllable * 12.9898) * 43758.5453).truncatingRemainder(dividingBy: 1) + return r > 0.12 ? -38 + r * 22 : -90 +} + +struct Finish: View { + let scheme: ColorScheme + let title: String + var frozen = false + @State private var recording = true + @State private var started = Date() + @State private var query = "" + + var body: some View { + Group { + if frozen { + content(elapsed: 12.3, dbfs: -26) + } else { + TimelineView(.animation(minimumInterval: 1.0 / 30.0)) { timeline in + let elapsed = timeline.date.timeIntervalSince(started) + let dbfs = recording ? simulatedDbfs(at: elapsed) : -120 + content(elapsed: recording ? elapsed : 0, dbfs: dbfs) + } + } + } + .environment(\.colorScheme, scheme) + } + + private func content(elapsed: Double, dbfs: Double) -> some View { + ZStack { + BrushedBackground() + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel("Afwerking · \(title)") + VStack(alignment: .leading, spacing: Layout.panelGap) { + HStack(alignment: .top, spacing: Layout.panelGap) { + VUMeterView(levelDbfs: dbfs, isActive: recording, initialFraction: frozen ? VUScale.fraction(forDbfs: dbfs) : 0) + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel("Tijd", style: Typography.labelSmall) + SegmentCounterView(Self.clock(elapsed)) + SilkLabel("Niveau", style: Typography.labelSmall) + LEDBarView(fraction: VUScale.fraction(forDbfs: dbfs)) + } + } + SeamDivider() + HStack(spacing: Layout.panelGap) { + Button { recording.toggle(); started = Date() } label: { + HStack { LampView(isOn: recording); Text("Rec") } + } + .buttonStyle(KeyButtonStyle(kind: .record, latched: recording)) + Button("Stop") { recording = false }.buttonStyle(KeyButtonStyle()) + Button("Kopieer") {}.buttonStyle(KeyButtonStyle()) + Button("Uit") {}.buttonStyle(KeyButtonStyle()).disabled(true) + Spacer() + LampView(isOn: recording, large: true) + LampView(isOn: true, tint: .amber) + } + } + .padding(Layout.panelPadding) + .background(PanelBackground()) + + VStack(alignment: .leading, spacing: 0) { + row("14:02:11", "Ik wil de meeting verplaatsen naar tomorrow morning.", selected: false) + row("14:03:40", "Hij stuurt het door naar Claude Code vandaag.", selected: true) + } + .background(PaperBackground()) + + HStack { + SilkLabel("Zoek") + Group { + if frozen { + Text("claude").textStyle(Typography.body).frame(maxWidth: .infinity, alignment: .leading) + } else { + TextField("", text: $query).textFieldStyle(.plain).textStyle(Typography.body) + } + } + .padding(Space.s3).background(WellBackground()) + } + } + .padding(Layout.panelPadding) + } + } + + private func row(_ time: String, _ text: String, selected: Bool) -> some View { + HStack(alignment: .firstTextBaseline, spacing: Space.s4) { + Text(time).textStyle(Typography.counterSmall).foregroundStyle(Palette.inkSecondary) + Text(text).textStyle(Typography.reading).foregroundStyle(Palette.inkPrimary) + Spacer() + } + .padding(.horizontal, Space.s4).padding(.vertical, Space.s3) + .frame(minHeight: Size.rowHeight) + .background(selected ? Palette.surfaceSelection : Color.clear) + } + + private static func clock(_ seconds: Double) -> String { + let total = Int(seconds * 10) + return String(format: "%02d:%02d.%d", (total / 600) % 100, (total / 10) % 60, total % 10) + } +} From 2d257a3bb9a9525fe87c88af40bb49d40c76671d Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:31:13 +0200 Subject: [PATCH 25/43] docs: fix plan 4 order (add SstIcon target together with its sources) Co-Authored-By: Claude Sonnet 5.5 --- docs/superpowers/plans/2026-10-01-app-4-shell.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/superpowers/plans/2026-10-01-app-4-shell.md b/docs/superpowers/plans/2026-10-01-app-4-shell.md index 48fd6dc..7aa308d 100644 --- a/docs/superpowers/plans/2026-10-01-app-4-shell.md +++ b/docs/superpowers/plans/2026-10-01-app-4-shell.md @@ -738,9 +738,10 @@ door: ```swift .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper", "SstData", "SstUI"]), - .executableTarget(name: "SstIcon", dependencies: ["SstUI"]), ``` +(Het `SstIcon`-target volgt in Task 7, samen met zijn bronbestanden; een target zonder bronnen laat SwiftPM elke build weigeren.) + - [ ] **Step 2: Oude app-bestanden opruimen** Run: `git rm Sources/Sst/MenuContent.swift` @@ -1646,6 +1647,14 @@ git commit -m "feat: turn Sst into a regular Mac app with settings, menus and me **Interfaces:** - Produces: `build/Sst.app` — een gewone Dock-app (geen `LSUIElement`), met icoon, versie 0.2.0, categorie productiviteit, Nederlandse microfoon-uitleg, ondertekend met je Apple Development-identiteit; `scripts/make-app.sh --install` kopieert naar `/Applications`. +- [ ] **Step 0: Target toevoegen** + +In `Package.swift`, voeg onder de `Sst`-regel toe: + +```swift + .executableTarget(name: "SstIcon", dependencies: ["SstUI"]), +``` + - [ ] **Step 1: Icoon-generator** `Sources/SstIcon/IconMaker.swift`: From 6f491accf11c2636db08c18cb497dce17c4dd086 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:32:04 +0200 Subject: [PATCH 26/43] feat: add GatedInserter, SwappableTranscriber and LevelSource Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstCore/AppGlue.swift | 68 ++++++++++++++++++++++++++ Tests/SstCoreTests/AppGlueTests.swift | 69 +++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 Sources/SstCore/AppGlue.swift create mode 100644 Tests/SstCoreTests/AppGlueTests.swift diff --git a/Sources/SstCore/AppGlue.swift b/Sources/SstCore/AppGlue.swift new file mode 100644 index 0000000..51c3cdb --- /dev/null +++ b/Sources/SstCore/AppGlue.swift @@ -0,0 +1,68 @@ +import Foundation + +/// Inserts text only while `shouldInsert()` says so (not when Sst itself is in front, nor for window recordings). +public struct GatedInserter: TextInserting { + private let inner: TextInserting + private let shouldInsert: () -> Bool + + public init(inner: TextInserting, shouldInsert: @escaping () -> Bool) { + self.inner = inner + self.shouldInsert = shouldInsert + } + + public func insert(_ text: String) async throws { + guard shouldInsert() else { return } + try await inner.insert(text) + } +} + +/// Lets the app replace the speech model (for example after a model switch) without rebuilding the controller. +public final class SwappableTranscriber: Transcriber, @unchecked Sendable { + public enum SwapError: Error, LocalizedError { + case noTranscriber + + public var errorDescription: String? { "Spraakmodel is nog niet geladen." } + } + + private let lock = NSLock() + private var current: Transcriber? + + public init(_ initial: Transcriber? = nil) { + current = initial + } + + public func swap(to transcriber: Transcriber?) { + lock.withLock { current = transcriber } + } + + public var hasTranscriber: Bool { + lock.withLock { current != nil } + } + + public func transcribe(_ samples: [Float]) async throws -> String { + try await transcribe(samples, vocabulary: []) + } + + public func transcribe(_ samples: [Float], vocabulary: [String]) async throws -> String { + guard let transcriber = lock.withLock({ current }) else { throw SwapError.noTranscriber } + return try await transcriber.transcribe(samples, vocabulary: vocabulary) + } +} + +/// The latest input level, written from the audio thread and read by the meter once per frame. +public final class LevelSource: @unchecked Sendable { + private let lock = NSLock() + private var value = AudioLevel.silenceFloorDbfs + + public init() {} + + public var dbfs: Float { lock.withLock { value } } + + public func update(_ dbfs: Float) { + lock.withLock { value = dbfs } + } + + public func reset() { + update(AudioLevel.silenceFloorDbfs) + } +} diff --git a/Tests/SstCoreTests/AppGlueTests.swift b/Tests/SstCoreTests/AppGlueTests.swift new file mode 100644 index 0000000..ca67bc7 --- /dev/null +++ b/Tests/SstCoreTests/AppGlueTests.swift @@ -0,0 +1,69 @@ +import Foundation +import Testing +@testable import SstCore + +struct GatedInserterTests { + @Test func insertsWhenAllowed() async throws { + let inner = FakeInserter() + try await GatedInserter(inner: inner, shouldInsert: { true }).insert("hallo") + #expect(inner.inserted == ["hallo"]) + } + + @Test func staysSilentWhenNotAllowed() async throws { + let inner = FakeInserter() + try await GatedInserter(inner: inner, shouldInsert: { false }).insert("hallo") + #expect(inner.inserted.isEmpty) + } + + @Test func askedAtInsertionTimeNotAtCreation() async throws { + let inner = FakeInserter() + var allowed = false + let gated = GatedInserter(inner: inner, shouldInsert: { allowed }) + allowed = true + try await gated.insert("nu wel") + #expect(inner.inserted == ["nu wel"]) + } +} + +struct SwappableTranscriberTests { + @Test func throwsWithoutATranscriber() async { + let swappable = SwappableTranscriber() + #expect(!swappable.hasTranscriber) + await #expect(throws: SwappableTranscriber.SwapError.self) { + _ = try await swappable.transcribe([0]) + } + } + + @Test func delegatesIncludingTheVocabulary() async throws { + let inner = FakeVocabularyTranscriber() + inner.result = "klaar" + let swappable = SwappableTranscriber(inner) + #expect(try await swappable.transcribe([0], vocabulary: ["Anthropic"]) == "klaar") + #expect(inner.vocabularies == [["Anthropic"]]) + #expect(try await swappable.transcribe([0]) == "klaar") + #expect(inner.vocabularies == [["Anthropic"], []]) + } + + @Test func swapReplacesTheTranscriber() async throws { + let first = FakeVocabularyTranscriber(), second = FakeVocabularyTranscriber() + first.result = "een"; second.result = "twee" + let swappable = SwappableTranscriber(first) + #expect(try await swappable.transcribe([0]) == "een") + swappable.swap(to: second) + #expect(try await swappable.transcribe([0]) == "twee") + swappable.swap(to: nil) + #expect(!swappable.hasTranscriber) + } +} + +struct LevelSourceTests { + @Test func startsSilentAndKeepsTheLatestValue() { + let source = LevelSource() + #expect(source.dbfs == AudioLevel.silenceFloorDbfs) + source.update(-23) + source.update(-18) + #expect(source.dbfs == -18) + source.reset() + #expect(source.dbfs == AudioLevel.silenceFloorDbfs) + } +} From fbc90cbd12bbf6e786d5eb699abc42ee4ff0952f Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:32:30 +0200 Subject: [PATCH 27/43] feat: add AppSettings Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstData/AppSettings.swift | 45 +++++++++++++++++++++++ Tests/SstDataTests/AppSettingsTests.swift | 38 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 Sources/SstData/AppSettings.swift create mode 100644 Tests/SstDataTests/AppSettingsTests.swift diff --git a/Sources/SstData/AppSettings.swift b/Sources/SstData/AppSettings.swift new file mode 100644 index 0000000..9747790 --- /dev/null +++ b/Sources/SstData/AppSettings.swift @@ -0,0 +1,45 @@ +import Combine +import Foundation + +/// User preferences, persisted in UserDefaults as soon as a value changes. +public final class AppSettings: ObservableObject { + public static let defaultModelVariant = "large-v3-v20240930_turbo" + + enum Key { + static let hotkey = "hotkeyID" + static let model = "modelVariant" + static let keepHistory = "keepHistory" + static let usePriming = "usePriming" + static let useCorrection = "useCorrection" + } + + private let defaults: UserDefaults + + /// `HotkeyBinding.id` of the key that has to be held. + @Published public var hotkeyID: String { + didSet { defaults.set(hotkeyID, forKey: Key.hotkey) } + } + @Published public var modelVariant: String { + didSet { defaults.set(modelVariant, forKey: Key.model) } + } + @Published public var keepHistory: Bool { + didSet { defaults.set(keepHistory, forKey: Key.keepHistory) } + } + /// Give the speech engine the dictionary words as context. + @Published public var usePriming: Bool { + didSet { defaults.set(usePriming, forKey: Key.usePriming) } + } + /// Apply the dictionary corrections after transcription. + @Published public var useCorrection: Bool { + didSet { defaults.set(useCorrection, forKey: Key.useCorrection) } + } + + public init(defaults: UserDefaults = .standard) { + self.defaults = defaults + hotkeyID = defaults.string(forKey: Key.hotkey) ?? "fn" + modelVariant = defaults.string(forKey: Key.model) ?? Self.defaultModelVariant + keepHistory = defaults.object(forKey: Key.keepHistory) as? Bool ?? true + usePriming = defaults.object(forKey: Key.usePriming) as? Bool ?? true + useCorrection = defaults.object(forKey: Key.useCorrection) as? Bool ?? true + } +} diff --git a/Tests/SstDataTests/AppSettingsTests.swift b/Tests/SstDataTests/AppSettingsTests.swift new file mode 100644 index 0000000..e384f41 --- /dev/null +++ b/Tests/SstDataTests/AppSettingsTests.swift @@ -0,0 +1,38 @@ +import Foundation +import Testing +@testable import SstData + +struct AppSettingsTests { + func freshDefaults() -> UserDefaults { + let name = "sst-tests-\(UUID().uuidString)" + let defaults = UserDefaults(suiteName: name)! + defaults.removePersistentDomain(forName: name) + return defaults + } + + @Test func defaultsMatchTheSpec() { + let settings = AppSettings(defaults: freshDefaults()) + #expect(settings.hotkeyID == "fn") + #expect(settings.modelVariant == "large-v3-v20240930_turbo") + #expect(settings.keepHistory) + #expect(settings.usePriming) + #expect(settings.useCorrection) + } + + @Test func changesArePersisted() { + let defaults = freshDefaults() + let settings = AppSettings(defaults: defaults) + settings.hotkeyID = "rightOption" + settings.modelVariant = "small" + settings.keepHistory = false + settings.usePriming = false + settings.useCorrection = false + + let reloaded = AppSettings(defaults: defaults) + #expect(reloaded.hotkeyID == "rightOption") + #expect(reloaded.modelVariant == "small") + #expect(!reloaded.keepHistory) + #expect(!reloaded.usePriming) + #expect(!reloaded.useCorrection) + } +} From ae2000867956b2ba6a62857cd2666b869c1bde22 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:33:22 +0200 Subject: [PATCH 28/43] feat: add ModelStorage and ModelCatalog; transcriber uses ModelStorage Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstWhisper/ModelStorage.swift | 73 +++++++++++++++++++ .../SstWhisper/WhisperKitTranscriber.swift | 47 ++++-------- Tests/SstWhisperTests/ModelStorageTests.swift | 63 ++++++++++++++++ 3 files changed, 149 insertions(+), 34 deletions(-) create mode 100644 Sources/SstWhisper/ModelStorage.swift create mode 100644 Tests/SstWhisperTests/ModelStorageTests.swift diff --git a/Sources/SstWhisper/ModelStorage.swift b/Sources/SstWhisper/ModelStorage.swift new file mode 100644 index 0000000..a352f84 --- /dev/null +++ b/Sources/SstWhisper/ModelStorage.swift @@ -0,0 +1,73 @@ +import Foundation + +/// A speech model the user can pick in Settings. +public struct ModelOption: Identifiable, Equatable, Sendable { + public let id: String // WhisperKit variant name + public let title: String + public let detail: String + public let approxSizeMB: Int + + public init(id: String, title: String, detail: String, approxSizeMB: Int) { + self.id = id + self.title = title + self.detail = detail + self.approxSizeMB = approxSizeMB + } +} + +public enum ModelCatalog { + public static let defaultVariant = "large-v3-v20240930_turbo" + + /// Sizes measured from the Hugging Face repository `argmaxinc/whisperkit-coreml`. + public static let all: [ModelOption] = [ + ModelOption(id: defaultVariant, title: "Whisper Large v3 Turbo", detail: "Aanbevolen · nauwkeurigst", approxSizeMB: 1638), + ModelOption(id: "large-v3-v20240930_turbo_632MB", title: "Whisper Large v3 Turbo (compact)", detail: "Kleiner, iets minder nauwkeurig", approxSizeMB: 646), + ModelOption(id: "small", title: "Whisper Small", detail: "Snel en licht, minder nauwkeurig", approxSizeMB: 486), + ] +} + +/// Where models live on disk: `/models/argmaxinc/whisperkit-coreml/openai_whisper-`. +public struct ModelStorage: Sendable { + public static let repo = "argmaxinc/whisperkit-coreml" + public static let requiredModels = ["MelSpectrogram", "AudioEncoder", "TextDecoder"] + + public let base: URL + + public init(base: URL = ModelStorage.defaultBase) { + self.base = base + } + + /// Application Support, so models are neither in ~/Documents (privacy prompt, iCloud) nor re-fetched. + public static var defaultBase: URL { + FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + .appending(path: "Sst/models", directoryHint: .isDirectory) + } + + var repoDirectory: URL { + base.appending(path: "models/\(Self.repo)", directoryHint: .isDirectory) + } + + private func folders(for variant: String) -> [URL] { + let names = (try? FileManager.default.contentsOfDirectory(atPath: repoDirectory.path)) ?? [] + return names + .filter { $0 == variant || $0 == "openai_whisper-\(variant)" } + .map { repoDirectory.appending(path: $0, directoryHint: .isDirectory) } + } + + /// The folder of a completely downloaded variant, or nil. + public func folder(for variant: String) -> URL? { + folders(for: variant).first { folder in + Self.requiredModels.allSatisfy { + FileManager.default.fileExists(atPath: folder.appending(path: "\($0).mlmodelc/coremldata.bin").path) + } + } + } + + public func isInstalled(_ variant: String) -> Bool { + folder(for: variant) != nil + } + + public func delete(_ variant: String) throws { + for folder in folders(for: variant) { try FileManager.default.removeItem(at: folder) } + } +} diff --git a/Sources/SstWhisper/WhisperKitTranscriber.swift b/Sources/SstWhisper/WhisperKitTranscriber.swift index b64781f..e0e81bf 100644 --- a/Sources/SstWhisper/WhisperKitTranscriber.swift +++ b/Sources/SstWhisper/WhisperKitTranscriber.swift @@ -10,44 +10,24 @@ public final class WhisperKitTranscriber: Transcriber { } private let modelName: String + private let storage: ModelStorage private var pipeline: WhisperKit? /// Download progress (0...1) during the one-time model download. May be called from any thread. public var onProgress: ((Double) -> Void)? - public init(model: String = "large-v3-v20240930_turbo") { + public init(model: String = ModelCatalog.defaultVariant, storage: ModelStorage = ModelStorage()) { self.modelName = model + self.storage = storage } - /// Hugging Face hub root; WhisperKit stores models in `/models//`. - /// Kept in Application Support so it is neither in ~/Documents (TCC prompt, iCloud) nor re-fetched. - private static var storageBase: URL { - FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] - .appending(path: "Sst/models", directoryHint: .isDirectory) - } + public var variant: String { modelName } - private static let repo = "argmaxinc/whisperkit-coreml" - - /// A previously downloaded variant folder, if it contains the compiled Core ML models. - private func localModelFolder(base: URL) -> URL? { - let repoDir = base.appending(path: "models/\(Self.repo)", directoryHint: .isDirectory) - let folders = (try? FileManager.default.contentsOfDirectory(atPath: repoDir.path)) ?? [] - return folders - .filter { $0.hasSuffix(modelName) } - .map { repoDir.appending(path: $0, directoryHint: .isDirectory) } - .first { folder in - ["MelSpectrogram", "AudioEncoder", "TextDecoder"].allSatisfy { - FileManager.default.fileExists( - atPath: folder.appending(path: "\($0).mlmodelc/coremldata.bin").path) - } - } - } - - private func loadPipeline(from folder: URL, base: URL) async throws -> WhisperKit { + private func loadPipeline(from folder: URL) async throws -> WhisperKit { try await WhisperKit(WhisperKitConfig( - downloadBase: base, + downloadBase: storage.base, modelFolder: folder.path, - tokenizerFolder: base, + tokenizerFolder: storage.base, prewarm: true, load: true, download: false @@ -58,22 +38,21 @@ public final class WhisperKitTranscriber: Transcriber { /// once downloaded, launching needs no network. public func load() async throws { guard pipeline == nil else { return } - let base = Self.storageBase - try FileManager.default.createDirectory(at: base, withIntermediateDirectories: true) + try FileManager.default.createDirectory(at: storage.base, withIntermediateDirectories: true) - if let folder = localModelFolder(base: base), - let loaded = try? await loadPipeline(from: folder, base: base) { + if let folder = storage.folder(for: modelName), + let loaded = try? await loadPipeline(from: folder) { pipeline = loaded return } let folder = try await WhisperKit.download( variant: modelName, - downloadBase: base, - from: Self.repo + downloadBase: storage.base, + from: ModelStorage.repo ) { [onProgress] progress in onProgress?(progress.fractionCompleted) } - pipeline = try await loadPipeline(from: folder, base: base) + pipeline = try await loadPipeline(from: folder) } public func transcribe(_ samples: [Float]) async throws -> String { diff --git a/Tests/SstWhisperTests/ModelStorageTests.swift b/Tests/SstWhisperTests/ModelStorageTests.swift new file mode 100644 index 0000000..40465ab --- /dev/null +++ b/Tests/SstWhisperTests/ModelStorageTests.swift @@ -0,0 +1,63 @@ +import Foundation +import Testing +@testable import SstWhisper + +struct ModelStorageTests { + func makeStorage() -> ModelStorage { + ModelStorage(base: FileManager.default.temporaryDirectory.appending(path: "sst-models-\(UUID().uuidString)")) + } + + /// Creates `//.mlmodelc/coremldata.bin` for the given models. + func install(_ storage: ModelStorage, folder: String, models: [String] = ModelStorage.requiredModels) throws { + for model in models { + let dir = storage.repoDirectory.appending(path: "\(folder)/\(model).mlmodelc", directoryHint: .isDirectory) + try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) + try Data([1]).write(to: dir.appending(path: "coremldata.bin")) + } + } + + @Test func emptyStorageHasNothingInstalled() { + let storage = makeStorage() + #expect(storage.folder(for: "small") == nil) + #expect(!storage.isInstalled("small")) + } + + @Test func completeInstallIsFound() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-large-v3-v20240930_turbo") + #expect(storage.isInstalled("large-v3-v20240930_turbo")) + #expect(storage.folder(for: "large-v3-v20240930_turbo")?.lastPathComponent == "openai_whisper-large-v3-v20240930_turbo") + } + + @Test func incompleteInstallDoesNotCount() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-small", models: ["MelSpectrogram", "AudioEncoder"]) + #expect(!storage.isInstalled("small")) + } + + @Test func variantsAreMatchedExactly() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-large-v3-v20240930_turbo_632MB") + #expect(!storage.isInstalled("large-v3-v20240930_turbo")) + #expect(storage.isInstalled("large-v3-v20240930_turbo_632MB")) + } + + @Test func deleteRemovesOnlyThatVariant() throws { + let storage = makeStorage() + try install(storage, folder: "openai_whisper-small") + try install(storage, folder: "openai_whisper-base") + try storage.delete("small") + #expect(!storage.isInstalled("small")) + #expect(storage.isInstalled("base")) + } + + @Test func deletingSomethingNotInstalledIsHarmless() throws { + try makeStorage().delete("small") + } + + @Test func catalogStartsWithTheDefaultAndHasUniqueIds() { + #expect(ModelCatalog.all.first?.id == ModelCatalog.defaultVariant) + #expect(Set(ModelCatalog.all.map(\.id)).count == ModelCatalog.all.count) + #expect(ModelCatalog.all.allSatisfy { $0.approxSizeMB > 0 }) + } +} From f808ff821c72dad4f47ad290668e3f3ff6936732 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:35:37 +0200 Subject: [PATCH 29/43] feat: add rocker toggle style Co-Authored-By: Claude Sonnet 5.5 --- Sources/SstUI/Controls.swift | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/Sources/SstUI/Controls.swift b/Sources/SstUI/Controls.swift index 72c2241..622914d 100644 --- a/Sources/SstUI/Controls.swift +++ b/Sources/SstUI/Controls.swift @@ -105,3 +105,39 @@ public struct LampView: View { .accessibilityHidden(true) } } + +/// A two-position rocker switch: a key cap sliding in a recessed track. +public struct RockerToggleStyle: ToggleStyle { + public init() {} + + public func makeBody(configuration: Configuration) -> some View { + HStack(spacing: Space.s4) { + configuration.label + Button { + configuration.isOn.toggle() + } label: { + RockerTrack(isOn: configuration.isOn) + } + .buttonStyle(.plain) + .accessibilityValue(configuration.isOn ? "aan" : "uit") + } + } + + private struct RockerTrack: View { + let isOn: Bool + @Environment(\.accessibilityReduceMotion) private var reduceMotion + + var body: some View { + ZStack(alignment: isOn ? .trailing : .leading) { + WellBackground() + PhysicalSurface( + fill: AnyShapeStyle(LinearGradient(colors: [Palette.surfaceKeyHigh, Palette.surfaceKeyLow], startPoint: .top, endPoint: .bottom)), + cornerRadius: Radius.sm, shadows: Shadows.keyRaised) + .frame(width: Size.switchWidth / 2, height: Size.switchHeight - Space.s2) + .padding(Space.s1) + } + .frame(width: Size.switchWidth, height: Size.switchHeight) + .animation(reduceMotion ? nil : .easeOut(duration: Motion.durationFast), value: isOn) + } + } +} From f510f02945b05c7275a103a9d89c0b9367571f7b Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:36:45 +0200 Subject: [PATCH 30/43] feat: turn Sst into a regular Mac app with settings, menus and menu bar item Co-Authored-By: Claude Sonnet 5.5 --- Package.swift | 2 +- Sources/Sst/AppCommands.swift | 37 ++++ Sources/Sst/AppIdentity.swift | 17 ++ Sources/Sst/AppModel.swift | 324 ++++++++++++++++++++++------ Sources/Sst/MainWindow.swift | 23 ++ Sources/Sst/MenuBarContent.swift | 23 ++ Sources/Sst/MenuContent.swift | 34 --- Sources/Sst/PermissionMonitor.swift | 87 ++++++++ Sources/Sst/SettingsView.swift | 221 +++++++++++++++++++ Sources/Sst/SstApp.swift | 21 +- Sources/Sst/Strings.swift | 61 ++++++ 11 files changed, 752 insertions(+), 98 deletions(-) create mode 100644 Sources/Sst/AppCommands.swift create mode 100644 Sources/Sst/AppIdentity.swift create mode 100644 Sources/Sst/MainWindow.swift create mode 100644 Sources/Sst/MenuBarContent.swift delete mode 100644 Sources/Sst/MenuContent.swift create mode 100644 Sources/Sst/PermissionMonitor.swift create mode 100644 Sources/Sst/SettingsView.swift create mode 100644 Sources/Sst/Strings.swift diff --git a/Package.swift b/Package.swift index 098a8bb..4cb6621 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( .product(name: "WhisperKit", package: "WhisperKit"), ] ), - .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper"]), + .executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper", "SstData", "SstUI"]), .executableTarget(name: "SstProbe", dependencies: ["SstCore", "SstWhisper"]), .executableTarget(name: "SstGallery", dependencies: ["SstUI"]), .testTarget(name: "SstCoreTests", dependencies: ["SstCore"]), diff --git a/Sources/Sst/AppCommands.swift b/Sources/Sst/AppCommands.swift new file mode 100644 index 0000000..2b42c4e --- /dev/null +++ b/Sources/Sst/AppCommands.swift @@ -0,0 +1,37 @@ +import SwiftUI + +struct AppCommands: Commands { + @ObservedObject var model: AppModel + @Environment(\.openWindow) private var openWindow + + var body: some Commands { + CommandGroup(after: .pasteboard) { + Divider() + Button(Strings.clearHistory) { + openWindow(id: "main") + model.confirmingClearHistory = true + } + } + CommandMenu(Strings.recordingMenu) { + Button(model.isRecording ? Strings.stopRecording : Strings.startRecording) { + model.toggleWindowRecording() + } + .keyboardShortcut("r", modifiers: [.command, .option]) + Divider() + Button(Strings.transcripts) { show(.transcripts) } + .keyboardShortcut("1", modifiers: .command) + Button(Strings.dictionary) { show(.dictionary) } + .keyboardShortcut("2", modifiers: .command) + Button(Strings.search) { + show(.transcripts) + model.focusSearch() + } + .keyboardShortcut("f", modifiers: .command) + } + } + + private func show(_ section: AppModel.Section) { + openWindow(id: "main") + model.show(section) + } +} diff --git a/Sources/Sst/AppIdentity.swift b/Sources/Sst/AppIdentity.swift new file mode 100644 index 0000000..f683c88 --- /dev/null +++ b/Sources/Sst/AppIdentity.swift @@ -0,0 +1,17 @@ +import Foundation + +/// The one place that knows the app's name, so renaming it later is a one-file change. +enum AppIdentity { + static let displayName = "Sst" + static let supportFolderName = "Sst" + static let brandCaption = "SST · FIELD RECORDER" +} + +enum AppPaths { + static var support: URL { + FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] + .appending(path: AppIdentity.supportFolderName, directoryHint: .isDirectory) + } + static var dictionary: URL { support.appending(path: "dictionary.txt") } + static var history: URL { support.appending(path: "history.json") } +} diff --git a/Sources/Sst/AppModel.swift b/Sources/Sst/AppModel.swift index 64a56b7..a223e3d 100644 --- a/Sources/Sst/AppModel.swift +++ b/Sources/Sst/AppModel.swift @@ -1,57 +1,125 @@ +import AppKit import AVFoundation -import ApplicationServices -import SwiftUI +import Combine import SstCore +import SstData import SstWhisper +import SwiftUI + +/// Decides at insertion time whether dictated text may be pasted. +/// Not when Sst itself is in front (it would land in Sst's own search field) and not for recordings started in the window. +final class InsertionPolicy { + var windowSession = false + + func allowed() -> Bool { + !windowSession && NSWorkspace.shared.frontmostApplication?.bundleIdentifier != Bundle.main.bundleIdentifier + } +} @MainActor final class AppModel: ObservableObject { - @Published var state: DictationState = .idle - @Published var modelStatus = "Model laden…" - @Published var modelReady = false - @Published var lastError: String? - @Published var micGranted = false - @Published var accessibilityGranted = false - @Published var inputMonitoringGranted = false + enum Section: String, CaseIterable, Identifiable { + case transcripts, dictionary + var id: String { rawValue } + } - private let transcriber = WhisperKitTranscriber() + enum ModelStatus: Equatable { + case loading(String) + case downloading(String, Double) + case ready + case failed(String) + } + + // Services + let settings: AppSettings + let permissions = PermissionMonitor() + let levelSource = LevelSource() + let history: HistoryStore + let dictionary: DictionaryStore + let storage = ModelStorage() + + // Observable state + @Published private(set) var state: DictationState = .idle + @Published private(set) var modelStatus: ModelStatus = .loading(ModelCatalog.defaultVariant) + @Published private(set) var activeVariant: String? + @Published private(set) var installedVariants: Set = [] + @Published private(set) var recordingStartedAt: Date? + @Published private(set) var lastDuration: TimeInterval? + /// Bumped whenever the history or dictionary changes, so views that cache derived data can refresh. + @Published private(set) var historyRevision = 0 + @Published private(set) var dictionaryRevision = 0 + @Published private(set) var searchFocusTick = 0 + @Published var lastError: String? + @Published var section: Section = .transcripts + @Published var confirmingClearHistory = false + + private let transcriber = SwappableTranscriber() + private let corrector: DictionaryCorrector private let hotkey = HotkeyMonitor() + private let policy: InsertionPolicy private let controller: DictationController + private var modelTask: Task? + private var loadingVariant: String? + private var cancellables = Set() init() { - controller = DictationController( - recorder: AudioRecorder(), + let settings = AppSettings() + let policy = InsertionPolicy() + let knowledge = SystemWordKnowledge() + let corrector = DictionaryCorrector(knowledge: knowledge) + let dictionary = DictionaryStore(fileURL: AppPaths.dictionary) + let history = HistoryStore(fileURL: AppPaths.history) + let levelSource = self.levelSource + let recorder = AudioRecorder() + recorder.onLevel = { levelSource.update($0) } + + self.settings = settings + self.policy = policy + self.corrector = corrector + self.dictionary = dictionary + self.history = history + self.controller = DictationController( + recorder: recorder, transcriber: transcriber, cleaner: PassthroughCleaner(), - inserter: TextInserter(pasteboard: SystemPasteboard(), paster: CGEventPaster()) - ) - controller.onStateChange = { [weak self] in - self?.state = $0 - if $0 == .recording { self?.lastError = nil } - } - controller.onError = { [weak self] in self?.lastError = $0 } - hotkey.onEvent = { [weak self] event in - guard let self, self.modelReady else { return } - if event == .pressed, AVCaptureDevice.authorizationStatus(for: .audio) != .authorized { - self.lastError = "Microfoon-toegang ontbreekt" - return - } - Task { await self.controller.handle(event) } - } + inserter: GatedInserter( + inner: TextInserter(pasteboard: SystemPasteboard(), paster: CGEventPaster()), + shouldInsert: { policy.allowed() }), + corrector: corrector, + vocabularyProvider: { settings.usePriming ? VocabularyBuilder.build(from: dictionary.entries) : [] }) + + wire() Task { await start() } } - var statusLine: String { - guard modelReady else { return modelStatus } - switch state { - case .idle: return "Klaar — houd Fn ingedrukt om te dicteren" - case .recording: return "Luistert…" - case .transcribing: return "Transcriberen…" - case .inserting: return "Invoegen…" + // MARK: derived + + var isRecording: Bool { state == .recording } + var hotkeyBinding: HotkeyBinding { HotkeyBinding.binding(id: settings.hotkeyID) ?? .fn } + var modelReady: Bool { modelStatus == .ready } + + /// Why recording is not possible right now, or nil when it is. + var recordingBlocker: String? { + if !permissions.allGranted { return Strings.openSystemSettings } + switch modelStatus { + case .ready: return nil + case .loading(let variant): return Strings.modelLoading(title(for: variant)) + case .downloading(_, let fraction): return Strings.modelDownloading(Int(fraction * 100)) + case .failed: return Strings.modelFailed } } - var iconName: String { + var statusLine: String { + if let blocker = recordingBlocker, state == .idle { return blocker } + switch state { + case .idle: return Strings.holdToDictate(hotkeyBinding.displayName) + case .recording: return Strings.statusListening + case .transcribing: return Strings.statusTranscribing + case .inserting: return Strings.statusInserting + } + } + + var menuIconName: String { switch state { case .idle: if lastError != nil { return "exclamationmark.triangle" } @@ -61,43 +129,177 @@ final class AppModel: ObservableObject { } } - func refreshPermissions() { - micGranted = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized - accessibilityGranted = AXIsProcessTrusted() - inputMonitoringGranted = CGPreflightListenEventAccess() + func title(for variant: String) -> String { + ModelCatalog.all.first { $0.id == variant }?.title ?? variant } - func retryHotkey() { - refreshPermissions() - do { - try hotkey.start() - lastError = nil - } catch { - lastError = error.localizedDescription + // MARK: lifecycle + + private func wire() { + controller.onStateChange = { [weak self] state in + guard let self else { return } + self.state = state + switch state { + case .recording: + self.lastError = nil + self.recordingStartedAt = Date() + self.levelSource.reset() + case .idle: + if let started = self.recordingStartedAt { self.lastDuration = Date().timeIntervalSince(started) } + self.recordingStartedAt = nil + self.levelSource.reset() + default: break + } + } + controller.onError = { [weak self] in self?.lastError = $0 } + controller.onResult = { [weak self] result in + guard let self, self.settings.keepHistory else { return } + self.history.add(HistoryEntry(result: result)) + } + hotkey.onEvent = { [weak self] event in self?.handleHotkey(event) } + hotkey.binding = hotkeyBinding + + settings.$hotkeyID + .sink { [weak self] _ in + guard let self else { return } + self.hotkey.binding = self.hotkeyBinding + self.objectWillChange.send() + } + .store(in: &cancellables) + settings.$useCorrection + .sink { [corrector] in corrector.setEnabled($0) } + .store(in: &cancellables) + permissions.objectWillChange + .sink { [weak self] _ in self?.objectWillChange.send() } + .store(in: &cancellables) + + dictionary.onChange = { [weak self] in + guard let self else { return } + self.corrector.update(entries: self.dictionary.entries) + self.dictionaryRevision += 1 + } + history.onChange = { [weak self] in self?.historyRevision += 1 } + NotificationCenter.default.addObserver(forName: NSApplication.willTerminateNotification, object: nil, queue: .main) { [history] _ in + MainActor.assumeIsolated { history.flush() } } } private func start() async { - _ = await AVCaptureDevice.requestAccess(for: .audio) - if !CGPreflightListenEventAccess() { _ = CGRequestListenEventAccess() } - if !AXIsProcessTrusted() { - _ = AXIsProcessTrustedWithOptions(["AXTrustedCheckOptionPrompt": true] as CFDictionary) + do { try dictionary.load() } catch { lastError = error.localizedDescription } + dictionary.startWatching() + if case .recoveredCorrupt = history.load() { + lastError = "Het geschiedenisbestand was beschadigd en is opzij gezet." } + refreshInstalled() + await permissions.requestMissing() retryHotkey() - transcriber.onProgress = { [weak self] fraction in - Task { @MainActor in - self?.modelStatus = fraction < 1 - ? "Model downloaden… \(Int(fraction * 100))%" - : "Model laden…" - } - } + activateModel(settings.modelVariant) + } + + func retryHotkey() { + permissions.refresh() do { - try await transcriber.load() - modelReady = true - modelStatus = "Klaar" + try hotkey.start() } catch { - modelStatus = "Model laden mislukt" lastError = error.localizedDescription } } + + // MARK: recording + + private func handleHotkey(_ event: HotkeyEvent) { + guard modelReady else { return } + if event == .pressed, AVCaptureDevice.authorizationStatus(for: .audio) != .authorized { + lastError = Strings.microphoneMissing + return + } + Task { await controller.handle(event) } + } + + /// Start/stop from the window or ⌥⌘R. The result goes to the history only; nothing is pasted. + func toggleWindowRecording() { + if isRecording { + stopWindowRecording() + } else { + startWindowRecording() + } + } + + func startWindowRecording() { + guard recordingBlocker == nil, state == .idle else { return } + policy.windowSession = true + Task { await controller.handle(.pressed) } + } + + func stopWindowRecording() { + guard policy.windowSession else { return } + Task { + await controller.handle(.released) + policy.windowSession = false + } + } + + // MARK: model + + func refreshInstalled() { + installedVariants = Set(ModelCatalog.all.map(\.id).filter { storage.isInstalled($0) }) + } + + /// Downloads (if needed) and loads `variant`. Dictation is off meanwhile; if it fails the previous model stays. + func activateModel(_ variant: String) { + modelTask?.cancel() + let previous = activeVariant + loadingVariant = variant + modelStatus = .loading(variant) + modelTask = Task { [weak self] in + guard let self else { return } + let candidate = WhisperKitTranscriber(model: variant, storage: storage) + candidate.onProgress = { [weak self] fraction in + Task { @MainActor in + guard let self, self.loadingVariant == variant else { return } + self.modelStatus = fraction < 1 ? .downloading(variant, fraction) : .loading(variant) + } + } + do { + try await candidate.load() + guard !Task.isCancelled else { return } + transcriber.swap(to: candidate) + activeVariant = variant + settings.modelVariant = variant + modelStatus = .ready + } catch { + guard !Task.isCancelled else { return } + lastError = error.localizedDescription + modelStatus = previous == nil ? .failed(error.localizedDescription) : .ready + } + if loadingVariant == variant { loadingVariant = nil } + refreshInstalled() + } + } + + func deleteModel(_ variant: String) { + guard variant != activeVariant else { return } + do { + try storage.delete(variant) + } catch { + lastError = error.localizedDescription + } + refreshInstalled() + } + + // MARK: navigation and history + + func show(_ section: Section) { + self.section = section + NSApp.activate(ignoringOtherApps: true) + } + + func focusSearch() { + searchFocusTick += 1 + } + + func clearHistory() { + history.removeAll() + history.flush() + } } diff --git a/Sources/Sst/MainWindow.swift b/Sources/Sst/MainWindow.swift new file mode 100644 index 0000000..ef1550c --- /dev/null +++ b/Sources/Sst/MainWindow.swift @@ -0,0 +1,23 @@ +import SwiftUI +import SstUI + +/// Placeholder shell; phase 5 replaces the content with the deck, history and dictionary. +struct MainWindow: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + ZStack { + BrushedBackground().ignoresSafeArea() + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(AppIdentity.brandCaption) + Text(model.statusLine) + .textStyle(Typography.reading) + .foregroundStyle(Palette.inkPrimary) + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, alignment: .leading) + .background(PanelBackground()) + } + .padding(Layout.panelPadding) + } + } +} diff --git a/Sources/Sst/MenuBarContent.swift b/Sources/Sst/MenuBarContent.swift new file mode 100644 index 0000000..3132fff --- /dev/null +++ b/Sources/Sst/MenuBarContent.swift @@ -0,0 +1,23 @@ +import SwiftUI + +/// The secondary interface: status and shortcut while working in another app. +struct MenuBarContent: View { + @EnvironmentObject private var model: AppModel + @Environment(\.openWindow) private var openWindow + + var body: some View { + Text(model.statusLine) + if let error = model.lastError { + Text(error) + } + Divider() + Button(Strings.openApp) { + openWindow(id: "main") + model.show(model.section) + } + SettingsLink { Text(Strings.settings) } + Divider() + Button(Strings.quit) { NSApplication.shared.terminate(nil) } + .keyboardShortcut("q") + } +} diff --git a/Sources/Sst/MenuContent.swift b/Sources/Sst/MenuContent.swift deleted file mode 100644 index 5d9b15f..0000000 --- a/Sources/Sst/MenuContent.swift +++ /dev/null @@ -1,34 +0,0 @@ -import AppKit -import SwiftUI - -struct MenuContent: View { - @ObservedObject var model: AppModel - - var body: some View { - Text(model.statusLine) - if let error = model.lastError { - Text("Fout: \(error)") - } - Divider() - permissionRow("Microfoon", granted: model.micGranted, pane: "Privacy_Microphone") - permissionRow("Accessibility", granted: model.accessibilityGranted, pane: "Privacy_Accessibility") - permissionRow("Input Monitoring", granted: model.inputMonitoringGranted, pane: "Privacy_ListenEvent") - Button("Rechten opnieuw controleren") { model.retryHotkey() } - Divider() - Button("Stop") { NSApplication.shared.terminate(nil) } - .keyboardShortcut("q") - } - - @ViewBuilder - private func permissionRow(_ title: String, granted: Bool, pane: String) -> some View { - if granted { - Text("✓ \(title)") - } else { - Button("⚠︎ \(title) — open Instellingen") { - if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?\(pane)") { - NSWorkspace.shared.open(url) - } - } - } - } -} diff --git a/Sources/Sst/PermissionMonitor.swift b/Sources/Sst/PermissionMonitor.swift new file mode 100644 index 0000000..d44f327 --- /dev/null +++ b/Sources/Sst/PermissionMonitor.swift @@ -0,0 +1,87 @@ +import AppKit +import ApplicationServices +import AVFoundation +import Combine + +/// Reads the three macOS permissions and keeps them current: on every app activation and while polling is on. +@MainActor +final class PermissionMonitor: ObservableObject { + enum Kind: CaseIterable, Identifiable { + case microphone, accessibility, inputMonitoring + + var id: Self { self } + + var title: String { + switch self { + case .microphone: Strings.microphone + case .accessibility: Strings.accessibility + case .inputMonitoring: Strings.inputMonitoring + } + } + + fileprivate var pane: String { + switch self { + case .microphone: "Privacy_Microphone" + case .accessibility: "Privacy_Accessibility" + case .inputMonitoring: "Privacy_ListenEvent" + } + } + } + + @Published private(set) var microphone = false + @Published private(set) var accessibility = false + @Published private(set) var inputMonitoring = false + + private var timer: Timer? + private var activation: AnyCancellable? + + init() { + refresh() + activation = NotificationCenter.default + .publisher(for: NSApplication.didBecomeActiveNotification) + .sink { [weak self] _ in MainActor.assumeIsolated { self?.refresh() } } + } + + var allGranted: Bool { microphone && accessibility && inputMonitoring } + + func isGranted(_ kind: Kind) -> Bool { + switch kind { + case .microphone: microphone + case .accessibility: accessibility + case .inputMonitoring: inputMonitoring + } + } + + func refresh() { + microphone = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized + accessibility = AXIsProcessTrusted() + inputMonitoring = CGPreflightListenEventAccess() + } + + /// Asks macOS for every permission that is still missing (each shows its own system prompt). + func requestMissing() async { + if !microphone { _ = await AVCaptureDevice.requestAccess(for: .audio) } + if !inputMonitoring { _ = CGRequestListenEventAccess() } + if !accessibility { _ = AXIsProcessTrustedWithOptions(["AXTrustedCheckOptionPrompt": true] as CFDictionary) } + refresh() + } + + func openSettings(for kind: Kind) { + if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?\(kind.pane)") { + NSWorkspace.shared.open(url) + } + } + + /// Polls while a view that shows the lamps is visible, so granting a permission shows up without a click. + func startPolling(every interval: TimeInterval = 2) { + guard timer == nil else { return } + timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in + MainActor.assumeIsolated { self?.refresh() } + } + } + + func stopPolling() { + timer?.invalidate() + timer = nil + } +} diff --git a/Sources/Sst/SettingsView.swift b/Sources/Sst/SettingsView.swift new file mode 100644 index 0000000..b9249dd --- /dev/null +++ b/Sources/Sst/SettingsView.swift @@ -0,0 +1,221 @@ +import AppKit +import SstCore +import SstData +import SstUI +import SstWhisper +import SwiftUI + +struct SettingsView: View { + private enum Tab: CaseIterable { + case hotkey, model, privacy + + var title: String { + switch self { + case .hotkey: Strings.tabHotkey + case .model: Strings.tabModel + case .privacy: Strings.tabPrivacy + } + } + } + + @State private var tab: Tab = .hotkey + + var body: some View { + ZStack { + BrushedBackground() + VStack(alignment: .leading, spacing: Layout.panelGap) { + HStack(spacing: Space.s3) { + ForEach(Tab.allCases, id: \.self) { candidate in + Button(candidate.title) { tab = candidate } + .buttonStyle(KeyButtonStyle(latched: tab == candidate)) + } + } + Group { + switch tab { + case .hotkey: HotkeySettingsView() + case .model: ModelSettingsView() + case .privacy: PrivacySettingsView() + } + } + .padding(Layout.panelPadding) + .frame(maxWidth: .infinity, alignment: .topLeading) + .background(PanelBackground()) + } + .padding(Layout.panelPadding) + } + .frame(width: Layout.settingsWidth) + .fixedSize(horizontal: false, vertical: true) + } +} + +// MARK: hotkey + +extension HotkeyBinding { + /// The matching flag in an AppKit event, used while capturing a new key. + fileprivate var eventFlag: NSEvent.ModifierFlags { + switch id { + case "fn": .function + case "rightOption": .option + case "rightCommand": .command + default: .control + } + } +} + +@MainActor +private final class HotkeyCapture: ObservableObject { + @Published var isCapturing = false + @Published var message: String? + private var monitor: Any? + + func start(onCapture: @escaping (HotkeyBinding) -> Void) { + stop() + isCapturing = true + message = Strings.capturing + monitor = NSEvent.addLocalMonitorForEvents(matching: [.flagsChanged, .keyDown]) { [weak self] event in + guard let self else { return event } + if event.type == .keyDown { + if event.keyCode == 53 { self.stop() } // Escape cancels + return nil + } + guard let binding = HotkeyBinding.binding(keyCode: Int(event.keyCode)) else { + self.message = Strings.hotkeyRejected + return nil + } + if event.modifierFlags.contains(binding.eventFlag) { + self.stop() + onCapture(binding) + } + return nil + } + } + + func stop() { + if let monitor { NSEvent.removeMonitor(monitor) } + monitor = nil + isCapturing = false + message = nil + } +} + +private struct HotkeySettingsView: View { + @EnvironmentObject private var settings: AppSettings + @StateObject private var capture = HotkeyCapture() + + private var current: HotkeyBinding { HotkeyBinding.binding(id: settings.hotkeyID) ?? .fn } + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.tabHotkey, style: Typography.heading) + HStack(spacing: Layout.panelGap) { + Text(current.displayName) + .textStyle(Typography.heading) + .foregroundStyle(Palette.inkPrimary) + .padding(.horizontal, Space.s4) + .frame(minHeight: Size.controlHeight) + .background(WellBackground()) + Button(Strings.captureHotkey) { + capture.start { settings.hotkeyID = $0.id } + } + .buttonStyle(KeyButtonStyle(latched: capture.isCapturing)) + } + if let message = capture.message { + Text(message).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + } + SeamDivider() + Text(HotkeyBinding.allowed.map(\.displayName).joined(separator: " · ")) + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + if current == .fn { + Text(Strings.globeHint).textStyle(Typography.meta).foregroundStyle(Palette.inkSecondary) + } + } + .onDisappear { capture.stop() } + } +} + +// MARK: model + +private struct ModelSettingsView: View { + @EnvironmentObject private var model: AppModel + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.tabModel, style: Typography.heading) + ForEach(ModelCatalog.all) { option in + row(option) + SeamDivider() + } + HStack { + Text(model.storage.base.path) + .textStyle(Typography.code) + .foregroundStyle(Palette.inkSecondary) + .lineLimit(1) + .truncationMode(.middle) + Spacer() + Button(Strings.showInFinder) { + NSWorkspace.shared.activateFileViewerSelecting([model.storage.base]) + } + .buttonStyle(KeyButtonStyle()) + } + } + .onAppear { model.refreshInstalled() } + } + + private func row(_ option: ModelOption) -> some View { + let isActive = model.activeVariant == option.id + let installed = model.installedVariants.contains(option.id) + let progress: Double? = { + if case .downloading(option.id, let fraction) = model.modelStatus { return fraction } + return nil + }() + return HStack(alignment: .center, spacing: Layout.panelGap) { + LampView(isOn: isActive, tint: .amber) + VStack(alignment: .leading, spacing: Space.s1) { + Text(option.title).textStyle(Typography.body).foregroundStyle(Palette.inkPrimary) + Text("\(option.detail) · \(option.approxSizeMB) MB") + .textStyle(Typography.meta) + .foregroundStyle(Palette.inkSecondary) + if let progress { + LEDBarView(fraction: progress) + } else if case .loading(option.id) = model.modelStatus { + SilkLabel(Strings.modelLoading(option.title), style: Typography.labelSmall) + } + } + Spacer() + if installed && !isActive { + Button(Strings.useModel) { model.activateModel(option.id) }.buttonStyle(KeyButtonStyle()) + Button(Strings.remove) { model.deleteModel(option.id) }.buttonStyle(KeyButtonStyle()) + } else if !installed { + Button(Strings.download) { model.activateModel(option.id) }.buttonStyle(KeyButtonStyle()) + } + } + } +} + +// MARK: privacy + +private struct PrivacySettingsView: View { + @EnvironmentObject private var model: AppModel + @EnvironmentObject private var settings: AppSettings + @State private var confirming = false + + var body: some View { + VStack(alignment: .leading, spacing: Layout.panelGap) { + SilkLabel(Strings.tabPrivacy, style: Typography.heading) + Toggle(isOn: $settings.keepHistory) { + Text(Strings.keepHistory).textStyle(Typography.body).foregroundStyle(Palette.inkPrimary) + } + .toggleStyle(RockerToggleStyle()) + SeamDivider() + Button(Strings.clearHistory) { confirming = true } + .buttonStyle(KeyButtonStyle()) + } + .confirmationDialog(Strings.clearHistoryConfirm, isPresented: $confirming) { + Button(Strings.erase, role: .destructive) { model.clearHistory() } + Button(Strings.cancel, role: .cancel) {} + } message: { + Text(Strings.clearHistoryDetail) + } + } +} diff --git a/Sources/Sst/SstApp.swift b/Sources/Sst/SstApp.swift index e4d86be..6799290 100644 --- a/Sources/Sst/SstApp.swift +++ b/Sources/Sst/SstApp.swift @@ -1,14 +1,31 @@ import SwiftUI +import SstUI @main struct SstApp: App { @StateObject private var model = AppModel() var body: some Scene { + Window(AppIdentity.displayName, id: "main") { + MainWindow() + .environmentObject(model) + .environmentObject(model.settings) + .frame(minWidth: Layout.windowMinWidth, minHeight: Layout.windowMinHeight) + } + .defaultSize(width: Layout.windowDefaultWidth, height: Layout.windowDefaultHeight) + .commands { AppCommands(model: model) } + + Settings { + SettingsView() + .environmentObject(model) + .environmentObject(model.settings) + } + MenuBarExtra { - MenuContent(model: model) + MenuBarContent() + .environmentObject(model) } label: { - Image(systemName: model.iconName) + Image(systemName: model.menuIconName) } } } diff --git a/Sources/Sst/Strings.swift b/Sources/Sst/Strings.swift new file mode 100644 index 0000000..cac1d72 --- /dev/null +++ b/Sources/Sst/Strings.swift @@ -0,0 +1,61 @@ +import Foundation + +/// All user-facing text, in one place (Dutch), so translating later is a single-file job. +enum Strings { + // sections + static let transcripts = "Transcripties" + static let dictionary = "Woordenboek" + static let controls = "Controles" + + // deck + static let record = "Rec" + static let stop = "Stop" + static let time = "Tijd" + static let level = "Niveau" + + // menus + static let recordingMenu = "Opname" + static let startRecording = "Start opname" + static let stopRecording = "Stop opname" + static let search = "Zoek" + static let clearHistory = "Wis geschiedenis…" + static let openApp = "Open Sst" + static let settings = "Instellingen…" + static let quit = "Stop Sst" + + // status + static let statusIdle = "Klaar" + static let statusListening = "Luistert…" + static let statusTranscribing = "Transcriberen…" + static let statusInserting = "Invoegen…" + static func modelLoading(_ title: String) -> String { "Model laden: \(title)…" } + static func modelDownloading(_ percent: Int) -> String { "Model downloaden… \(percent)%" } + static let modelFailed = "Model laden mislukt" + static func holdToDictate(_ keyName: String) -> String { "Houd \(keyName) ingedrukt om te dicteren" } + + // permissions + static let microphone = "Microfoon" + static let accessibility = "Accessibility" + static let inputMonitoring = "Input Monitoring" + static let modelLamp = "Model" + static let microphoneMissing = "Microfoon-toegang ontbreekt" + static let openSystemSettings = "Open Instellingen" + + // settings + static let tabHotkey = "Sneltoets" + static let tabModel = "Model" + static let tabPrivacy = "Privacy" + static let captureHotkey = "Leg nieuwe toets vast" + static let capturing = "Druk op de toets die je wilt gebruiken…" + static let hotkeyRejected = "Die toets kan niet. Kies Fn, Rechter Option, Rechter Command of Control." + static let globeHint = "Zet bij Fn in Systeeminstellingen → Toetsenbord ‘Druk op 🌐 voor’ op ‘Doe niets’." + static let useModel = "Gebruik" + static let download = "Download" + static let remove = "Verwijder" + static let showInFinder = "Toon in Finder" + static let keepHistory = "Transcripties bewaren" + static let clearHistoryConfirm = "Alle transcripties wissen?" + static let clearHistoryDetail = "Dit kan niet ongedaan worden gemaakt. Audio wordt nooit bewaard." + static let cancel = "Annuleer" + static let erase = "Wis" +} From 15c9582e12fe355c4530e3980e439041a0b12673 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:38:49 +0200 Subject: [PATCH 31/43] fix: apply hotkey changes immediately, reset stuck window session, retry hotkey after permission grant Co-Authored-By: Claude Sonnet 5.5 --- Sources/Sst/AppModel.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Sources/Sst/AppModel.swift b/Sources/Sst/AppModel.swift index a223e3d..9dc054a 100644 --- a/Sources/Sst/AppModel.swift +++ b/Sources/Sst/AppModel.swift @@ -160,12 +160,17 @@ final class AppModel: ObservableObject { hotkey.binding = hotkeyBinding settings.$hotkeyID - .sink { [weak self] _ in + .sink { [weak self] id in guard let self else { return } - self.hotkey.binding = self.hotkeyBinding + self.hotkey.binding = HotkeyBinding.binding(id: id) ?? .fn self.objectWillChange.send() } .store(in: &cancellables) + permissions.$inputMonitoring + .removeDuplicates() + .filter { $0 } + .sink { [weak self] _ in self?.retryHotkey() } + .store(in: &cancellables) settings.$useCorrection .sink { [corrector] in corrector.setEnabled($0) } .store(in: &cancellables) @@ -228,7 +233,10 @@ final class AppModel: ObservableObject { func startWindowRecording() { guard recordingBlocker == nil, state == .idle else { return } policy.windowSession = true - Task { await controller.handle(.pressed) } + Task { + await controller.handle(.pressed) + if state != .recording { policy.windowSession = false } + } } func stopWindowRecording() { From cfe8208e11711d4a2d277a3d52015f7fcb639b56 Mon Sep 17 00:00:00 2001 From: Theo Weijmer Date: Thu, 1 Oct 2026 15:41:20 +0200 Subject: [PATCH 32/43] feat: app icon generator, signed app bundle script with install option Co-Authored-By: Claude Sonnet 5.5 --- Design/AppIcon.icns | Bin 0 -> 1078042 bytes Package.swift | 1 + Sources/SstIcon/IconMaker.swift | 67 ++++++++++++++++++++++++++++++++ scripts/make-app.sh | 43 ++++++++++++++++---- scripts/make-icon.sh | 10 +++++ 5 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 Design/AppIcon.icns create mode 100644 Sources/SstIcon/IconMaker.swift create mode 100755 scripts/make-icon.sh diff --git a/Design/AppIcon.icns b/Design/AppIcon.icns new file mode 100644 index 0000000000000000000000000000000000000000..5ec0b5a508e1c1e32c267b6b8d39abed249fc0e9 GIT binary patch literal 1078042 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 z�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�pY6i?&D4T2*O=b5RHkN>4n!@p(@4PV>|e|^65 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%0Ag1-v#>CnWBUz053&rI7#w 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 z

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

s z`rO9uy5CsPFWi3ndrlrde$B~Kr%qm{?{4ZB0dI^$8sMI$xR~3rnj&7rsH!jtw39M9 zmr28ffam3>w))I3Phz!s(6&u=)bavR&A(^$q%ZT>wQ)958;u@Xbp1+5940ptvcDUj zlYFFrGtI+2YxL+pxQR>8Y7E&T=}yZ3?7F3h)u_+h!N1rcMin?rsGfEfX0w_ zmgJ14b}Ia`y!3c$01@9B(PnnUucqR+Kap%r@+X=waZ8PgAIRLna2#s;)u8NaX5fJJ zcw2#!Jo>y!=VH`jhRhMa#Ft&!_r)Z9J*(|;_FNK8$T{I5gD1Rlv?fm37dI>zKEd%m zuuop#C-8EYJX*MnvnG-+JVYobZ+ypN`s6_IlL`3lA!`Q=pSJw5JYte+cc@i>9T!j7 z!V>wSEnb#v^+iI0>z~X+gIO-~PyHCQe>5Y3zHAMA{HBD9PkCX0l%C*A)@-IEVpL}N z(<8E9>sobkhmGtf&W!^l7HncbSzGNtb_yrqx-QI)A4aQJn{LMsX<7fow~S+8h3^Z2 z#y|lX^kgXyh@z1Zvfgyn672Ec61L=YkUh*XFMR!M@aP*EK<3-Xzj)+zV6p7X`&^ zO=XGL_T7rY2nT!-g65oN?Z;Db%K#^5ed@bk-3y5PTNd~EevvNVJpLMR#>yvN`-mU% zN^-=(MxD5@iFxoDaeNG}-SMG(6HvYeSUQZWP7^)dAvShOCiIgIzXnOsN?fu7k=tsmSf8!H- z=0jf)c&g|?Z2dnUpe25}M!01tOEG9RtwZO>QKKhE{b_y0!#r?0bl}q$F(o0>MI$CX zS&x~2bOM(PPtp4VRX!k_z-d&Qi(ETX`;QC+U!ul==}A@i{{RMqj1un=Spm0622g=aR@gxnl*sQ~4m?PK8TJe7Y|H2tN7V9o3wi z{bZrHT-Tci9(eGz$FDy2sgLN_+Kyk%R{;TFLTI9>&BOpX6QdS@X-s-H#UX$evoD^y zMkb(nPYu3kWtiy)NKDj%5eWm}vrvOy7-eh7OU{_{afOdHv~#v`%a|B${P+|sT(i}} z_bc>Rz=~Sdl2CjKKIY>~Uzj8h!Sm6J5beG3JIE0$DCt~m{6Pu+I{)z*cL_|Aiff(! z8K3yU=WQYIZI4Zg2e(H>&qW)k!t^VjV=|%GX!BVNwPTZbX~I*3LBK|he->5kq7o$6 z{u-6|8GmZ2WoH#!vh5L&PkgY6&CsUDZY*KPpjwY$iG)y`FpXHSlg(62>44So2S2n@ zCIDl#tynO+=vY&GHUF@K1gA&apD=QUaQd!B^%n=*jF`fJ{U^d^^`vO+wYy!zUW-i2kB5 z3lpos2tLCCzkER#dx+^s9beQ!R0VJR7h{&js-EV}17C zHrux)eqw3hcr!fMfsZs&jsRR)1sfYqow?9DF16_tM&zw+8$pCGyWUI^B_EXHmaoT8 zypA8l9)EwOPU0C)d{F#^epIJz{td9kgr`4u%x@y_qd!NF>y!Vw?QpehGj9Gs9v{M! ztUtA{_ECM#L^iI{uK`}AHu4va!PhG&{TS7Gz00%0D+eM*O5sve;YlFKm4UGV>E|^CD{% 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�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

Tqc@cuK!u>a* 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