Trilium als Archiv: verwaltete Instanz pro Projekt, MD-Archiv und Dokument-Tab entfernt
- domain/trilium.rs: Runtime-Download (gepinnt 0.104.0), Instanz pro Projekt auf 127.0.0.1 mit FNV-Port, ETAPI (archive_note, search_notes), Token im Keyring - Setup bis aufs Passwort automatisiert: trilium_connect macht new-document (skipDemoDb), set-password und ETAPI-Login; Modul-Flag erst nach Token-Erfolg - write_panel legt direkt eine Trilium-Notiz an (text|path, folder/description/tags); noteopen-Kanal, das Terminal-Fenster öffnet die Notiz (ersetzt archive_panel) - MD-Altbestand raus: archive*/wiki*, archiveHome samt Permissions, panel_set/panel_load/ wiki_open, panel_archive*/archive_docs/*_archive_home, reveal_path, spellcheck - Dokument-Tab raus: Panel = ToDo/Befehle/Suche + Archiv-Knopf (trilium_open); panel-view/archive-form gelöscht, Markup/CSS/Messages bereinigt - Lösch-Stufe Archiv: Instanz stoppen, Trilium-Datadir und Keyring-Token löschen
This commit is contained in:
@@ -1,246 +0,0 @@
|
||||
// Wiki-Ansicht: Ordner-Baum links, flache Inhaltsliste rechts, Zeilen-Aktionen
|
||||
// (öffnen, umbenennen, löschen), Ordner-Umbenennen, Leerzustände.
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { initWikiView } from "./wiki-view";
|
||||
|
||||
function setup() {
|
||||
document.body.innerHTML = `<div id="w"></div>`;
|
||||
const openDoc = vi.fn();
|
||||
const openWiki = vi.fn();
|
||||
const actions = {
|
||||
rename: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
moveFolder: vi.fn(),
|
||||
createFolder: vi.fn(),
|
||||
createDoc: vi.fn(),
|
||||
};
|
||||
const view = initWikiView(document.getElementById("w")!, {
|
||||
openDoc,
|
||||
openWiki,
|
||||
actions,
|
||||
});
|
||||
return { view, openDoc, openWiki, actions };
|
||||
}
|
||||
|
||||
const doc = (relpath: string, name: string, extra: object = {}) => ({
|
||||
relpath,
|
||||
name,
|
||||
title: name.toUpperCase(),
|
||||
description: null,
|
||||
tags: [],
|
||||
date: "2026-07-19",
|
||||
backlinks: 0,
|
||||
...extra,
|
||||
});
|
||||
|
||||
const page = JSON.stringify({
|
||||
kind: "page",
|
||||
home: "/tmp/archiv",
|
||||
tag: null,
|
||||
total: 3,
|
||||
tags: [],
|
||||
folders: [
|
||||
{
|
||||
name: "",
|
||||
docs: [
|
||||
doc("2026-07-19_1000-wurzel-doc.md", "wurzel-doc", {
|
||||
backlinks: 2,
|
||||
description: "Beschreibung",
|
||||
}),
|
||||
],
|
||||
},
|
||||
{ name: "konzepte", docs: [doc("konzepte/2026-07-19_1005-neu.md", "neu")] },
|
||||
{
|
||||
name: "konzepte/panel",
|
||||
docs: [doc("konzepte/panel/2026-07-19_1010-alt.md", "alt")],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
describe("initWikiView — Baum und Liste", () => {
|
||||
it("Baum zeigt nur Ordner, Wurzel ist gewählt und listet ihren Inhalt", () => {
|
||||
const { view } = setup();
|
||||
view.set(page);
|
||||
// Baum: Wurzel-Knopf + Ordner (keine Dokumentzeilen im Baum).
|
||||
expect(document.querySelector(".wiki-tree-root")!.className).toContain("active");
|
||||
const folders = [...document.querySelectorAll(".wiki-tree .wiki-tree-name")].map(
|
||||
(e) => e.textContent,
|
||||
);
|
||||
expect(folders).toEqual(["konzepte", "panel"]);
|
||||
// Liste rechts: nur der direkte Wurzel-Inhalt.
|
||||
expect(document.querySelector(".wiki-head-title")!.textContent).toBe("Archiv");
|
||||
expect(document.querySelector(".wiki-head-right")!.textContent).toBe("1 Dokument");
|
||||
const rows = [...document.querySelectorAll(".wiki-doc .wiki-doc-title")].map(
|
||||
(e) => e.textContent,
|
||||
);
|
||||
expect(rows).toEqual(["WURZEL-DOC"]);
|
||||
expect(document.querySelector(".wiki-doc-back")!.textContent).toBe("↩ 2");
|
||||
expect(document.querySelector(".wiki-doc-desc")!.textContent).toBe("Beschreibung");
|
||||
});
|
||||
|
||||
it("Ordner-Klick wählt aus und zeigt dessen flache Liste", () => {
|
||||
const { view } = setup();
|
||||
view.set(page);
|
||||
const names = document.querySelectorAll<HTMLElement>(".wiki-tree .wiki-tree-name");
|
||||
names[0].click(); // konzepte
|
||||
expect(document.querySelector(".wiki-head-title")!.textContent).toBe("konzepte");
|
||||
expect(document.querySelector(".wiki-head-sub")!.textContent).toBe(
|
||||
"/tmp/archiv/konzepte",
|
||||
);
|
||||
const rows = [...document.querySelectorAll(".wiki-doc .wiki-doc-title")].map(
|
||||
(e) => e.textContent,
|
||||
);
|
||||
// Nur direkter Inhalt — das Dokument aus konzepte/panel erscheint nicht.
|
||||
expect(rows).toEqual(["NEU"]);
|
||||
expect(
|
||||
document.querySelector(".wiki-tree summary.active .wiki-tree-name")!.textContent,
|
||||
).toBe("konzepte");
|
||||
});
|
||||
|
||||
it("Dokument-Klick öffnet über den absoluten Pfad", () => {
|
||||
const { view, openDoc } = setup();
|
||||
view.set(page);
|
||||
document.querySelector<HTMLElement>(".wiki-doc")!.click();
|
||||
expect(openDoc).toHaveBeenCalledWith("/tmp/archiv/2026-07-19_1000-wurzel-doc.md");
|
||||
});
|
||||
|
||||
it("Auswahl übersteht ein Puffer-Update, verschwundener Ordner fällt zur Wurzel", () => {
|
||||
const { view } = setup();
|
||||
view.set(page);
|
||||
document.querySelectorAll<HTMLElement>(".wiki-tree .wiki-tree-name")[0].click();
|
||||
view.set(page); // Update: Auswahl bleibt
|
||||
expect(document.querySelector(".wiki-head-title")!.textContent).toBe("konzepte");
|
||||
view.set(
|
||||
JSON.stringify({
|
||||
kind: "page",
|
||||
home: "/tmp/archiv",
|
||||
tag: null,
|
||||
total: 1,
|
||||
tags: [],
|
||||
folders: [{ name: "", docs: [doc("2026-07-19_1000-wurzel-doc.md", "wurzel-doc")] }],
|
||||
}),
|
||||
);
|
||||
expect(document.querySelector(".wiki-head-title")!.textContent).toBe("Archiv");
|
||||
});
|
||||
});
|
||||
|
||||
describe("initWikiView — Aktionen", () => {
|
||||
it("Stift an der Zeile: Inline-Umbenennen mit Enter, ohne das Dokument zu öffnen", () => {
|
||||
const { view, actions, openDoc } = setup();
|
||||
view.set(page);
|
||||
const row = document.querySelector<HTMLElement>(".wiki-doc")!;
|
||||
row.querySelector<HTMLElement>(".cmd-edit")!.click();
|
||||
const input = row.querySelector<HTMLInputElement>(".wiki-tree-input")!;
|
||||
expect(input.value).toBe("wurzel-doc");
|
||||
input.value = "Neuer Name";
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
|
||||
expect(actions.rename).toHaveBeenCalledWith(
|
||||
"2026-07-19_1000-wurzel-doc.md",
|
||||
"Neuer Name",
|
||||
);
|
||||
expect(openDoc).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Escape stellt die Zeile wieder her", () => {
|
||||
const { view, actions } = setup();
|
||||
view.set(page);
|
||||
document.querySelector<HTMLElement>(".wiki-doc .cmd-edit")!.click();
|
||||
const input = document.querySelector<HTMLInputElement>(".wiki-doc .wiki-tree-input")!;
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
|
||||
expect(document.querySelector(".wiki-doc .wiki-doc-title")!.textContent).toBe(
|
||||
"WURZEL-DOC",
|
||||
);
|
||||
expect(actions.rename).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Löschen meldet mit relpath, ohne das Dokument zu öffnen", () => {
|
||||
const { view, actions, openDoc } = setup();
|
||||
view.set(page);
|
||||
document.querySelector<HTMLElement>(".wiki-doc .cmd-del")!.click();
|
||||
expect(actions.remove).toHaveBeenCalledWith("2026-07-19_1000-wurzel-doc.md");
|
||||
expect(openDoc).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Plus im Listenkopf: Formular legt Dokument im gewählten Ordner an", () => {
|
||||
const { view, actions, openDoc } = setup();
|
||||
view.set(page);
|
||||
document.querySelectorAll<HTMLElement>(".wiki-tree .wiki-tree-name")[0].click();
|
||||
document.querySelector<HTMLElement>(".wiki-head .wiki-add")!.click();
|
||||
const form = document.querySelector<HTMLElement>(".wiki-form")!;
|
||||
expect(form.querySelector(".wiki-form-title")!.textContent).toBe("Neues Dokument");
|
||||
const input = form.querySelector<HTMLInputElement>("input")!;
|
||||
input.value = "Deploy Notiz";
|
||||
form.querySelector<HTMLElement>(".wiki-form-submit")!.click();
|
||||
expect(actions.createDoc).toHaveBeenCalledWith("konzepte", "Deploy Notiz");
|
||||
expect(document.querySelector(".wiki-form")).toBeNull();
|
||||
expect(openDoc).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Abbrechen und Escape schließen das Formular ohne Aktion", () => {
|
||||
const { view, actions } = setup();
|
||||
view.set(page);
|
||||
document.querySelector<HTMLElement>(".wiki-head .wiki-add")!.click();
|
||||
document.querySelector<HTMLElement>(".wiki-form-cancel")!.click();
|
||||
expect(document.querySelector(".wiki-form")).toBeNull();
|
||||
|
||||
document.querySelector<HTMLElement>(".wiki-head .wiki-add")!.click();
|
||||
document
|
||||
.querySelector<HTMLElement>(".wiki-form")!
|
||||
.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
|
||||
expect(document.querySelector(".wiki-form")).toBeNull();
|
||||
expect(actions.createDoc).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Plus im Baum: Formular legt Ordner an, Pfad mit gewähltem Ordner vorbefüllt", () => {
|
||||
const { view, actions } = setup();
|
||||
view.set(page);
|
||||
document.querySelectorAll<HTMLElement>(".wiki-tree .wiki-tree-name")[0].click();
|
||||
document.querySelector<HTMLElement>(".wiki-tree-head .wiki-add")!.click();
|
||||
const input = document.querySelector<HTMLInputElement>(".wiki-form input")!;
|
||||
expect(input.value).toBe("konzepte/");
|
||||
input.value = "konzepte/neu";
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
|
||||
expect(actions.createFolder).toHaveBeenCalledWith("konzepte/neu");
|
||||
});
|
||||
|
||||
it("Ordner-Stift: Inline-Eingabe des Pfads, Enter meldet moveFolder", () => {
|
||||
const { view, actions } = setup();
|
||||
view.set(page);
|
||||
const summary = document.querySelector<HTMLElement>(".wiki-tree summary")!;
|
||||
summary.querySelector<HTMLElement>(".cmd-edit")!.click();
|
||||
const input = summary.querySelector<HTMLInputElement>(".wiki-tree-input")!;
|
||||
expect(input.value).toBe("konzepte");
|
||||
input.value = "notizen/2026";
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
|
||||
expect(actions.moveFolder).toHaveBeenCalledWith("konzepte", "notizen/2026");
|
||||
});
|
||||
});
|
||||
|
||||
describe("initWikiView — Leerzustände", () => {
|
||||
it("leeres Archiv erklärt das Archivieren", () => {
|
||||
const { view } = setup();
|
||||
view.set(
|
||||
JSON.stringify({
|
||||
kind: "page",
|
||||
home: "/a",
|
||||
tag: null,
|
||||
total: 0,
|
||||
tags: [],
|
||||
folders: [],
|
||||
}),
|
||||
);
|
||||
expect(document.querySelector(".wiki-empty")!.textContent).toContain(
|
||||
"Das Archiv ist leer.",
|
||||
);
|
||||
});
|
||||
|
||||
it("leerer Puffer bietet den Übersichts-Einstieg", () => {
|
||||
const { view, openWiki } = setup();
|
||||
view.set("");
|
||||
expect(view.empty()).toBe(true);
|
||||
document.querySelector<HTMLElement>(".wiki-empty .wiki-chip")!.click();
|
||||
expect(openWiki).toHaveBeenCalledWith("tag:");
|
||||
view.set(page);
|
||||
expect(view.empty()).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user