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:
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @file Lays out the Changes panel's History as a commit graph (the JetBrains /
|
||||
* `git log --graph` shape): each commit gets a column, and each row lists the
|
||||
* line segments drawn through it — a lane passing by, the lanes converging on
|
||||
* the commit from above, and the lines leaving it for its parents below.
|
||||
* Expects the commits in an order where no parent precedes a child
|
||||
* (`git log --date-order`); a parent outside the list leaves its lane running
|
||||
* off the bottom.
|
||||
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
||||
*/
|
||||
|
||||
/** One line in a row, in column units; y runs 0 (top) → 0.5 (node) → 1 (bottom). */
|
||||
export interface GraphSeg {
|
||||
x1: number;
|
||||
y1: number;
|
||||
x2: number;
|
||||
y2: number;
|
||||
color: number;
|
||||
}
|
||||
|
||||
export interface GraphRow {
|
||||
col: number;
|
||||
color: number;
|
||||
segs: GraphSeg[];
|
||||
}
|
||||
|
||||
export function commitGraph(commits: { sha: string; parents: string[] }[]): {
|
||||
rows: GraphRow[];
|
||||
width: number;
|
||||
} {
|
||||
// lanes[j] = the sha the lane in column j is waiting for; colors[j] its hue.
|
||||
const lanes: (string | null)[] = [];
|
||||
const colors: number[] = [];
|
||||
const hue = (j: number) => colors[j] ?? 0;
|
||||
let nextColor = 0;
|
||||
let width = 1;
|
||||
const open = () => {
|
||||
const free = lanes.indexOf(null);
|
||||
const j = free < 0 ? lanes.length : free;
|
||||
colors[j] = nextColor++;
|
||||
return j;
|
||||
};
|
||||
|
||||
const rows = commits.map((c) => {
|
||||
const segs: GraphSeg[] = [];
|
||||
let col = lanes.indexOf(c.sha);
|
||||
// A tip nothing above points at: a lane of its own, no line from the top.
|
||||
if (col < 0) {
|
||||
col = open();
|
||||
lanes[col] = null;
|
||||
}
|
||||
lanes.forEach((s, j) => {
|
||||
if (s === null) return;
|
||||
segs.push(
|
||||
s === c.sha
|
||||
? { x1: j, y1: 0, x2: col, y2: 0.5, color: hue(j) }
|
||||
: { x1: j, y1: 0, x2: j, y2: 1, color: hue(j) }
|
||||
);
|
||||
});
|
||||
const color = hue(col);
|
||||
lanes.forEach((s, j) => {
|
||||
if (s === c.sha) lanes[j] = null;
|
||||
});
|
||||
|
||||
c.parents.forEach((p, i) => {
|
||||
let j = i === 0 ? col : lanes.indexOf(p);
|
||||
if (j < 0) j = open();
|
||||
lanes[j] = p;
|
||||
segs.push({ x1: col, y1: 0.5, x2: j, y2: 1, color: hue(j) });
|
||||
});
|
||||
while (lanes.length && lanes[lanes.length - 1] === null) lanes.pop();
|
||||
width = Math.max(width, lanes.length, col + 1);
|
||||
return { col, color, segs };
|
||||
});
|
||||
return { rows, width };
|
||||
}
|
||||
Reference in New Issue
Block a user