# SubagentList

> Fan-out panel for parallel workers: one row per delegated agent with its status dot, model badge, task, and its own progress bar.

- Package: `fancy-ui-svelte` (Svelte 5 + Tailwind CSS v4)
- Category: AI Agents
- Tags: ai, agents, parallel, progress, workers
- Docs: https://fancy-ui.rama.app/docs/components/subagent-list
- Source: https://github.com/RamaHerbin/fancy-ui/tree/main/src/lib/fancy-ui/subagent-list

## Import

```svelte
<script lang="ts">
  import { SubagentList } from 'fancy-ui-svelte';
</script>
```

Setup (once per project): `npm install fancy-ui-svelte`, then add `@import "fancy-ui-svelte/tailwind.css";` after `@import "tailwindcss";` in your main CSS file. Full guide: https://fancy-ui.rama.app/llms.txt

React: `import { SubagentList } from 'fancy-ui-react';` (same prop names; `class` becomes `className`, snippets become `ReactNode`).

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `agents` (required) | `SubagentData[]` | — | The delegated workers, in the order they were spawned: id, name, task, status, and optionally progress and model |
| `label` | `string` | — | Accessible name for the list. Left unset it is derived from the statuses — "2 agents running" while anything runs, "3 agents finished" once the last one lands |
| `onSelect` | `(agent: SubagentData, index: number) => void` | — | Called when a row is activated; supplying it turns every row into a button |
| `compact` | `boolean` | `false` | Tighter rows with the task line dropped |
| `sound` | `boolean` | `false` | Plays select when a subagent row is activated, once the user has enabled sound |

## Snippets

| Snippet | Description |
| --- | --- |
| `item` | Replaces the built-in row body, keeping the status dot |

## Examples

### Basic Usage

```svelte
<script lang="ts">
	import { onMount } from "svelte";
	import { SubagentList } from "fancy-ui-svelte";
	import type { SubagentData } from "fancy-ui-svelte";

	/** How often the fan-out redraws. */
	const TICK_MS = 120;
	/** How long the finished roster is held before the demo spawns them again. */
	const HOLD_MS = 2600;

	// One worker per line: when it is spawned, and how long it takes to land. The
	// whole demo is a function of these, so a frame can be computed from a single
	// elapsed number rather than from whatever the previous frame happened to be.
	const script = [
		{
			id: "researcher",
			name: "Researcher",
			task: "Read the changelog for breaking changes",
			model: "mini",
			startAt: 0,
			durationMs: 3600,
		},
		{
			id: "writer",
			name: "Writer",
			task: "Draft the migration note",
			model: "pro",
			startAt: 900,
			durationMs: 5400,
		},
		{
			id: "reviewer",
			name: "Reviewer",
			task: "Check every code sample still compiles",
			model: "pro",
			startAt: 1800,
			durationMs: 4200,
		},
	];

	const CYCLE_MS = Math.max(...script.map((s) => s.startAt + s.durationMs)) + HOLD_MS;

	/**
	 * The roster at a given point in the cycle. Pure: nothing here reads the state
	 * it is about to write, so the tick can never chase its own tail.
	 */
	function frame(elapsed: number): SubagentData[] {
		return script
			.filter((worker) => elapsed >= worker.startAt)
			.map(({ id, name, task, model, startAt, durationMs }) => {
				const ran = elapsed - startAt;
				return ran >= durationMs
					? { id, name, task, model, status: "done" as const }
					: { id, name, task, model, status: "running" as const, progress: ran / durationMs };
			});
	}

	let agents = $state<SubagentData[]>(frame(0));

	onMount(() => {
		// A fan-out that respawns forever is exactly what reduced motion is asking
		// us not to run, so it gets the finished roster and no timer at all.
		if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
			agents = frame(CYCLE_MS);
			return;
		}

		const started = Date.now();
		const timer = setInterval(() => {
			agents = frame((Date.now() - started) % CYCLE_MS);
		}, TICK_MS);
		return () => clearInterval(timer);
	});
</script>

<div class="bg-card w-full max-w-xl rounded-lg border p-5">
	<p class="text-muted-foreground mb-3 text-xs font-medium tracking-wide uppercase">
		Parallel workers
	</p>
	<SubagentList {agents} />
	<p class="text-muted-foreground mt-3 text-xs">
		Each worker arrives when it is spawned and fills its own bar. The list names itself for screen
		readers as it goes — "3 agents running", then "3 agents finished".
	</p>
</div>
```
