// model.js: a segment never leaves its own row
if (i > top1 && i <= iBase) {
const d = i === iBase
? `M${x1} -16C${x1} 6 ${x0} 2 ${x0} 16` // curve off main
: `M${x1} -16V16`; // straight through
line(d, branch);
}
// main joins the branch at the merge commit, rows above
const ty = 16 - ROW * (i - iMm);
.g svg { position: absolute; inset: 0; overflow: visible; }
.row { height: 32px; transition: height .34s var(--ease), opacity .34s; }
.row.enter { height: 0; opacity: 0; }Merge conflicts
An agent opens a pull request, finds that main has moved underneath it, resolves the conflicts and merges, all inside one card.
Press Create PR, or type in the box and press Enter. Hover or focus “3 conflicts” to see the lines, and the check chips to see each command.
Sample repository, people and commits. Nothing is pushed anywhere.
Key CSS & implementation tricks
.shim {
--sb: var(--mut); --sh: var(--fg);
background: linear-gradient(100deg, var(--sb) 36%,
var(--sh) 47%, var(--sh) 53%, var(--sb) 64%);
background-size: 280% 100%;
background-clip: text; color: transparent;
animation: shim 2s linear infinite;
}
.ttl.shim.tone-you { --sb: var(--fg); --sh: var(--faint); }
/* 4 + 28 = one 32px row */
.g path.flow { stroke-dasharray: 4 28; animation: flow 1.6s linear infinite; }
@keyframes flow { to { stroke-dashoffset: 32; } }
S.card.flow = ['m', 'mm']; // resolving: main flows into the branch
S.card.flow = []; // waiting for you: lights outexport function scrambleFrame(text, progress, random = Math.random) {
const n = Math.floor(progress * progress * text.length); // fast, then slow
let tail = '';
for (let i = n; i < text.length; i++)
tail += text[i] === ' ' ? ' ' : NOISE[Math.floor(random() * NOISE.length)];
return { head: text.slice(0, n), tail }; // tail is drawn fainter
}
// Only a change of shape blurs; a ticking counter does not.
export const shape = s => String(s).replace(/\d+/g, '#');
if (shape(before) !== shape(after)) restart(el, 'blurin');const wait = ms => new Promise(r => ff ? r() : setTimeout(r, ms))
.then(() => { if (id !== runId) throw CANCEL; });
async function flow() {
mark('idle');
await gate('start'); // Create PR, Enter, or Autoplay
/* … */
mark('conflicts');
await gate('resolve');
}
function start(target) { // jump: replay instantly up to a mark
runId++; ff = target;
root.classList.add('ff');
flow();
}
foot = { icon: 'spin', step: ['testing', n, TESTS] }; // keys, not strings