Skip to content
Kura

File Changes

experimental

The files an agent touched: a compact folder tree or flat list with A/M/D/R status, +/− counts, live “editing” markers and selection.

@kura-ui/react/file-changesCopy for agentsMarkdownRegistry JSON

Changed files

minimal
Changes+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
Changes
src/auth
client.tsreading, modified, being read

Flat list

List view: file name first, directory dimmed.

variant
Changes+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
Changes+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.ts+13−1613 additions, 16 deletions
@@ −1,7 +1,8 @@
import { cache } from "react";
removed: import { fetchJson } from "@/lib/http";
removed: import type { Session, User } from "./types";
added: import { createAuthClient, type Session, type User } from "@acme/auth";
​
removed: const SESSION_URL = "/api/v1/session";
removed: const REFRESH_URL = "/api/v1/session/refresh";
added: export const auth = createAuthClient({
added: baseUrl: process.env.NEXT_PUBLIC_AUTH_URL,
added: refresh: "auto",
added: });
​
@@ −9,14 +10,10 @@
export const fetchUser = cache(async (): Promise<User | null> => {
removed: const res = await fetchJson<Session>(SESSION_URL, { credentials: "include" });
removed: if (!res.ok) return null;
removed: return res.data.user;
added: const session = await auth.getSession();
added: return session?.user ?? null;
});
​
removed: export async function refreshSession(token: string): Promise<Session> {
removed: const res = await fetchJson<Session>(REFRESH_URL, {
removed: method: "POST",
removed: body: JSON.stringify({ token }),
removed: });
removed: if (!res.ok) throw new Error(`refresh failed: ${res.status}`);
removed: return res.data;
added: export async function refreshSession(session: Session): Promise<Session> {
added: const next = await auth.refresh(session.refreshToken);
added: if (!next) throw new Error("refresh failed: session revoked");
added: return next;
}
@@ −24,3 +21,3 @@
export function isExpired(session: Session, now = Date.now()): boolean {
removed: return session.expiresAt < now;
added: return session.expiresAt <= now;
}
@@ −28,3 +25,3 @@
export async function signOut(): Promise<void> {
removed: await fetchJson(SESSION_URL, { method: "DELETE", credentials: "include" });
added: await auth.signOut();
}

Installation

npx shadcn@latest add https://kuro.abstergo.fyi/r/file-changes.json

Copies 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

PropTypeDefaultDescription
files*readonly FileChange[]—`{ path, status: "A" | "M" | "D" | "R", oldPath?, additions?, deletions?, state?: "reading" | "editing" }`. Paths are repo-relative with "/" separators.
titleReact.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.
viewTogglebooleantrueShow the tree/list toggle in the header.
selectedPathstring | null—The selected file path (controlled). Pass null for no selection.
defaultSelectedPathstring | nullnullInitially selected file when uncontrolled.
onSelect(path: string) => void—Called when a file is chosen by click, Enter or Space.
defaultCollapsedreadonly string[]—Folder paths collapsed initially, e.g. ["src/auth/__tests__"].
maxHeightnumber—Scroll the rows inside this height (px).
actionsReact.ReactNode—Extra header controls.
emptyReact.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

  1. FileChanges`data-view="tree" | "list"`.data-slot="file-changes"
  2. HeaderTitle, summary (`file-changes-summary`: “7 files +33 −38”), actions, view toggle.data-slot="file-changes-header"
  3. Rowsrole="tree" (tree view) or role="listbox" (list view).data-slot="file-changes-rows"
  4. 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"
  5. 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"
  6. NameMono file name; shimmers while editing, struck through when deleted.data-slot="file-changes-name"
  7. Counts+N −M in mono, or “editing” (accent, pulsing dot) / “reading”.data-slot="file-changes-counts"
  8. StatusStatus letter: A green, M amber, D red, R muted; sr-only word (“, modified”).data-slot="file-changes-status"
  9. 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.
KeysAction
ArrowDown / ArrowUpNext / previous row
ArrowRightExpand a folder, or move into it
ArrowLeftCollapse a folder, or move to the parent folder
Home / EndFirst / last row
Enter / SpaceSelect 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.