114 Commits
Author SHA1 Message Date
ScarriffleandClaude Opus 5 b3ad9c49d6 Den Tastenabgriff näher an die Hardware setzen
An der Sitzung angesetzt fing er synthetische Ereignisse zuverlässig ab — die
echten Lautstärketasten aber nicht: nachgemessen läuft der Abgriff, und ein
selbst erzeugter Tastendruck wird erkannt und verbraucht, während der Druck auf
die Taste weiterhin Apples Einblendung auslöste. Auf neueren macOS-Fassungen
werden die Tasten offenbar weiter vorn behandelt, bevor die Sitzung sie sieht.

`cghidEventTap` sitzt davor. Ob das reicht, zeigt erst die echte Taste — synthetisch
ist beides nicht zu unterscheiden.

Die Protokollzeilen bleiben vorerst drin: ohne sie ist „es geht immer noch nicht"
nicht von „der Abgriff läuft gar nicht" zu trennen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 21:05:25 +02:00
ScarriffleandClaude Opus 5 91b83928ca Der Einblendung zuvorkommen, statt sie abschalten zu wollen
Der Schluss im letzten Commit war falsch. Aus „der Dienst liegt in der
Benutzer-Domäne" hatte ich geschlossen, dass `launchctl bootout` ihn dort
aushängt. Nachgemessen, diesmal ausführend:

    Boot-out failed: 150: Operation not permitted while
    System Integrity Protection is engaged

SIP schützt den Dienst, nicht nur den Dateipfad; die Domäne spielt keine Rolle.
Ein rein lesender Spike konnte das nicht zeigen — nur der Versuch. `SystemOSD`
fliegt deshalb wieder raus: Code, der behauptet zu wirken und es nicht tut, ist
schlimmer als keiner.

Stattdessen ein `CGEventTap` auf die Lautstärketasten. Onyx regelt selbst und
verbraucht das Ereignis — was das System nie erreicht, blendet es auch nicht
ein. Preis ist die Berechtigung „Bedienungshilfen"; ohne sie bleibt alles wie
vorher, und der Dialog wird angefragt statt stumm zu scheitern.

Vorteil gegenüber dem Aushängen: es verschwindet nur die Einblendung für die
Tasten, die Onyx auch bedient. Die Feststelltaste behält ihre — beim Aushängen
wäre sie mit verschwunden, und dafür hat Onyx keinen Ersatz.

Der Rückruf steht als eigene Funktion und nicht als Verschluss in `start()`: als
Verschluss brachte er Swift 6.3.3 im Durchgang `SendNonSendable` zum Absturz.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:02:35 +02:00
ScarriffleandClaude Opus 5 a3399d3a96 Zwei Anzeigen für einen Handgriff sind eine zu viel
Apples eigene Einblendung lässt sich aushängen, ohne Root und ohne SIP
anzufassen: `com.apple.OSDUIHelper` liegt in der GUI-Domäne des angemeldeten
Benutzers, nicht im System. `launchctl bootout` dort hinein genügt.

Standardmäßig aus, denn es verschwindet **jede** Einblendung — auch Helligkeit
und Feststelltaste, und für die Feststelltaste hat Onyx keinen Ersatz. Beim
Beenden wird wieder eingehängt: eine Einstellung darf nicht überleben, was sie
eingestellt hat.

Dazu zwei Fehler im Anlernen behoben. Der Posenname stand als roher Schlüssel da
— die Zeichenketten liegen im Bündel von `FaceProvider`, gesucht wurden sie im
Hauptbündel. Und „Gesicht vergleichen" blieb nach dem Anlernen grau, weil
`isEnrolled` bei jedem Aufruf den Schlüsselbund las: richtig, aber für SwiftUI
unsichtbar, weil es nichts gab, woran eine Ansicht die Änderung hätte bemerken
können.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 22:26:41 +02:00
ScarriffleandClaude Opus 5 fd29df51cf Im Band ist Platz für mehr als einen Wert
Die Begründung für „ein Wert je Seite" war falsch gerechnet: der Arm ist bei
fünf Kacheln je 376 Punkte breit, und ein Wert braucht davon knapp sechzig. Die
Zahl im Kopf war die der schmalen Insel, nicht die des ausgefahrenen Arms.

Jetzt bis zu vier Elemente je Seite, in wählbarer Reihenfolge — Uhr, Wetter,
Akku, CPU, Speicher, Temperatur. Kein Ziehen zum Umsortieren: bei höchstens vier
Einträgen ist Herausnehmen und wieder Hinzufügen schneller als eine Ziehgeste,
die man erst finden muss.

„Nichts" ist keine Wahl mehr, sondern die leere Liste — zwei Wege, dasselbe zu
sagen, waren einer zu viel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:32:24 +02:00
ScarriffleandClaude Opus 5 e3b215a1dd Ein eigener Sperrbildschirm — und ohne gespeichertes Passwort
Kameraaufnahme, Anlernen über drei Posen, Fenster je Bildschirm auf
Bildschirmschoner-Ebene, Passwortrückfall.

**Das Anmeldepasswort wird nicht gespeichert.** Ein Spike hat den Weg
freigeräumt: `ODRecord.verifyPassword` ist öffentliche API, die Eingabe geht
direkt hinein, das Ergebnis ist ein `Bool`, danach ist die Zeichenkette wieder
weg. Damit fällt der Grund weg, aus dem vergleichbare Programme sich das
Passwort geben lassen und in die Keychain legen.

Angelernt wird über drei Posen mit je fünf Aufnahmen. Der Merkmalsvektor ist ein
allgemeiner Bildmerkmalsvektor und kein Gesichtsmerkmal — er reagiert auf
Kopfhaltung und Beleuchtung mit. Eine einzige Aufnahme beschreibt genau eine
Haltung bei genau einem Licht.

Die Vektoren liegen in der Keychain, nicht in einer Datei: aus ihnen lässt sich
kein Bild zurückgewinnen, aber wer sie hat, kann prüfen, ob eine bestimmte
Person vor der Kamera stand.

Die Einschränkung steht auf dem Sperrbildschirm selbst und nicht im
Kleingedruckten: Bequemlichkeit, keine Sicherheitsgrenze; der Vergleich läuft
über die Webcam und lässt sich mit einem Foto täuschen. Wer sich darauf
verlässt, tut es dort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:22:32 +02:00
ScarriffleandClaude Opus 5 3da673662f Zwischenablage, Notizen und der Gesichtsvergleich
Drei neue Module nach dem Bauplan von `ShelfProvider`: reine Regeln getrennt vom
Zustand, versionierte Indexdatei, Inhalte in Dateien statt in `UserDefaults` —
was dort steht, liest jedes Programm mit `defaults read` mit.

Die Zwischenablage achtet `ConcealedType`, `TransientType` und
`AutoGeneratedType`. Diese Marken sind eine Verabredung zwischen Programmen und
kein Systemdienst; wer sie missachtet, sammelt fremde Passwörter ein.

Der Gesichtsvergleich stand im Plan als der eine echte Unsicherheitsfaktor. Ein
Spike hat ihn erledigt: `VNGenerateFaceprintRequest` gibt es nicht, aber
`VNGenerateImageFeaturePrintRequest` ist öffentlich, liefert 768 Elemente und
ein Abstandsmaß — Faktor 3,5 Trennschärfe. Kein eigenes CoreML-Modell nötig.

