Skip to content

Add lazy child loading for large and remote trees #16

Description

@RayZhao1998

What to build

Add an opt-in lazy-child data path for large, remote, or expensive hierarchies without breaking the existing eager PreparedTree API.

The model should accept an asynchronous children provider, expose unloaded/loading/loaded/failed state to every renderer, and load a branch only when interaction requires it. Existing eager trees should continue to use the same public initializers and behavior, while both eager and lazy inputs converge on one internal tree-query/store seam so renderers no longer depend directly on PreparedTree storage.

This feature owns hierarchy state, request coordination, validation, and renderer updates. It does not enumerate the filesystem, watch directories, persist state, or choose a cache-eviction policy for callers.

Acceptance criteria

  • Existing PreparedTree, FileTreeModel(preparedTree), FileTree, and FileTreeView call sites remain source-compatible and do not become implicitly asynchronous.
  • TreeKit provides a Sendable lazy children provider with separate root loading, mightHaveChildren, and child-loading capabilities.
  • Renderers query hierarchy data through a model/store interface instead of reaching through the model to preparedTree internals.
  • Every expandable item has explicit unloaded, loading, loaded, or failed state; row context exposes enough state for custom loading, error, and retry presentation without fake nodes.
  • Expanding an unloaded branch starts one request, concurrent expansion requests coalesce, and collapsing a successfully loaded branch retains its children for the model lifetime.
  • Reset and cancellation invalidate in-flight generations, and stale results cannot overwrite newer model state even when the underlying task does not stop immediately.
  • Loaded nodes are validated for globally unique and stable IDs before one atomic model transaction publishes them; invalid results preserve the previous hierarchy and surface a typed error.
  • Failed loads can be retried explicitly and do not leave disclosure, selection, focus, or expansion in an inconsistent state.
  • Lazy reveal can traverse unloaded ancestors through an explicit ID path or provider resolver, coalescing existing requests before selecting and scrolling to the target.
  • SwiftUI, AppKit, and UIKit expose equivalent expansion, retry, selection, focus, accessibility, and reveal semantics.
  • The Demo includes deterministic slow, failed, retried, cancelled, and remote-style lazy branches alongside the existing eager tree.
  • Tests cover cancellation, stale completion, retry, duplicate IDs, hidden descendant expansion, reveal through unloaded ancestors, reset during load, sorting, selection retention, and concurrent expansion.
  • Performance evidence compares eager and lazy modes at 2,500, 100,000, and at least 500,000 potential nodes, reporting discovered/loaded node counts, physical and peak footprint, main-thread time, and visible-content update latency.
  • DocC and README document eager versus lazy input, provider ownership, failure behavior, caching guarantees, and the filesystem/persistence boundary.

Related

  • Roadmap: close the @pierre/trees feature gaps #7 intentionally treated lazy loading as a non-goal for the previous feature-parity roadmap.
  • The repository design notes in Docs/LazyLoading.md define the initial compatibility and state-machine direction.

Non-goals

  • Filesystem enumeration or directory watching.
  • Git repository state or built-in Git synchronization.
  • Persistence of expansion, selection, or loaded children.
  • Automatic LRU/time-based eviction before profiling demonstrates a need and its interaction semantics are specified.
  • Web SSR, hydration, React, DOM, or CSS-specific APIs.

Blocked by

None - can start immediately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions