collet 0.1.0

Relentless agentic coding orchestrator with zero-drop agent loops
Documentation
<script lang="ts">
	import { appStore } from '$lib/store.svelte.js';
	import MessageBubble from './MessageBubble.svelte';
	import StreamingMessage from './StreamingMessage.svelte';
	import ToolCallBlock from './ToolCallBlock.svelte';

	let messagesEl: HTMLDivElement | undefined = $state();

	$effect(() => {
		if (messagesEl && (appStore.messages.length || appStore.streamingTokens)) {
			messagesEl.scrollTop = messagesEl.scrollHeight;
		}
	});
</script>

<div class="chat-area" bind:this={messagesEl} role="log" aria-label="Chat messages" aria-live="polite">
	<div class="chat-inner">
		{#each appStore.messages as msg (msg)}
			<MessageBubble role={msg.role} content={msg.content} />
		{/each}

		<StreamingMessage />
		<ToolCallBlock />

		{#if appStore.error}
			<div class="error-banner" role="alert">
				<span class="error-icon">!</span>
				<span>{appStore.error}</span>
			</div>
		{/if}

		{#if appStore.hiveAgents.size > 0}
			<div class="hive-panel">
				<div class="hive-header">Hive Agents</div>
				{#each [...appStore.hiveAgents.values()] as agent (agent.id)}
					<div class="hive-agent" class:done={agent.done} class:success={agent.success}>
						<span class="agent-status">{agent.done ? (agent.success ? '✓' : '✗') : '⟳'}</span>
						<span class="agent-name">{agent.name}</span>
						<span class="agent-task">{agent.task}</span>
						{#if !agent.done}
							<span class="agent-iter">#{agent.iteration}</span>
						{/if}
					</div>
				{/each}
			</div>
		{/if}
	</div>
</div>

<style>
	.chat-area {
		flex: 1;
		overflow-y: auto;
		scrollbar-width: thin;
		scrollbar-color: var(--border) transparent;
	}

	.chat-inner {
		max-width: 800px;
		margin: 0 auto;
		padding: 1.5rem 1rem;
		display: flex;
		flex-direction: column;
		gap: 0.75rem;
	}

	.error-banner {
		display: flex;
		align-items: center;
		gap: 0.5rem;
		padding: 0.75rem 1rem;
		background: color-mix(in srgb, var(--red) 12%, transparent);
		border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
		border-radius: var(--radius, 8px);
		color: var(--red);
		font-size: 0.875rem;
	}

	.error-icon { font-weight: bold; flex-shrink: 0; }

	.hive-panel {
		border: 1px solid var(--border);
		border-radius: var(--radius, 8px);
		padding: 0.75rem 1rem;
		background: var(--bg-surface);
		font-size: 0.875rem;
	}

	.hive-header {
		color: var(--cyan);
		margin-bottom: 0.375rem;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		font-size: 0.75rem;
	}

	.hive-agent {
		display: flex;
		gap: 0.5rem;
		padding: 0.2rem 0;
		color: var(--fg-dim);
	}

	.hive-agent.done.success { color: var(--green); }
	.hive-agent.done:not(.success) { color: var(--red); }

	.agent-status { flex-shrink: 0; width: 1.25ch; }
	.agent-name { color: var(--fg); flex-shrink: 0; }
	.agent-task { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
	.agent-iter { margin-left: auto; color: var(--fg-dim); flex-shrink: 0; }

	@media (max-width: 768px) {
		.chat-inner { padding: 1rem 0.75rem; }
	}
</style>