# Queue

The agent's todo list, split into what is left and what is done.

Source: https://suiss.app/ui/docs/ai/queue

**React**

**Example**

```tsx
import {
  CheckCircleIcon,
  CircleIcon,
  ListChecksIcon,
  ListDashesIcon,
  RecordIcon,
} from "@phosphor-icons/react"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Message, MessageContent } from "@/components/ui/message"
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"

type Todo = {
  content: string
  status: "pending" | "in_progress" | "completed"
}

const todos: Todo[] = [
  { content: "Add a dark variant to the theme tokens", status: "completed" },
  { content: "Read the theme from a cookie", status: "completed" },
  { content: "Add a toggle to the settings page", status: "in_progress" },
  { content: "Check contrast on the charts", status: "pending" },
]

function TodoIcon({ status }: { status: Todo["status"] }) {
  if (status === "completed") {
    return (
      <CheckCircleIcon />
    )
  }
  if (status === "in_progress") {
    return (
      <RecordIcon />
    )
  }
  return (
    <CircleIcon />
  )
}

function TodoLine({ todo }: { todo: Todo }) {
  return (
    <TraceLine status={todo.status === "in_progress" ? "running" : undefined}>
      <TraceIcon>
        <TodoIcon status={todo.status} />
      </TraceIcon>
      <TraceTitle>{todo.content}</TraceTitle>
    </TraceLine>
  )
}

export default function QueueDemo() {
  const pending = todos.filter((todo) => todo.status !== "completed")
  const completed = todos.filter((todo) => todo.status === "completed")

  return (
    <div className="flex w-full max-w-sm flex-col gap-4 py-6 text-sm">
      <Message align="end">
        <MessageContent>
          <Bubble align="end" variant="muted">
            <BubbleContent>Add dark mode to the settings page.</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message>
        <MessageContent className="gap-4">
          <Trace>
            <TraceCollapsible defaultOpen>
              <TraceCollapsibleTrigger>
                <TraceLine>
                  <TraceIcon>
                    <ListDashesIcon />
                  </TraceIcon>
                  <TraceTitle>{pending.length} to do</TraceTitle>
                </TraceLine>
              </TraceCollapsibleTrigger>
              <TraceCollapsibleContent>
                <Trace>
                  {pending.map((todo) => (
                    <TodoLine key={todo.content} todo={todo} />
                  ))}
                </Trace>
              </TraceCollapsibleContent>
            </TraceCollapsible>
            <TraceCollapsible defaultOpen>
              <TraceCollapsibleTrigger>
                <TraceLine>
                  <TraceIcon>
                    <ListChecksIcon />
                  </TraceIcon>
                  <TraceTitle>{completed.length} completed</TraceTitle>
                </TraceLine>
              </TraceCollapsibleTrigger>
              <TraceCollapsibleContent>
                <Trace>
                  {completed.map((todo) => (
                    <TodoLine key={todo.content} todo={todo} />
                  ))}
                </Trace>
              </TraceCollapsibleContent>
            </TraceCollapsible>
          </Trace>
        </MessageContent>
      </Message>
    </div>
  )
}
```

Queue is the agent's todo list. Todos sit in two sections, To do and Completed, and the icon on each todo shows its status. The agent updates the list as it works, so a todo moves from one section to the other when it is done.

