Pro

File changes

Pro component

The files an agent changed, one 32px row each: what happened to the file, its folder muted and cut from the start so the name stays whole, and lines added and removed. A totals line, selection that follows the arrow keys, loading and empty states, and a chip for naming a file inline.

FilesLoadingEmpty
6 files62 lines added, 163 removed
  • tests/e2e/checkout.spec.ts, Modified, 3 lines added, 2 removed
  • src/checkout/payment-form.tsx, Modified, 9 lines added, 3 removed
  • src/checkout/use-cart-total.ts, Modified, 14 lines added, 7 removed
  • tests/e2e/fixtures/slow-tax.ts, Added, 22 lines added
  • src/webhooks/send.ts, Renamed from src/webhooks/deliver.ts, 14 lines added, 9 removed
  • src/invoices/pdf/components/legacy/legacy-render.tsx, Deleted, 142 lines removed
checkout.spec.ts3 lines added, 2 removed
test("pays for a cart", async ({ page }) => {
await fillCard(page, TEST_CARD);
Removed: await page.waitForTimeout(500);
Removed: await page.getByRole("button", { name: "Pay" }).click();
Added: const pay = page.getByRole("button", { name: "Pay" });
Added: await expect(pay).toBeEnabled({ timeout: 5_000 });
Added: await pay.click();
});
Inline, as chips

The test was racing the tax quote in , so I made it wait for Pay instead.

Installation

This is a Pro component. UI Polish Pro costs $149 one time and includes every Pro component, block and template, now and later. Add your license key to your MCP connection or components.json, then install it either way below.

With UI Polish connected, ask your agent:

Add the UI Polish file changes to this project.

Or with the shadcn CLI:

npx shadcn@latest add @uipolish/file-changes
Brings inDiff stat, Icons, Skeleton, Text

The files an agent changed, one 32px row each: a mark for what happened to the file (added, modified, deleted, renamed), its folder in the muted color and cut from the start when there's no room, its name whole, and lines added and removed at the end. A totals line sits on top, the selection follows the arrow keys so a diff beside the list keeps up, and the list has loading and empty states. FileChip names one file inline, in a message or under a reply.

TSX
<FileChanges  files={[    { path: "tests/e2e/checkout.spec.ts", kind: "modified", added: 6, removed: 4 },    { path: "src/checkout/use-cart-total.ts", kind: "modified", added: 14, removed: 7 },    { path: "tests/e2e/fixtures/slow-tax.ts", kind: "added", added: 22, removed: 0 },  ]}  value={selected}  onValueChange={openDiff}/><FileChip path="src/lib/rate-limit.ts" kind="added" added={41} removed={0} onClick={() => openDiff("src/lib/rate-limit.ts")} />

When to use it

  • The changed files of an agent's turn, a commit or a pull request, beside the diff they open.
  • A repository's files with the changed ones marked (leave added, removed and kind out for unchanged files).
  • FileChip: a file an answer or a prompt names, as a pressable chip.

When not to

  • A tree people expand and collapse folder by folder: this is a flat list of paths, which is what a review needs.
  • The diff itself: use Diff view beside the list.
  • More than a few thousand rows: virtualize them; the rows keep a fixed height so they can be.

Parts

PartWhat it is
FileChangesThe list, with its totals line
FileChipOne file inline: the mark, the name in mono, its stat. A button with onClick; inline steps it down to 20px for a line of text
FilePathA path as the rows show it: the folder muted and cut from its start, the name whole
ChangeMarkThe icon set's change square: plus, dot, minus or arrow; a plain file when nothing changed
splitPath, totalChanges"src/app/page.tsx" → ["src/app/", "page.tsx"]; files, added and removed across a list

Props

PropWhat it does
files{ path, added?, removed?, kind?, from? }[]. from is the old path of a rename
value, onValueChangeThe selected path; called as the selection moves. Without onValueChange the list is read-only
labelNames the list for screen readers. “Changed files” by default
totalsThe “4 files +54 −12” line. On by default
actionsSomething at the end of the totals line: a filter, a Collapse
loading, loadingLabelSkeleton rows after 300ms, the label announced
emptyWhat shows with no files. “No files changed yet.” by default

Layout

PartSize
Row32px (40px on touch), 12px in, the panel's row radius
Mark14px, beside 13px text: green added, red deleted, muted otherwise
Path13px mono. The folder takes what's left after the name and shrinks first, cut at its start with a fade
Stat13px, figures of equal width, at the row's end
Chip24px tag on the field edge (20px inline); the name in mono, never cut unless the line can't hold it

States

StateWhat you seeHow
RestMark, muted folder, name in the text color, stat
HoverA faint fill on the row@media (hover: hover)
SelectedThe kit's flat pill, as the current row everywherearia-selected
FocusA line inside the row:focus-visible
Long path…components/settings/ cut at the start, the name whole; the full path in title
RenamedThe arrow mark; “Renamed from old/path” read out; old → new in titlekind: "renamed", from
Unchanged fileA plain file mark, no statno kind
LoadingAfter 300ms, six skeleton rows of the real heightloading
EmptyOne quiet line where the list would beempty

Keyboard

KeyDoes
TabInto the list, on the selected row (or the last focused, or the first)
↑ ↓Move and select, so the diff beside it follows
Home, EndFirst, last
Enter, SpaceSelect the focused row

Motion

WhatDurationEasing
Row fill on hover, press, select--ui-dur-instant--ui-ease-color
A change mark changing kindthe icon set's morph

Nothing else moves: a list people scan holds still.

Accessibility

  • A selectable list is a listbox of options with one tab stop; a read-only one is a plain list.
  • Each row reads its path, then what happened and the counts in words (“Modified, 6 lines added, 4 removed”); the visible stat is hidden from screen readers so it isn't read twice.
  • Paths are isolated left to right, so a right-to-left page never reorders their slashes.
  • Chips take a 40px target on touch without growing.

Before / after

Before: a path cut at its end, which hides the file name, a status letter in a colored box, and rows that need a mouse.

TSX
<li className="truncate">{file.path} <span className="text-green-600">+{file.added}</span></li>

After:

TSX
<FileChanges files={files} value={selected} onValueChange={setSelected} />