feat: Strg+S speichert das Dokument direkt als Datei (D74)

Die Geste, die jeder zuerst versucht, tat bisher das Falsche (Browser-Dialog
„Seite speichern"). Jetzt ruft sie saveLocalFile(): mit gemerktem Handle
dialogfrei in dieselbe Datei, sichtbar bestätigt durch einen Petrol-Haken am
Dokumentnamen; ohne Handle wie der Menü-Eintrag. Legenden-Zeile hint_save in
neun Sprachen. Nebenbefund behoben: adoptFile() wartet jetzt auf die aus
IndexedDB zurückgeholten Handles — ein Doppelklick unmittelbar nach dem
App-Start legte sonst ein Duplikat an statt dasselbe Dokument zu
aktualisieren.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-25 15:15:50 +02:00
co-authored by Claude Fable 5
parent 45a52c15f9
commit 72ab9746d5
5 changed files with 127 additions and 12 deletions
+2
View File
@@ -19,6 +19,8 @@ reverse.
## 2026-08-25
- Ctrl+S saves the document as a file — with a remembered file it writes straight back in place, and the document name flashes as confirmation
- Fix: a file double-clicked right after the app starts could open as a duplicate document instead of updating its own
- The installed app registers for `.werkbaum` files — a double-click in the file manager opens them straight into the editor
- A service worker starts the editor offline; navigations stay network-first, so the update notice keeps telling the truth
- Werkbaum installs as an app: a web app manifest with standalone display and icons rendered from the brand mark
+67
View File
@@ -5476,3 +5476,70 @@ auf eine `.werkbaum`-Datei und die persistente Schreibberechtigung der
installierten App sind Betriebssystem-Dialoge und bleiben ein Handtest auf
echter Hardware; gemessen ist alles bis an diese Kante (Manifest gültig,
Consumer registriert, `adoptFile()`-Weg seit D72 geprüft).
## D74 — Strg+S speichert direkt: die D72-Zurückstellung ist umgekehrt
D72-Nachtrag hielt fest: „Kein Strg+S — erwogen und zurückgestellt", mit zwei
Einwänden — der Browser-Default (Seite speichern) müsste abgefangen werden,
und ohne Handle öffnete die Geste unvermittelt einen Dialog. Mit der
installierten PWA (D73) kippt die Abwägung, und der Nutzer hat es benannt:
Ein lokal geladenes Dokument soll **direkt** speicherbar sein, ohne Dialog.
Genau dafür ist Strg+S die Geste, die jeder zuerst versucht — und ohne
eigenen Handler tut sie das Schlimmstmögliche: Sie öffnet den
„Seite speichern"-Dialog des Browsers, also einen Dialog, der nicht einmal
das Dokument speichert.
**Die beiden D72-Einwände, neu bewertet:**
- **Der Browser-Default ist kein Preis, sondern der Anlass.** `preventDefault`
ist eine Zeile; was sie unterdrückt, war vorher der einzige Effekt der
Geste — und der falsche.
- **„Ohne Handle unvermittelt ein Dialog" trägt nicht mehr.** Die Geste
**heißt** Speichern; ein Speicher-Dialog auf eine Speichern-Geste ist keine
Überraschung, sondern das Verhalten jedes Editors beim ersten Strg+S.
Danach ist das Handle gemerkt und jede weitere Geste dialogfrei.
**Verhalten = der Menü-Eintrag, nur als Geste.** Strg+S (macOS auch Cmd+S)
ruft dasselbe `saveLocalFile()`: mit gemerktem Handle in dieselbe Datei
(die einmalige Schreibberechtigungs-Nachfrage des Browsers bleibt — der
Tastendruck ist die dafür nötige Nutzergeste; die installierte App kann sie
mit „Bei jedem Besuch zulassen" dauerhaft erteilen), ohne Handle der
Speichern-Dialog (Chromium) bzw. Download (Firefox/Safari). Pad-Dokumente
dürfen wie über das Menü gespeichert werden (D72: der Schreibschutz gilt dem
Textfeld, nicht dem Export). `e.repeat` ist ausgefiltert — eine gehaltene
Taste speichert einmal.
**Stilles Speichern braucht eine sichtbare Antwort.** Der In-Place-Weg zeigt
sonst nichts — die Geste wirkte tot, und niemand wüsste, ob gespeichert ist.
Rückmeldung im Haus-Idiom (`flashBtn`, D54: 1,5 s Petrol samt Haken), am
**Dokumentnamen** in der Editor-Titelzeile — der benennt, was gespeichert
wurde. Dialog und Download sind selbst sichtbar und brauchen keine.
**Die Legenden-Zeile, die D72 zur Bedingung machte, ist da:** `hint_save` in
allen neun Sprachen, als zweite Zeile der Bedienungs-Zeile am Ende der
Legende (D25-Idiom: Auffindbarkeit gehört zur Geste).
**Nebenbefund, mitbehoben: der launchQueue-Empfänger wartete nicht auf die
gemerkten Handles.** `idbLoadHandles()` läuft asynchron beim Start; ein
Doppelklick, der die App erst startet, konnte seinen Consumer **vor** dem
Laden der Handle-Map erreichen — der `isSameEntry`-Abgleich lief dann über
eine leere Map, und dieselbe Datei wurde als Duplikat angelegt statt ihr
Dokument zu aktualisieren (genau die Zusage aus D73, „dieselbe Datei landet
im selben Dokument", wäre im häufigsten PWA-Startweg gebrochen).
`adoptFile()` wartet jetzt auf das `handlesReady`-Promise, bevor es
abgleicht — an der einen Stelle, die die Map braucht, statt in jedem
Aufrufer. Der Race ist zeitabhängig und im Werkzeug nicht deterministisch
auslösbar; der Fix ist eine Ordnungszusage im Code, geprüft per Review und
dadurch, dass der Picker-Weg (der dasselbe `adoptFile()` nimmt) unverändert
funktioniert.
**Nachgemessen** am gebauten Stand (dist auf localhost, Picker gestubbt —
die Werkzeuggrenze aus D72: den nativen Dialog kann die Automatisierung
nicht bedienen, die Logik dahinter schon): Erster Strg+S ruft den
Speichern-Dialog genau einmal (richtiger `suggestedName`) und schreibt;
zweiter Strg+S schreibt **ohne** Dialog in dasselbe Handle (0 weitere
Picker-Aufrufe, 2 Schreibvorgänge). Beide waren **echte** Tastendrücke
(CDP), und kein Browser-Dialog erschien — `preventDefault` greift. Den
Petrol-Haken am Dokumentnamen hat erst ein synthetischer Strg+S nach 400 ms
gezeigt (Klasse `done`, ✓, `#0F766E`): Der Werkzeug-Umlauf ist langsamer als
die 1,5 s des Blitzes — eine Messgrenze, kein Befund. Die Legende zeigt die
neue Zeile in DE und EN.
+7
View File
@@ -88,6 +88,7 @@
- [^] #ed.snaps.manual: Save a state by hand, before a larger change (XS) %% ten minutes is the wrong beat for that moment
- [^] #ed.files: Open and save .werkbaum files (S)
- [^] #ed.files.inplace: Saving writes back into the opened file (S) %% File System Access, Chromium
- [x] #ed.files.ctrls: Ctrl+S saves the document as a file (XS) %% in place with a remembered handle
+ [^] #ed.fresh: Show what is new since your last visit (S)
- [^] #ed.fresh.news: A star in the header, with the last few days (S) %% see D58
+ [?] #ed.percolor: A pastel colour per person (S)
@@ -615,6 +616,12 @@
handle: saving writes back into the same file, and the same file reopens
into the same document. Firefox and Safari keep the plain download.
#ed.files.ctrls
The save gesture everyone tries first. Without it the browser offers to
save the page — the one dialog nobody wants. With a remembered handle it
writes straight back in place and flashes the document name as its answer;
otherwise it behaves like the menu entry.
#ed.fresh
For documents that come from outside, nodes that went to production since
your last visit get a yellow halo. New means live, not "line added" — a line