Each section is a `TraceCollapsible` with a nested `Trace` of todos. For steps the agent decides on up front, use [Plan](https://suiss.app/ui/docs/ai/plan); for steps it takes as it goes, use [Task](https://suiss.app/ui/docs/ai/task).

## Installation

Queue is built with the [Trace](https://suiss.app/ui/docs/components/trace) component.

```bash title="Terminal"
npx shadcn@latest add @esuiss/trace
```

Works in any project with a `components.json`. See [Installation](https://suiss.app/ui/docs/installation#add-components) for the one-line registry setup. Or use the [suiss/ui CLI](https://suiss.app/ui/docs/cli):

```bash title="Terminal"
npx @esuiss/ui@latest add trace
```

## Usage

```tsx
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"
```

```tsx
<Trace>
  <TraceCollapsible defaultOpen>
    <TraceCollapsibleTrigger>
      <TraceLine>
        <TraceIcon>
          <ListTodoIcon />
        </TraceIcon>
        <TraceTitle>2 to do</TraceTitle>
      </TraceLine>
    </TraceCollapsibleTrigger>
    <TraceCollapsibleContent>
      <Trace>
        <TraceLine status="running">
          <TraceIcon>
            <CircleDotIcon />
          </TraceIcon>
          <TraceTitle>Update the registry snapshots</TraceTitle>
        </TraceLine>
        <TraceLine>
          <TraceIcon>
            <CircleIcon />
          </TraceIcon>
          <TraceTitle>Add tests for the combobox</TraceTitle>
        </TraceLine>
      </Trace>
    </TraceCollapsibleContent>
  </TraceCollapsible>
  <TraceCollapsible>
    <TraceCollapsibleTrigger>
      <TraceLine>
        <TraceIcon>
          <ListChecksIcon />
        </TraceIcon>
        <TraceTitle>1 completed</TraceTitle>
      </TraceLine>
    </TraceCollapsibleTrigger>
    <TraceCollapsibleContent>
      <Trace>
        <TraceLine>
          <TraceIcon>
            <CircleCheckIcon />
          </TraceIcon>
          <TraceTitle>Add a changeset</TraceTitle>
        </TraceLine>
      </Trace>
    </TraceCollapsibleContent>
  </TraceCollapsible>
</Trace>
```

Map each todo status to a section, an icon and a `TraceLine` status:

| Todo status | Section | Icon | `TraceLine` status |
| --- | --- | --- | --- |
| `pending` | To do | Circle | — |
| `in_progress` | To do | Circle with a dot | `running` while the agent works |
| `completed` | Completed | Circle with a check | — |

## Examples

### Descriptions

A todo with a description is a `TraceCollapsible` that opens to show it. A todo without one stays a `TraceLine`.

**Example**

```tsx
import {
  CheckCircleIcon,
  CircleIcon,
  ListChecksIcon,
  ListDashesIcon,
} from "@phosphor-icons/react"
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"

const todos: { title: string; description?: string; done: boolean }[] = [
  {
    title: "Write the migration guide",
    description: "Cover the renamed props and the new data attributes.",
    done: true,
  },
  { title: "Add a changeset", done: true },
  {
    title: "Fix the focus ring on the select trigger",
    description: "It is clipped by overflow-hidden on the field.",
    done: false,
  },
  { title: "Update the registry snapshots", done: false },
  {
    title: "Add tests for the combobox",
    description: "Typing, clearing and keyboard selection.",
    done: false,
  },
]

function TodoRow({ todo }: { todo: (typeof todos)[number] }) {
  const line = (
    <TraceLine>
      <TraceIcon>
        {todo.done ? (
          <CheckCircleIcon />
        ) : (
          <CircleIcon />
        )}
      </TraceIcon>
      <TraceTitle>{todo.title}</TraceTitle>
    </TraceLine>
  )

  if (!todo.description) {
    return line
  }

  return (
    <TraceCollapsible>
      <TraceCollapsibleTrigger>{line}</TraceCollapsibleTrigger>
      <TraceCollapsibleContent>{todo.description}</TraceCollapsibleContent>
    </TraceCollapsible>
  )
}

export default function QueueDescriptions() {
  const pending = todos.filter((todo) => !todo.done)
  const completed = todos.filter((todo) => todo.done)

  return (
    <Trace className="max-w-sm">
      <TraceCollapsible defaultOpen>
        <TraceCollapsibleTrigger>
          <TraceLine>
            <TraceIcon>
              <ListDashesIcon />
            </TraceIcon>
            <TraceTitle>{pending.length} to do</TraceTitle>
          </TraceLine>
        </TraceCollapsibleTrigger>
        <TraceCollapsibleContent keepMounted>
          <Trace>
            {pending.map((todo) => (
              <TodoRow key={todo.title} todo={todo} />
            ))}
          </Trace>
        </TraceCollapsibleContent>
      </TraceCollapsible>
      <TraceCollapsible>
        <TraceCollapsibleTrigger>
          <TraceLine>
            <TraceIcon>
              <ListChecksIcon />
            </TraceIcon>
            <TraceTitle>{completed.length} completed</TraceTitle>
          </TraceLine>
        </TraceCollapsibleTrigger>
        <TraceCollapsibleContent keepMounted>
          <Trace>
            {completed.map((todo) => (
              <TodoRow key={todo.title} todo={todo} />
            ))}
          </Trace>
        </TraceCollapsibleContent>
      </TraceCollapsible>
    </Trace>
  )
}
```

## AI SDK

The agent writes its todos with a tool, `writeTodos` in this example. Each call sends the whole list, so render the last call and skip one whose input is still streaming.

```tsx
const todos = message.parts
  .filter((part) => part.type === "tool-writeTodos")
  .filter((part) => part.state !== "input-streaming")
  .at(-1)?.input?.todos
```

Set `running` on the todo in progress only while the chat streams, so its title stops shimmering when the agent stops.

## API Reference

See [Trace](https://suiss.app/ui/docs/components/trace) for every part and prop.

**React Native, SwiftUI, Jetpack Compose**

```tsx
import { CheckCircleIcon, CircleIcon, ListChecksIcon, ListDashesIcon, RecordIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Icon } from "@/components/ui/icon"
import { Message, MessageContent } from "@/components/ui/message"
import { Trace, TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceIcon, TraceLine, TraceTitle } from "@/components/ui/trace"

export function AiQueueExample() {
  return (
    <View className="w-full gap-4">
      <Message align="end">
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>Add dark mode to the settings page.</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Trace>
        <TraceCollapsible defaultOpen>
          <TraceCollapsibleTrigger>
            <TraceLine>
              <TraceIcon>
                <Icon as={ListDashesIcon} />
              </TraceIcon>
              <TraceTitle>2 to do</TraceTitle>
            </TraceLine>
          </TraceCollapsibleTrigger>
          <TraceCollapsibleContent>
            <Trace>
              <TraceLine status="running">
                <TraceIcon>
                  <Icon as={RecordIcon} />
                </TraceIcon>
                <TraceTitle>Add a toggle to the settings page</TraceTitle>
              </TraceLine>
              <TraceLine status="pending">
                <TraceIcon>
                  <Icon as={CircleIcon} />
                </TraceIcon>
                <TraceTitle>Check contrast on the charts</TraceTitle>
              </TraceLine>
            </Trace>
          </TraceCollapsibleContent>
        </TraceCollapsible>
        <TraceCollapsible defaultOpen>
          <TraceCollapsibleTrigger>
            <TraceLine>
              <TraceIcon>
                <Icon as={ListChecksIcon} />
              </TraceIcon>
              <TraceTitle>2 completed</TraceTitle>
            </TraceLine>
          </TraceCollapsibleTrigger>
          <TraceCollapsibleContent>
            <Trace>
              <TraceLine>
                <TraceIcon>
                  <Icon as={CheckCircleIcon} />
                </TraceIcon>
                <TraceTitle>Add a dark variant to the theme tokens</TraceTitle>
              </TraceLine>
              <TraceLine>
                <TraceIcon>
                  <Icon as={CheckCircleIcon} />
                </TraceIcon>
                <TraceTitle>Read the theme from a cookie</TraceTitle>
              </TraceLine>
            </Trace>
          </TraceCollapsibleContent>
        </TraceCollapsible>
      </Trace>
    </View>
  )
}
```

```swift
import EsuissUI
import PhosphorSwift
import SwiftUI

struct QueueExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 16) {
      Message(align: .end) {
        MessageContent {
          Bubble(variant: .muted, align: .end) { BubbleContent("Add dark mode to the settings page.") }
        }
      }
      Trace {
        TraceCollapsible(initiallyExpanded: true) {
          TraceCollapsibleTrigger {
            TraceLine {
              TraceIcon { Ph.listDashes.regular }
              TraceTitle(verbatim: "2 to do")
            }
          }
          TraceCollapsibleContent {
            Trace {
              TraceLine(status: .running) {
                TraceIcon { Ph.record.regular }
                TraceTitle(verbatim: "Add a toggle to the settings page")
              }
              TraceLine(status: .pending) {
                TraceIcon { Ph.circle.regular }
                TraceTitle(verbatim: "Check contrast on the charts")
              }
            }
          }
        }
        TraceCollapsible(initiallyExpanded: true) {
          TraceCollapsibleTrigger {
            TraceLine {
              TraceIcon { Ph.listChecks.regular }
              TraceTitle(verbatim: "2 completed")
            }
          }
          TraceCollapsibleContent {
            Trace {
              TraceLine {
                TraceIcon { Ph.checkCircle.regular }
                TraceTitle(verbatim: "Add a dark variant to the theme tokens")
              }
              TraceLine {
                TraceIcon { Ph.checkCircle.regular }
                TraceTitle(verbatim: "Read the theme from a cookie")
              }
            }
          }
        }
      }
    }
  }
}
```

```kotlin
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.CheckCircle
import com.adamglin.phosphoricons.regular.Circle
import com.adamglin.phosphoricons.regular.ListChecks
import com.adamglin.phosphoricons.regular.ListDashes
import com.adamglin.phosphoricons.regular.Record
import com.esuiss.ui.components.Bubble
import com.esuiss.ui.components.BubbleContent
import com.esuiss.ui.components.BubbleVariant
import com.esuiss.ui.components.Icon
import com.esuiss.ui.components.Message
import com.esuiss.ui.components.MessageAlign
import com.esuiss.ui.components.MessageContent
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.TraceIcon
import com.esuiss.ui.components.TraceLine
import com.esuiss.ui.components.TraceStatus
import com.esuiss.ui.components.TraceTitle

@Composable
fun QueueAIDefault() {
    Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) {
        Message(Modifier.fillMaxWidth(), align = MessageAlign.End) {
            MessageContent {
                Bubble(variant = BubbleVariant.Muted) { BubbleContent("Add dark mode to the settings page.") }
            }
        }
        Trace {
            TraceCollapsible(initiallyExpanded = true) {
                TraceCollapsibleTrigger {
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.ListDashes, contentDescription = null) }
                        TraceTitle("2 to do")
                    }
                }
                TraceCollapsibleContent {
                Trace {
                    TraceLine(status = TraceStatus.Running) {
                        TraceIcon { Icon(PhosphorIcons.Regular.Record, contentDescription = null) }
                        TraceTitle("Add a toggle to the settings page")
                    }
                    TraceLine(status = TraceStatus.Pending) {
                        TraceIcon { Icon(PhosphorIcons.Regular.Circle, contentDescription = null) }
                        TraceTitle("Check contrast on the charts")
                    }
                }
                }
            }
            TraceCollapsible(initiallyExpanded = true) {
                TraceCollapsibleTrigger {
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.ListChecks, contentDescription = null) }
                        TraceTitle("2 completed")
                    }
                }
                TraceCollapsibleContent {
                Trace {
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.CheckCircle, contentDescription = null) }
                        TraceTitle("Add a dark variant to the theme tokens")
                    }
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.CheckCircle, contentDescription = null) }
                        TraceTitle("Read the theme from a cookie")
                    }
                }
                }
            }
        }
    }
}
```

Queue on mobile is built from the same parts as on the web: [Trace](https://suiss.app/ui/docs/components/trace), [Message](https://suiss.app/ui/docs/components/message), [Bubble](https://suiss.app/ui/docs/components/bubble). The example above is a static demo; connect it to your agent by rendering the same parts from your messages.

## Installation

**React Native**

```bash title="Terminal"
npx @esuiss/ui@latest add trace message bubble
```

**SwiftUI**

```bash title="Terminal"
npx @esuiss/ui@latest add trace message bubble --platform swift
```

**Jetpack Compose**

```bash title="Terminal"
npx @esuiss/ui@latest add trace message bubble --platform kotlin
```

## API Reference

See [Trace](https://suiss.app/ui/docs/components/trace), [Message](https://suiss.app/ui/docs/components/message), [Bubble](https://suiss.app/ui/docs/components/bubble).

