ToolChips
One agent run as compact rows: individually expandable tool calls, closing on the file-diff chips.
ToolChips
Basic Usage
A Run in Progress
Rows grow as data arrives, and each one expands on its own to show what the tool actually did. Upstream's 700ms per-row reveal is a demo script and lives in the demo layer.
Loading demo...
TxDiffChips
The diff section stands on its own. TxDiffChips is exported from the same directory and renders "what this run changed" from a list of { file, add, del }, with no surrounding tool-call flow.
<script setup lang="ts">
import { TxDiffChips } from '@talex-touch/tuffex/tool-chips'
const diffs = [
{ file: 'flavors.css', add: 13, del: 0 },
{ file: 'menu.ts', add: 8, del: 2 },
]
</script>
<template>
<TxDiffChips :diffs="diffs" :more-count="2" @select="openFile" />
</template>
TxToolChips renders exactly this component internally: diffs / moreCount / moreLabelFormatter pass straight through, and diffClick and more are its events bubbling up.
TxDiffChips Props
| Prop | Type | Default | Description |
|---|---|---|---|
diffs | ToolChipDiff[] | — | The { file, add, del } list. Required. |
moreCount | number | 0 | Diffs not listed; above zero it renders +N more. |
moreLabelFormatter | (count: number) => string | n => `+${n} more` | Text of the overflow control. |
staggerStep | number | 80 | Entrance stagger between chips, in ms. |
TxDiffChips Events
| Event | Arguments | Description |
|---|---|---|
select | (diff: ToolChipDiff) | A diff chip was pressed. |
more | () | The overflow control was pressed. |
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
rows | ToolChipRow[] | — | Row data: { id, label, chip?, icon?, mono?, detailMono?, detail? }. Required. |
diffs | ToolChipDiff[] | [] | Diff data. When empty the whole diff section, divider included, is not rendered. |
summary | string | — | Header text. Falls back to summaryFormatter. |
summaryFormatter | (rowCount: number) => string | n => `${n} tool call(s)` | Default header text, pluralised by count. |
open | boolean | — | v-model:open, the whole run. Omit to let the component own it. |
defaultOpen | boolean | true | Initial open state when uncontrolled. |
expandedRows | string[] | — | v-model:expandedRows, ids of the expanded rows. |
defaultExpandedRows | string[] | [] | Rows expanded on first render when uncontrolled. |
moreCount | number | 0 | Diffs not listed in diffs. |
moreLabelFormatter | (count: number) => string | n => `+${n} more` | Text of the overflow control. |
Events
| Event | Arguments | Description |
|---|---|---|
update:open | (open: boolean) | The whole run was toggled. |
update:expandedRows | (ids: string[]) | The expanded set changed. |
toggle | (id: string, expanded: boolean) | A row was toggled, carrying the state after the change. |
rowClick | (row: ToolChipRow) | A row was pressed; fires alongside toggle. |
diffClick | (diff: ToolChipDiff) | A diff chip was pressed. |
more | () | The diff overflow control was pressed. |
Slots
| Slot | Scope | Description |
|---|---|---|
row-icon | { row } | Replaces the row glyph. Required for any icon outside the four built-ins. |
chip | { row } | Replaces the trailing chip's contents. |
detail | { row } | Replaces the expanded detail body. |
diffs | { diffs } | Replaces the whole diff section, keeping the divider. |
Exposed
| Method | Description |
|---|---|
expand(id) / collapse(id) | Expands or collapses one row; a no-op if it is already in that state. |
expandAll() / collapseAll() | Bulk operations. |
Interaction Contract
- Rows are keyed by
row.id, not by label. Upstream keys on the label, so two rows sharing one expand together. - The built-in glyphs are only
think/write/run/read. Any other value is accepted but needs therow-iconslot, or the glyph slot renders empty. - Hovering fades the tool glyph out and the chevron in, in place, and an expanded row keeps the chevron. Touch has no hover, so under
@media (hover: none)the chevron is always shown — otherwise nothing on a phone says the row expands. - The
chipisflex: 1and truncates: it takes the remaining row width and ends a long path in an ellipsis. - A detail line's
toneis onlyadd(success) ordel(danger); anything else stays muted. - The removed count uses a U+2212 minus sign (
−) rather than a hyphen, so it aligns with the plus. Adelof zero hides the counter entirely. - Every number is
tabular-nums, so widths do not jitter as a run streams. openandexpandedRowsare both dual-mode: supply the prop and it wins, omit it and the component owns it. Events fire either way.- The component never plays itself. Upstream's 700ms per-row timer is a demo script; a real host pushes rows into
rowsas they arrive, and the 300ms per-row entrance produces the same impression.
Best Practices
- Put "what was done" in
label(Write 204 lines) and "what it was done to" inchip(a file name, a command) — those two are all a reader can scan while collapsed. - Set
monoon paths and commands, not on prose; mixing the mono face into sentences makes the row rhythm look uneven. - Keep details to two or three lines. Long logs belong in
TxToolCallCard, whose log surface follows its own tail. - List only the first few diffs and hand the rest to
moreCountrather than laying out dozens of files. - Lift
expandedRowsinto the host on a streaming surface, so remounting the component does not drop the reader's choice.
Source
- Component source:
packages/tuffex/packages/components/src/tool-chips/src/TxToolChips.vue,src/TxDiffChips.vue. - Types:
packages/tuffex/packages/components/src/tool-chips/src/types.ts. - Verified coverage:
packages/tuffex/packages/components/src/tool-chips/__tests__/tool-chips.test.ts(17 cases) covers the derived header count and its pluralisation, one-to-onearia-controls, independent per-row expansion keyed by id, controlledexpandedRowsprecedence, mono flags,tonecolouring, conditional diff rendering, event forwarding,expandAll/collapseAll, and both U+2212 and the stagger variable. - Adapted from Beautiful UI (https://www.beautifului.dev), © 2026 Shane Levine, MIT.
查看源码
packages/tuffex/packages/components/src/tool-chips/index.ts