Add desktop navigation tree

This commit is contained in:
2026-08-10 16:03:30 +02:00
parent 49451bc988
commit 08f74bfa72
3 changed files with 825 additions and 45 deletions

View File

@@ -15,12 +15,49 @@ use crate::{
const EXTENSIONS_DIRECTORY: &str = ".extensions";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum TreeNodeKind {
Directory,
Entry,
}
/// Stable storage-owned identity for a navigation tree object.
///
/// Frontends retain this value across refreshed [`TreeModel`] instances
/// instead of reconstructing identity from rendered labels.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum TreeNodeId {
Directory(DirectoryPath),
Entry(EntryPath),
}
impl TreeNodeId {
pub fn path(&self) -> &Path {
match self {
Self::Directory(path) => path.as_path(),
Self::Entry(path) => path.as_path(),
}
}
pub fn kind(&self) -> TreeNodeKind {
match self {
Self::Directory(_) => TreeNodeKind::Directory,
Self::Entry(_) => TreeNodeKind::Entry,
}
}
pub fn is_directory(&self) -> bool {
matches!(self, Self::Directory(_))
}
pub fn entry(&self) -> Option<&EntryPath> {
match self {
Self::Entry(path) => Some(path),
Self::Directory(_) => None,
}
}
}
/// Presentation-safe state calculated by storage services for a tree object.
/// Frontends render these flags and never infer them from names or paths.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
@@ -55,8 +92,7 @@ impl TreeNodeIndicators {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TreeNode {
name: String,
path: String,
kind: TreeNodeKind,
id: TreeNodeId,
indicators: TreeNodeIndicators,
children: Vec<TreeNode>,
}
@@ -67,11 +103,18 @@ impl TreeNode {
}
pub fn path(&self) -> &str {
&self.path
self.id
.path()
.to_str()
.expect("tree construction rejects non-UTF-8 paths")
}
pub fn kind(&self) -> TreeNodeKind {
self.kind
self.id.kind()
}
pub fn id(&self) -> &TreeNodeId {
&self.id
}
pub fn indicators(&self) -> TreeNodeIndicators {
@@ -630,8 +673,10 @@ fn finalize_children(node: MutableNode, parent: &Path) -> Result<Vec<TreeNode>,
let children = finalize_children(child, &path)?;
Ok(TreeNode {
name,
path: path_text(&path)?,
kind,
id: match kind {
TreeNodeKind::Directory => TreeNodeId::Directory(DirectoryPath::parse(&path)?),
TreeNodeKind::Entry => TreeNodeId::Entry(EntryPath::parse(&path)?),
},
indicators: TreeNodeIndicators::default(),
children,
})