Technische ID als Identität der Notizen; Pfad wird zur Eigenschaft

Bisher war der Dateipfad die Identität einer Notiz: Umbenennen brach
Verweise, und der Pfad tauchte in der Oberfläche auf (Tooltips, Menüpunkte
"Dateiname ändern"/"Ordnerpfad ändern"). Jede Notiz trägt jetzt eine
technische ID im Frontmatter (id: <uuid>), die nie wechselt; der
Systempfad ist nur noch eine Eigenschaft.

- ensure_ids ergänzt fehlende IDs beim Seitenaufbau (wie ensure_node_texts);
  frontmatter() schreibt sie bei jedem Anlegen als erste Zeile.
- Doc, WikiDocEntry, FolderNode und Suchtreffer führen die ID mit;
  resolve_id löst sie auf den aktuellen relpath auf.
- Commands sprechen IDs: archive_read/write/set_title/delete nehmen `id`,
  archive_create_doc/create_folder die ID des Elternknotens plus Namen,
  panel_archive_cmd den Zielknoten als ID.
- Frontend adressiert durchgehend über IDs: Auswahl, Klapp-Zustand,
  Verlauf des Zurück-Knopfs, Wikilink-Sprünge, Suchtreffer-Vormerkung und
  der Zielordner-Baum des Archiv-Dialogs.
- Der Pfad ist aus der Oberfläche verschwunden: die Menüpunkte zum
  Umbenennen von Datei und Ordner samt Commands (archive_rename,
  archive_move, archive_move_folder) sind entfallen — es gibt nur noch den
  Titel, Name und Titel können nicht mehr auseinanderlaufen.

Offen dadurch: Verschieben im Baum fehlt (ToDo: Drag & Drop).

Archiv-Dialog nebenbei überarbeitet: modales Popup im Design der
Archiv-Ansicht mit Abbrechen und zuverlässigem Escape, Zielordner über
einen einklappbaren Baum statt Pfadeingabe, zusätzlicher Knopf "Auf Platte
legen" (panel_save_as); Archivieren öffnet keinen Dateimanager mehr.
This commit is contained in:
marcus hinz
2026-07-26 17:53:19 +02:00
parent 9de58d43e8
commit 2251a1d45f
26 changed files with 984 additions and 403 deletions
+60 -29
View File
@@ -177,7 +177,9 @@ function b64ToBytes(b64: string): Uint8Array {
return bytes;
}
await win.listen<string>("pty-output", (e) => term.write(b64ToBytes(e.payload)));
await win.listen<string>("pty-output", (e) =>
term.write(b64ToBytes(e.payload)),
);
await win.listen("pty-exit", () =>
term.write(`\r\n\x1b[90m${t("panel.processEnded")}\x1b[0m\r\n`),
);
@@ -281,41 +283,70 @@ document.getElementById("panel-hide")!.addEventListener("click", () => {
// Archivieren: Button klappt das Formular (Ordner/Beschreibung/Schlagwörter)
// auf; Abschicken wählt notfalls erst das Archiv-Home per Dialog.
const archiveBtn = document.getElementById("panel-archive")!;
const archiveForm = initArchiveForm(archiveBtn, async (meta) => {
try {
const configured = await invoke<string | null>("panel_archive_dir_cmd", {
project,
});
let dir: string | undefined;
if (!configured) {
const { open } = await import("@tauri-apps/plugin-dialog");
const chosen = await open({ directory: true, title: t("panel.chooseArchiveDir") });
if (!chosen) return;
dir = chosen as string;
const archiveForm = initArchiveForm(
archiveBtn,
async (meta) => {
try {
const configured = await invoke<string | null>("panel_archive_dir_cmd", {
project,
});
let dir: string | undefined;
if (!configured) {
const { open } = await import("@tauri-apps/plugin-dialog");
const chosen = await open({
directory: true,
title: t("panel.chooseArchiveDir"),
});
if (!chosen) return;
dir = chosen as string;
}
await view.flush(); // offene Bearbeitung erst speichern — archiviert, was zu sehen ist
await invoke<string>("panel_archive_cmd", {
project,
dir,
folder: meta.folder ?? null,
description: meta.description ?? null,
tags: meta.tags,
});
flash(archiveBtn, "copied", 1400);
} catch (e) {
flash(archiveBtn, "error", 1400);
panelToast(`Archivieren fehlgeschlagen: ${e}`);
invoke("term_log", { msg: `archive: ${e}` });
}
await view.flush(); // offene Bearbeitung erst speichern — archiviert, was zu sehen ist
const path = await invoke<string>("panel_archive_cmd", {
project,
dir,
folder: meta.folder ?? null,
description: meta.description ?? null,
tags: meta.tags,
});
flash(archiveBtn, "copied", 1400);
invoke("reveal_path_cmd", { path });
} catch (e) {
flash(archiveBtn, "error", 1400);
panelToast(`Archivieren fehlgeschlagen: ${e}`);
invoke("term_log", { msg: `archive: ${e}` });
}
});
},
{
// Ordner-Vorschläge aus dem Archiv; „Auf Platte legen" schreibt den
// Entwurf an einen frei gewählten Pfad (ohne Archiv, ohne Frontmatter).
folders: () => invoke<string[]>("archive_folders", { project }),
onSave: async () => {
try {
const { save } = await import("@tauri-apps/plugin-dialog");
const path = await save({
title: t("archiveForm.saveTitle"),
defaultPath: "entwurf.md",
});
if (!path) return;
await view.flush();
await invoke("panel_save_as", { project, path });
flash(archiveBtn, "copied", 1400);
} catch (e) {
flash(archiveBtn, "error", 1400);
panelToast(`Speichern fehlgeschlagen: ${e}`);
}
},
},
);
archiveBtn.addEventListener("click", () => archiveForm.toggle());
// Splitter: Panelbreite ziehen.
splitter.addEventListener("mousedown", (e) => {
e.preventDefault();
const move = (ev: MouseEvent) => {
const w = Math.max(240, Math.min(window.innerWidth - 200, window.innerWidth - ev.clientX));
const w = Math.max(
240,
Math.min(window.innerWidth - 200, window.innerWidth - ev.clientX),
);
panel.style.width = `${w}px`;
fit.fit();
};