frontend: Dokument-Wähler — Beispiel englisch, Inline-Umbenennen, gezielter Reset
Behebt drei Punkte am Mehrfach-Dokument-Feature (D22): - Beispiel-Dokument heißt jetzt fest „Example" (englisch, nicht mehr lokalisiert „Beispiel") und trägt die reservierte id `example`. Alt-Zustände (zufällige id, „Beispiel") werden beim Laden adoptiert, sofern der Text noch unverändert ist (=== INITIAL) — echte Inhalte nie. - Umbenennen läuft inline im Menü (Textfeld: Enter bestätigt, Esc bricht ab, Blur bestätigt) statt über window.prompt, das in manchen Browser-Kontexten unterdrückt ist („Umbenennen funktioniert nicht"). - Reset (Debug-Knopf, non-prod) setzt nur noch das Beispiel-Dokument auf INITIAL/„Example" zurück und verwirft Ansichts-/Sprach-/Update-Flags; alle anderen Dokumente bleiben erhalten (vorher wurden pauschal alle werkbaum-*- Schlüssel gelöscht). i18n-Keys docDefaultName/docRenamePrompt entfallen (nicht mehr gebraucht). D22 + frontend/CLAUDE.md nachgezogen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
36d79b82c4
commit
64e4e60b53
+29
-6
@@ -348,6 +348,13 @@ skaliert und passt zum bereits etablierten Overlay-Idiom (Sprache/Download, D17)
|
|||||||
Auf kleinem Bildschirm öffnet dasselbe Menü als absolut positioniertes Overlay
|
Auf kleinem Bildschirm öffnet dasselbe Menü als absolut positioniertes Overlay
|
||||||
unter der Titelzeile.
|
unter der Titelzeile.
|
||||||
|
|
||||||
|
**Umbenennen inline, nicht per `window.prompt`.** Das Umbenennen ersetzt den
|
||||||
|
Namen des aktiven Dokuments im Menü durch ein Textfeld (Enter bestätigt, Esc
|
||||||
|
bricht ab, Fokusverlust bestätigt). Grund: `window.prompt` ist in manchen
|
||||||
|
Browser-Kontexten unterdrückt/deaktiviert — dort „funktionierte Umbenennen
|
||||||
|
nicht". Das Inline-Feld hängt an keiner nativen Dialog-API und ist zudem die
|
||||||
|
rundere UX.
|
||||||
|
|
||||||
**Vereinbarkeit mit D14 (Text ist das führende Format):** Jedes Dokument ist nur
|
**Vereinbarkeit mit D14 (Text ist das führende Format):** Jedes Dokument ist nur
|
||||||
ein Notationstext plus `name` (Metadatum) — **kein** erfundenes Strukturformat.
|
ein Notationstext plus `name` (Metadatum) — **kein** erfundenes Strukturformat.
|
||||||
Damit ist das Modell vorwärtskompatibel zum geplanten Persistenz-/Taiga-Backend
|
Damit ist das Modell vorwärtskompatibel zum geplanten Persistenz-/Taiga-Backend
|
||||||
@@ -355,11 +362,27 @@ Damit ist das Modell vorwärtskompatibel zum geplanten Persistenz-/Taiga-Backend
|
|||||||
1:1 auf „mehrere gespeicherte Notationstexte mit Name/id" ab; der localStorage-
|
1:1 auf „mehrere gespeicherte Notationstexte mit Name/id" ab; der localStorage-
|
||||||
Array ist der client-seitige Platzhalter, bis das Backend existiert.
|
Array ist der client-seitige Platzhalter, bis das Backend existiert.
|
||||||
|
|
||||||
**Migration & Reset:** Fehlt `werkbaum-docs`, wird der bestehende Einzeltext
|
**Beispiel-Dokument mit reservierter id + festem englischem Namen.** Das
|
||||||
(oder `INITIAL`) verlustfrei in **ein** Dokument gepackt (Name in der erkannten
|
Beispiel trägt die reservierte id `example` und heißt fest **„Example"** —
|
||||||
UI-Sprache, `docDefaultName`). Der Reset löscht wie bisher alle `werkbaum-*`-
|
**unabhängig von der UI-Sprache** (nicht lokalisiert), passend dazu, dass der
|
||||||
Schlüssel → beim nächsten Laden greift dieselbe Migration. Wird das letzte
|
Beispieltext selbst nur noch englisch ist (breiteres Publikum). Die reservierte
|
||||||
Dokument gelöscht, entsteht wieder das Beispiel-Dokument (INITIAL). Die
|
id macht den Reset **zielgenau** (siehe unten). Alt-Zustände aus der ersten
|
||||||
GUI-Ansichts-Einstellungen (Modus, Zoom, Aufteilung; `werkbaum-ui`) bleiben
|
Fassung (zufällige id, lokalisierter Name „Beispiel"/…) werden beim Laden
|
||||||
|
**adoptiert**: Ein noch **unverändertes** erstes Dokument (`text === INITIAL`)
|
||||||
|
bekommt nachträglich `id: example` und den Namen „Example"; echte, bereits
|
||||||
|
bearbeitete Inhalte werden nie adoptiert.
|
||||||
|
|
||||||
|
**Migration:** Fehlt `werkbaum-docs`, wird der bestehende Einzeltext (oder
|
||||||
|
`INITIAL`) verlustfrei in **ein** Beispiel-Dokument gepackt. Wird das letzte
|
||||||
|
Dokument gelöscht, entsteht wieder das Beispiel-Dokument (INITIAL).
|
||||||
|
|
||||||
|
**Reset ist auf das Beispiel begrenzt.** Der Reset (Debug-Knopf, nur außerhalb
|
||||||
|
des Prod-Builds) setzt **nur das Beispiel-Dokument** auf `INITIAL`/„Example"
|
||||||
|
zurück und verwirft die Ansichts-/Metadaten-Schlüssel (`werkbaum-ui`,
|
||||||
|
`werkbaum-lang`, Update-Flags) — **alle anderen Dokumente bleiben unangetastet**.
|
||||||
|
Zuvor löschte er pauschal **alle** `werkbaum-*`-Schlüssel und damit auch fremde
|
||||||
|
Dokumente; das war zu grob, sobald man mehrere Dokumente pflegt.
|
||||||
|
|
||||||
|
Die GUI-Ansichts-Einstellungen (Modus, Zoom, Aufteilung; `werkbaum-ui`) bleiben
|
||||||
bewusst **global** über alle Dokumente — pro-Dokument-Ansichtszustand wäre eine
|
bewusst **global** über alle Dokumente — pro-Dokument-Ansichtszustand wäre eine
|
||||||
spätere Erweiterung.
|
spätere Erweiterung.
|
||||||
|
|||||||
+20
-9
@@ -90,15 +90,26 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
|
|||||||
zurückschreibt.
|
zurückschreibt.
|
||||||
- Dokumente (D22): mehrere umschaltbare Notationstexte. `loadDocs()` migriert bei
|
- Dokumente (D22): mehrere umschaltbare Notationstexte. `loadDocs()` migriert bei
|
||||||
fehlendem `werkbaum-docs` den bestehenden `werkbaum-src` (oder `INITIAL`) in
|
fehlendem `werkbaum-docs` den bestehenden `werkbaum-src` (oder `INITIAL`) in
|
||||||
**ein** Dokument; `initDocs()` (Aufruf **nach** `applyLang`, damit der
|
**ein** Dokument; `initDocs()` (Aufruf **nach** `applyLang`) holt den aktiven
|
||||||
Standardname in der erkannten UI-Sprache steht) holt den aktiven Text in den
|
Text in den Editor. `saveSrc()` schreibt den Editortext ins aktive Dokument.
|
||||||
Editor. `saveSrc()` schreibt den Editortext ins aktive Dokument. Der Wähler ist
|
Der Wähler ist ein Dropdown in der Editor-Titelzeile (`#docTrigger`/`#docMenu`,
|
||||||
ein Dropdown in der Editor-Titelzeile (`#docTrigger`/`#docMenu`, ersetzt die
|
ersetzt die frühere feste „Struktur (Text)"-Beschriftung); Wechseln/Neu/
|
||||||
frühere feste „Struktur (Text)"-Beschriftung); Wechseln/Neu/Umbenennen/Löschen
|
Umbenennen/Löschen in `switchDoc/newDoc/renameDoc/deleteDoc`. Jedes Dokument ist
|
||||||
in `switchDoc/newDoc/renameDoc/deleteDoc`. Jedes Dokument ist nur Text + Name
|
nur Text + Name (kein Strukturformat, D14) — vorwärtskompatibel zum Backend
|
||||||
(kein Strukturformat, D14) — vorwärtskompatibel zum Backend (D13). Ansichts-
|
(D13). Ansichts-State (`werkbaum-ui`) bleibt global über alle Dokumente. Ein
|
||||||
State (`werkbaum-ui`) bleibt global über alle Dokumente. Ein leerer Editortext
|
leerer Editortext bleibt leer.
|
||||||
bleibt leer; das letzte gelöschte Dokument wird als `INITIAL` neu gesät.
|
- Beispiel-Dokument (D22): reservierte id `EXAMPLE_ID = 'example'`, fester
|
||||||
|
englischer Name `EXAMPLE_NAME = 'Example'` (nicht lokalisiert). `loadDocs()`
|
||||||
|
adoptiert einen Alt-Zustand (zufällige id, „Beispiel") nur, wenn dessen
|
||||||
|
`text === INITIAL` (nie echte Nutzerinhalte). `resetToDefaults()` setzt **nur**
|
||||||
|
das Beispiel-Dokument (id `example`) auf `INITIAL`/„Example" zurück und verwirft
|
||||||
|
`werkbaum-ui`/`werkbaum-lang`/Update-Flags — **andere Dokumente bleiben stehen**
|
||||||
|
(nicht mehr pauschal alle `werkbaum-*` löschen!). Das letzte gelöschte Dokument
|
||||||
|
wird als Beispiel neu gesät.
|
||||||
|
- Umbenennen ist **inline** (kein `window.prompt` — in manchen Browser-Kontexten
|
||||||
|
unterdrückt): `renameDoc()` setzt `renamingId`, `renderDocMenu()` rendert dann
|
||||||
|
ein `<input class="docrename">` (Enter = `commitRename`, Esc = `cancelRename`,
|
||||||
|
Blur = commit). Doc-Namen sind Nutzerdaten und werden **nicht** übersetzt.
|
||||||
- Kleiner Bildschirm: `body.mobile` (per `matchMedia`, ≤ 640 px) stapelt
|
- Kleiner Bildschirm: `body.mobile` (per `matchMedia`, ≤ 640 px) stapelt
|
||||||
Diagramm/Editor mit **stufenlosem** Splitter (kein Snap/Collapse wie auf
|
Diagramm/Editor mit **stufenlosem** Splitter (kein Snap/Collapse wie auf
|
||||||
Desktop): der Gutter-Drag ruft `setMobileDrow()` (klemmt `--drow` zwischen den
|
Desktop): der Gutter-Drag ruft `setMobileDrow()` (klemmt `--drow` zwischen den
|
||||||
|
|||||||
+89
-44
@@ -574,8 +574,8 @@ const I18N = {
|
|||||||
editorTitle:"Struktur (Text)", diagramTitle:"Diagramm",
|
editorTitle:"Struktur (Text)", diagramTitle:"Diagramm",
|
||||||
docSwitchTooltip:"Dokument wählen oder verwalten", docMenuAria:"Dokumente",
|
docSwitchTooltip:"Dokument wählen oder verwalten", docMenuAria:"Dokumente",
|
||||||
docNew:"Neues Dokument", docRename:"Umbenennen", docDelete:"Löschen",
|
docNew:"Neues Dokument", docRename:"Umbenennen", docDelete:"Löschen",
|
||||||
docDefaultName:"Beispiel", docNewName:"Unbenannt",
|
docNewName:"Unbenannt",
|
||||||
docRenamePrompt:"Dokument umbenennen:", docDeleteConfirm:"Dokument „{name}“ löschen?",
|
docDeleteConfirm:"Dokument „{name}“ löschen?",
|
||||||
copy:"kopieren", copyDone:"kopiert ✓", copyTooltip:"Text in die Zwischenablage kopieren",
|
copy:"kopieren", copyDone:"kopiert ✓", copyTooltip:"Text in die Zwischenablage kopieren",
|
||||||
copyDiagramTooltip:"Diagramm als PNG-Bild in die Zwischenablage kopieren",
|
copyDiagramTooltip:"Diagramm als PNG-Bild in die Zwischenablage kopieren",
|
||||||
downloadDiagramTooltip:"Diagramm als SVG-Datei herunterladen (z. B. für LibreOffice: Einfügen → Bild)",
|
downloadDiagramTooltip:"Diagramm als SVG-Datei herunterladen (z. B. für LibreOffice: Einfügen → Bild)",
|
||||||
@@ -619,8 +619,8 @@ const I18N = {
|
|||||||
editorTitle:"Structure (text)", diagramTitle:"Diagram",
|
editorTitle:"Structure (text)", diagramTitle:"Diagram",
|
||||||
docSwitchTooltip:"Choose or manage document", docMenuAria:"Documents",
|
docSwitchTooltip:"Choose or manage document", docMenuAria:"Documents",
|
||||||
docNew:"New document", docRename:"Rename", docDelete:"Delete",
|
docNew:"New document", docRename:"Rename", docDelete:"Delete",
|
||||||
docDefaultName:"Example", docNewName:"Untitled",
|
docNewName:"Untitled",
|
||||||
docRenamePrompt:"Rename document:", docDeleteConfirm:"Delete document “{name}”?",
|
docDeleteConfirm:"Delete document “{name}”?",
|
||||||
copy:"copy", copyDone:"copied ✓", copyTooltip:"Copy text to clipboard",
|
copy:"copy", copyDone:"copied ✓", copyTooltip:"Copy text to clipboard",
|
||||||
copyDiagramTooltip:"Copy diagram as a PNG image to the clipboard",
|
copyDiagramTooltip:"Copy diagram as a PNG image to the clipboard",
|
||||||
downloadDiagramTooltip:"Download diagram as an SVG file (e.g. for LibreOffice: Insert → Image)",
|
downloadDiagramTooltip:"Download diagram as an SVG file (e.g. for LibreOffice: Insert → Image)",
|
||||||
@@ -664,8 +664,8 @@ const I18N = {
|
|||||||
editorTitle:"Estructura (texto)", diagramTitle:"Diagrama",
|
editorTitle:"Estructura (texto)", diagramTitle:"Diagrama",
|
||||||
docSwitchTooltip:"Elegir o gestionar documento", docMenuAria:"Documentos",
|
docSwitchTooltip:"Elegir o gestionar documento", docMenuAria:"Documentos",
|
||||||
docNew:"Nuevo documento", docRename:"Renombrar", docDelete:"Eliminar",
|
docNew:"Nuevo documento", docRename:"Renombrar", docDelete:"Eliminar",
|
||||||
docDefaultName:"Ejemplo", docNewName:"Sin título",
|
docNewName:"Sin título",
|
||||||
docRenamePrompt:"Renombrar documento:", docDeleteConfirm:"¿Eliminar el documento «{name}»?",
|
docDeleteConfirm:"¿Eliminar el documento «{name}»?",
|
||||||
copy:"copiar", copyDone:"copiado ✓", copyTooltip:"Copiar el texto al portapapeles",
|
copy:"copiar", copyDone:"copiado ✓", copyTooltip:"Copiar el texto al portapapeles",
|
||||||
copyDiagramTooltip:"Copiar el diagrama como imagen PNG al portapapeles",
|
copyDiagramTooltip:"Copiar el diagrama como imagen PNG al portapapeles",
|
||||||
downloadDiagramTooltip:"Descargar el diagrama como archivo SVG (p. ej. para LibreOffice: Insertar → Imagen)",
|
downloadDiagramTooltip:"Descargar el diagrama como archivo SVG (p. ej. para LibreOffice: Insertar → Imagen)",
|
||||||
@@ -709,8 +709,8 @@ const I18N = {
|
|||||||
editorTitle:"Structure (texte)", diagramTitle:"Diagramme",
|
editorTitle:"Structure (texte)", diagramTitle:"Diagramme",
|
||||||
docSwitchTooltip:"Choisir ou gérer le document", docMenuAria:"Documents",
|
docSwitchTooltip:"Choisir ou gérer le document", docMenuAria:"Documents",
|
||||||
docNew:"Nouveau document", docRename:"Renommer", docDelete:"Supprimer",
|
docNew:"Nouveau document", docRename:"Renommer", docDelete:"Supprimer",
|
||||||
docDefaultName:"Exemple", docNewName:"Sans titre",
|
docNewName:"Sans titre",
|
||||||
docRenamePrompt:"Renommer le document :", docDeleteConfirm:"Supprimer le document « {name} » ?",
|
docDeleteConfirm:"Supprimer le document « {name} » ?",
|
||||||
copy:"copier", copyDone:"copié ✓", copyTooltip:"Copier le texte dans le presse-papiers",
|
copy:"copier", copyDone:"copié ✓", copyTooltip:"Copier le texte dans le presse-papiers",
|
||||||
copyDiagramTooltip:"Copier le diagramme comme image PNG dans le presse-papiers",
|
copyDiagramTooltip:"Copier le diagramme comme image PNG dans le presse-papiers",
|
||||||
downloadDiagramTooltip:"Télécharger le diagramme en fichier SVG (p. ex. pour LibreOffice : Insertion → Image)",
|
downloadDiagramTooltip:"Télécharger le diagramme en fichier SVG (p. ex. pour LibreOffice : Insertion → Image)",
|
||||||
@@ -754,8 +754,8 @@ const I18N = {
|
|||||||
editorTitle:"Struktura (tekst)", diagramTitle:"Diagram",
|
editorTitle:"Struktura (tekst)", diagramTitle:"Diagram",
|
||||||
docSwitchTooltip:"Wybierz lub zarządzaj dokumentem", docMenuAria:"Dokumenty",
|
docSwitchTooltip:"Wybierz lub zarządzaj dokumentem", docMenuAria:"Dokumenty",
|
||||||
docNew:"Nowy dokument", docRename:"Zmień nazwę", docDelete:"Usuń",
|
docNew:"Nowy dokument", docRename:"Zmień nazwę", docDelete:"Usuń",
|
||||||
docDefaultName:"Przykład", docNewName:"Bez nazwy",
|
docNewName:"Bez nazwy",
|
||||||
docRenamePrompt:"Zmień nazwę dokumentu:", docDeleteConfirm:"Usunąć dokument „{name}”?",
|
docDeleteConfirm:"Usunąć dokument „{name}”?",
|
||||||
copy:"kopiuj", copyDone:"skopiowano ✓", copyTooltip:"Kopiuj tekst do schowka",
|
copy:"kopiuj", copyDone:"skopiowano ✓", copyTooltip:"Kopiuj tekst do schowka",
|
||||||
copyDiagramTooltip:"Kopiuj diagram jako obraz PNG do schowka",
|
copyDiagramTooltip:"Kopiuj diagram jako obraz PNG do schowka",
|
||||||
downloadDiagramTooltip:"Pobierz diagram jako plik SVG (np. dla LibreOffice: Wstaw → Obraz)",
|
downloadDiagramTooltip:"Pobierz diagram jako plik SVG (np. dla LibreOffice: Wstaw → Obraz)",
|
||||||
@@ -799,8 +799,8 @@ const I18N = {
|
|||||||
editorTitle:"Структура (текст)", diagramTitle:"Диаграмма",
|
editorTitle:"Структура (текст)", diagramTitle:"Диаграмма",
|
||||||
docSwitchTooltip:"Выбрать документ или управлять им", docMenuAria:"Документы",
|
docSwitchTooltip:"Выбрать документ или управлять им", docMenuAria:"Документы",
|
||||||
docNew:"Новый документ", docRename:"Переименовать", docDelete:"Удалить",
|
docNew:"Новый документ", docRename:"Переименовать", docDelete:"Удалить",
|
||||||
docDefaultName:"Пример", docNewName:"Без названия",
|
docNewName:"Без названия",
|
||||||
docRenamePrompt:"Переименовать документ:", docDeleteConfirm:"Удалить документ «{name}»?",
|
docDeleteConfirm:"Удалить документ «{name}»?",
|
||||||
copy:"копировать", copyDone:"скопировано ✓", copyTooltip:"Скопировать текст в буфер обмена",
|
copy:"копировать", copyDone:"скопировано ✓", copyTooltip:"Скопировать текст в буфер обмена",
|
||||||
copyDiagramTooltip:"Скопировать диаграмму как изображение PNG в буфер обмена",
|
copyDiagramTooltip:"Скопировать диаграмму как изображение PNG в буфер обмена",
|
||||||
downloadDiagramTooltip:"Скачать диаграмму как файл SVG (напр. для LibreOffice: Вставка → Изображение)",
|
downloadDiagramTooltip:"Скачать диаграмму как файл SVG (напр. для LibreOffice: Вставка → Изображение)",
|
||||||
@@ -844,8 +844,8 @@ const I18N = {
|
|||||||
editorTitle:"संरचना (टेक्स्ट)", diagramTitle:"आरेख",
|
editorTitle:"संरचना (टेक्स्ट)", diagramTitle:"आरेख",
|
||||||
docSwitchTooltip:"दस्तावेज़ चुनें या प्रबंधित करें", docMenuAria:"दस्तावेज़",
|
docSwitchTooltip:"दस्तावेज़ चुनें या प्रबंधित करें", docMenuAria:"दस्तावेज़",
|
||||||
docNew:"नया दस्तावेज़", docRename:"नाम बदलें", docDelete:"हटाएँ",
|
docNew:"नया दस्तावेज़", docRename:"नाम बदलें", docDelete:"हटाएँ",
|
||||||
docDefaultName:"उदाहरण", docNewName:"बिना शीर्षक",
|
docNewName:"बिना शीर्षक",
|
||||||
docRenamePrompt:"दस्तावेज़ का नाम बदलें:", docDeleteConfirm:"दस्तावेज़ „{name}“ हटाएँ?",
|
docDeleteConfirm:"दस्तावेज़ „{name}“ हटाएँ?",
|
||||||
copy:"कॉपी करें", copyDone:"कॉपी हो गया ✓", copyTooltip:"टेक्स्ट को क्लिपबोर्ड पर कॉपी करें",
|
copy:"कॉपी करें", copyDone:"कॉपी हो गया ✓", copyTooltip:"टेक्स्ट को क्लिपबोर्ड पर कॉपी करें",
|
||||||
copyDiagramTooltip:"आरेख को PNG छवि के रूप में क्लिपबोर्ड पर कॉपी करें",
|
copyDiagramTooltip:"आरेख को PNG छवि के रूप में क्लिपबोर्ड पर कॉपी करें",
|
||||||
downloadDiagramTooltip:"आरेख को SVG फ़ाइल के रूप में डाउनलोड करें (जैसे LibreOffice: सम्मिलित करें → छवि)",
|
downloadDiagramTooltip:"आरेख को SVG फ़ाइल के रूप में डाउनलोड करें (जैसे LibreOffice: सम्मिलित करें → छवि)",
|
||||||
@@ -889,8 +889,8 @@ const I18N = {
|
|||||||
editorTitle:"结构(文本)", diagramTitle:"图表",
|
editorTitle:"结构(文本)", diagramTitle:"图表",
|
||||||
docSwitchTooltip:"选择或管理文档", docMenuAria:"文档",
|
docSwitchTooltip:"选择或管理文档", docMenuAria:"文档",
|
||||||
docNew:"新建文档", docRename:"重命名", docDelete:"删除",
|
docNew:"新建文档", docRename:"重命名", docDelete:"删除",
|
||||||
docDefaultName:"示例", docNewName:"未命名",
|
docNewName:"未命名",
|
||||||
docRenamePrompt:"重命名文档:", docDeleteConfirm:"删除文档“{name}”?",
|
docDeleteConfirm:"删除文档“{name}”?",
|
||||||
copy:"复制", copyDone:"已复制 ✓", copyTooltip:"将文本复制到剪贴板",
|
copy:"复制", copyDone:"已复制 ✓", copyTooltip:"将文本复制到剪贴板",
|
||||||
copyDiagramTooltip:"将图表作为 PNG 图片复制到剪贴板",
|
copyDiagramTooltip:"将图表作为 PNG 图片复制到剪贴板",
|
||||||
downloadDiagramTooltip:"将图表下载为 SVG 文件(例如用于 LibreOffice:插入 → 图像)",
|
downloadDiagramTooltip:"将图表下载为 SVG 文件(例如用于 LibreOffice:插入 → 图像)",
|
||||||
@@ -934,8 +934,8 @@ const I18N = {
|
|||||||
editorTitle:"構造(テキスト)", diagramTitle:"ダイアグラム",
|
editorTitle:"構造(テキスト)", diagramTitle:"ダイアグラム",
|
||||||
docSwitchTooltip:"ドキュメントを選択・管理", docMenuAria:"ドキュメント",
|
docSwitchTooltip:"ドキュメントを選択・管理", docMenuAria:"ドキュメント",
|
||||||
docNew:"新規ドキュメント", docRename:"名前を変更", docDelete:"削除",
|
docNew:"新規ドキュメント", docRename:"名前を変更", docDelete:"削除",
|
||||||
docDefaultName:"例", docNewName:"無題",
|
docNewName:"無題",
|
||||||
docRenamePrompt:"ドキュメントの名前を変更:", docDeleteConfirm:"ドキュメント「{name}」を削除しますか?",
|
docDeleteConfirm:"ドキュメント「{name}」を削除しますか?",
|
||||||
copy:"コピー", copyDone:"コピーしました ✓", copyTooltip:"テキストをクリップボードにコピー",
|
copy:"コピー", copyDone:"コピーしました ✓", copyTooltip:"テキストをクリップボードにコピー",
|
||||||
copyDiagramTooltip:"ダイアグラムを PNG 画像としてクリップボードにコピー",
|
copyDiagramTooltip:"ダイアグラムを PNG 画像としてクリップボードにコピー",
|
||||||
downloadDiagramTooltip:"ダイアグラムを SVG ファイルとしてダウンロード(例:LibreOffice の 挿入 → 画像)",
|
downloadDiagramTooltip:"ダイアグラムを SVG ファイルとしてダウンロード(例:LibreOffice の 挿入 → 画像)",
|
||||||
@@ -1072,6 +1072,11 @@ let hadStoredUI = false; /* gab es beim Laden schon gespeicherte GUI-Einstellun
|
|||||||
nur ein Notationstext + Name (Metadatum) — kein eigenes Strukturformat (D14).
|
nur ein Notationstext + Name (Metadatum) — kein eigenes Strukturformat (D14).
|
||||||
Der aktive Text wird zusätzlich in LS_SRC gespiegelt (Abwärtskompatibilität
|
Der aktive Text wird zusätzlich in LS_SRC gespiegelt (Abwärtskompatibilität
|
||||||
+ Migration bestehender Einzeltexte). */
|
+ Migration bestehender Einzeltexte). */
|
||||||
|
/* Das Beispiel-Dokument trägt eine reservierte id und einen festen englischen
|
||||||
|
Namen: So trifft der Reset genau dieses eine Dokument (D22) und der Name ist
|
||||||
|
unabhängig von der UI-Sprache englisch — wie der Beispieltext selbst (breiteres
|
||||||
|
Publikum). */
|
||||||
|
const EXAMPLE_ID = 'example', EXAMPLE_NAME = 'Example';
|
||||||
let docs = []; /* [{id, name, text}] */
|
let docs = []; /* [{id, name, text}] */
|
||||||
let activeId = null;
|
let activeId = null;
|
||||||
function uid(){ return 'd' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7); }
|
function uid(){ return 'd' + Date.now().toString(36) + Math.random().toString(36).slice(2, 7); }
|
||||||
@@ -1100,11 +1105,20 @@ function loadDocs(){
|
|||||||
!arr.every(d => d && typeof d.id === 'string' && typeof d.text === 'string')){
|
!arr.every(d => d && typeof d.id === 'string' && typeof d.text === 'string')){
|
||||||
let legacy = null;
|
let legacy = null;
|
||||||
try{ legacy = localStorage.getItem(LS_SRC); }catch(_){}
|
try{ legacy = localStorage.getItem(LS_SRC); }catch(_){}
|
||||||
arr = [{ id: uid(), name: t('docDefaultName'), text: (legacy !== null) ? legacy : INITIAL }];
|
arr = [{ id: EXAMPLE_ID, name: EXAMPLE_NAME, text: (legacy !== null) ? legacy : INITIAL }];
|
||||||
}
|
}
|
||||||
docs = arr;
|
docs = arr;
|
||||||
let a = null;
|
let a = null;
|
||||||
try{ a = localStorage.getItem(LS_ACTIVE); }catch(_){}
|
try{ a = localStorage.getItem(LS_ACTIVE); }catch(_){}
|
||||||
|
/* Alt-Zustand (erste Version: zufällige id, lokalisierter Name): ein noch
|
||||||
|
unverändertes Beispiel-Dokument bekommt nachträglich die reservierte id und
|
||||||
|
den englischen Namen, damit Namensfix und Reset auch dort greifen. Nur bei
|
||||||
|
unverändertem Text (=== INITIAL), um echte Nutzerinhalte nie zu adoptieren. */
|
||||||
|
if(!docs.some(d => d.id === EXAMPLE_ID) && docs[0] && docs[0].text === INITIAL){
|
||||||
|
if(a === docs[0].id) a = EXAMPLE_ID;
|
||||||
|
docs[0].id = EXAMPLE_ID;
|
||||||
|
docs[0].name = EXAMPLE_NAME;
|
||||||
|
}
|
||||||
activeId = docs.some(d => d.id === a) ? a : docs[0].id;
|
activeId = docs.some(d => d.id === a) ? a : docs[0].id;
|
||||||
}
|
}
|
||||||
function saveSrc(){
|
function saveSrc(){
|
||||||
@@ -1171,16 +1185,37 @@ function updateDocName(){
|
|||||||
const d = activeDoc();
|
const d = activeDoc();
|
||||||
if(docNameEl) docNameEl.textContent = d ? d.name : '';
|
if(docNameEl) docNameEl.textContent = d ? d.name : '';
|
||||||
}
|
}
|
||||||
|
let renamingId = null; /* id des gerade inline umbenannten Dokuments (oder null) */
|
||||||
function renderDocMenu(){
|
function renderDocMenu(){
|
||||||
docList.innerHTML = docs.map(d =>
|
docList.innerHTML = docs.map(d => {
|
||||||
`<button type="button" class="docitem" role="menuitemradio" data-id="${d.id}" ` +
|
if(d.id === renamingId){
|
||||||
|
/* Inline-Umbenennen direkt im Menü (kein window.prompt — das ist in
|
||||||
|
manchen Browser-Kontexten unterdrückt). Wert/Handler unten in JS. */
|
||||||
|
return `<div class="docitem editing"><span class="doccheck" aria-hidden="true"></span>` +
|
||||||
|
`<input type="text" class="docrename"></div>`;
|
||||||
|
}
|
||||||
|
return `<button type="button" class="docitem" role="menuitemradio" data-id="${d.id}" ` +
|
||||||
`aria-checked="${d.id === activeId ? 'true' : 'false'}">` +
|
`aria-checked="${d.id === activeId ? 'true' : 'false'}">` +
|
||||||
`<span class="doccheck" aria-hidden="true">✓</span>` +
|
`<span class="doccheck" aria-hidden="true">✓</span>` +
|
||||||
`<span class="docitem-name">${esc(d.name)}</span></button>`
|
`<span class="docitem-name">${esc(d.name)}</span></button>`;
|
||||||
).join('');
|
}).join('');
|
||||||
|
if(renamingId){
|
||||||
|
const inp = docList.querySelector('.docrename');
|
||||||
|
const d = docs.find(x => x.id === renamingId);
|
||||||
|
if(inp && d){
|
||||||
|
inp.setAttribute('aria-label', t('docRename'));
|
||||||
|
inp.value = d.name;
|
||||||
|
inp.focus(); inp.select();
|
||||||
|
inp.addEventListener('keydown', e => {
|
||||||
|
if(e.key === 'Enter'){ e.preventDefault(); commitRename(); }
|
||||||
|
else if(e.key === 'Escape'){ e.preventDefault(); cancelRename(); }
|
||||||
|
});
|
||||||
|
inp.addEventListener('blur', commitRename);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function openDocMenu(){ renderDocMenu(); docMenu.hidden = false; docTrigger.setAttribute('aria-expanded', 'true'); }
|
function openDocMenu(){ renderDocMenu(); docMenu.hidden = false; docTrigger.setAttribute('aria-expanded', 'true'); }
|
||||||
function closeDocMenu(){ docMenu.hidden = true; docTrigger.setAttribute('aria-expanded', 'false'); }
|
function closeDocMenu(){ renamingId = null; docMenu.hidden = true; docTrigger.setAttribute('aria-expanded', 'false'); }
|
||||||
function toggleDocMenu(){ docMenu.hidden ? openDocMenu() : closeDocMenu(); }
|
function toggleDocMenu(){ docMenu.hidden ? openDocMenu() : closeDocMenu(); }
|
||||||
/* Beim Wechseln/Anlegen/Löschen zuerst den aktuellen Editortext ins aktive
|
/* Beim Wechseln/Anlegen/Löschen zuerst den aktuellen Editortext ins aktive
|
||||||
Dokument sichern, dann das Ziel laden und neu rendern. */
|
Dokument sichern, dann das Ziel laden und neu rendern. */
|
||||||
@@ -1203,30 +1238,32 @@ function newDoc(){
|
|||||||
src.focus();
|
src.focus();
|
||||||
}
|
}
|
||||||
function renameDoc(){
|
function renameDoc(){
|
||||||
const d = activeDoc();
|
if(!activeDoc()) return;
|
||||||
if(!d) return;
|
renamingId = activeId; /* aktives Dokument inline umbenennen */
|
||||||
const name = window.prompt(t('docRenamePrompt'), d.name);
|
|
||||||
if(name === null) return;
|
|
||||||
const trimmed = name.trim();
|
|
||||||
if(!trimmed) return;
|
|
||||||
d.name = trimmed;
|
|
||||||
persistDocs();
|
|
||||||
updateDocName();
|
|
||||||
renderDocMenu();
|
renderDocMenu();
|
||||||
}
|
}
|
||||||
|
function commitRename(){
|
||||||
|
if(!renamingId) return;
|
||||||
|
const inp = docList.querySelector('.docrename');
|
||||||
|
const d = docs.find(x => x.id === renamingId);
|
||||||
|
const val = inp ? inp.value.trim() : '';
|
||||||
|
renamingId = null;
|
||||||
|
if(d && val){ d.name = val; persistDocs(); updateDocName(); }
|
||||||
|
renderDocMenu();
|
||||||
|
}
|
||||||
|
function cancelRename(){ renamingId = null; renderDocMenu(); }
|
||||||
function deleteDoc(){
|
function deleteDoc(){
|
||||||
const d = activeDoc();
|
const d = activeDoc();
|
||||||
if(!d) return;
|
if(!d) return;
|
||||||
if(!window.confirm(t('docDeleteConfirm', {name: d.name}))) return;
|
if(!window.confirm(t('docDeleteConfirm', {name: d.name}))) return;
|
||||||
docs = docs.filter(x => x.id !== d.id);
|
docs = docs.filter(x => x.id !== d.id);
|
||||||
if(!docs.length) docs = [{ id: uid(), name: t('docDefaultName'), text: INITIAL }];
|
if(!docs.length) docs = [{ id: EXAMPLE_ID, name: EXAMPLE_NAME, text: INITIAL }];
|
||||||
activeId = docs[0].id;
|
activeId = docs[0].id;
|
||||||
loadActiveIntoEditor();
|
loadActiveIntoEditor();
|
||||||
persistDocs();
|
persistDocs();
|
||||||
closeDocMenu();
|
closeDocMenu();
|
||||||
}
|
}
|
||||||
/* Dokumente laden + aktiven Text in den Editor holen. Nach applyLang aufgerufen,
|
/* Dokumente laden + aktiven Text in den Editor holen (nach applyLang). */
|
||||||
damit der migrierte Standardname in der erkannten UI-Sprache steht. */
|
|
||||||
function initDocs(){
|
function initDocs(){
|
||||||
restoring = true;
|
restoring = true;
|
||||||
loadDocs();
|
loadDocs();
|
||||||
@@ -1610,19 +1647,27 @@ function checkAndShowUpdateNotification(){
|
|||||||
/* ---------- Reset-Funktion für Testing ---------- */
|
/* ---------- Reset-Funktion für Testing ---------- */
|
||||||
function resetToDefaults(){
|
function resetToDefaults(){
|
||||||
const confirmed = confirm(
|
const confirmed = confirm(
|
||||||
'Wirklich alles auf Defaults zurücksetzen?\n\n' +
|
'Beispiel-Dokument und Einstellungen zurücksetzen?\n\n' +
|
||||||
'⚠️ Der Editor-Inhalt und alle Einstellungen werden gelöscht.\n' +
|
'Das Beispiel-Dokument wird auf den Ausgangstext zurückgesetzt, die\n' +
|
||||||
'Diese Aktion kann nicht rückgängig gemacht werden.'
|
'Ansichts-Einstellungen (Modus, Zoom, Aufteilung, Sprache) verworfen.\n' +
|
||||||
|
'Alle anderen Dokumente bleiben unangetastet.'
|
||||||
);
|
);
|
||||||
|
|
||||||
if(!confirmed) return;
|
if(!confirmed) return;
|
||||||
|
|
||||||
/* Lösche alle Werkbaum-Einträge aus localStorage */
|
/* Nicht-Dokument-Zustand auf Defaults (UI, Sprache, Update-Flags) — die
|
||||||
const keysToDelete = Array.from({length: localStorage.length}, (_, i) => localStorage.key(i))
|
Dokumentenliste (werkbaum-docs) bleibt erhalten (D22). */
|
||||||
.filter(k => k && k.startsWith('werkbaum-'));
|
['werkbaum-ui','werkbaum-lang','werkbaum-html-hash','werkbaum-update-available','werkbaum-update-log']
|
||||||
|
.forEach(k => { try{ localStorage.removeItem(k); }catch(_){} });
|
||||||
|
|
||||||
keysToDelete.forEach(k => localStorage.removeItem(k));
|
/* Nur das Beispiel-Dokument auf den Ausgangstext zurücksetzen; existiert es
|
||||||
logUpdate('🔄 Alle Einstellungen zurückgesetzt');
|
nicht (mehr), wird es neu angelegt. Andere Dokumente bleiben unberührt. */
|
||||||
|
const ex = docs.find(d => d.id === EXAMPLE_ID);
|
||||||
|
if(ex){ ex.text = INITIAL; ex.name = EXAMPLE_NAME; }
|
||||||
|
else { docs.unshift({ id: EXAMPLE_ID, name: EXAMPLE_NAME, text: INITIAL }); }
|
||||||
|
activeId = EXAMPLE_ID;
|
||||||
|
persistDocs();
|
||||||
|
logUpdate('🔄 Beispiel-Dokument und Einstellungen zurückgesetzt');
|
||||||
|
|
||||||
/* Kurze Verzögerung, damit Logging sichtbar wird, dann reload */
|
/* Kurze Verzögerung, damit Logging sichtbar wird, dann reload */
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
@@ -340,6 +340,13 @@
|
|||||||
.docitem[aria-checked="true"]{color:var(--or);font-weight:600}
|
.docitem[aria-checked="true"]{color:var(--or);font-weight:600}
|
||||||
.docitem .doccheck{flex:0 0 auto;width:14px;text-align:center;opacity:0}
|
.docitem .doccheck{flex:0 0 auto;width:14px;text-align:center;opacity:0}
|
||||||
.docitem[aria-checked="true"] .doccheck{opacity:1}
|
.docitem[aria-checked="true"] .doccheck{opacity:1}
|
||||||
|
.docitem.editing{cursor:default}
|
||||||
|
.docrename{
|
||||||
|
flex:1 1 auto;min-width:0;
|
||||||
|
border:1px solid var(--or);border-radius:6px;
|
||||||
|
padding:5px 7px;color:var(--ink);background:var(--card);
|
||||||
|
font-family:inherit;font-size:.86rem;outline:none;
|
||||||
|
}
|
||||||
.docmenu-actions{
|
.docmenu-actions{
|
||||||
display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;padding-top:6px;
|
display:flex;flex-wrap:wrap;gap:4px;margin-top:6px;padding-top:6px;
|
||||||
border-top:1px solid rgba(36,52,71,.12);
|
border-top:1px solid rgba(36,52,71,.12);
|
||||||
|
|||||||
Reference in New Issue
Block a user