`FaceMatcher` entscheidet über mehrere Posen, mehrere Einzelbilder und eine
Sperre nach Fehlversuchen. Die Schwelle liegt bewusst nicht mittig: ein falsches
Ja ist ein Fremder am entsperrten Rechner, ein falsches Nein nur ein Passwort,
das man ohnehin kennt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:06:06 +02:00
ScarriffleandClaude Opus 5 f289d02c37 Der Akku bekommt ein Gedächtnis
Verlauf über 24 Stunden, sieben Tage und dreißig Tage, aufgezeichnet alle fünf
Minuten. Als Säulen und nicht als Linie: eine Linie verbindet auch dort, wo
nichts gemessen wurde, und ein zugeklappter Rechner hatte keinen Ladestand —
eine Linie darüber behauptet einen.

Dazu Segelmodus und Hitzeschutz als reine Regeln im Helfer. Beide haben zwei
Schwellen, eine zum Anhalten und eine zum Weitermachen. Ohne diesen Abstand
hinge der Akku am Limit und würde bei jedem verlorenen Prozent nachgeladen —
hunderte winzige Ladevorgänge am Tag, und genau die altern ihn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:06:06 +02:00
ScarriffleandClaude Opus 5 edb327cabe Kacheln und Fenster sprechen dieselbe Sprache
Alle Kacheln, Popovers und Fenster auf die Designschicht umgestellt: rund
hundert gesetzte Schriftgrößen und 159 gesetzte Abstände und Radien sind jetzt
Token. Übrig bleibt `spacing: 0` — das ist eine Aussage, keine geratene Zahl.

Die Einstellungen bekommen eine Seitenleiste statt sieben Reitern, deren
deutsche Beschriftungen zusammen 880 Punkte brauchten. Panel-Token stehen nicht
mehr in Fenstern: dort gilt Systemsemantik, damit der Hellmodus nicht schwarz
auf schwarz endet.

Neu ist ein Schaufenster, das alle Bauteile nebeneinander zeigt — ohne das
lässt sich eine Änderung an einem Token nur prüfen, indem man die App bedient,
bis jede Kachel einmal zu sehen war.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:05:51 +02:00
ScarriffleandClaude Opus 5 9b600de9a9 Live Activities unter der Notch, nicht daneben
Neben ihr wären die Arme zwangsläufig gleich breit — die Silhouette zeichnet den
Steg mittig um die Hardware. Ein Symbol links und ein Balken rechts hieße dann
132 Punkte Schwarz für ein Symbol. Darunter hängt die Fläche frei und wird so
breit, wie ihr Inhalt es braucht.

Lautstärke, Strom, Titelwechsel und Helligkeit. Gemeldet wird nur, was sich
**geändert** hat: `PowerWatcher` schweigt beim ersten gelesenen Zustand, sonst
begrüßte Onyx jeden Start mit „am Netz".

Die Helligkeit kostete einen Spike. Von drei Wegen trägt nur `DisplayServices`,
und nur für den eingebauten Bildschirm. `CoreDisplay` — der Weg, den jede
Anleitung empfiehlt — gibt für alle Bildschirme 1.0 zurück, auch für den, der
auf 62 % stand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:05:13 +02:00
ScarriffleandClaude Opus 5 c05ccb8ae1 Die Notch wird zur Insel, wo es keine Hardware gibt
Die Silhouette bestand aus zwei überlappenden Teilpfaden. Gefüllt stimmte die
Vereinigung, aber die Kontur zeichnete jeden Teilpfad vollständig nach — auch
dort, wo er innen lag. Dazu kippte die Form ab einer Breite in einen anderen
Zweig, und die beiden Zweige waren nicht dieselbe Geometrie. Zusammen war das
das Flackern an den Rundungen. Jetzt ein durchgehender Umriss, dessen Zweige
ineinander übergehen statt zu springen.

Neu ist die Wahl zwischen verschmolzen und schwebend. Eine echte Notch **muss**
an der Kante kleben und oben eckig sein, sonst klafft ein Spalt zum Gehäuse;
eine Insel **muss** Abstand halten und rundum gerundet sein, sonst sieht sie aus
wie ein abgeschnittenes Rechteck. Beides zu mischen geht nicht, deshalb eine
Wahl und keine zwei Schalter.

Ihre Höhe rät Onyx nicht mehr, sondern misst die Menüleiste: 24 Punkte minus
Luft oben und unten. Vorher hing ein festes Maß unten heraus.

Dazu „nur der Hauptbildschirm" als dritte Anzeigewahl — gemeint ist der mit der
Menüleiste, nicht `NSScreen.main`, das mit dem aktiven Fenster mitwandert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:05:13 +02:00
ScarriffleandClaude Opus 5 ecb39fbe5f Eine Designschicht statt hundertfach gesetzter Zahlen
Token für Schrift, Symbolgröße, Abstand, Radius und Bewegung, dazu sieben
Bauteile, die vorher in jeder Kachel neu gebaut wurden — Karte, Abschnittstitel,
Hinweis, Pillenknopf, Regler, Ablegefläche, Haarlinie.

Symbole bekommen eine **eigene** Staffel und nicht die der Schrift: ein
9-Punkt-Symbol neben 9-Punkt-Text wirkt kleiner, nicht gleich groß. Beide in
eine Staffel zu zwingen hieße, an jeder Stelle gegen sie zu arbeiten.

Das Flächenrezept — Tint, Bänderung, Kante — stand zweimal da, einmal für das
Panel und einmal für Fenster. Es ist jetzt `OnyxStone`, generisch über die Form,
damit auch die animierende Notch-Silhouette es benutzen kann.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:04:51 +02:00
ScarriffleandClaude Opus 5 49a3ff0548 Panel je Bildschirm messen, nicht am schmalsten
Die Breite kam vom **schmalsten** aktiven Bildschirm — mit der Begründung,
dasselbe Panel erscheine überall. Ein Fernseher widerlegt das: bei 720 Punkten
Breite bleiben drei Spalten, vier Widgets brechen auf zwei Reihen um, und das
Panel wird hoch statt breit. Auf dem Notebook, wo achtmal Platz gewesen wäre.

Jetzt rechnet jeder Bildschirm für sich. Der Koordinator kennt seine
Bildschirme ohnehin und reicht die Kennung schon durch — sie wurde nur nicht
benutzt.

Die zweite Hälfte war, dass es nach dem Abstecken so blieb: die Größe wurde
einmal gerechnet und nie wieder. `NSScreen` meldet sich nicht bei SwiftUI,
also gibt es jetzt einen mitgezählten Bildschirmwechsel, den die Ansicht
mitliest, und beim Wechsel werden die Fenstergrößen neu gesetzt — verzögert um
einen Durchlauf, damit der Koordinator seine Fenster vorher neu aufgebaut hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 19:59:46 +02:00
ScarriffleandClaude Opus 5 286d886e9e Das Mikrofon nur noch auf Knopfdruck
Ein offener Eingangsstrom ist bei Bluetooth-Hörern kein stiller Mitleser.
Gemessen an AirPods Pro:

  vorher    48000 Hz, Gerätelautstärke 0.0
  mit Mikro 24000 Hz, Gerätelautstärke 0.6
  danach    48000 Hz, Gerätelautstärke 0.0

macOS schaltet sie ins Telefonprofil: halbe Abtastrate, und der
Lautstärkeregler des Geräts springt. Die Musik wird hörbar lauter und
schlechter — beim Öffnen des Popovers, und beim Schließen wieder zurück.
Dasselbe passiert, wenn am Telefon eine App das Mikrofon greift.

