feat: edit in the Changes panel, git graph history, plan quota (v2.3.0)
- Changes panel: a working file opens in a CodeMirror editor with its diff inline (DiffEditor.tsx, @codemirror/merge, lazy-loaded); Changes folds the unchanged lines, Whole file shows them; side by side at 50/50 and full width; a Revert per block; original from HEAD or the branch's merge base (`mergeBase` on GET /git/changes?range=branch) - Every diff: green = line added whole, red = removed whole, blue = edited in place (--diff-mod) - History drawn as a commit graph (#58: `parents` on GET /git/log, --date-order; lib/commitGraph.ts) - Per-workspace never-stage list, drawn orange in the Files tree (--kept); closed Terminal rows past three deleted - Status bar plan quota for Claude (per account) and Codex (GET /api/pricing/quota, server/lib/quota.js); compact hover panel that stays open, Refresh button with a spinner - Settings → Appearance holds language, theme and pet; Ctrl+Shift+C copies; compact sidebar navigation and short session times
This commit is contained in:
@@ -59,6 +59,22 @@ describe("LaneDiffView — side by side", () => {
|
||||
expect(screen.getByTestId("lane-diff-split-right").className).toContain("overflow-auto");
|
||||
});
|
||||
|
||||
it("keeps an empty filler row one line tall, so the columns stay in step", () => {
|
||||
// Two lines removed, none added: the right column's two rows are fillers.
|
||||
const file = fileWith([
|
||||
{ type: "del", oldLine: 1, newLine: null, text: "gone one" },
|
||||
{ type: "del", oldLine: 2, newLine: null, text: "gone two" },
|
||||
]);
|
||||
render(<LaneDiffView file={file} layout="split" />);
|
||||
const fillers = [
|
||||
...screen
|
||||
.getByTestId("lane-diff-split-right")
|
||||
.querySelectorAll<HTMLElement>("div.flex.w-max"),
|
||||
];
|
||||
expect(fillers).toHaveLength(2);
|
||||
for (const row of fillers) expect(row.style.minHeight).toBe("16px");
|
||||
});
|
||||
|
||||
it("caps a review diff only when it windows; the single-file view fills its parent", () => {
|
||||
// Unwindowed, a review-list diff grows so the PANEL is the one vertical
|
||||
// scroller (nested 60vh boxes made hunk navigation land nowhere); a
|
||||
@@ -180,24 +196,40 @@ function rowOf(container: HTMLElement, needle: string): HTMLElement {
|
||||
}
|
||||
|
||||
describe("LaneDiffView — row styling", () => {
|
||||
it("tints add/del rows with their status fill and left bar; context stays plain", () => {
|
||||
const { container } = render(<LaneDiffView file={ROWS} layout="unified" />);
|
||||
const add = rowOf(container, "x = 2");
|
||||
const del = rowOf(container, "x = 1");
|
||||
// A line added or removed whole: nothing in common with its pair.
|
||||
const WHOLE = fileWith([
|
||||
{ type: "ctx", oldLine: 1, newLine: 1, text: "keep()" },
|
||||
{ type: "del", oldLine: 2, newLine: null, text: "abc" },
|
||||
{ type: "add", oldLine: null, newLine: 2, text: "xyz" },
|
||||
]);
|
||||
|
||||
it("tints whole-line add/del rows green/red with a left bar; context stays plain", () => {
|
||||
const { container } = render(<LaneDiffView file={WHOLE} layout="unified" />);
|
||||
const add = rowOf(container, "xyz");
|
||||
const del = rowOf(container, "abc");
|
||||
const ctx = rowOf(container, "keep()");
|
||||
expect(add.className).toContain("bg-status-success/20");
|
||||
expect(add.className).toContain("--status-success");
|
||||
expect(del.className).toContain("bg-status-danger/20");
|
||||
expect(del.className).toContain("--status-danger");
|
||||
expect(ctx.className).not.toMatch(/status-(success|danger)/);
|
||||
expect(ctx.className).not.toMatch(/status-(success|danger)|diff-mod/);
|
||||
});
|
||||
|
||||
it("tints a line edited in place blue on both sides", () => {
|
||||
const { container } = render(<LaneDiffView file={ROWS} layout="unified" />);
|
||||
for (const text of ["x = 1", "x = 2"]) {
|
||||
const row = rowOf(container, text);
|
||||
expect(row.className).toContain("bg-diff-mod/20");
|
||||
expect(row.className).not.toMatch(/status-(success|danger)/);
|
||||
}
|
||||
});
|
||||
|
||||
it("colours the +/- marker bold in its status colour, context marker muted", () => {
|
||||
const { container } = render(<LaneDiffView file={ROWS} layout="unified" />);
|
||||
const { container } = render(<LaneDiffView file={WHOLE} layout="unified" />);
|
||||
const marker = (row: HTMLElement) =>
|
||||
row.querySelector<HTMLElement>("span.select-none:not(.w-10)")!;
|
||||
const plus = marker(rowOf(container, "x = 2"));
|
||||
const minus = marker(rowOf(container, "x = 1"));
|
||||
const plus = marker(rowOf(container, "xyz"));
|
||||
const minus = marker(rowOf(container, "abc"));
|
||||
const ctx = marker(rowOf(container, "keep()"));
|
||||
expect(plus.textContent).toBe("+");
|
||||
expect(plus.className).toContain("font-bold");
|
||||
@@ -210,7 +242,7 @@ describe("LaneDiffView — row styling", () => {
|
||||
|
||||
it("emphasises only the changed span of a paired del/add", () => {
|
||||
const { container } = render(<LaneDiffView file={ROWS} layout="unified" />);
|
||||
const emph = [...container.querySelectorAll<HTMLElement>("span.bg-fg-primary\\/15")].map(
|
||||
const emph = [...container.querySelectorAll<HTMLElement>("span.bg-diff-mod\\/40")].map(
|
||||
(e) => e.textContent
|
||||
);
|
||||
expect(emph).toEqual(["1", "2"]);
|
||||
@@ -226,7 +258,7 @@ describe("LaneDiffView — row styling", () => {
|
||||
layout="unified"
|
||||
/>
|
||||
);
|
||||
expect(container.querySelector("span.bg-fg-primary\\/15")).toBeNull();
|
||||
expect(container.querySelector("span.bg-diff-mod\\/40")).toBeNull();
|
||||
});
|
||||
|
||||
it("split view tints each side, and a filler row opposite an unpaired add stays plain", () => {
|
||||
|
||||
Reference in New Issue
Block a user