collet 0.1.1

Relentless agentic coding orchestrator with zero-drop agent loops
Documentation
<script lang="ts">
	import { projectsStore } from '$lib/stores/projects.svelte.js';
	import type { ProjectItem } from '$lib/api.js';

	let { onswitch }: { onswitch: (workingDir: string) => void } = $props();

	function projectName(workingDir: string | null): string {
		if (!workingDir) return 'Unknown';
		const segments = workingDir.replace(/\/+$/, '').split('/');
		return segments[segments.length - 1] || workingDir;
	}

	function projectInitial(workingDir: string | null): string {
		return projectName(workingDir).charAt(0).toUpperCase();
	}
</script>

<div class="project-list" role="listbox" aria-label="Projects">
	{#if projectsStore.loading}
		<div class="empty">Loading...</div>
	{:else if projectsStore.projects.length === 0}
		<div class="empty">No projects</div>
	{:else}
		{#each projectsStore.projects as project (project.id)}
			{@const isActive = projectsStore.activeProjectId === project.id}
			<div
				class="project-item"
				class:active={isActive}
				onclick={() => { if (project.working_dir) onswitch(project.working_dir); }}
				onkeydown={(e) => { if (e.key === 'Enter' && project.working_dir) onswitch(project.working_dir); }}
				tabindex="0"
				role="option"
				aria-selected={isActive}
			>
				<div class="project-icon" class:active={isActive}>
					{projectInitial(project.working_dir)}
				</div>
				<div class="project-info">
					<div class="project-name">{projectName(project.working_dir)}</div>
					<div class="project-path">{project.working_dir}</div>
				</div>
				{#if project.session_count > 0}
					<span class="session-badge">{project.session_count}</span>
				{/if}
			</div>
		{/each}
	{/if}
</div>

<style>
	.project-list {
		padding: 0.25rem 0;
	}

	.empty {
		padding: 1rem;
		color: var(--fg-dim);
		font-size: 0.875rem;
		text-align: center;
	}

	.project-item {
		display: flex;
		align-items: center;
		gap: 0.625rem;
		padding: 0.5rem 1rem;
		cursor: pointer;
		border-left: 2px solid transparent;
		transition: background 0.1s;
	}

	.project-item:hover {
		background: var(--bg-elevated);
	}

	.project-item.active {
		background: var(--bg-elevated);
		border-left-color: var(--accent);
	}

	.project-item:focus-visible {
		outline: 2px solid var(--accent);
		outline-offset: -2px;
	}

	.project-icon {
		width: 1.75rem;
		height: 1.75rem;
		border-radius: 6px;
		background: var(--bg);
		color: var(--fg-dim);
		display: flex;
		align-items: center;
		justify-content: center;
		font-weight: 700;
		font-size: 0.75rem;
		flex-shrink: 0;
	}

	.project-icon.active {
		background: var(--accent-dim);
		color: var(--accent);
	}

	.project-info {
		flex: 1;
		min-width: 0;
	}

	.project-name {
		font-size: 0.875rem;
		font-weight: 500;
		color: var(--fg);
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.project-item.active .project-name {
		color: var(--fg-bright);
		font-weight: 600;
	}

	.project-path {
		font-size: 0.6875rem;
		color: var(--fg-dim);
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
	}

	.session-badge {
		font-size: 0.6875rem;
		color: var(--fg-dim);
		background: var(--bg);
		padding: 0.125rem 0.375rem;
		border-radius: 9999px;
		flex-shrink: 0;
	}
</style>