Das darf nicht passieren, weil jemand ein Lautstärkefenster aufmacht. Der
Mikrofonpegel hängt deshalb an einem Knopf in der Eingabezeile, mit dem
Hinweis im Tooltip, was er auslöst. Beim Schließen des Popovers geht er in
jedem Fall wieder aus.

Der Ausgangspegel bleibt automatisch: der läuft über einen Process Tap und
fasst das Gerät nicht an.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 09:32:19 +02:00
ScarriffleandClaude Opus 5 4dee12e4b9 Das Wetter sah nur einmal je Programmstart nach
Der Abruf hing an `task` der View — und die erscheint bei einem Panel, das
immer existiert, ein einziges Mal. Wer Onyx tagelang laufen lässt, sieht
tagelang dieselbe Temperatur. Gemessen: der Zwischenspeicher trug den Stand
vom 14. August, 15:17, und stand am 17. um 23:13 unverändert da — nachts
35 Grad, weil sie vom Nachmittag drei Tage vorher stammten.

Jetzt sieht das Modell im Takt der Haltbarkeit nach, also alle fünfzehn
Minuten. Teurer wird es dadurch nicht: `load` fragt nur nach, wenn der
gespeicherte Stand abgelaufen ist. Und der Ort wird dabei neu bestimmt — beim
Wechsel der Stadt stand sonst weiter das Wetter der alten da. Genau das war
hier zu sehen: Lemgo, längst verlassen.

Dazu die zweite Hälfte des Fehlers: ein „fertiger" Stand wurde immer als
frisch gezeichnet, egal wie alt er war. Warnzeichen und Zeitstempel gab es nur
bei einem gescheiterten Abruf. Ein Messwert gilt jetzt ab einer Stunde als
alt — großzügiger als die Haltbarkeit, damit ein einzelner verpasster Abruf
noch keine Warnung auslöst.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 23:23:50 +02:00
ScarriffleandClaude Opus 5 ee848b86cc Das Aufnahmesymbol kam vom Mixer, nicht vom Pegel
Gemessen über kAudioProcessPropertyIsRunningInput: mit laufendem Mixer nimmt
Onyx auf, ohne ihn nicht. Ein Process Tap ist für macOS eine Tonaufnahme —
auch wenn damit nur die Lautstärke geregelt wird. Solange der Mixer Chrome und
Firefox abgreift, steht das orange Symbol in der Leiste.

Der Mixer stand seit der Fehlersuche am Wochenende auf „automatisch starten",
deshalb war es dauerhaft. Das Häkchen steht jetzt wieder aus.

Ein Hinweis unter dem Häkchen sagt das jetzt, wo die Entscheidung fällt: wer
das Symbol ohne Erklärung sieht, sucht den Fehler bei sich oder hält die App
für unsauber.

Dazu ein echter Fund an derselben Stelle: der Mikrofonpegel hing an
`onDisappear` der View — und das bleibt bei einem NSPopover aus. Dessen
Controller lebt nach dem Schließen weiter, die View verschwindet nie aus der
Hierarchie. Die Messung wäre also beim ersten Öffnen angegangen und nie wieder
aus. Sie hängt jetzt am Popover selbst: `MenuBarModule` bekommt
`popoverDidOpen`/`popoverDidClose`, gemeldet auch dann, wenn das Popover von
selbst zugeht — beim Wechsel in eine andere App etwa.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 15:20:07 +02:00
ScarriffleandClaude Opus 5 c94c6166f5 Der Farbwechsel kommt jetzt auch an
Farbton und Symbolfarbe standen beim **Erzeugen** der Fenster, also außerhalb
jeder View. Damit wird der Wert genau einmal gelesen und SwiftUI erfährt nie,
dass er sich geändert hat: die Reiter, Knöpfe und Symbole blieben in der
Farbe stehen, die beim Öffnen galt, während der Farbwähler daneben schon die
neue zeigte.

Jetzt stehen beide im Rumpf der jeweiligen View — Panel, Einstellungen,
Einrichtung. Dort registriert SwiftUI den Zugriff und zeichnet neu.

Bei den Popovers blieb es beim Erzeugen: die entstehen bei jedem Öffnen neu,
lesen also ohnehin den aktuellen Wert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:18:06 +02:00
ScarriffleandClaude Opus 5 1432ef3a6f Der Systemfarbknopf zeigt, welche Farbe er nimmt
Er tut, was draufsteht — nur weiß niemand auswendig, welche Farbe das System
gerade führt. Dann wirkt das Ergebnis wie ein Fehler statt wie eine Übernahme.
Jetzt sitzt der Farbfleck daneben.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:00:56 +02:00
ScarriffleandClaude Opus 5 9975694d6a Terminliste: an welchem Tag denn?
Zweimal „Nina arbeiten, 8:00" untereinander sind zwei verschiedene Tage, und
nichts verriet welche. Die Uhrzeit beantwortet nur die halbe Frage.

Jetzt steht über jedem Tag eine Überschrift: „Heute", „Morgen", danach
Wochentag und Datum. Die Wörter für die nächsten beiden Tage, weil sie kürzer
sind als jedes Datum und sofort beantworten, was man wissen will. Der
Wochentag allein wäre in drei Wochen mehrdeutig, deshalb steht das Datum
dabei.

Ganztägige Termine stehen jetzt über den Uhrzeiten desselben Tages. Sie
beginnen um Mitternacht und standen deshalb zwischen den frühen Terminen —
dabei gelten sie für den ganzen Tag.

Die Entscheidung „heute/morgen/später" liegt getrennt von ihrer Beschriftung,
damit sie ohne Sprachkatalog prüfbar bleibt: im Testlauf steht sonst der rohe
Schlüssel statt des Wortes, und der Test prüft die Übersetzung statt der
Logik.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:52:16 +02:00
ScarriffleandClaude Opus 5 0df566d531 Symbole folgen der Akzentfarbe, nicht der des Systems
SwiftUI zeichnet Symbole in Listen und Formularen auf macOS in der
Akzentfarbe des Systems — nicht in der des Fensters. `.tint` an der Wurzel
half deshalb nur der Reiterleiste; die Widget-Liste blieb systemgrün neben
einer blauen Leiste, obwohl beides derselben Einstellung folgen soll.

Ein eigener Label-Stil setzt die Farbe am Symbol. Er vererbt sich über die
Umgebung, also genügt eine Zeile an denselben vier Wurzeln, an denen auch der
Farbton gesetzt wird: Panel, Popovers, Einstellungen, Einrichtung.

Ein Symbol, das seine Farbe selbst mitbringt, behält sie — die innere Angabe
schlägt die äußere. Der grüne Haken für „erteilt" bringt sie jetzt am Symbol
mit statt am ganzen Label: Grün heißt dort „erteilt" und ist keine
Geschmacksfrage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:48:57 +02:00
ScarriffleandClaude Opus 5 fde3e864c0 Zwei-Faktor-Anmeldung bei Calendarr
Deine Zugangsdaten stimmten. Der Fehler lag im gewählten Endpunkt:

  auth_router.py:81   if user.totp_enabled:
                          raise HTTPException(401, detail="2fa_required")

/api/auth/token nimmt nur ein Formular mit Benutzername und Passwort entgegen
und weist Konten mit zweitem Faktor grundsätzlich ab — mit demselben 401 wie
ein falsches Passwort. Deshalb stand da „Benutzername oder Passwort stimmen
nicht", obwohl beides stimmte.

Angemeldet wird jetzt über /api/auth/login: der Weg nimmt den Code entgegen
und kennt remember_me. Das ist hier keine Bequemlichkeit, sondern die
Bedingung — mit zweitem Faktor kann Onyx sich nicht selbst neu anmelden, den
Code hat nur der Mensch. Ein gewöhnliches Token hielte sieben Tage,
remember_me hält hundertachtzig.

Damit wandert auch das Token in den Schlüsselbund. Bei einem Konto mit
zweitem Faktor ist es das einzige, womit Onyx noch hineinkommt; ginge es beim
Beenden verloren, müsste man sich bei jedem Start neu anmelden.

Die drei Fälle sind jetzt auseinandergehalten und einzeln beschriftet:
falsches Passwort, fehlender Code, falscher Code. Der Unterschied ist der
zwischen „Passwort neu tippen" und „App aufmachen" — und er steckt beim
Server nur im Begleittext eines 401.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:44:26 +02:00
ScarriffleandClaude Opus 5 e2b86c0216 Calendarr direkt vom Server
Bisher las Onyx nur den Schnappschuss aus dem gemeinsamen Ordner. Der ist so
aktuell wie der letzte Start der Calendarr-App — wer sie eine Woche nicht
öffnet, sieht wochenalte Termine, und Onyx kann nichts dafür und nichts
dagegen. Der Live-Weg stand im Plan als „bewusst weggelassen, später ohne
Umbau nachrüstbar". Das stimmte: das Quellenprotokoll hat gehalten.

Ein einziger Aufruf genügt. /api/caldav/events fasst CalDAV-Konten, lokale
Kalender, Geburtstage, iCal-Abos und Google bereits zusammen — nachgebaut wird
nichts.

Angemeldet wird mit Adresse, Benutzername und Passwort statt mit einem Token,
und der Grund steht im Backend:

  auth.py:17   ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7   # 7 days

Ein eingetragenes Token wäre nach einer Woche tot. Onyx holt sich seines
deshalb selbst und erneuert es, bevor es abläuft — die Gültigkeit liest es aus
dem Token, statt sieben Tage anzunehmen; die stehen heute so im Server und
morgen vielleicht nicht mehr. Kommt trotzdem ein 401, wird einmal neu
angemeldet und der Abruf wiederholt.

Adresse und Benutzername stehen in den Einstellungen, das Passwort im
Schlüsselbund — `defaults read` ist keine Stelle, an der ein Passwort
mitgelesen werden soll. Geprüft wird beim Verbinden, nicht erst beim nächsten
Laden: ein Tippfehler, den man am Folgetag bemerkt, ist ein vermeidbarer.

Der Schnappschuss bleibt das Netz darunter. Ohne Zugangsdaten ändert sich
nichts, und ohne Netzwerk hält er, was Calendarr zuletzt geschrieben hat.
Nachgesehen wird am Server alle fünf Minuten statt alle zwei: ein Dateizugriff
kostet nichts, eine Anfrage über das Netz schon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:34:57 +02:00
ScarriffleandClaude Opus 5 3c36092712 Knopf zum Nachsehen bei Calendarr
Onyx holt sich nichts, es liest den Schnappschuss und erfährt neue über eine
Darwin-Nachricht. Wer Calendarr gerade erst gestartet hat, will aber nicht auf
den nächsten Blick warten, sondern jetzt nachsehen.

Der Knopf steht in jedem Zustand, nicht nur wenn Termine da sind: gerade wenn
„noch nichts geschrieben" oder „bitte anmelden" dasteht, will man es nach dem
Beheben sofort noch einmal versuchen. Beim Laden zeigt er einen Kreisel, in
fester Breite — sonst zuckt die Zeile, weil der Kreisel schmaler ist als der
Pfeil.

Dabei aufgefallen: die Statustexte für „noch nichts geschrieben", „abgemeldet"
und „zu neues Format" standen ohne Übersetzung im Katalog. Wer in einen dieser
Zustände geriet, las den rohen Schlüssel. Jetzt übersetzt, dazu die zwei
Zustände, die gar nicht behandelt waren — kein Kalenderzugriff und „wird
gelesen".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:23:34 +02:00
ScarriffleandClaude Opus 5 c8f3054036 Calendarr: ein Blick alle zwei Minuten unter die Benachrichtigung
Onyx holt sich nichts, sondern liest den Schnappschuss, den Calendarr in den
gemeinsamen Ordner schreibt — kein Netzzugriff, kein zweiter Zwischenspeicher.
Gemeldet wird das über eine Darwin-Nachricht, und darauf hört Onyx.

Die Nachricht ist aber flüchtig: schreibt Calendarr, während Onyx nicht läuft
oder gerade startet, kommt sie nie an. Der Schnappschuss steht dann so lange
still, bis Calendarr das nächste Mal schreibt — und die Terminliste ist aus
unerfindlichen Gründen von gestern.

Also zusätzlich alle zwei Minuten nachsehen, solange Calendarr überhaupt
gefragt wird. Das kostet das Einlesen einer Datei von vierzehn Kilobyte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:15:58 +02:00
ScarriffleandClaude Opus 5 102c870835 Helfer nach dem Umzug: die App repariert ihre Anmeldung selbst
macOS bindet einen privilegierten Dienst beim Anmelden an das Programm, das
ihn angemeldet hat. Zieht das Programm um — aus „Downloads" nach „Programme",
oder vom Bauverzeichnis in ein installiertes Bundle —, ist das ein anderes
Bundle. Der alte Dienst läuft weiter und redet nicht mit dem neuen Programm.

Von außen ist das die verwirrendste Kombination, die es gibt: der Zustand
meldet „eingerichtet", und trotzdem kommt bei jedem Zugriff „Kommunikation
mit der Hilfs-App nicht möglich". Ohne Reparatur bleibt nur, den Helfer von
Hand zu entfernen und neu einzurichten — und darauf muss man erst kommen.

Scheitert die Verbindung, während der Zustand „eingerichtet" meldet, meldet
die App den Dienst jetzt einmal ab und wieder an. Einmal je Programmlauf:
öfter wäre eine Schleife. Danach steht er auf „wartet auf Freigabe" — das
Verschieben eines Programms mit Root-Dienst darf niemand still hinnehmen,
auch diese App nicht.

Dazu ein Fehler im DMG-Bau, der beim Nachbauen auffiel: war ein gleichnamiges
Volume noch eingehängt, hängte macOS das neue als „Onyx 1" ein, während das
AppleScript weiter „Onyx" einrichtete. Das Abbild kam ohne Fenstergestaltung
heraus, ohne dass irgendetwas fehlschlug. Jetzt wird vorher abgehängt und der
Einhängepunkt aus der Ausgabe gelesen statt geraten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 13:08:45 +02:00
ScarriffleandClaude Opus 5 928ee90b5e Das Installationsfenster sieht aus wie eines
Zwei Symbole auf grauem Grund sagen nicht, was zu tun ist. Jetzt: dunkler
Verlauf in den Onyx-Farben, Titel, ein Satz darunter, eine Haarlinie und ein
Pfeil, der vom Programm zum Programme-Ordner zeigt. Die Fassung steht in der
Fußzeile — damit man später weiß, was man installiert hat.

