collet 0.1.0

Relentless agentic coding orchestrator with zero-drop agent loops
Documentation
<script lang="ts">
	let { diff }: { diff: string } = $props();

	interface ParsedLine {
		type: 'header' | 'hunk' | 'added' | 'removed' | 'context' | 'meta';
		content: string;
		oldNum: number | null;
		newNum: number | null;
	}

	const parsed = $derived.by((): ParsedLine[] => {
		const lines = diff.split('\n');
		const result: ParsedLine[] = [];
		let oldLine = 0;
		let newLine = 0;

		for (const line of lines) {
			if (
				line.startsWith('--- ') ||
				line.startsWith('+++ ') ||
				line.startsWith('diff ') ||
				line.startsWith('index ')
			) {
				result.push({ type: 'header', content: line, oldNum: null, newNum: null });
			} else if (line.startsWith('@@ ')) {
				const m = line.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
				if (m) {
					oldLine = parseInt(m[1], 10);
					newLine = parseInt(m[2], 10);
				}
				result.push({ type: 'hunk', content: line, oldNum: null, newNum: null });
			} else if (line.startsWith('+')) {
				result.push({ type: 'added', content: line.slice(1), oldNum: null, newNum: newLine++ });
			} else if (line.startsWith('-')) {
				result.push({ type: 'removed', content: line.slice(1), oldNum: oldLine++, newNum: null });
			} else {
				const content = line.startsWith(' ') ? line.slice(1) : line;
				result.push({ type: 'context', content, oldNum: oldLine++, newNum: newLine++ });
			}
		}
		return result;
	});
</script>

<div class="diff-wrap">
	{#each parsed as line}
		<div class="diff-row {line.type}">
			{#if line.type === 'added' || line.type === 'removed' || line.type === 'context'}
				<span class="gutter">{line.oldNum ?? ''}</span>
				<span class="gutter">{line.newNum ?? ''}</span>
				<span class="sign">{line.type === 'added' ? '+' : line.type === 'removed' ? '-' : ' '}</span>
			{:else}
				<span class="gutter-full"></span>
			{/if}
			<span class="code">{line.content}</span>
		</div>
	{/each}
</div>

<style>
	.diff-wrap {
		border: 1px solid var(--border);
		border-radius: 4px;
		overflow: hidden;
		font-family: var(--font-mono);
		font-size: 0.8rem;
		background: var(--bg-elevated);
	}

	.diff-row {
		display: flex;
		line-height: 1.4;
		min-height: 1.4em;
		white-space: pre;
	}

	.diff-row:hover {
		filter: brightness(1.06);
	}

	.gutter {
		min-width: 3rem;
		padding: 0 0.4rem;
		text-align: right;
		color: var(--fg-dim);
		background: var(--bg-surface);
		border-right: 1px solid var(--border);
		user-select: none;
		flex-shrink: 0;
	}

	.sign {
		width: 1.2rem;
		padding: 0 0.2rem;
		text-align: center;
		flex-shrink: 0;
	}

	/* Spans two gutters + sign column for header/hunk rows */
	.gutter-full {
		flex: 0 0 calc(3rem * 2 + 1.2rem + 2px);
		background: var(--bg-surface);
		border-right: 1px solid var(--border);
	}

	.code {
		padding: 0 0.5rem;
		flex: 1;
		overflow-x: auto;
	}

	.diff-row.added {
		background: color-mix(in srgb, var(--green) 10%, transparent);
		color: var(--green);
	}

	.diff-row.added .gutter {
		background: color-mix(in srgb, var(--green) 6%, var(--bg-surface));
	}

	.diff-row.removed {
		background: color-mix(in srgb, var(--red) 10%, transparent);
		color: var(--red);
	}

	.diff-row.removed .gutter {
		background: color-mix(in srgb, var(--red) 6%, var(--bg-surface));
	}

	.diff-row.hunk {
		background: color-mix(in srgb, var(--cyan) 8%, transparent);
		color: var(--cyan);
	}

	.diff-row.header {
		background: color-mix(in srgb, var(--fg-dim) 6%, transparent);
		color: var(--fg-dim);
		font-style: italic;
	}
</style>