refactor: Router zusammenfuehren und offenen Workspace im Context fuehren

Die App hatte zwei Routable-Enums, zwischen denen ein Signal<bool> umschaltete.
Der Schalter wurde nie gesetzt - beim ersten Umlegen waere aufgefallen, dass
sich beide Router dieselbe History teilen und beide "/" belegen: Wer im
Selector auf "Open Workspace" klickt, steht auf /ws_open, und der
Editor-Router kennt nur / und /editor.

Jetzt ein Enum, der Editor-Teil unter #[nest("/workspace")]. An die Stelle des
bool tritt Signal<Option<Workspace>> in einem Context-Provider - Props
scheiden aus, weil sich durch ein Outlet keine durchreichen lassen. Fehlt der
Workspace, schickt ein Guard im EditorShell-Layout zurueck zum Selector; der
Fall tritt real bei Hot-Reload auf, wo der Context faellt und die Route bleibt.

Der Selector verliert seine Reiter und wird eine einzige Route. Das folgt aus
dem UI-Prototyp in Issue #8: Oeffnen ist der Alltagsfall, Hinzufuegen passiert
pro Workspace einmal - zwei gleichrangige Reiter verlangten bei jedem Start
eine Entscheidung, die fast immer dieselbe ist. Damit entfallen der leere
Stub create_new_workspace.rs, die Tab-Konstante fuer den Selector und die
zugehoerigen CSS-Klassen.

Weil EnumIter nicht mehr gebraucht wird, faellt strum_macros als direkte
Abhaengigkeit weg; strum re-exportiert die Derives bereits.

Entscheidungen dazu in den Issues #5, #6 und #8.
This commit is contained in:
2026-08-28 08:57:19 +02:00
parent d2f5316a52
commit 91fd351d59
12 changed files with 143 additions and 131 deletions

View File

@@ -1,32 +1,24 @@
use dioxus::prelude::*;
use dioxus_code::Theme;
use dioxus_code_editor::{CodeEditor, Language};
use strum::EnumMessage;
use crate::components::custom_link::custom_link_component::CustomLink;
use strum::{EnumMessage, IntoEnumIterator};
use strum_macros::EnumIter;
use crate::route::Route;
use crate::workspace::Workspace;
const HEADER_SVG: Asset = asset!("/assets/header.svg");
#[derive(Debug, Clone, Routable, PartialEq, EnumIter, EnumMessage)]
#[rustfmt::skip]
pub enum EditorRoute {
#[layout(Navbar)]
#[route("/")]
#[strum(message = "Home")]
Home {},
#[route("/editor")]
#[strum(message = "Editor")]
Editor {},
}
/// Home page
#[component]
fn Home() -> Element {
pub fn Home() -> Element {
rsx! {
Hero {}
}
}
// TODO: Unveränderter Dioxus-Vorlagentext. Ersetzen, sobald klar ist, was
// beim Öffnen eines Workspace angezeigt wird, bevor eine Notiz gewählt wurde.
#[component]
pub fn Hero() -> Element {
rsx! {
@@ -46,7 +38,7 @@ pub fn Hero() -> Element {
}
#[component]
fn Editor() -> Element {
pub fn Editor() -> Element {
let mut source = use_signal(String::new);
rsx! {
@@ -60,15 +52,34 @@ fn Editor() -> Element {
}
}
/// Shared navbar component.
/// Die Editor-Hülle: Datei-Leiste links, Editorfläche rechts.
///
/// Trägt zugleich den Zustands-Guard — ohne offenen Workspace hat der Editor
/// nichts anzuzeigen. Der Fall ist real: Hot-Reload setzt den Context zurück,
/// während die Route bestehen bleibt.
#[component]
fn Navbar() -> Element {
pub fn EditorShell() -> Element {
let workspace = use_context::<Signal<Option<Workspace>>>();
let navigator = use_navigator();
use_effect(move || {
if workspace.read().is_none() {
navigator.replace(Route::Selector {});
}
});
if workspace.read().is_none() {
return rsx! {};
}
rsx! {
div {
class: "layout-container",
div {
class: "files-bar",
for entry in EditorRoute::iter() {
// TODO: Durch den Notiz-Baum des offenen Workspace ersetzen
// (rekursiv eingelesen, aufklappbar). Bis dahin die Routen.
for entry in Route::EDITOR_TABS {
CustomLink {
key: "{entry}",
label: entry.get_message().unwrap_or_default(),
@@ -79,8 +90,8 @@ fn Navbar() -> Element {
}
div {
class: "editor-container",
Outlet::<EditorRoute> { }
Outlet::<Route> { }
}
}
}
}
}