Der Hintergrund wird gezeichnet, nicht mitgeliefert: ein Bild im Verzeichnis
wäre eine Datei, die niemand mehr anfasst und die bei jeder Farbänderung
veraltet. So stehen die Onyx-Farben genau einmal, im selben Werkzeugkasten wie
der Rest.

Der Aufwand steckt nicht im Bild, sondern in der Reihenfolge: Fenstergröße,
Symbolplätze und Hintergrund leben in der .DS_Store des Abbilds. Die schreibt
nur der Finder, und nur in ein beschreibbares Abbild. Also erst UDRW, dann
über AppleScript einrichten, warten bis der Finder geschrieben hat, dann
zusammenpressen. Schlägt die Automatisierung fehl, bleibt das Fenster schlicht
statt das Skript abzubrechen.

Als Bibliothek, weil beide Wege dasselbe Fenster zeigen sollen — der lokale
wie der notarisierte. Und als TIFF mit beiden Auflösungen: als reines PNG wäre
der Hintergrund auf einem Retina-Bildschirm doppelt so groß wie das Fenster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:57:51 +02:00
ScarriffleandClaude Opus 5 d90352a86b DMG für den eigenen Rechner
Der App Store ist für Onyx kein Weg: er verlangt die Sandbox, und ohne die
gäbe es keinen privilegierten Helfer, keinen IOKit-Zugriff, keine
Audio-Taps — also weder Lüftersteuerung noch Ladelimit, Hardwaremesswerte
oder Mixer. Das steht so in der Planung, war aber nie als Skript hinterlegt,
das man ohne Zertifikatsantrag laufen lassen kann.

Scripts/local-dmg.sh füllt die Lücke: Release-Build mit dem
Entwicklerzertifikat, dieselben drei Prüfungen wie beim Verteilen —
Signatur, App-Group, Kennung des Helfers — und ein DMG mit Verweis auf den
Programme-Ordner.

Der Unterschied zu release.sh ist nicht die Verpackung, sondern die
Signatur. Auf dem Mac, auf dem das Zertifikat zuhause ist, läuft es; auf
einem fremden meldet Gatekeeper einen unbekannten Entwickler. Für die
Weitergabe bleibt es bei Developer ID und Notarisierung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 12:52:17 +02:00
ScarriffleandClaude Opus 5 c6f0a5a848 Vorschau liest macOS' eigene Hintergrundverwaltung
NSWorkspace.desktopImageURL ist die alte Schnittstelle: sie meldet das letzte
Bild, das als Datei gesetzt wurde — auch dann noch, wenn es längst gelöscht
ist. Genau das war hier der Fall, der Pfad zeigt auf eine Datei, die es nicht
mehr gibt. Seit macOS 14 führt das System die Auswahl in
com.apple.wallpaper/Store/Index.plist, je Bildschirm und je Schreibtisch, und
Anbieter wie Fotos hinterlegen dort ihre Zwischenspeicher.

Die Datei ist doppelt verpackt: die Konfiguration je Bildschirm steckt als
eigene Property-Liste in einem Datenfeld der äußeren. Ohne den zweiten
Durchgang findet man gar nichts.

Gelesen wird nur der Eintrag dieses Bildschirms, nachgeschlagen über die
Display-Kennung. In der Datei stehen auch alte Bildschirme, der
Bildschirmschoner und alles, was einmal gesetzt war — das Neueste von allem zu
nehmen führt zuverlässig zu einem Bild, das mit dem Schreibtisch nichts zu tun
hat. Ausprobiert: dabei kam ein weißes Platzhalterbild aus einem Kurzbefehl
heraus. Fehlt der Eintrag oder die Datei, ist ein Systembild die ehrlichere
Antwort als ein Rateergebnis.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 00:08:44 +02:00
ScarriffleandClaude Opus 5 152d36c904 Vorschau: Notch zurück, und der eigene Hintergrund wird wirklich versucht
Die Notch verschwand, sobald zum ersten Mal ein Bild geladen wurde: ein
skalierbares Bild meldet seine eigene Größe als Wunschgröße, und die tausend
Bildpunkte der Vorlage haben den Stapel auf tausend Punkte aufgeblasen. Notch,
Menüleiste und Auslösefläche saßen dann am Rand dieser unsichtbaren Fläche und
lagen außerhalb des Ausschnitts. Vorher fiel es nicht auf, weil nie ein Bild
ankam. Das Bild wird jetzt auf Maß gebracht, bevor es in den Stapel geht.

Und der eigentliche Wunsch: dein Hintergrundbild statt eines Ersatzes. Es
liegt auf dem Schreibtisch, und dorthin fragt macOS beim ersten Lesen einmal
nach. Die Prüfung mit isReadableFile, die ich vorher davorgesetzt hatte,
beantwortet macOS in diesen Ordnern mit „nein", ohne zu fragen — die Vorschau
blieb damit dauerhaft beim Ersatzbild, obwohl eine Freigabe möglich gewesen
wäre. Jetzt wird gelesen statt geprüft, mit den drei nötigen Erklärungen in
der Info.plist. Wird die Freigabe verweigert, bleibt es beim Systembild.

Ein Tahoe-Hintergrund als Ersatz ist nicht drin: unter den Systembildern liegt
davon nur „Tahoe Day.mov", ein Video von 170 Megabyte. Deshalb weiter Sonoma.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:54:11 +02:00
ScarriffleandClaude Opus 5 a2be84f4d5 Absturz beim Laden des Hintergrundbilds — und derselbe Fehler in der Ablage
Was in einer View steht, ist an den Hauptthread gebunden: die Konformität zu
`View` vererbt das, auch an statische Methoden, die damit nichts zu tun haben.
Das Laden lief absichtlich nebenher — und lief damit in die Isolationsprüfung,
die das Programm anhält. Der Compiler hat genau das gemeldet:

  warning: call to main actor-isolated static method 'systemWallpaper()' in a
  synchronous nonisolated context
  note: main actor isolation inferred from conformance to protocol 'View'

Eine Warnung, keine Fehlermeldung. Ich hatte den Bau nur nach `error:`
durchsucht und sie deshalb nicht gesehen.

Das Laden liegt jetzt in einem eigenen Typ neben der View, wo es die Bindung
nicht gibt. Dieselbe Suche über den ganzen Bau hat einen zweiten Fall
gefunden: `FileDropView.stage` wird aus dem Abschlussblock von
`loadFileRepresentation` gerufen, also von irgendeinem Thread, gehört aber zu
einem `ViewModifier`. Das hätte jeden Dateiabwurf auf die Ablage angehalten.

Dazu die Verbesserung, die beim letzten Mal wegen eines falschen
Arbeitsverzeichnisses nicht angekommen ist: gesucht wird zuerst unter den
Originalen und erst danach in `.thumbnails`. Die kleinen Bilder messen 214
Punkte in der Breite und sind auf einem Retina-Schirm sichtbar unscharf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:43:22 +02:00
ScarriffleandClaude Opus 5 ca9e272ad8 Einstellungen: die Reiter schalten wieder, und die Leiste passt ins Fenster
Zwei Folgen der eigenen Reiterleiste, beide meine Schuld.

Der Reiter oben sprang um, der Inhalt blieb stehen: der gewählte Reiter liegt
im Fenster-Controller — er muss dort liegen, damit ein erneutes Öffnen ihn
setzen kann, etwa auf „Berechtigungen", wenn eine fehlt. Der Controller war
aber nicht beobachtbar. `TabView` fiel das nie auf, weil es seinen Zustand
selbst führte und die Bindung nur benachrichtigte. Ein `switch` im Rumpf
braucht dagegen eine Änderungsmeldung, und die kam nie. Jetzt ist der
Controller `@Observable`, und beide Richtungen funktionieren.

Zu breit war sie wegen `fixedSize`: sieben deutsche Beschriftungen brauchen
rund 880 Punkte, und die nahm sich die Leiste auch dann, wenn das Fenster 780
breit war — sie ragte links und rechts über die Kante hinaus, der erste und
der letzte Reiter waren nicht mehr erreichbar. Ohne feste Breite bleibt sie
im Fenster, und das Fenster ist jetzt 920 breit statt 780, mit 900 als
Mindestmaß. Darunter würde gestaucht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:38:40 +02:00
ScarriffleandClaude Opus 5 2c3b3cdd55 Einstellungen: die Linie unter den Reitern ist weg, und eine Fläche für alles
Die Linie kam von `TabView` selbst: es zieht unter seiner Leiste einen Trenner
quer durchs Fenster, und der lässt sich nicht abschalten. Der Abstand nach
oben, den ich beim letzten Mal eingebaut habe, hat deshalb nichts geändert —
der Trenner ist mitgewandert. Jetzt ist die Reiterleiste selbst gebaut, ein
Auswahlfeld über dem Inhalt, dann gibt es die Linie gar nicht erst.

Der zweite Punkt hat dieselbe Wurzel: die Formulare bringen ihren eigenen
Untergrund mit, der Rest des Fensters hat den des Systems. In Dunkelgrau
liegen die beiden ein, zwei Prozent auseinander — sichtbar als Kante quer
durchs Fenster, genau dort, wo das Formular anfängt. Der Untergrund der
Formulare ist jetzt abgeschaltet, und das ganze Fenster hat einen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:34:47 +02:00
ScarriffleandClaude Opus 5 6c92e7fed6 Auslösefläche: die Vorschau zeigt wieder einen Bildschirm
Dein Hintergrundbild liegt in ~/Desktop, und dorthin lässt macOS keine App
ohne Freigabe. NSWorkspace gibt den Pfad trotzdem heraus, NSImage gibt danach
still nil zurück — und die Vorschau blieb schwarz, ohne jeden Hinweis worauf.
Gemessen: isReadableFile meldet false, das Laden schlägt fehl.

Um eine Ordnerfreigabe zu bitten, nur um ein Vorschaubildchen zu malen, wäre
die falsche Frage. Also wird jetzt vorher geprüft, ob sich die Datei lesen
lässt, und wenn nicht, kommt ein Standardhintergrund von macOS —
/System/Library/Desktop Pictures, von neu nach alt durchgesehen, hier landet
es bei Sonoma. Die Bilder liegen auf jedem Mac und werden nur gelesen.

Geladen über CGImageSource statt NSImage: die Vorlagen sind 6016 Punkte im
Quadrat und zwanzig Megabyte schwer, gebraucht werden tausend Punkte. Und ein
Hintergrundbild kann mehrere Bilder enthalten, hell und dunkel im selben HEIC.

Das Dekodieren dauert gut hundert Millisekunden, deshalb läuft es neben dem
Zeichnen statt davor — sonst hätte das Fenster beim Öffnen so lange gestanden.
Danach liegt das Bild im Cache.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:30:43 +02:00
ScarriffleandClaude Opus 5 2aa60eedf9 Farben einstellbar — und erst mal überhaupt eine Farbe
Das Grün, das überall auftauchte, war die System-Akzentfarbe: `.tint` und
`.accentColor` werden ohne eigene Angabe dorthin durchgereicht, und das
betraf alle Popovers und das ganze Einstellungsfenster. Die Notch-Widgets
dagegen benutzten Onyx' eigenes Blau. Zwei Farben, ungewollt.

Jetzt eine, und die ist einstellbar. Ein neuer Reiter „Farben": Akzentfarbe
mit sechs Vorschlägen und einem Knopf, der die Systemfarbe übernimmt, dazu
das Paar für Herunter- und Hochladen samt Tauschknopf.

Die Signalfarben bleiben fest und stehen nur zur Ansicht dort. Gelb heißt
Warnung, Rot heißt kritisch, Grün heißt in Ordnung — bei Temperatur, Akku,
Lüfter und Fehlern. Wer sie umstellen kann, kann ihre Bedeutung zerstören;
ein rotes „alles gut" liest niemand richtig. Sichtbar sind sie trotzdem,
sonst sucht man die Warnfarbe im Reiter und findet sie nirgends.

Umgesetzt über die Tokens statt über vierzig Fundstellen: `Onyx.Color.accent`
ist keine Konstante mehr, sondern liest bei jedem Zugriff aus `OnyxTheme`.
Damit merkt SwiftUI die Abhängigkeit und zeichnet neu, sobald die Farbe sich
ändert. Dazu ein `.tint` an den vier Wurzeln — Panel, Popovers, Einstellungen,
Einrichtung —, damit auch Haken, Regler und Auswahlfelder mitziehen.

Nebenbei die Beschriftung: „Je Programm" heißt jetzt „Programme". Großgesetzt
las sich das als englisches „J-E".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 23:24:40 +02:00
ScarriffleandClaude Opus 5 7a3d2c1c39 Pegel auch für Ausgabe und Mikrofon
Die Programmzeilen hatten ihren mitlaufenden Ausschlag, die beiden Systemregler
nicht. Jetzt beide, in derselben Form: der Pegel läuft im Regler mit, nicht
daneben.

Der Ausgangspegel kommt über einen globalen Tap — denselben Weg wie der Mixer,
nur ohne Stummschalten und ohne Ausgabe. Ausgenommen werden die Programme, die
der Mixer bereits abgreift: ein Tap sieht das Signal vor dem Stummschalten,
sie zählten sonst doppelt — einmal als Original, das niemand hört, und einmal
als das, was Onyx daraus macht. Ändert sich diese Liste, wird der Tap neu
aufgebaut.

Der Mikrofonpegel geht nur mit echter Aufnahme. Es gibt keine Eigenschaft am
Gerät, die den Ausschlag verrät — einen Pegel bekommt nur, wer das Signal
liest, und genau das meldet macOS mit dem orangen Punkt. Das ist richtig so,
also läuft die Messung nur, solange die Ansicht offen ist, und hört beim
Schließen sofort auf. Ein Zähler statt eines Schalters, weil Panel und Popover
gleichzeitig offen sein können.

Geräte, die keine Fließkommawerte liefern, bleiben ohne Pegel: als Fließkomma
gelesene Ganzzahlen ergeben keinen falschen Ausschlag, sondern Unsinn.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 18:17:40 +02:00
ScarriffleandClaude Opus 5 9df2ddb80b Akku-Popover: „Lädt" sitzt auf der Höhe des Rings
Ein HStack zentriert über die volle Höhe seiner Inhalte. Die Beschriftung unter
dem Ring gehört dazu, also rutschte alles daneben um deren halbe Höhe nach
unten. Bei drei Zeilen fällt das kaum auf, bei einer einzelnen schon: „Lädt"
stand sichtbar tiefer als die Mitte, auf die man schaut.

