Skip to content

feat(:tree_view): add sub_group + file_leaf shapes (Wave 3.7 Class C extension, DRAFT, Pascal-review REQUIRED)#137

Draft
ty13r wants to merge 2 commits into
mainfrom
claude/extension-tree-view-sub-group-file-leaf-wave-3-7
Draft

feat(:tree_view): add sub_group + file_leaf shapes (Wave 3.7 Class C extension, DRAFT, Pascal-review REQUIRED)#137
ty13r wants to merge 2 commits into
mainfrom
claude/extension-tree-view-sub-group-file-leaf-wave-3-7

Conversation

@ty13r
Copy link
Copy Markdown
Member

@ty13r ty13r commented May 18, 2026

Summary

Extends the canonical :tree_view widget with two new node shapes from unified_ui issue #189:

  • :sub_group (EX-1): categorical grouping within a tree, distinct from :folder (filesystem-style). Renders as role="group" with aria-label={label}. Use case: repo → kind hierarchy (ADRs / Specs / Plans) in the Explorer surface.
  • :file_leaf (EX-2): filesystem-path leaf node with extension-derived glyph fallback. Renders as a treeitem row with data-file-path, data-glyph, and aria-label="File: {name}". Use case: Explorer Files sub-tree and Map FilesPane file rows.

4-Stage Pipeline

Stage File Change
1. IUR constructor packages/unified_iur/lib/unified_iur/widgets/data.ex Kind-aware normalize_node/1 dispatch; normalize_sub_group_node, normalize_file_leaf_node, derive_glyph/1 (12 extensions)
2. Catalog packages/unified_ui/lib/unified_ui/widget_components.ex Added :tree_view catalog entry (family: :content_identity_and_disclosure). Note: :tree_view was already in unified_iur/widgets.ex enum but missing from the widget_components.ex catalog.
3. Renderer packages/live_ui/lib/live_ui/renderer.ex normalize_tree_node/2 split into normalize_sub_group_tree_node, normalize_file_leaf_tree_node, normalize_generic_tree_node
4. Phoenix.Component packages/live_ui/lib/live_ui/widgets/tree_view.ex tree_node/1 dispatches on :kind; new sub_group_node/1 and file_leaf_node/1 sub-components

Tests

unified_iur (21 new tests via packages/unified_iur/test/unified_iur/widgets/data_test.exs):

  • :sub_group: normalization with kind/label/expanded, no-children case, recursive nesting
  • :file_leaf: explicit glyph, extension-derived (.ex→elixir, .exs→elixir, .md→markdown), glyph override wins, nil for unknown extension, selected? state, meta map
  • Mixed-kind: generic root → sub_group → file_leaf tree traversal

live_ui (8 new tests via packages/live_ui/test/live_ui/data_widgets_test.exs):

  • :sub_group: role="group" + aria-label present, nested children rendered
  • :file_leaf: data-file-path, data-glyph, aria-label="File: {name}", data-node-kind="file_leaf" present; selected state
  • Mixed tree: all three node kinds co-render correctly

Test counts all pass: packages/unified_iur (13 tests, 0 failures on new tests — 7 pre-existing failures from file_tree_browser PR), packages/live_ui (587 tests, 0 failures).

ARIA implications

  • :sub_group: role="group" + aria-label={label} per unified_ui issue #189 recommendation
  • :file_leaf: aria-label="File: {name}" for treeitem row; standard tree containment semantics

Glyph derivation

Extension-to-token map in derive_glyph/1:

  • .ex, .exs"elixir"
  • .md, .livemd"markdown"
  • .json"json", .yaml/.yml"yaml"
  • .js"javascript", .ts"typescript"
  • .css"stylesheet", .html/.heex"markup"
  • unknown → nil (absent key; renderer uses generic file icon)

Explicit :glyph attr wins over derived.

