Workspace: anlegen, wiederfinden, oeffnen #1

Closed
opened 2026-08-27 12:31:21 +00:00 by RDietermann · 1 comment
Owner

Destination

Eine Spec, mit der sich das Workspace-Thema am Stück bauen lässt: einen Workspace anlegen, einen bekannten wiederfinden und öffnen — bis zu dem Punkt, an dem der geöffnete Workspace seine Notizen als Baum in der files-bar zeigt. Desktop-only.

Notes

Domain: DioNotes — lokale Markdown-Notizen-App, Dioxus 0.7, Desktop (dx serve --platform desktop).

Planen, nicht bauen. Jedes Ticket liefert eine Entscheidung, keine Implementierung. Wenn eine Session den Drang verspürt, einfach loszucoden, ist das das Signal, dass die Map zu Ende ist.

Skills pro Session: /grilling und /domain-modeling; /research für AFK-Recherche.

Rahmenentscheidungen aus dem Charting — stehen fest, solange die Destination nicht neu gezogen wird:

  • Ein Workspace ist ein Ordner auf der Platte, Notizen sind .md-Dateien darin. Kein Container-Format, keine Datenbank.
  • Ein .dionotes/-Marker macht einen Ordner zum Workspace. Ohne Marker ist es kein Workspace.
  • Genau ein Workspace gleichzeitig offen.
  • Öffnen über eine Liste zuletzt benutzter Workspaces, Anlegen über den nativen Ordner-Dialog des Betriebssystems.
  • Die files-bar zeigt einen aufklappbaren Baum, nicht eine flache Liste.
  • Der App-Start landet immer im Selector — kein automatisches Wiederöffnen.
  • Kein Filewatcher.

Decisions so far

  • Nativer Ordner-Dialog unter Dioxus 0.7 Desktop — rfd 0.17.2 steckt über dioxus-desktop bereits im Abhängigkeitsgraph. Zwei Wege: input { r#type: "file", directory: true } plus FileData::path() ganz ohne neue Dependency, oder rfd::AsyncFileDialog für einen programmatisch geöffneten Dialog. Async ist Pflicht (sync friert den Event-Loop ein), Abbruch liefert None, Windows läuft ohne Feature-Flag. Langfassung: Branch research/ordner-dialog.
  • Ablageort für die anwendungsweite Konfiguration — Windows: %LOCALAPPDATA%\DioNotes\config\ (Local statt Roaming, weil absolute Workspace-Pfade maschinenspezifisch sind). Linux: $XDG_STATE_HOME, nicht ~/.config — die XDG-Spec führt „recently used files" wörtlich als State. Ein Ausdruck deckt alle drei Plattformen: state_dir().unwrap_or_else(|| config_local_dir()), ohne #[cfg]. Das Verzeichnis muss die App selbst anlegen. Dioxus hat keine eigene Abstraktion — Config::with_data_directory ist das WebView-Profil, nicht die App-Konfiguration. Langfassung: Branch research/config-ablageort.
  • Dateiformate: App-Konfiguration und .dionotes-Marker — RON für beide Dateien, Pfadermittlung mit dirs (nicht directories), App-Verzeichnis überall kleingeschrieben dionotes. Die Workspace-Liste (.../dionotes/workspaces.ron) enthält nur Pfade; ihre Reihenfolge ist die Sortierung, kein Anzeigename, kein Zeitstempel, keine Schema-Version — beschädigt wird sie verworfen. Der Marker (.dionotes/workspace.ron) enthält heute genau schema_version: 1, kein created_at, keine Vorratsfelder — beschädigt wird der Workspace nicht geöffnet und der Marker nicht neu angelegt, weil das später den Passwortschutz aushebeln würde.
  • App-Zustand des offenen Workspace und die Router-Struktur — Ein Router statt zwei: die beiden Enums verschmelzen, der Editor-Teil liegt unter #[nest("/workspace")]. Der heutige Zwei-Router-Aufbau ist nie gelaufen und hätte beim ersten Umschalten den Editor-Router mit einer URL konfrontiert, die er nicht kennt (/ws_open). Der offene Workspace lebt als Signal<Option<Workspace>> mit struct Workspace { root: PathBuf } im Context-Provider — Props scheiden aus, weil sich durch ein Outlet keine durchreichen lassen. workspace_selected: Signal<bool> entfällt ersatzlos. Bei fehlendem Workspace schickt ein Guard im EditorShell-Layout zurück zum Selector. „Workspace wechseln" braucht keinen Platzhalter. Später durch #8 präzisiert: Der Selector wurde auf eine Route reduziert, SELECTOR_TABS entfällt.
  • Datenstruktur des Notiz-Baums — Verschachteltes Enum TreeNode { File | Folder } mit einer einzigen File-Variante für alle Dateitypen; Aufbau über std::fs::read_dir mit eigener Rekursion. Eingesammelt wird alles außer Namen mit führendem . (Dateien und Ordner, womit .dionotes/ und .git/ ohne Ausnahmeliste wegfallen) und außer Symlinks. Leere Ordner werden angezeigt, sortiert wird Ordner-vor-Dateien und alphabetisch ohne Beachtung der Groß-/Kleinschreibung. Der Aufklapp-Zustand liegt als Signal<HashSet<PathBuf>> neben dem Baum, damit ein Refresh ihn nicht mitreißt, und wird nicht persistiert. Der Baum ist abgeleiteter Zustand und gehört nicht in struct Workspace.
  • Fehlerfälle beim Anlegen und Öffnen — Leitregel statt Fallliste: Zustand → inline, Unterbrechung einer laufenden Aktion → Dialog. Listeneinträge werden bei Problemen markiert, nie automatisch entfernt (der häufigste Grund ist ein abgezogener Stick, kein gelöschter Ordner), mit drei unterschiedlich benannten Zuständen: nicht erreichbar / kein Workspace mehr / beschädigt. Ein Klick auf einen markierten Eintrag versucht es erneut. Beim Anlegen: abgebrochener Dialog ist kein Fehler, ein bereits vorhandener Workspace wird geöffnet statt abgelehnt, ein nicht-leerer Ordner löst immer eine Rückfrage aus, fehlende Schreibrechte brechen ab.
  • Selector-UI: Anlegen und Öffnen — Von drei Prototyp-Varianten gewinnt „Liste führt": ein Bildschirm statt Reiter, max. 40rem, die Workspace-Liste scrollt zwischen fester Kopf- und Fußzeile, das Hinzufügen sitzt als Nebenaktion in der Fußzeile und wird nur beim leeren Erststart zur Primäraktion. Listeneintrag = Ordnername plus Pfad darunter (links gekürzt) plus Statusabzeichen. Knopfbeschriftung „Workspace hinzufügen…", weil derselbe Knopf neue und wiederaufgenommene Workspaces abdeckt. Folge für #5: Ohne Reiter wird der Selector eine einzige Route, SELECTOR_TABS entfällt ersatzlos. Prototyp: Branch prototype/selector-ui.

Not yet specified

  • Live-Aktualisierung bei externen Änderungen. Ordner-auf-Platte wurde unter anderem wegen Sync über Dropbox/Syncthing/Git gewählt — damit ändern sich Dateien, während die App läuft. Ein Filewatcher ist für diese Map ausgeschlossen, aber die Frage kommt wieder; sie hängt teilweise am Notiz-Handling (was passiert mit einer offenen, extern geänderten Notiz?).
  • Große Workspaces. Ab welcher Notizzahl das rekursive Einlesen beim Öffnen spürbar wird, und ob es dann Lazy Loading oder einen Index braucht. Noch kein Messwert, keine Zahl, keine Frage.
  • Workspace umbenennen, verschieben, entfernen. Der Umgang mit der Liste über das Hinzufügen hinaus. Das Entfernen von Hand ist inzwischen eine feste Anforderung aus #6, weil defekte Einträge nie automatisch verschwinden.
  • Nebenläufige Zustandsprüfung der Listeneinträge. Aus #6: Die Markierungen setzen eine Prüfung pro Eintrag beim Anzeigen voraus, und ein nicht eingehängtes Netzlaufwerk kann path.exists() sekundenlang blockieren — ausgerechnet im Selector, den jeder Start zuerst zeigt. Dass die Prüfung nebenläufig laufen muss, steht fest; wie das in Dioxus 0.7 sauber aussieht (use_resource pro Eintrag oder eine gemeinsame Aufgabe), ist noch nicht scharf.
  • Linux-Build. Der Ordner-Dialog braucht dort ein Portal-Backend oder GTK3-Header — unter Windows und macOS ist er voraussetzungsfrei. Wird erst zur Frage, wenn für Linux gebaut wird.
  • Verschlüsselung der Notizinhalte. Der geplante Passwortschutz soll ausdrücklich gegen jemanden mit Dateisystemzugriff wirken. Ein Hash im Marker leistet das nicht — er sperrt nur den Weg über DioNotes, während die .md-Dateien unverschlüsselt daneben liegen. Eigener Effort, der spätestens mit dem Passwortschutz auf den Tisch muss; Schlüsselableitung, Verschlüsselung im Ruhezustand und der Umgang mit dem geöffneten Editor hängen daran.
  • Workspace-eigene Einstellungen und Versionierungsoptionen. Sollen später in den Marker. Welche Einstellungen das sind und was „Versionierungsoptionen" konkret bedeutet (Snapshots? Git? Nur ein Schalter?), ist noch nicht scharf genug für ein Ticket.

Out of scope

  • Notiz-Handling — anlegen, umbenennen, Inhalt speichern, Autosave, löschen, Konflikte. Eigener Effort; die Grenze dieser Map liegt beim geöffneten Workspace mit gefülltem Baum.
  • Web- und Mobile-Plattform. Desktop-only entschieden; im Browser gäbe es keinen freien Dateisystemzugriff, das wäre ein eigenes Storage-Konzept und damit ein eigener Effort.
## Destination Eine Spec, mit der sich das Workspace-Thema am Stück bauen lässt: einen Workspace anlegen, einen bekannten wiederfinden und öffnen — bis zu dem Punkt, an dem der geöffnete Workspace seine Notizen als Baum in der `files-bar` zeigt. Desktop-only. ## Notes **Domain:** DioNotes — lokale Markdown-Notizen-App, Dioxus 0.7, Desktop (`dx serve --platform desktop`). **Planen, nicht bauen.** Jedes Ticket liefert eine Entscheidung, keine Implementierung. Wenn eine Session den Drang verspürt, einfach loszucoden, ist das das Signal, dass die Map zu Ende ist. **Skills pro Session:** `/grilling` und `/domain-modeling`; `/research` für AFK-Recherche. **Rahmenentscheidungen aus dem Charting** — stehen fest, solange die Destination nicht neu gezogen wird: - Ein Workspace **ist ein Ordner auf der Platte**, Notizen sind `.md`-Dateien darin. Kein Container-Format, keine Datenbank. - Ein `.dionotes/`-**Marker** macht einen Ordner zum Workspace. Ohne Marker ist es kein Workspace. - **Genau ein** Workspace gleichzeitig offen. - Öffnen über eine **Liste zuletzt benutzter** Workspaces, Anlegen über den **nativen Ordner-Dialog** des Betriebssystems. - Die `files-bar` zeigt einen **aufklappbaren Baum**, nicht eine flache Liste. - Der App-Start landet **immer im Selector** — kein automatisches Wiederöffnen. - **Kein Filewatcher.** ## Decisions so far <!-- ein Eintrag pro geschlossenem Ticket --> - [Nativer Ordner-Dialog unter Dioxus 0.7 Desktop](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/2) — `rfd 0.17.2` steckt über `dioxus-desktop` bereits im Abhängigkeitsgraph. Zwei Wege: `input { r#type: "file", directory: true }` plus `FileData::path()` ganz ohne neue Dependency, oder `rfd::AsyncFileDialog` für einen programmatisch geöffneten Dialog. Async ist Pflicht (sync friert den Event-Loop ein), Abbruch liefert `None`, Windows läuft ohne Feature-Flag. Langfassung: Branch `research/ordner-dialog`. - [Ablageort für die anwendungsweite Konfiguration](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/3) — Windows: `%LOCALAPPDATA%\DioNotes\config\` (Local statt Roaming, weil absolute Workspace-Pfade maschinenspezifisch sind). Linux: `$XDG_STATE_HOME`, **nicht** `~/.config` — die XDG-Spec führt „recently used files" wörtlich als State. Ein Ausdruck deckt alle drei Plattformen: `state_dir().unwrap_or_else(|| config_local_dir())`, ohne `#[cfg]`. Das Verzeichnis muss die App selbst anlegen. Dioxus hat keine eigene Abstraktion — `Config::with_data_directory` ist das WebView-Profil, nicht die App-Konfiguration. Langfassung: Branch `research/config-ablageort`. - [Dateiformate: App-Konfiguration und .dionotes-Marker](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/4) — **RON** für beide Dateien, Pfadermittlung mit `dirs` (nicht `directories`), App-Verzeichnis überall kleingeschrieben `dionotes`. Die **Workspace-Liste** (`.../dionotes/workspaces.ron`) enthält nur Pfade; ihre Reihenfolge *ist* die Sortierung, kein Anzeigename, kein Zeitstempel, keine Schema-Version — beschädigt wird sie verworfen. Der **Marker** (`.dionotes/workspace.ron`) enthält heute genau `schema_version: 1`, kein `created_at`, keine Vorratsfelder — beschädigt wird der Workspace *nicht* geöffnet und der Marker *nicht* neu angelegt, weil das später den Passwortschutz aushebeln würde. - [App-Zustand des offenen Workspace und die Router-Struktur](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/5) — **Ein** Router statt zwei: die beiden Enums verschmelzen, der Editor-Teil liegt unter `#[nest("/workspace")]`. Der heutige Zwei-Router-Aufbau ist nie gelaufen und hätte beim ersten Umschalten den Editor-Router mit einer URL konfrontiert, die er nicht kennt (`/ws_open`). Der offene Workspace lebt als `Signal<Option<Workspace>>` mit `struct Workspace { root: PathBuf }` im Context-Provider — Props scheiden aus, weil sich durch ein `Outlet` keine durchreichen lassen. `workspace_selected: Signal<bool>` entfällt ersatzlos. Bei fehlendem Workspace schickt ein Guard im `EditorShell`-Layout zurück zum Selector. „Workspace wechseln" braucht keinen Platzhalter. **Später durch #8 präzisiert:** Der Selector wurde auf *eine* Route reduziert, `SELECTOR_TABS` entfällt. - [Datenstruktur des Notiz-Baums](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/7) — Verschachteltes Enum `TreeNode { File | Folder }` mit einer einzigen `File`-Variante für alle Dateitypen; Aufbau über `std::fs::read_dir` mit eigener Rekursion. Eingesammelt wird **alles** außer Namen mit führendem `.` (Dateien *und* Ordner, womit `.dionotes/` und `.git/` ohne Ausnahmeliste wegfallen) und außer Symlinks. Leere Ordner werden angezeigt, sortiert wird Ordner-vor-Dateien und alphabetisch ohne Beachtung der Groß-/Kleinschreibung. Der Aufklapp-Zustand liegt als `Signal<HashSet<PathBuf>>` **neben** dem Baum, damit ein Refresh ihn nicht mitreißt, und wird nicht persistiert. Der Baum ist abgeleiteter Zustand und gehört nicht in `struct Workspace`. - [Fehlerfälle beim Anlegen und Öffnen](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/6) — Leitregel statt Fallliste: **Zustand → inline, Unterbrechung einer laufenden Aktion → Dialog.** Listeneinträge werden bei Problemen **markiert, nie automatisch entfernt** (der häufigste Grund ist ein abgezogener Stick, kein gelöschter Ordner), mit drei unterschiedlich benannten Zuständen: nicht erreichbar / kein Workspace mehr / beschädigt. Ein Klick auf einen markierten Eintrag versucht es erneut. Beim Anlegen: abgebrochener Dialog ist kein Fehler, ein bereits vorhandener Workspace wird **geöffnet statt abgelehnt**, ein nicht-leerer Ordner löst immer eine Rückfrage aus, fehlende Schreibrechte brechen ab. - [Selector-UI: Anlegen und Öffnen](https://gitlab.hive-brain.de/RDietermann/dionotes-client/issues/8) — Von drei Prototyp-Varianten gewinnt **„Liste führt"**: ein Bildschirm statt Reiter, max. 40rem, die Workspace-Liste scrollt zwischen fester Kopf- und Fußzeile, das Hinzufügen sitzt als Nebenaktion in der Fußzeile und wird nur beim leeren Erststart zur Primäraktion. Listeneintrag = Ordnername plus Pfad darunter (links gekürzt) plus Statusabzeichen. Knopfbeschriftung **„Workspace hinzufügen…"**, weil derselbe Knopf neue *und* wiederaufgenommene Workspaces abdeckt. **Folge für #5:** Ohne Reiter wird der Selector eine einzige Route, `SELECTOR_TABS` entfällt ersatzlos. Prototyp: Branch `prototype/selector-ui`. ## Not yet specified - **Live-Aktualisierung bei externen Änderungen.** Ordner-auf-Platte wurde unter anderem wegen Sync über Dropbox/Syncthing/Git gewählt — damit ändern sich Dateien, während die App läuft. Ein Filewatcher ist für diese Map ausgeschlossen, aber die Frage kommt wieder; sie hängt teilweise am Notiz-Handling (was passiert mit einer offenen, extern geänderten Notiz?). - **Große Workspaces.** Ab welcher Notizzahl das rekursive Einlesen beim Öffnen spürbar wird, und ob es dann Lazy Loading oder einen Index braucht. Noch kein Messwert, keine Zahl, keine Frage. - **Workspace umbenennen, verschieben, entfernen.** Der Umgang mit der Liste über das Hinzufügen hinaus. Das Entfernen von Hand ist inzwischen eine feste Anforderung aus #6, weil defekte Einträge nie automatisch verschwinden. - **Nebenläufige Zustandsprüfung der Listeneinträge.** Aus #6: Die Markierungen setzen eine Prüfung pro Eintrag beim Anzeigen voraus, und ein nicht eingehängtes Netzlaufwerk kann `path.exists()` sekundenlang blockieren — ausgerechnet im Selector, den jeder Start zuerst zeigt. Dass die Prüfung nebenläufig laufen muss, steht fest; wie das in Dioxus 0.7 sauber aussieht (`use_resource` pro Eintrag oder eine gemeinsame Aufgabe), ist noch nicht scharf. - **Linux-Build.** Der Ordner-Dialog braucht dort ein Portal-Backend oder GTK3-Header — unter Windows und macOS ist er voraussetzungsfrei. Wird erst zur Frage, wenn für Linux gebaut wird. - **Verschlüsselung der Notizinhalte.** Der geplante Passwortschutz soll ausdrücklich gegen jemanden mit Dateisystemzugriff wirken. Ein Hash im Marker leistet das nicht — er sperrt nur den Weg über DioNotes, während die `.md`-Dateien unverschlüsselt daneben liegen. Eigener Effort, der spätestens mit dem Passwortschutz auf den Tisch muss; Schlüsselableitung, Verschlüsselung im Ruhezustand und der Umgang mit dem geöffneten Editor hängen daran. - **Workspace-eigene Einstellungen und Versionierungsoptionen.** Sollen später in den Marker. Welche Einstellungen das sind und was „Versionierungsoptionen" konkret bedeutet (Snapshots? Git? Nur ein Schalter?), ist noch nicht scharf genug für ein Ticket. ## Out of scope - **Notiz-Handling** — anlegen, umbenennen, Inhalt speichern, Autosave, löschen, Konflikte. Eigener Effort; die Grenze dieser Map liegt beim geöffneten Workspace mit gefülltem Baum. - **Web- und Mobile-Plattform.** Desktop-only entschieden; im Browser gäbe es keinen freien Dateisystemzugriff, das wäre ein eigenes Storage-Konzept und damit ein eigener Effort.
RDietermann added the wayfinder:map label 2026-08-27 12:31:21 +00:00
Author
Owner

Die Map ist erfüllt

Alle sieben Tickets sind geschlossen; es steht keine Entscheidung mehr aus, bevor gebaut werden kann. Die Begründungen liegen jeweils im Ticket, die Kurzfassungen oben unter Decisions so far.

Vorgeschlagene Bau-Reihenfolge

  1. Router und Zustand umbauen (#5, präzisiert durch #8) — die beiden Routen-Enums zu einem verschmelzen, workspace_selected: Signal<bool> durch Signal<Option<Workspace>> im Context ersetzen. Fasst als Einziges bestehenden Code an; alles Weitere baut darauf auf.
  2. Konfiguration lesen und schreiben (#3, #4) — dirs, RON, workspaces.ron. Ohne Oberfläche prüfbar.
  3. Ordner-Dialog und Anlegen-Flow (#2, #4, #6) — Dialog, Marker anlegen, die Fehlerfälle.
  4. Selector-UI (#8) — die Variante „Liste führt"; der Prototyp auf prototype/selector-ui zeigt Aufbau und Zustände.
  5. Notiz-Baum und files-bar (#7) — rekursives Einlesen, Baumdarstellung, Aufklapp-Zustand.

Was offen bleibt — und nicht hierher gehört

Die Punkte unter Not yet specified sind bewusst nicht Teil dieser Map. Zwei davon sind eigene Vorhaben, sobald sie anstehen:

  • Verschlüsselung der Notizinhalte. Der geplante Passwortschutz soll gegen Dateisystemzugriff wirken — ein Hash im Marker leistet das nicht.
  • Notiz-Handling (anlegen, umbenennen, speichern, löschen) — von Beginn an außerhalb dieser Map.

Wer daran weiterarbeitet, beginnt mit einer neuen Map, nicht mit dieser.

## Die Map ist erfüllt Alle sieben Tickets sind geschlossen; es steht keine Entscheidung mehr aus, bevor gebaut werden kann. Die Begründungen liegen jeweils im Ticket, die Kurzfassungen oben unter *Decisions so far*. ### Vorgeschlagene Bau-Reihenfolge 1. **Router und Zustand umbauen** (#5, präzisiert durch #8) — die beiden Routen-Enums zu einem verschmelzen, `workspace_selected: Signal<bool>` durch `Signal<Option<Workspace>>` im Context ersetzen. Fasst als Einziges bestehenden Code an; alles Weitere baut darauf auf. 2. **Konfiguration lesen und schreiben** (#3, #4) — `dirs`, RON, `workspaces.ron`. Ohne Oberfläche prüfbar. 3. **Ordner-Dialog und Anlegen-Flow** (#2, #4, #6) — Dialog, Marker anlegen, die Fehlerfälle. 4. **Selector-UI** (#8) — die Variante „Liste führt"; der Prototyp auf `prototype/selector-ui` zeigt Aufbau und Zustände. 5. **Notiz-Baum und files-bar** (#7) — rekursives Einlesen, Baumdarstellung, Aufklapp-Zustand. ### Was offen bleibt — und nicht hierher gehört Die Punkte unter *Not yet specified* sind bewusst nicht Teil dieser Map. Zwei davon sind eigene Vorhaben, sobald sie anstehen: - **Verschlüsselung der Notizinhalte.** Der geplante Passwortschutz soll gegen Dateisystemzugriff wirken — ein Hash im Marker leistet das nicht. - **Notiz-Handling** (anlegen, umbenennen, speichern, löschen) — von Beginn an außerhalb dieser Map. Wer daran weiterarbeitet, beginnt mit einer neuen Map, nicht mit dieser.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RDietermann/dionotes-client#1