File Changes
experimentalThe files an agent touched: a compact folder tree or flat list with A/M/D/R status, +/− counts, live “editing” markers and selection.
Changed files
minimal
Changes7 files+33−3833 additions, 38 deletions
src
auth
__tests__
login.test.ts+7−5, modified
client.ts+13−16, modified
types.ts+6, added
lib
legacy-session.ts−10, deleted
routes/account
page.tsx+4−4, modified
middleware.ts+2−2, modified
package.json+1−1, modified
Live run
Files appear as the agent reads and edits them — the active one shimmers with an “editing” marker — then the run settles. Controlled selection.
full
Changes1 file
src/auth
client.tsreading, modified, being read
Flat list
List view: file name first, directory dimmed.
variant
Changes7 files+33−3833 additions, 38 deletions
package.json+1−1, modified
login.test.tssrc/auth/__tests__+7−5, modified
client.tssrc/auth+13−16, modified
types.tssrc/auth+6, added
legacy-session.tssrc/lib−10, deleted
middleware.tssrc+2−2, modified
page.tsxsrc/routes/account+4−4, modified
With DiffView
FileChanges as navigation; DiffView shows the selected file parsed from the same unified diff.
composition
Changes4 files+22−3322 additions, 33 deletions
src
auth
__tests__
login.test.ts+7−5, modified
client.ts+13−16, modified
lib
legacy-session.ts−10, deleted
middleware.ts+2−2, modified
client.ts1 file+13−1613 additions, 16 deletions
@@ −1,7 +1,8 @@
@@ −9,14 +10,10 @@const REFRESH_URL = "/api/v1/session/refresh";
@@ −24,3 +21,3 @@export async function refreshSession(token: string): Promise<Session> {
@@ −28,3 +25,3 @@export function isExpired(session: Session, now = Date.now()): boolean {
Installation
npx shadcn@latest add https://kuro.abstergo.fyi/r/file-changes.jsonCopies the source into components/kura/file-changes with its dependencies (kura-theme, icon-button, shimmer-text, use-controllable-state, utils). Tokens come from the kura-theme item.
When to use it
Use it for
- Show which files a coding agent has changed during or after a run (a sidebar next to the conversation or a diff).
- Indicate live activity: which file the agent is reading or editing right now.
- A file picker for a review screen: select a file to show its diff in diff-view.
Reach for something else when
- Showing the actual line changes — use diff-view (pair them: FileChanges as navigation, DiffView as content).
- A step-by-step log of the agent's actions — use agent-timeline or task-list.
- A general file browser or editor tree with create/rename/drag — this is a read-only change list.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| files* | readonly FileChange[] | — | `{ path, status: "A" | "M" | "D" | "R", oldPath?, additions?, deletions?, state?: "reading" | "editing" }`. Paths are repo-relative with "/" separators. |
| title | React.ReactNode | "Changes" | Header title. |
| view | "tree" | "list" | — | tree = folders (single-child chains compacted like VS Code, folders first); list = flat, file name then dimmed directory. Controlled. |
| defaultView | "tree" | "list" | "tree" | Initial view when uncontrolled. |
| onViewChange | (view: "tree" | "list") => void | — | Called when the header toggle switches views. |
| viewToggle | boolean | true | Show the tree/list toggle in the header. |
| selectedPath | string | null | — | The selected file path (controlled). Pass null for no selection. |
| defaultSelectedPath | string | null | null | Initially selected file when uncontrolled. |
| onSelect | (path: string) => void | — | Called when a file is chosen by click, Enter or Space. |
| defaultCollapsed | readonly string[] | — | Folder paths collapsed initially, e.g. ["src/auth/__tests__"]. |
| maxHeight | number | — | Scroll the rows inside this height (px). |
| actions | React.ReactNode | — | Extra header controls. |
| empty | React.ReactNode | — | Rendered when `files` is empty. Default an icon + “No changes yet”. |
All native attributes of the root element, className and ref pass through.
Anatomy
- FileChanges`data-view="tree" | "list"`.
data-slot="file-changes" - HeaderTitle, summary (`file-changes-summary`: “7 files +33 −38”), actions, view toggle.
data-slot="file-changes-header" - Rowsrole="tree" (tree view) or role="listbox" (list view).
data-slot="file-changes-rows" - Foldertreeitem with aria-expanded, `data-state="open" | "closed"`; collapsed folders show their file count and a pulsing dot if a file inside is being edited.
data-slot="file-changes-folder" - Filetreeitem/option with aria-selected, `data-status="A" | "M" | "D" | "R"`, `data-state="reading" | "editing"`, `data-selected`. Selected rows get bg-kura-active and a 2px accent bar.
data-slot="file-changes-file" - NameMono file name; shimmers while editing, struck through when deleted.
data-slot="file-changes-name" - Counts+N −M in mono, or “editing” (accent, pulsing dot) / “reading”.
data-slot="file-changes-counts" - StatusStatus letter: A green, M amber, D red, R muted; sr-only word (“, modified”).
data-slot="file-changes-status" - GuideHairline indent guides in the tree.
data-slot="file-changes-guide"
Accessibility
- Role:
tree | listbox - Tree view is a WAI-ARIA tree (treeitems with aria-level/posinset/setsize, aria-expanded on folders, aria-selected on files); list view is a listbox of options.
- One roving tab stop: Tab enters the list at the selected (or first) row.
- Each file's accessible name includes its status word and live state (“client.ts … , modified, being edited”).
- The view toggle buttons use aria-pressed and have tooltips.
- Reduced motion: The editing pulse and name shimmer stop; row fade-in and chevron rotation are instant.
| Keys | Action |
|---|---|
| ArrowDown / ArrowUp | Next / previous row |
| ArrowRight | Expand a folder, or move into it |
| ArrowLeft | Collapse a folder, or move to the parent folder |
| Home / End | First / last row |
| Enter / Space | Select a file, or toggle a folder |
Notes for agents
- Map git porcelain codes directly: A/M/D/R (treat C copies as A, T type changes as M).
- Set `state: "editing"` on the file the agent is writing and clear it when done — the row shimmers and collapsed ancestor folders show a live dot.
- Selection is single and files-only; folders toggle. Use onSelect to drive a DiffView or editor beside it.
- Counts are optional; omit additions/deletions when unknown rather than passing 0.