suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
  • AI
GitHubCreate
DocsComponentsPatternsInteractionsChartsBlocksAICreate
AI21
  • Overview
Conversation
  • Chat
  • Message
  • Prompt Input
  • Suggestion
  • Model Selector
  • Context
  • Checkpoint
  • Inline Citation
Agent steps
  • Chain of Thought
  • Reasoning
  • Tool
  • Confirmation
  • Task
  • Plan
  • Queue
  • Subagent
  • Sources
Output
  • Terminal
  • Stack Trace
  • Test Results
  • File Tree

suiss/ui · AI

File Tree

The files an agent changed, shown as a folder tree you can open, with the selected file marked.

"use client"

import { CaretRightIcon, FileIcon, FolderIcon } from "@phosphor-icons/react"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"
import { cn } from "@/lib/utils"

type FileNode =
  | { type: "file"; name: string; path: string }
  | { type: "folder"; name: string; path: string; children: FileNode[] }

File Tree shows the files the agent changed as folders you can open. A folder is a Collapsible and a file is a Button. For a single file the agent read or edited, use a Tool row instead.

Installation

File Tree is built with the Collapsible and Button components.

$ pnpm dlx shadcn@latest add @esuiss/collapsible @esuiss/button

Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add collapsible button

Usage

Render nodes recursively, and nest each folder's children in its CollapsibleContent.

if (node.type === "folder") {
  return (
    <Collapsible defaultOpen>
      <CollapsibleTrigger
        render={
          <Button
            variant="ghost"
            size="sm"
            className="group/folder w-full justify-start"
          />
        }
      >
        <ChevronRightIcon className="transition-transform group-aria-expanded/folder:rotate-90" />
        <FolderIcon />
        {node.name}
      </CollapsibleTrigger>
      <CollapsibleContent>
        <div className="ms-4 flex flex-col">
          {node.children.map((child) => (
            <FileTreeNode key={child.path} node={child} />
          ))}
        </div>
      </CollapsibleContent>
    </Collapsible>
  )
}
if (node.type === "folder") {
  return (
    <Collapsible defaultOpen>
      <CollapsibleTrigger asChild>
        <Button
          variant="ghost"
          size="sm"
          className="group/folder w-full justify-start"
        >
          <ChevronRightIcon className="transition-transform group-aria-expanded/folder:rotate-90" />
          <FolderIcon />
          {node.name}
        </Button>
      </CollapsibleTrigger>
      <CollapsibleContent>
        <div className="ms-4 flex flex-col">
          {node.children.map((child) => (
            <FileTreeNode key={child.path} node={child} />
          ))}
        </div>
      </CollapsibleContent>
    </Collapsible>
  )
}
if (node.type === "folder") {
  return (
    <Collapsible defaultExpanded>
      <Button
        slot="trigger"
        variant="ghost"
        size="sm"
        className="group/folder w-full justify-start"
      >
        <ChevronRightIcon className="transition-transform group-aria-expanded/folder:rotate-90" />
        <FolderIcon />
        {node.name}
      </Button>
      <CollapsibleContent>
        <div className="ms-4 flex flex-col">
          {node.children.map((child) => (
            <FileTreeNode key={child.path} node={child} />
          ))}
        </div>
      </CollapsibleContent>
    </Collapsible>
  )
}

A file is a ghost button that selects it. Set aria-current on the selected file.

<Button
  variant="ghost"
  size="sm"
  className="w-full justify-start"
  aria-current={selected === node.path ? "true" : undefined}
>
  <FileIcon />
  {node.name}
</Button>

API Reference

See Collapsible and Button.

Accessibility

  • Each folder is a button with aria-expanded, and opens and closes with Enter or Space.
  • Mark the selected file with aria-current as well as a background, so the selection is announced.
  • The chevron and the folder and file icons are decorative; the name is the accessible label.
import { FileIcon, FolderIcon } from "phosphor-react-native"
import { Icon } from "@/components/ui/icon"
import { Trace, TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceDescription, TraceIcon, TraceLine, TraceTitle } from "@/components/ui/trace"

export function AiFileTreeExample() {
  return (
    <Trace>
      <TraceCollapsible defaultOpen>
        <TraceCollapsibleTrigger>
          <TraceLine>
            <TraceIcon>
              <Icon as={FolderIcon} />
            </TraceIcon>
            <TraceTitle>app</TraceTitle>
          </TraceLine>
        </TraceCollapsibleTrigger>
import EsuissUI
import PhosphorSwift
import SwiftUI

struct FileTreeExample: View {
  var body: some View {
    Trace {
      TraceCollapsible(initiallyExpanded: true) {
        TraceCollapsibleTrigger {
          TraceLine {
            TraceIcon { Ph.folder.regular }
            TraceTitle(verbatim: "app")
          }
        }
        TraceCollapsibleContent {
          Trace {
import androidx.compose.runtime.Composable
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.File
import com.adamglin.phosphoricons.regular.Folder
import com.esuiss.ui.components.Icon
import com.esuiss.ui.components.Trace
import com.esuiss.ui.components.TraceCollapsible
import com.esuiss.ui.components.TraceCollapsibleContent
import com.esuiss.ui.components.TraceCollapsibleTrigger
import com.esuiss.ui.components.TraceDescription
import com.esuiss.ui.components.TraceIcon
import com.esuiss.ui.components.TraceLine
import com.esuiss.ui.components.TraceTitle

@Composable

File Tree on mobile is built from the same parts as on the web: Trace. The example above is a static demo; connect it to your agent by rendering the same parts from your messages.

Installation

$ pnpm dlx @esuiss/ui@latest add trace
$ pnpm dlx @esuiss/ui@latest add trace --platform swift
$ pnpm dlx @esuiss/ui@latest add trace --platform kotlin

API Reference

See Trace.

PreviousTest Results

On this page

  1. 01Installation
  2. 02Usage
  3. 03API Reference
  4. 04Accessibility
  5. 05Installation
  6. 06API Reference