Skip to content

hue tree / DAG view — Feature Requirements (interactive component)

Status: partial — the tree component + the TUI file explorer shipped (d47a0d01); DAG views planned · Date: 2026-07-30 · Scope: a reusable interactive tree and DAG view component — a sparkles:ui widget rendered on GUI / TUI / HTML. TUI design reference: folke/snacks.nvim's explorer. Use cases: file explorer, tree-sitter inspector, file outline, git graph, dependency graph.

NOTE

Forward-looking — every row is not started. Substrate: sparkles:core-cli already ships a static tree producer (ui.tree) and the tree-view case study grounds the interactive design; this component adds interaction (expand/collapse state, cursor, mouse, filtering) and DAG support. Status legend and IDs: see the overview.

IMPORTANT

The generic tree is a toolkit component, specified as WGT12/VMD1VMD6; this page specifies hue's use of it. Two consequences worth stating up front:

  • TVU1 becomes the directory target. A directory argument opens the file explorer rather than the bespoke index view, and the static gallery becomes the explorer's HTML flavor.
  • The case study's verdict is take snacks' features, ratatui's architecture: snacks' own tree is an incidental data structure (a mutable singleton with parent back-references, and view state and decoration stored on data nodes), so the feature set is the reference but the three-layer split — data / interaction state / view, with a pure flatten between them — is the shape.
  • Expand/collapse state is the toolkit's shared disclosure machine (STM5), the same one folding uses.

Design & rationale

The tree/DAG view is a level-3 widget in the UI component library (ui-architecture.md WGT): one view(state) → Widget definition, rendered on all three targets. Its structure maps onto the library's levels:

  • State machine (level 1, STM) — expand/collapse of nodes (the same collapse model as content folding FLD2), cursor/selection, and viewport. Presentation-free.
  • Layout (level 2, LAY) — indented rows for trees; rail/lane or layered placement for DAGs.
  • Rendering — per-node icon + label + decorations, indent guides, edges; painted per backend (canvas / cells / HTML).

It is the shared component behind several hue features that today would each hand-roll a tree: the tree-sitter inspector overlay (TSI), a file outline, and more (see TVU). The core-cli ui.tree static renderer is the precedent to generalize.

Tree view component (TRV)

Modeled on snacks.nvim's explorer for the TUI idiom.

IDRequirementStatusTraces to
TRV1A tree view must render a hierarchical node model as indented rows: each node an icon + label + optional trailing decorations/badges, with indent guides and an expand/collapse marker on parent nodes.full (d47a0d01) — tree_widget.treeView: guides, marker, DbI icon/label; badges openproposed sparkles:ui tree widget; core-cli.ui.tree
TRV2Expand/collapse must be a presentation-free state machine (shared with folding FLD2 / STM); collapsing hides a subtree, expanding reveals it; expand-all / collapse-all / expand-to-level.full (d47a0d01) — the shared DisclosureState (STM5), also driving foldingSTM; FLD2
TRV3Navigation — a cursor moves by visible row (↑/↓, j/k); ←/h collapses (or moves to parent), →/l expands (or enters first child); Home/End, page scroll; mouse click selects, click on the marker toggles (GUI/TUI SGR mouse); the pane shows a scrollbar when it overflows, and the wheel scrolls the pane under the cursor (tree or document), not the focused one.partial (d47a0d01) — ↑↓/jk/Home/End/PgUp/PgDn, ←/→ collapse-or-parent / expand-or-pick, click selects (second click activates); marker-click toggle opengui.d/previewer.d input; TIN
TRV4Lazy children — a node's children may be produced on demand (for large or filesystem-backed trees), so expansion, not construction, drives cost.full (d47a0d01) — the explorer loads children one level past open (honest markers), recursing only the visible+open chainproposed node-provider callback
TRV5Filtering / live search — an incremental filter must narrow visible nodes (matching nodes + their ancestors kept), snacks-explorer style.full (d47a0d01) — the explorer's / filter: rebuild per keystroke, matches + ancestors (broot mode)reuse FND input model
TRV6Per-node decorations must be data-driven — icon (Nerd-Font, with the FNT8 tofu caveat), label style, and trailing badges (e.g. git status, counts) — supplied by the use-case adapter (TVU), not hardcoded.partial (d47a0d01) — DbI icon/label/slot capabilities; trailing badges (git status) openadapter-supplied node view
TRV7Selection must yield a stable node identity / payload to the caller (e.g. a file path, a CST node, a commit) so actions (open, reveal, jump) act on it.full (d47a0d01) — the explorer returns the picked file path; the caller opens itnode payload contract

DAG support (DAG)

Trees are the common case; several use cases are directed acyclic graphs (shared children, multiple parents) that a strict tree can't express.