Eine eigene Ausrichtung löst das an der Stelle, an der es entsteht: der Ring
meldet seine Mitte, die Spalte daneben richtet sich danach — unabhängig davon,
wie viele Zeilen sie hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 18:08:25 +02:00
ScarriffleandClaude Opus 5 e732394c6c Akku-Popover: der Ladezustand steht neben dem Ring, nicht darunter
„Am Netzteil" stand rechts vom Ring, „Am Netz, lädt nicht" darunter — zweimal
dieselbe Auskunft an zwei Stellen. Und die zweite stand allein unter dem
Diagramm, wo sie aussah, als gehörte sie zu dessen Beschriftung.

Jetzt steht beides neben dem Ring: die Schätzung und darunter der Grund dafür.
Am Netz entfällt die Ersatzzeile „Am Netzteil" ganz — die Zeile darunter sagt
ohnehin, was los ist. Im Akkubetrieb ohne Schätzung bleibt „wird berechnet".

Dazu: der Rücksetzknopf im Mixer schaltet den Overdrive mit ab. Er ist keine
eigene Einstellung, sondern die Freigabe für einen Wert, den es danach nicht
mehr gibt — bliebe er stehen, zeigte die Zeile 100 % und der Regler stünde
trotzdem im Boost-Bereich. Der Knopf erscheint deshalb auch, wenn nur der
Overdrive an ist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 18:05:20 +02:00
ScarriffleandClaude Opus 5 ed8b095377 Overdrive je Programm: bis 400 %, aber nur auf Ansage
Der Regler ging bisher immer bis 200 %, ohne dass man das wollen musste. Jetzt
endet er bei 100 % — der Stellung, in der ein Programm klingt, wie es gemeint
war —, und ein Blitz in der Zeile gibt den Bereich darüber frei. Je Programm:
ein Browser braucht ihn für eine leise Konferenz, die Musik daneben nicht.

Oberhalb von 100 % gilt die Kennlinie nicht mehr. Dort steht die Zahl für den
Faktor selbst: 400 % sind das Vierfache der Amplitude. Mit der Kennlinie wären
daraus 4^1,66 ≈ 10,4 geworden — eine Beschriftung, die etwas anderes verspricht,
als der Regler tut. Bei genau 100 % stimmen beide Hälften überein, die Kurve hat
also keinen Sprung, nur einen Knick. Unterhalb bleibt alles wie es war.

Beim Abschalten wird die Lautstärke zurückgeholt. Sonst bliebe ein Programm
dreifach verstärkt, während sein Regler am rechten Anschlag steht und 100 %
anzeigt. Dasselbe beim Laden alter Einstellungen: ein gespeichertes 180 % aus
der Zeit ohne Freigabe fällt auf 100 %.

Die 100 %-Markierung im Regler erscheint nur noch mit Overdrive — ohne ihn läge
sie am rechten Anschlag und sagte dort nichts mehr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 18:02:43 +02:00
ScarriffleandClaude Opus 5 d001e439a6 Mixer war still: er hat sich selbst abgegriffen
Der Mixer gibt den Ton der bearbeiteten Programme selbst aus — und steht damit
als Tonquelle in seiner eigenen Liste. Eine Zeile wird geregelt, sobald man ihren
Regler anfasst. Ein Klick auf Onyx' eigene Zeile genügte also.

Der Selbst-Tap läuft wie jeder andere mit mutedWhenTapped und schaltet damit
Onyx' gesamte Ausgabe stumm — die Wiedergabe *aller* bearbeiteten Programme.
Völlige Stille, während die Pegelanzeigen weiter ausschlagen, weil die Taps ja
Signal bekommen. Und der Zustand stand in den Einstellungen, überlebte also
jeden Neustart.

Gemessen (Spikes/mixer-path-probe.swift): Onyx' Ausgabepegel klettert von 0,388
auf 2,833, sobald er sich selbst bearbeitet. Der Weg an sich ist in Ordnung —
Tap, Aggregate-Gerät und IOProc liefern einzeln wie zu dritt volle Pegel, das
war der Hauptverdacht und ist widerlegt.

Onyx erscheint jetzt nicht mehr in der eigenen Liste, lässt sich nicht auswählen,
und ein gespeicherter Eintrag wird beim Laden entfernt und zurückgeschrieben.

Dazu ein zweiter Fund aus derselben Ecke: setVolume und setMuted griffen auf
taps[bundleID] zu. Die Taps liegen aber unter Kennung#Prozessnummer, weil ein
Programm mehrere Tonquellen haben kann — der Zugriff ging immer ins Leere. Der
Regler wirkte erst, wenn die Auffrischung drei Sekunden später nachzog.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 17:57:15 +02:00
ScarriffleandClaude Opus 5 ed5bd01482 Wetterkachel füllt die Kachel und ist lesbar
Alles stand im oberen Drittel, die untere Hälfte war leer — und die
Stundenwerte in acht Punkten, die man auf Armlänge nicht entziffert.

Der Aufbau folgt jetzt der Reihenfolge, in der man liest: erst was jetzt ist
— großes Symbol, große Temperatur, Höchst- und Tiefstwert daneben, darunter
der Ort und die gefühlte Temperatur —, dann getrennt durch eine Linie, was
noch kommt.

Vier Stunden statt fünf: bei 180 Punkten Breite bleiben so gut vierzig je
Spalte, und die Zahlen lassen sich in einer Größe setzen, die man auch liest.
Die fünfte Stunde war der Grund, warum alles zusammengedrückt war.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:07:26 +02:00
ScarriffleandClaude Opus 5 504c518cfb Akku-Popover sagt, was gerade geschieht
„Am Netz" beantwortet die Frage nicht, die man hat, wenn der Ladestand
stehen bleibt: lädt es gerade nicht, weil das Limit erreicht ist, oder
stimmt etwas nicht? Jetzt steht es dort — lädt, angehalten wegen Ladelimit
mit der Prozentzahl, am Netz ohne zu laden, oder Akkubetrieb.

Der Vergleich mit dem Limit hat zwei Punkte Toleranz: der Ladestand pendelt
um die Grenze, und auf das Prozent genau zu vergleichen hieße, den Grund die
halbe Zeit zu verschweigen. Weit unter dem Limit wird dagegen kein
Limit-Halt behauptet — dann steht das Laden aus einem anderen Grund, und
einen falschen zu nennen wäre schlimmer als keinen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:31:19 +02:00
ScarriffleandClaude Opus 5 312514e6ce Eigene Aggregate-Geräte gehören nicht in die Geräteauswahl
Für jedes geregelte Programm legt Onyx ein Aggregate-Gerät an. Sie sind als
privat markiert — das verbirgt sie aber nur vor **fremden** Prozessen, nicht
vor Onyx selbst. In der Ausgabeauswahl standen sie deshalb mitten zwischen
den echten Geräten, und wer eines davon wählt, hängt seinen Ton in eine
Schleife: die Ausgabe eines Taps wird zur Quelle desselben Taps.

Erkannt werden sie über einen Vorsatz in der Gerätekennung, nicht über den
Namen. Der Name enthält den Programmnamen und ließe sich von einem echten
Gerät nicht sicher unterscheiden — die Kennung vergibt Onyx selbst.

Der Vorsatz nennt Onyx beim Namen: taucht so ein Gerät doch einmal irgendwo
auf, im Audio-MIDI-Setup etwa, sieht man, wer es angelegt hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:24:12 +02:00
ScarriffleandClaude Opus 5 c990ea558b Blitz blendet zwischen zwei fertigen Fassungen über
Deine Idee war die bessere. Eine helle Fläche mit dunklem Rand ließ sich aus
einem einzelnen Symbol nur nachbauen, indem dasselbe Zeichen zweimal in
verschiedener Größe übereinander gezeichnet wurde — und die
Größenverhältnisse sahen nie stimmig aus, wie man an den drei Anläufen
sieht. Zwei fertige Fassungen ineinander zu blenden ist der geradere Weg:
gezeichnet wird genau das, was in der Datei steht.

