From 731a3833455e14dd0194ef112cfa050ed6b5508e Mon Sep 17 00:00:00 2001 From: mhoennig Date: Tue, 25 Aug 2026 15:45:53 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Speichern-Dialog=20zeigt=20auf=20die=20O?= =?UTF-8?q?riginaldatei=20statt=20auf=20einen=20=E2=80=9E(1)"-Nachbarn=20(?= =?UTF-8?q?D74-Nachtrag)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mit bekanntem, aber nicht beschreibbarem Handle bekommt showSaveFilePicker startIn + den exakten Dateinamen; ohne Handle teilen sich Öffnen und Speichern eine Picker-id, damit der Dialog im Plan-Ordner aufgeht statt in Downloads. Der Abbruch-Kreislauf (Dialog schlägt Falsches vor -> Abbruch -> kein Handle gemerkt -> wieder Dialog) ist damit durchbrochen. Co-Authored-By: Claude Fable 5 --- docs/CHANGELOG.md | 1 + docs/DECISIONS.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ frontend/src/app.js | 21 +++++++++++++++++++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 3f802d6..38b37e3 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -19,6 +19,7 @@ reverse. ## 2026-08-25 +- Fix: the save dialog suggested a "name (1)" neighbour in the wrong folder — it now points at the original file, and open/save dialogs remember the plan folder - 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 diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index de31a5d..caec9c9 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -5543,3 +5543,47 @@ 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. + +**Nachtrag — der Speichern-Dialog zeigt jetzt auf die Originaldatei.** +Gemeldet: „beim Ctrl-S erscheint immer ein Dialog und der Dateiname mit (1) +dahinter, ich möchte aber direkt speichern." Der Befund hat zwei Schichten: + +- **„Immer ein Dialog" ist der Abbruch-Kreislauf.** Ein Handle wird erst nach + einem **abgeschlossenen** Dialog gemerkt (D72-Nachtrag) — wer den Dialog + abbricht, steht beim nächsten Strg+S wieder davor. Abgebrochen wird er zu + Recht, wenn er das Falsche vorschlägt, und genau das tat er: +- **Das „(1)" ist Chromiums Ausweich-Vorschlag.** `showSaveFilePicker` öffnet + ohne weitere Angaben im zuletzt benutzten Ordner und macht aus dem + Namensvorschlag einen „name (1)"-Nachbarn, wenn dort schon eine gleichnamige + Datei liegt. Wer **den** bestätigt, speichert an der falschen Stelle — der + Dialog lud also zum Fehler ein und zum Abbruch gleichermaßen. + +Zwei Handgriffe, beide am Picker-Aufruf: + +- **Mit bekanntem, aber nicht beschreibbarem Handle** (der Fall „Berechtigung + verweigert" oder ein gescheiterter Schreibversuch) bekommt der Dialog + `startIn: ` und den **exakten Dateinamen** des Handles: Er öffnet im + Ordner der Originaldatei mit ihrem Namen. Einmal Ersetzen bestätigen, und + das neue Handle ist beschreibbar — jedes weitere Strg+S ist still. +- **Ohne bekanntes Handle** teilen sich Öffnen- und Speichern-Dialog eine + Picker-`id` (`werkbaum-files`): Chromium merkt sich je id den zuletzt + benutzten Ordner, der Speichern-Dialog geht also dort auf, wo zuletzt + geöffnet wurde — statt in Downloads. Kein `id` im startIn-Fall: Ein + gemerkter Ordner überstimmte sonst das startIn. + +Dazu gehört die Einordnung, die kein Code ändern kann: Der **erste** Strg+S +je Datei zeigt ohne beschreibbares Handle rechtens einen Dialog (die API +verlangt es), und nach einem App-Neustart fragt der Browser einmal nach der +Schreibberechtigung — „Bei jedem Besuch zulassen" der installierten App +räumt auch das ab. Direkt heißt: ab dem zweiten Mal. + +**Nachgemessen** (dist, Picker gestubbt): Ohne Handle trägt der Aufruf +`suggestedName: "Example.werkbaum"` und `id: "werkbaum-files"`; mit +verweigertem Handle (`queryPermission → 'denied'`) trägt er den exakten +Handle-Namen und `startIn` = genau dieses Handle, ohne `id`; nach dem +Dialog wird geschrieben. Wegwerf-Dokument über die echte UI angelegt und +gelöscht (übrig: Example, Werkbaum). Ob der Nutzer zusätzlich noch das +**alte, vor dem Deploy geöffnete PWA-Fenster** vor sich hatte (dort gab es +den Strg+S-Handler noch nicht — die Taste ging an den Browser, dessen +„Seite speichern" hängt bei Wiederholung ebenfalls „ (1)" an), ließ sich +von hier nicht feststellen; ein Neustart der App stellt es klar. diff --git a/frontend/src/app.js b/frontend/src/app.js index 1c80b3d..6243d25 100644 --- a/frontend/src/app.js +++ b/frontend/src/app.js @@ -3977,7 +3977,10 @@ async function adoptFile(handle, name, text){ } async function openWithPicker(){ let handle; - try{ [handle] = await window.showOpenFilePicker({types: FILE_TYPES}); } + /* id: Chromium merkt sich je Picker-id den zuletzt benutzten Ordner — + Öffnen und handle-loses Speichern teilen sich eine, damit beide Dialoge + im Plan-Ordner aufgehen statt in Downloads (D74-Nachtrag). */ + try{ [handle] = await window.showOpenFilePicker({types: FILE_TYPES, id: 'werkbaum-files'}); } catch(_){ return; } /* Abbruch des Dialogs */ let text; try{ text = await (await handle.getFile()).text(); }catch(_){ return; } @@ -4008,9 +4011,23 @@ async function saveToKnownFile(d){ }catch(_){ return false; } } async function saveWithPicker(d){ + /* Der Dialog zeigt auf die ORIGINALDATEI, wenn wir eine kennen (Handle + vorhanden, aber nicht beschreibbar — etwa nach verweigerter + Berechtigung): startIn öffnet in ihrem Ordner, suggestedName ist ihr + exakter Name. Ohne das schlägt Chromium im zuletzt benutzten Ordner + einen „name (1)"-Nachbarn vor — wer den abbricht, bekommt den Dialog + bei jedem Strg+S wieder, denn gemerkt wird ein Handle erst nach einem + ABGESCHLOSSENEN Dialog. Wer die Originaldatei wählt und das Ersetzen + bestätigt, hat ein beschreibbares Handle — jedes weitere Strg+S ist + still. Ohne bekanntes Handle hilft die geteilte Picker-id (siehe + openWithPicker). D74-Nachtrag. */ + const known = fileHandles.get(d.id); + const opts = known + ? {suggestedName: known.name, startIn: known, types: FILE_TYPES} + : {suggestedName: saveFileName(d.name), types: FILE_TYPES, id: 'werkbaum-files'}; let handle; try{ - handle = await window.showSaveFilePicker({suggestedName: saveFileName(d.name), types: FILE_TYPES}); + handle = await window.showSaveFilePicker(opts); }catch(_){ return; } /* Abbruch: bewusst kein Download hinterher */ try{ await writeToHandle(handle, d.text); }catch(_){ return; } fileHandles.set(d.id, handle);