IDRequirementStatusTraces to
DAG1The model must accept a DAG (a node reachable by multiple parents): the same node shown once with in/out edges, not duplicated per path; a cycle guard must degrade a mis-supplied cyclic graph safely.not startedproposed graph model
DAG2A rail/lane renderer must draw commit-graph-style edges — the │ ├ ╯ ╰ ┬ lane glyphs of git log --graph — for linear-ish DAGs (git graph); reusing native box-drawing (no procedural BOX in the TUI).not startedgit-graph lane layout
DAG3A layered (Sugiyama-style) node-link renderer must place a general DAG in ranks with routed edges — for dependency graphs; may be GUI-first (the canvas suits free node-link), with a rail/indented fallback on the TUI.not startedlayered graph layout (GUI canvas)
DAG4The renderer choice (indented-tree-with-backedges · rail/lane · layered node-link) must be per use case; the model is one, the presentation is selected by the adapter (TVU).not startedTVU adapter selects renderer

Use cases (TVU)

Each use case is a thin adapter supplying the node model + per-node decorations + renderer choice; the component is shared.

IDUse caseModel → adapterStatusTraces to
TVU1File explorerfilesystem tree (lazy dirs TRV4), file-type + git-status icons/badges (TRV6), open/reveal actions (TRV7) — the snacks-explorer use case; entered by a directory CLI target (SRC4).partial — the TUI + GUI workspace panes ship with badges, filters and re-rooting; the HTML flavor (XPL1) is openproposed FS adapter; sparkles:build-primitives walker; SRC4
TVU2Tree-sitter inspectorthe CST as a tree (named/anonymous nodes, field names, S-expression); renders the overlays.md TSI panel.not startedsparkles:tree-sitter CST; TSI
TVU3File outlinedocument symbols — code structure (functions/classes) from the CST, or headings from MdDoc — a jump-to-symbol outline.not startedsparkles:syntax CST / md/model.d; FSR3
TVU4Git graphthe commit DAG, rail/lane rendered (DAG2); refs/branches as node badges. First concrete consumers: pick two commits → diff them (DVS3) and the stacked-PR tree (DPR5, the git-spice/av fliptree).not startedgit adapter → DAG2
TVU5Dependency graph (build)a build-system / module DAG (targets → deps), layered or rail rendered (DAG3/DAG2).not startedbuild-graph adapter → DAG3
TVU6Changed-files treethe diff/PR session (DVS4) as a tree: per-file status glyphs (the XPF1 git-status machinery), ± line stats, viewed-mark checkboxes (DCM5), conflict badges (CFV3); selecting a file jumps the diff pane (DVG1).full (2ac6dd0b)diff-session adapter → TRV; DVS4

Explorer workspace integration (XPL)

The explorer is not a separate mode but hue's workspace shell: a neovim-style split with the tree in a left pane and the open document in the right pane, on every backend. The current full-screen tree → viewer → tree loop is the interim shape it replaces.

