// Each color keeps at least 3:1 contrast against both the light (#ffffff) // and dark (#0d1117) canvas backgrounds so lanes stay traceable in either theme. const COLORS = [ "#0969da", "#bf3989", "#bf8700", "#1a7f37", "#8250df", "#bc4c00", "#1b7c83", "#cf222e", ]; export const LANE_COLORS = COLORS; function nextColor(index) { return COLORS[index % COLORS.length]; } export function layoutCommitGraph(commits) { let lanes = []; let colorIndex = 0; let maxLanes = 1; const rows = commits.map((commit) => { let laneIndex = lanes.findIndex((lane) => lane.sha === commit.sha); if (laneIndex === -1) { lanes.push({ sha: commit.sha, color: nextColor(colorIndex++) }); laneIndex = lanes.length - 1; } const before = lanes.map((lane) => ({ ...lane })); const current = before[laneIndex]; const after = lanes.map((lane) => ({ ...lane })); const firstParent = commit.parents[0] || null; if (!firstParent) { after.splice(laneIndex, 1); } else { const existingFirstParent = after.findIndex((lane, index) => index !== laneIndex && lane.sha === firstParent ); if (existingFirstParent >= 0) { after.splice(laneIndex, 1); } else { after[laneIndex] = { sha: firstParent, color: current.color }; } } for (const parent of commit.parents.slice(1)) { if (after.some((lane) => lane.sha === parent)) continue; const insertAt = Math.min(laneIndex + 1, after.length); after.splice(insertAt, 0, { sha: parent, color: nextColor(colorIndex++) }); } const transitions = []; before.forEach((lane, index) => { if (index === laneIndex) return; const target = after.findIndex((candidate) => candidate.sha === lane.sha); if (target >= 0) { transitions.push({ from: index, to: target, color: lane.color, kind: "pass" }); } }); commit.parents.forEach((parent, parentIndex) => { const target = after.findIndex((lane) => lane.sha === parent); if (target >= 0) { transitions.push({ from: laneIndex, to: target, color: parentIndex === 0 ? current.color : after[target].color, kind: parentIndex === 0 ? "first-parent" : "merge-parent", }); } }); maxLanes = Math.max(maxLanes, before.length, after.length); lanes = after; return { commit, laneIndex, color: current.color, transitions, lanesBefore: before.length, lanesAfter: after.length, }; }); return { rows, maxLanes }; }