Components/ToolChips

ToolChips

One agent run as compact rows: individually expandable tool calls, closing on the file-diff chips.

VerifiedSince 0.3.9

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

PropTypeDefaultDescription
diffsToolChipDiff[]The { file, add, del } list. Required.
moreCountnumber0Diffs not listed; above zero it renders +N more.
moreLabelFormatter(count: number) => stringn => `+${n} more`Text of the overflow control.
staggerStepnumber80Entrance stagger between chips, in ms.

TxDiffChips Events

EventArgumentsDescription
select(diff: ToolChipDiff)A diff chip was pressed.
more()The overflow control was pressed.

API

Props

PropTypeDefaultDescription
rowsToolChipRow[]Row data: { id, label, chip?, icon?, mono?, detailMono?, detail? }. Required.
diffsToolChipDiff[][]Diff data. When empty the whole diff section, divider included, is not rendered.
summarystringHeader text. Falls back to summaryFormatter.
summaryFormatter(rowCount: number) => stringn => `${n} tool call(s)`Default header text, pluralised by count.
openbooleanv-model:open, the whole run. Omit to let the component own it.
defaultOpenbooleantrueInitial open state when uncontrolled.
expandedRowsstring[]v-model:expandedRows, ids of the expanded rows.
defaultExpandedRowsstring[][]Rows expanded on first render when uncontrolled.
moreCountnumber0Diffs not listed in diffs.
moreLabelFormatter(count: number) => stringn => `+${n} more`Text of the overflow control.

Events

EventArgumentsDescription
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

SlotScopeDescription
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

MethodDescription
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 the row-icon slot, 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 chip is flex: 1 and truncates: it takes the remaining row width and ends a long path in an ellipsis.
  • A detail line's tone is only add (success) or del (danger); anything else stays muted.
  • The removed count uses a U+2212 minus sign () rather than a hyphen, so it aligns with the plus. A del of zero hides the counter entirely.
  • Every number is tabular-nums, so widths do not jitter as a run streams.
  • open and expandedRows are 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 rows as 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" in chip (a file name, a command) — those two are all a reader can scan while collapsed.
  • Set mono on 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 moreCount rather than laying out dozens of files.
  • Lift expandedRows into 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-one aria-controls, independent per-row expansion keyed by id, controlled expandedRows precedence, mono flags, tone colouring, 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