Erst die dunkle voll deckend, dann die helle mit steigender Deckkraft
darüber. Zeichnete man beide anteilig, schiene dazwischen der Hintergrund
durch und der Blitz wirkte blass.

Ohne eigenen Rand bekommt der Blitz den vollen Innenraum des Akkus statt
Innenraum minus Rand — er ist damit wieder etwas größer, bleibt aber
innerhalb des Rahmens. Der Test hält die neue Grenze fest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:20:06 +02:00
ScarriffleandClaude Opus 5 80b083259e Blitz misst sich am Innenraum, nicht an der Außenhöhe
Bezugsgröße war die volle Höhe des Akkus. Der Rahmen nimmt aber oben und
unten je einen Punkt, die Füllung sitzt zwei Punkte eingerückt, und um das
Zeichen kommt noch sein eigener Rand — so füllte der Blitz mitsamt Rand elf
von zwölf Punkten und stieß an den Rahmen.

Jetzt ist der Innenraum die Bezugsgröße: sechs Punkte Zeichen plus zwei Rand
in acht Punkten Innenraum. Ein Test hält fest, dass jedes Zeichen samt Rand
hineinpasst — und dass es nicht ins andere Extrem fällt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:13:41 +02:00
ScarriffleandClaude Opus 5 2bf76eb060 Fehlendes return nachgetragen
Der vorige Commit war nicht übersetzbar: mit einer Zeile vor dem `switch`
ist er kein Ausdruck mehr und braucht ein ausdrückliches `return`. Meine
Prüfung lief in dem Moment gegen ein anderes Verzeichnis und hat den Fehler
nicht gemeldet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:10:37 +02:00
ScarriffleandClaude Opus 5 782d2af4fd Blitz kleiner: der Rand braucht auch Platz
Das Zeichen bekam fast die volle Akkuhöhe, und der Rand kommt außen noch
dazu — dadurch stieß es oben und unten an den Rahmen. Die nutzbare Höhe
zieht den Rand jetzt ab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:09:48 +02:00
ScarriffleandClaude Opus 5 e2f2ea3427 Blitz gefüllt
Die Vorlage war reine Kontur (`fill:none`) — als Umriss im Akku war der
Blitz zu zurückhaltend. Jetzt trägt sie dieselbe Farbe auch als Fläche; der
Strich bleibt stehen und macht die Form kräftiger.

Gezeichnet wird weiter zweifach: einmal größer in der Gegenfarbe als Rand,
einmal in der fadenden Farbe darüber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:06:23 +02:00
ScarriffleandClaude Opus 5 2490a90519 Blitz aus der mitgelieferten Vorlage, weiterhin fadend
`bolt.fill` aus SF Symbols ist eine massive Fläche; die neue Datei ist eine
Kontur mit kräftigem Strich und trifft die Form besser.

Gezeichnet wird sie zweifach wie zuvor: einmal etwas größer in der
Gegenfarbe als Rand, einmal in der fadenden Farbe darüber. Damit bleibt der
Blitz in jedem Moment des Fadens lesbar — über der hellen Füllung trägt ihn
der Rand, über dem leeren Teil er sich selbst.

Fehlt die Datei, bleibt das Systemsymbol. Dieselbe Vorlage auch in der
Akku-Kachel, damit nicht zwei verschiedene Blitze in einer App stehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:05:34 +02:00
ScarriffleandClaude Opus 5 49e1a46928 Widget-Optionen statt „Quellen", Reiterleiste von der Trennlinie herunter
Die Leiste saß mittig auf der Trennlinie unter dem Titelbalken.
`titlebarAppearsTransparent` allein reichte nicht — macOS zieht darunter
zusätzlich einen Trenner quer durchs Fenster. Der ist jetzt aus. Man setzt
keinen Titel auf eine Trennlinie.

„Quellen" war die Sicht des Programmierers: für den Nutzer ist die
Datenquelle eine Einstellung **des Kalenderwidgets** unter mehreren. Der
Bereich heißt jetzt „Widget-Optionen" und hat einen Abschnitt je Widget.

Kalender: woher die Daten kommen, Monatsraster oder Terminliste — und
endlich, welche Kalender überhaupt mitzählen. Gespeichert werden die
**abgewählten**, nicht die gewählten: kommt ein neuer Kalender dazu, ist er
damit automatisch dabei, statt dass man ihn suchen müsste, ohne zu wissen,
dass es ihn gibt. Der Filter lässt die Abdeckung unberührt — welche Kalender
man sehen will, sagt nichts darüber aus, für welchen Zeitraum Daten
vorliegen.

Wetter: dem Standort folgen oder einen festen Ort setzen, per Stadt oder
Postleitzahl. WeatherKit will Koordinaten, der Nutzer kennt Ortsnamen — die
Umsetzung passiert einmal beim Einstellen statt bei jedem Abruf. Der Ort
überlebt jetzt auch den Neustart; vorher stand nach jedem Start wieder der
Aufenthaltsort da.

Die Ablage-Einstellungen sind vom Widget-Reiter hierher gewandert, wo die
anderen Widget-Optionen stehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:00:48 +02:00
ScarriffleandClaude Opus 5 ab96086db7 Reiterleiste liegt nicht mehr auf der Trennlinie
Sie saß genau auf der Kante unter dem Titelbalken. `TabView` in einem
gewöhnlichen Fenster bekommt den Abstand nicht von selbst — in Apples
`Settings`-Szene schon, und die benutzt Onyx bewusst nicht, weil sie über
einen privaten Selektor öffnet und bei Apps ohne Menüleiste unzuverlässig
ankommt.

Zwölf Punkte Abstand nach oben, und der Titelbalken geht durchsichtig in den
Inhalt über, statt ihn mit einer Linie abzuschneiden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:53:35 +02:00
ScarriffleandClaude Opus 5 784b210bc2 Vorschau mit Schreibtischhintergrund, Fenster breit genug für die Reiter
Die Vorschau der Auslösefläche war eine schwarze Fläche — als Bildschirm
kaum zu erkennen. Jetzt liegt der echte Schreibtischhintergrund darunter.
Apples eigene Bilder lassen sich nicht mitliefern (Lizenz, und ein
Tahoe-Hintergrund wiegt zweistellige Megabyte); der eigene ist ohnehin der
bessere Anker, weil man genau ihn wiedererkennt. Fehlt er, bleibt ein
Verlauf in Onyx-Farben.

Dabei fiel auf, dass Notch und Auslösefläche oberhalb des Bildschirms
schwebten statt in ihm zu sitzen: die Rundung lag auf dem Hintergrund
allein, nicht auf dem Ganzen. Sie schneidet jetzt Hintergrund, Menüleiste
und Fläche gemeinsam ab.

Und das Einstellungsfenster war zu schmal für sechs Reiter — die Leiste
wurde rechts abgeschnitten, „Berechtigungen" war nicht mehr erreichbar. 780
statt 620 Punkte, Mindestbreite 740. Beim Nachrechnen der reinen Textbreiten
kommt man auf 560 und liegt damit deutlich daneben: macOS legt je Reiter
Innenabstand und Trenner dazu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:51:37 +02:00