IDRequirementStatusTraces to
XPL1The explorer must be available on all three targets — TUI, GUI, and HTML (the gallery's index becomes the tree's static flavor, per TRB3's pure-CSS <details> doctrine) — one tree definition, per-backend canvases.not startedTRB1TRB3; gallery.md GAL5
XPL2Split-pane layout (neovim snacks.explorer style): the tree in a left sidebar, the currently open document in the right pane — one window/screen, two viewports; the sidebar is toggleable and the split ratio is configurable.partial (3116fb2c+) — the TUI and GUI workspaces ship (toggleable sidebar, one loop each, focus routing; --tree-width seeds the split and dragging the divider resizes it live on both backends (STM8); the GUI pane gained the live filter); the HTML flavor is openLAY7 viewports; ui/layout.md
XPL3The currently open document is highlighted in the tree (a distinct row style from the cursor), and stays highlighted as the tree is scrolled, filtered, or re-expanded; both indicators (open document, cursor row) take their colors from the current theme, not from palette constants.full (0648bdc3) — TUI: the open document's node carries a distinct slot through rebuildsTRV7 node identity; Slot.selection
XPL4Prev/next document navigation ([/], set navigation) must update the explorer too: the new document's node is selected and revealed (ancestor dirs auto-expanded, scrolled into view) — the tree and the viewer never disagree on "current".full (3116fb2c) — TUI: [/] walk the tree's files; GUI: every openPath (tree, set navigation, startup) revealsnavigation.md GNV; TRV3
XPL5The explorer must follow the current theme: cycling the theme re-skins the tree (page colors + the palette its slots resolve against) in the same frame, on every backend — not just the document's syntax colors.full (36cf5cea) — GUI applyTheme re-skins the tree; the TUI workspace re-resolves it after every theme cycleapplyTheme/workspace theme sync; ui/theme.md THM
XPL6Tree rows must show file-type icons (vscode-icons / snacks-explorer style): per-extension Nerd glyphs with their brand colors, a distinct icon for an open vs closed directory, and a generic fallback — data supplied by the FS adapter through the tree view's DbI icon capability, never hardcoded in the component (TRV6). The folder icon is the disclosure indicator: the explorer suppresses the separate / marker (it would duplicate the icon), and one space separates the guide connector from every icon.full (36cf5cea) — per-extension Nerd glyphs + brand colors via the treeView iconFg capability; open/closed folder pair from the disclosure stateNerd devicons; FsEntry.icon; TRV6

Explorer feature scope (XPF)

Scoped against a survey of snacks.nvim's explorer (2026-07-30). In: the pure filesystem/git features below. Out, by decision: file operations (hue stays a viewer — add/rename/delete/move/copy/paste belong to the shell/editor), filesystem watching (manual refresh is the contract instead), LSP-coupled rename/buffer follow-through, and a separate deep-search mode (the existing live filter already searches the whole tree).

IDRequirementStatusTraces to
XPF1Git status integration: one async git status --porcelain -z --ignored=matching per repo root with a TTL cache and a generation guard (a stale in-flight result must never clobber a newer one); per-file badges; worst-status-wins ancestor propagation (ignored deliberately not propagated); whole-ignored/untracked-dir status inherited by contents without listing them; git-ignored rows dimmed; ]/[ next/prev-change navigation while the tree pane is focused (the viewer keeps the brackets for document navigation; a two-key ]g sequence cannot coexist with the workspace's bracket bindings).full (afd3a1d1)git_status.d; FsEntry.badge/gitSt; jumpChange
XPF2Filters & toggles: a hidden-dotfile toggle and a git-ignored toggle (runtime keys, state shown in the chrome), plus glob include/exclude with snacks' precedence — include overrides hidden, ignored, and exclude.full (7200a489+) — H/I toggles + chrome state; --include/--exclude globs with snacks precedence (one visible() predicate)visible/globAny; --include/--exclude
XPF3Re-rooting + close-all: re-root to the selected item's directory, re-root to the parent, re-root outward when revealing a file outside the root (XPL4), and collapse-all.full (361f5eb8) — Shift-R/u re-root (reveal re-roots outward), c collapsesrerootSel/rerootParent/closeAll; reveal
XPF4Manual refresh: a refresh key re-reads the filesystem (invalidating loaded children and the git cache) while preserving the open set — the open/expanded split's payoff. This is the deliberate alternative to filesystem watching.full (afd3a1d1) — r re-lists + forces the git snapshot, preserving the open setrefreshNow; the open/expanded split
XPF5Diagnostics badge seam (future): a provider contract — any source of per-file severities (a compiler/linter channel) feeds worst-severity-wins propagation onto files and ancestors, rendered like the git badges. The propagation is trivial; only the source is missing, so this row waits on one.researchedsnacks diagnostics.lua (40-line propagation)

Per-backend rendering (TRB)

IDRequirementStatusTraces to
TRB1GUI — canvas rows + indent guides + edges via sparkles:raylib-text primitives; mouse hit-test; the layered DAG renderer (DAG3) is GUI-first.not startedgui.md; TGT1
TRB2TUI — cell rows, box-drawing indent guides + git-rail glyphs (native), SGR-mouse + keys; snacks-explorer parity.not startedtui.md; core-cli.ui.tree
TRB3HTML — a nested <ul>/<details> tree with pure-CSS expand/collapse (no JS — the folding/notifier doctrine); DAG as a rail SVG/CSS or indented-with-backedges.not startedapp.d HTML branch; FLD10

Milestones

MilestoneScopeStatusRequirements
V0Interactive tree (expand/collapse state, cursor, mouse) — GUI + TUInot startedTRV1TRV3, TRB1/2
V1Lazy children + filtering + decorations; the file-explorer adapternot startedTRV4TRV7, TVU1
V2Tree-sitter inspector + file outline adaptersnot startedTVU2, TVU3
V3DAG model + git-rail renderer; the git-graph adapternot startedDAG1, DAG2, TVU4
V4Layered node-link renderer; the dependency-graph adapter (GUI-first)not startedDAG3, TVU5
V5HTML <details>/rail renderingnot startedTRB3

Relationship to existing specs

PieceRole
ui-architecture.md WGT/STM/LAYthe widget/state/layout levels this component instantiates
folding.md FLD2shares the expand/collapse state machine
overlays.md TSIthe tree-sitter inspector — rendered by TVU2
sparkles:core-cli ui.tree (static)the precedent renderer to generalize (TRV1)
tree-view case studyinteractive-tree design grounding
docs/specs/tuithe sparkles:tui cell-grid substrate (an interactive tree is a named consumer there)

UI architecture · Overlays · GUI requirements · TUI requirements · Overview