// Mirrors the JSON the review server produces — see src/review/model.zig, whose // field names are the wire format. // // There is no repository in here to choose between: a review pane is bound to one // work tree, and the tab's path is what names it. Everything below describes that // one review. export type Side = 'old' | 'new'; export type Author = 'user' | 'claude'; export type Status = 'draft' | 'submitted' | 'resolved'; export type Level = 'line' | 'file' | 'review'; // DraftTarget is what an in-progress (unsent) comment is anchored to. export type DraftTarget = | { level: 'line'; file: string; side: Side; startLine: number; endLine: number; changeKey: string; // key of the end line, where the composer renders } | { level: 'file'; file: string } | { level: 'review' }; export interface Reply { id: string; author: Author; body: string; createdAt: string; } export interface DiffContext { base: string; uncommitted: boolean; // The sha of one commit out of the range, when the view is narrowed to it: the // diff is then that commit alone, and `uncommitted` no longer applies. Absent // for the whole change set, which is what a review opens on. // // Part of the context rather than a display preference beside it, because a // line number only means something inside one revision — line 40 as one commit // left it isn't line 40 at the tip of the branch — so a comment written here // must not be placed on the full diff's lines. commit?: string; } // Commit is one entry in the list of commits a diff spans, as the left rail shows // them. `files`/`additions`/`deletions` are what the commit changed on its own, // and are 0 for a merge, whose diff git doesn't summarize. export interface Commit { sha: string; shortSha: string; author: string; date: string; subject: string; files: number; additions: number; deletions: number; } export interface Comment { id: string; level: Level; file: string; side: Side; line: number; endLine: number; body: string; author: Author; status: Status; replies: Reply[]; context: DiffContext; createdAt: string; updatedAt: string; } export interface RepoInfo { path: string; branch: string; branches: string[]; refs: string[]; // The ref the base picker offers directly under HEAD, which a review opens on: // the release branch, or main, depending on the repo. Decided server-side (see // git.suggestedBase) so the rule lives in one place. Empty when there's nothing // worth suggesting. suggestedBase: string; } // What GET api/repo answers with for a tab that has a review open: the // repository, plus the comment counts and the diff selection the server has on // record. export interface RepoState extends RepoInfo { drafts: number; openComments: number; // The selection this tab last published (see api.setContext). Absent until the // page publishes one. The browser is the source of truth for what's on screen; // this copy is what lets an agent review, and anchor comments to, the same diff. context?: DiffContext | null; // Whether this machine has an Android Studio for the file headers to open // files in (see api.openInStudio). studio: boolean; } export interface DiffFile { oldPath: string; newPath: string; status: string; additions: number; deletions: number; } export interface DiffPayload { context: DiffContext; patch: string; files: DiffFile[]; // The commits the change set is made of, oldest first — the whole range, even // when `context.commit` narrows the patch to one of them, so the list you // picked from is still there to pick again. Filled in for an oversized diff // too, where choosing a single commit is the fastest way to something readable. commits: Commit[] | null; // The range holds more commits than `commits` lists (the newest are kept). moreCommits?: boolean; // The change set is past what the UI can render, so the server withheld the // patch. `files` is still filled in, so the size can be described before // anything asks for it again with force. See git.Repo.Diff. oversized?: boolean; // A digest of what this selection resolved to when the patch was made. The // page holds on to it and asks the server for the current one on a timer, so // it can say the diff has been overtaken instead of reloading it mid-read. // Empty when the patch was withheld — nothing on screen to go stale. revision?: string; }