fix(menü): Wiederherstellen nur noch über den Neu-laden-Knopf der Titelzeile (D81-Nachtrag 2)

Das Zeilen-Symbol im Dokumenten-Menü konnte ein UNGEÖFFNETES mitgeliefertes
Dokument zurücksetzen — ohne zu sehen, was man verwirft (Nutzer-Einwand).
Umbenennen und Löschen bleiben je Zeile; restoreDoc() wirkt nur noch auf das
geöffnete Dokument und prüft das selbst.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-27 05:33:34 +02:00
co-authored by Claude Fable 5
parent 67d0ef421b
commit ed81a2fe41
5 changed files with 38 additions and 26 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ reverse.
## 2026-08-26 ## 2026-08-26
- The document picker moved into the app header: `Werkbaum name` opens the menu — on the phone it is reachable from both panes, and the name takes over the subtitle line - The document picker moved into the app header: `Werkbaum name` opens the menu — on the phone it is reachable from both panes, and the name takes over the subtitle line
- The document menu groups by kind — included, yours, sources — carries rename, delete and restore on each row, and scrolls instead of being cut off on small screens - The document menu groups by kind — included, yours, sources — carries rename and delete on each row, and scrolls instead of being cut off on small screens
- The editor title bar is labelled `Text editor` again and holds the document's own buttons: save, snapshot, history, reload from the source, and a short `Share` button that puts the plan on a server - The editor title bar is labelled `Text editor` again and holds the document's own buttons: save, snapshot, history, reload from the source, and a short `Share` button that puts the plan on a server
- The address bar follows the document you switch to: `?live=` and `?sourceUrl=` name what is in front of you, so a reload brings back the same plan - The address bar follows the document you switch to: `?live=` and `?sourceUrl=` name what is in front of you, so a reload brings back the same plan
- Switching to a server document in the picker now really opens it live — before it only showed its last state - Switching to a server document in the picker now really opens it live — before it only showed its last state
+12
View File
@@ -7041,3 +7041,15 @@ cross-origin.
Abbruch tut nichts; die Probe gegen das echte Backend liefert Abbruch tut nichts; die Probe gegen das echte Backend liefert
`{name:"editor-backend"}`. `infoUrl()` liegt headless in live.js (39 `{name:"editor-backend"}`. `infoUrl()` liegt headless in live.js (39
Live-Tests, +1). Live-Tests, +1).
**Nachtrag 2 — Wiederherstellen fliegt aus den Menü-Zeilen wieder raus.**
Nutzer-Einwand nach dem ersten Arbeiten damit: Im Menü ließe sich ein
**ungeöffnetes** Dokument zurücksetzen — „was keinen Sinn ergäbe". Der
Einwand trifft: Wiederherstellen verwirft Änderungen, und wer das Dokument
nicht vor sich hat, sieht nicht, was er verwirft. Umbenennen und Löschen
bleiben je Zeile (dort ist der Blick auf den Inhalt nicht nötig — der Name
steht da, und Löschen fragt nach); Wiederherstellen wirkt nur noch über den
**Neu-laden-Knopf der Editor-Titelzeile**, also auf das geöffnete Dokument.
`restoreDoc()` prüft das selbst (`d.id !== activeId` → nichts), statt sich
auf den Aufrufer zu verlassen. Der D81-Haupttext oben beschreibt insoweit
den Zwischenstand von einem Tag.
+4 -3
View File
@@ -532,9 +532,10 @@
#ed.docs.picker #ed.docs.picker
The app header reads "Werkbaum > name": the chip opens the menu, grouped by The app header reads "Werkbaum > name": the chip opens the menu, grouped by
kind (shipped, yours, sources), with rename, delete and restore on each row. kind (shipped, yours, sources), with rename and delete on each row. Restore
It scrolls as a whole instead of being cut off on small screens, and on the lives on the editor title bar's reload button instead, acting only on the
phone it is reachable from both panes. open document. The menu scrolls as a whole instead of being cut off on
small screens, and on the phone it is reachable from both panes.
#ed.docs.url #ed.docs.url
A link can carry the address of a text file, which is then fetched on every A link can carry the address of a text file, which is then fetched on every
+6 -4
View File
@@ -155,10 +155,12 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
Text in den Editor. `saveSrc()` schreibt den Editortext ins aktive Dokument. Text in den Editor. `saveSrc()` schreibt den Editortext ins aktive Dokument.
Der Wähler ist eine **Brotkrume im App-Kopf** (`#docTrigger`/`#docMenu`, Der Wähler ist eine **Brotkrume im App-Kopf** (`#docTrigger`/`#docMenu`,
„Werkbaum Name", D81); das Menü gruppiert nach Dokumentart (`docKind()` in „Werkbaum Name", D81); das Menü gruppiert nach Dokumentart (`docKind()` in
docurl.js, headless getestet) und trägt Umbenennen/Löschen/Wiederherstellen docurl.js, headless getestet) und trägt Umbenennen/Löschen als Symbole
als Symbole **je Zeile**`renameDoc/deleteDoc/restoreDoc` nehmen deshalb **je Zeile**`renameDoc/deleteDoc` nehmen deshalb eine **id**, nicht das
eine **id**, nicht das aktive Dokument, und Verwaltungs-Aktionen lassen das aktive Dokument, und Verwaltungs-Aktionen lassen das Menü offen.
Menü offen. Die Editor-Titelzeile heißt wieder „Text-Editor" und trägt die Wiederherstellen gibt es im Menü bewusst **nicht** (D81-Nachtrag 2): Es
läuft über den Neu-laden-Knopf der Titelzeile und wirkt nur auf das
geöffnete Dokument — `restoreDoc()` prüft das selbst. Die Editor-Titelzeile heißt wieder „Text-Editor" und trägt die
Stand-Knöpfe (`saveBtn`/`snapAddBtn`/`snapBtn`/`reloadBtn`/`shareBtn` in der Stand-Knöpfe (`saveBtn`/`snapAddBtn`/`snapBtn`/`reloadBtn`/`shareBtn` in der
`.standgroup`); ihren Zustand setzt `updateDocButtons()` — aufgerufen aus `.standgroup`); ihren Zustand setzt `updateDocButtons()` — aufgerufen aus
`updateDocName()` UND am `input`-Ereignis, denn die Abweichung vom `updateDocName()` UND am `input`-Ereignis, denn die Abweichung vom
+15 -18
View File
@@ -3571,15 +3571,17 @@ function serverHostOf(id){
if(!roh.startsWith('live:')) return null; if(!roh.startsWith('live:')) return null;
try{ return new URL(roh.slice(5)).host; }catch(_){ return null; } try{ return new URL(roh.slice(5)).host; }catch(_){ return null; }
} }
/* Kleine Zeilen-Icons (Feather-Stil wie überall): Stift, Papierkorb, /* Kleine Zeilen-Icons (Feather-Stil wie überall): Stift, Papierkorb.
Wiederherstellen. Als Konstanten, damit der Renderer lesbar bleibt. */ Als Konstanten, damit der Renderer lesbar bleibt. */
const IC_RENAME = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg>'; const IC_RENAME = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg>';
const IC_DELETE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>'; const IC_DELETE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/></svg>';
const IC_RESTORE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.7 5v5.5h5.5"/><path d="M4 15a8.5 8.5 0 1 0 .5-7.5L1.7 10.5"/></svg>';
/* Eine Menüzeile: Wählen-Knopf + Zeilen-Aktionen als GESCHWISTER ein Knopf /* Eine Menüzeile: Wählen-Knopf + Zeilen-Aktionen als GESCHWISTER ein Knopf
im Knopf wäre ungültiges HTML. Die Aktionen sind immer sichtbar (Touch im Knopf wäre ungültiges HTML. Die Aktionen sind immer sichtbar (Touch
kennt kein Hover): Umbenennen und Löschen überall, Wiederherstellen nur an kennt kein Hover): Umbenennen und Löschen, beides überall. Wiederherstellen
einem mitgelieferten Dokument, das vom Auslieferungsstand abweicht. */ gibt es hier bewusst NICHT (D81-Nachtrag 2): Es wirkt über den
Neu-laden-Knopf der Editor-Titelzeile nur auf das geöffnete Dokument
ein ungeöffnetes zurückzusetzen, ohne zu sehen, was man verwirft, ergäbe
keinen Sinn. */
function docRowHtml(d){ function docRowHtml(d){
if(d.id === renamingId){ if(d.id === renamingId){
/* Inline-Umbenennen direkt im Menü (kein window.prompt das ist in /* Inline-Umbenennen direkt im Menü (kein window.prompt das ist in
@@ -3593,8 +3595,6 @@ function docRowHtml(d){
verriete den Unterschied. */ verriete den Unterschied. */
const host = serverHostOf(d.id); const host = serverHostOf(d.id);
const zusatz = host ? `<span class="docitem-where">${esc(host)}</span>` : ''; const zusatz = host ? `<span class="docitem-where">${esc(host)}</span>` : '';
const shipped = shippedStateOf(d.id);
const abweichend = shipped && (d.text !== shipped.text || d.name !== shipped.name);
const iconBtn = (act, label, svg, extra) => const iconBtn = (act, label, svg, extra) =>
`<button type="button" class="dociconbtn${extra || ''}" data-act="${act}" ` + `<button type="button" class="dociconbtn${extra || ''}" data-act="${act}" ` +
`title="${esc(label)}" aria-label="${esc(label)}">${svg}</button>`; `title="${esc(label)}" aria-label="${esc(label)}">${svg}</button>`;
@@ -3604,7 +3604,6 @@ function docRowHtml(d){
`<span class="doccheck" aria-hidden="true">✓</span>` + `<span class="doccheck" aria-hidden="true">✓</span>` +
`<span class="docitem-name">${esc(d.name)}</span>${zusatz}</button>` + `<span class="docitem-name">${esc(d.name)}</span>${zusatz}</button>` +
`<span class="docacts">` + `<span class="docacts">` +
(abweichend ? iconBtn('restore', t('docRestore'), IC_RESTORE) : '') +
iconBtn('rename', t('docRename'), IC_RENAME) + iconBtn('rename', t('docRename'), IC_RENAME) +
iconBtn('delete', t('docDelete'), IC_DELETE, ' docdelbtn') + iconBtn('delete', t('docDelete'), IC_DELETE, ' docdelbtn') +
`</span></div>`; `</span></div>`;
@@ -3682,22 +3681,21 @@ function updateDocButtons(){
speichern.title = t('docSaveFile') + (h ? '\n' + h.name : ''); speichern.title = t('docSaveFile') + (h ? '\n' + h.name : '');
} }
} }
/* Wiederherstellen gilt jetzt je Zeile des Menüs UND für das aktive /* Original wiederherstellen erreichbar über den Neu-laden-Knopf der
Dokument dem Neu-laden-Knopf der Titelzeile. */ Editor-Titelzeile und damit nur für das GEÖFFNETE mitgelieferte Dokument
(D81-Nachtrag 2): Ein ungeöffnetes zurückzusetzen, ohne zu sehen, was man
verwirft, ergäbe keinen Sinn. */
function restoreDoc(id){ function restoreDoc(id){
const d = docs.find(x => x.id === id); const d = docs.find(x => x.id === id);
const shipped = d && shippedStateOf(d.id); const shipped = d && shippedStateOf(d.id);
if(!shipped) return; if(!shipped || d.id !== activeId) return;
if(!window.confirm(t('docRestoreConfirm', {name: d.name}))) return; if(!window.confirm(t('docRestoreConfirm', {name: d.name}))) return;
d.text = shipped.text; d.text = shipped.text;
d.name = shipped.name; d.name = shipped.name;
if(d.id === activeId){ foldOverrides.clear();
foldOverrides.clear(); loadActiveIntoEditor();
loadActiveIntoEditor();
}
persistDocs(); persistDocs();
if(!docMenu.hidden) renderDocMenu(); if(!docMenu.hidden) renderDocMenu(); /* der Name kann sich zurückgeändert haben */
updateDocButtons();
} }
/* Aus der Quelle neu laden (D81): mitgeliefert Original wiederherstellen; /* Aus der Quelle neu laden (D81): mitgeliefert Original wiederherstellen;
URL-Dokument frisch holen (die URL ist die Quelle der Wahrheit, D23); URL-Dokument frisch holen (die URL ist die Quelle der Wahrheit, D23);
@@ -4964,7 +4962,6 @@ docList.addEventListener('click', e => {
if(!id) return; if(!id) return;
if(akt.dataset.act === 'rename') renameDoc(id); if(akt.dataset.act === 'rename') renameDoc(id);
else if(akt.dataset.act === 'delete') deleteDoc(id); else if(akt.dataset.act === 'delete') deleteDoc(id);
else if(akt.dataset.act === 'restore') restoreDoc(id);
return; return;
} }
const btn = e.target.closest('.docpick'); const btn = e.target.closest('.docpick');