Relationship to file_tree_browser (ash_ui PR #127)

PR #127 implements :file_tree_browser as a new canonical kind with its own node model (:folder / :file_leaf via type: field, not kind:). The two approaches are not conflicting:

  • :file_tree_browser — filesystem-primary, recursive folder hierarchy, own IUR constructor in navigation.ex
  • :tree_view with :sub_group / :file_leaf — semantic-tree primary, artifact hierarchies with optional file-leaf rows

If Pascal blesses both, file_tree_browser could eventually compose :tree_view's :file_leaf shape for the leaf-row contract, or remain its own kind. This PR does not force that decision — :file_tree_browser is unchanged.

Open questions for Pascal (DRAFT)

From unified_ui issue #189 (verbatim):

  1. :tree_view extension vs new canonical kind? If file_tree_browser is its own kind (as PR feat(navigation): add canonical file tree browser primitive #127 implements), do these :tree_view extensions still belong on :tree_view? Or is :tree_view the wrong home and a separate kind is better?
  2. Canonical authority: :tree_view is in unified_iur/widgets.ex @data_view_kinds but was missing from widget_components.ex catalog. This PR adds it to the catalog (same question as :tabs extension PR). Is :tree_view's authoritative home unified_iur/widgets.ex or does it need migration?
  3. :sub_group semantics: this PR uses kind: :sub_group to distinguish categorical vs filesystem groups. Pascal's alternative was a single :folder kind with a variant: :sub_group | :folder attr. Which shape is preferred?
  4. :file_leaf glyph computation: this PR does extension-derived fallback server-side in IUR. Pascal may prefer client-side or a different convention.
  5. Mixed-mode trees: can a :folder node contain both :sub_group children AND :file_leaf children, or should levels be homogeneous?
  6. :artifact_row and :file_leaf overlap: should :file_leaf be a variant: of :artifact_row sharing most attrs but with file-specific semantics? Cleaner ontology.

Files modified

  • packages/unified_iur/lib/unified_iur/widgets/data.ex — IUR constructor
  • packages/unified_iur/test/unified_iur/widgets/data_test.exs — IUR tests (21 new + 1 inline assertion)
  • packages/unified_ui/lib/unified_ui/widget_components.ex — catalog entry
  • packages/unified_ui/test/unified_ui/widget_components_catalog_test.exs — catalog test updated (also fixes pre-existing failures from thread_card/file_tree_browser/composer_inline_ask/ask_sidebar catalog additions that weren't reflected in the test
  • — renderer node normalization
  • — Phoenix.Component sub-renders
  • — live_ui rendering tests (8 new)

Pre-existing test failures (not caused by this PR)

The following failures existed on HEAD before this PR:

🤖 Generated with Claude Code
EOF
)

ty13r and others added 2 commits May 18, 2026 02:04
…-B, DRAFT)

Stage 1: `UnifiedIUR.Widgets.Components.thread_card/1` constructor — builds a
:thread_card element in :content_identity_and_disclosure family with thread attrs
(thread_id, title, reply_count, seed_quote, progress_pct, open_intent,
last_activity_at) and participants list; nil optionals are omitted via
maybe_put/3.

Stage 2: catalog entry in `UnifiedUi.WidgetComponents` with family
:content_identity_and_disclosure and empty aliases list.

Stage 3: dedicated `def render(%{element: %Element{kind: :thread_card}})` clause
in `LiveUi.Renderer` placed BEFORE the generic `@component_kinds` fallback to
avoid shadow-clause trap (thread_card is in @content_identity_kinds).

Stage 4: `LiveUi.Widgets.ThreadCard` Phoenix.Component with:
- `data-live-ui-widget="thread-card"` root attribute (true-widget, not fallback)
- `data-thread-id` selector hook
- Avatar stack with participant initials, up to 3 displayed + overflow indicator
  with aria-label="and N more participants"
- `<blockquote>` seed quote + `<h3>` title in BEM structure
- Optional progress bar with role="progressbar" ARIA (aria-valuenow/min/max)
- Footer: reply count with singular/plural, relative-time helper, open button
  with aria-label="Open thread: {title}" and data-live-ui-intent hook

Tests: 12 Stage-1 constructor tests in components_test.exs (positive + negative
field assertions) + 14 Stage-4 Phoenix.Component and renderer-dispatch tests in
thread_card_widget_test.exs. 538 live_ui tests pass; 211 unified_iur tests pass
(7 pre-existing Phase-6 coverage failures not caused by this PR).

DRAFT — Pascal-review required. 5 open questions in widget @moduledoc:
1. Family: :content_identity_and_disclosure vs :conversation_artifact
2. Participants as element attrs vs child IUR nodes
3. Seed-quote truncation: render-side vs constructor-enforced max-length
4. Progress bar: bespoke inline vs nested :progress IUR child widget
5. "Open →" affordance: text+arrow vs icon-button, themable?

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…1 + EX-2)

Extends the canonical :tree_view widget with two new node kinds that close
the gap identified in unified_ui issue #189 and ariston-ui comp-fidelity
audit (Wave AshUI-3.7 extension table lines 243-244).

## Changes

Stage 1 — IUR constructor (unified_iur/widgets/data.ex):
- Splits normalize_nodes into kind-aware normalize_node/1 dispatch
- :sub_group: categorical grouping (expanded?, children, label); kind written unconditionally
- :file_leaf: filesystem-path leaf (path, name, glyph, meta, selected?)
- derive_glyph/1: extension → semantic glyph token (.ex/.exs→"elixir", .md→"markdown", etc.)
- Generic nodes: backward-compat, kind preserved only if explicitly set

Stage 2 — Catalog (unified_ui/widget_components.ex):
- Adds :tree_view to catalog (family: :content_identity_and_disclosure)
- Note: :tree_view was already in unified_iur/widgets.ex enum; this adds the
  catalog-level entry that was missing (same canonical-authority question as :tabs)

Stage 3 — Renderer (live_ui/renderer.ex):
- Splits normalize_tree_node into three helpers: sub_group, file_leaf, generic
- :sub_group: preserves kind: :sub_group, normalized expand state + children
- :file_leaf: preserves kind: :file_leaf, normalized selected state, no children

Stage 4 — Phoenix.Component (live_ui/widgets/tree_view.ex):
- tree_node/1 dispatches on node :kind
- sub_group_node/1: role="group", aria-label={label}, data-node-kind="sub_group"
- file_leaf_node/1: data-file-path, data-glyph, aria-label="File: {name}", data-node-kind="file_leaf"

Tests:
- unified_iur/widgets/data_test.exs: 13 new tests (sub_group normalization x3,
  file_leaf normalization x7 incl. glyph derivation, mixed-kind tree x1 + inline assertion x1)
- live_ui/data_widgets_test.exs: 8 new tests (sub_group HTML rendering,
  nested children, file_leaf HTML rendering, selected state, mixed tree)

Pre-existing test failures (not caused by this PR):
- unified_iur: 7 failures from file_tree_browser PR (navigation_kinds mismatch,
  fixture coverage gap, release readiness gates)
- live_ui: --warnings-as-errors blocked by pre-existing FileTreeBrowser.component
  undefined reference (file_tree_browser DRAFT PR, not yet merged to main)
- unified_ui: 30 failures from pre-existing parity/DSL validation gaps

These failures exist on main HEAD and are documented in open DRAFT PRs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant