feat(electrobun): project status dots on group icons (SVG, per-project color by agent status)

This commit is contained in:
Hibryda 2026-03-27 02:26:51 +01:00
parent 41c3bc8e60
commit ec12310801
4 changed files with 106 additions and 4 deletions

View file

@ -9,6 +9,7 @@
import SearchOverlay from "./SearchOverlay.svelte";
import SplashScreen from "./SplashScreen.svelte";
import ProjectWizard from "./ProjectWizard.svelte";
import GroupStatusDots from "./GroupStatusDots.svelte";
import { themeStore } from "./theme-store.svelte.ts";
import { fontStore } from "./font-store.svelte.ts";
import { keybindingStore } from "./keybinding-store.svelte.ts";
@ -381,10 +382,11 @@
aria-label="{group.name} (Ctrl+{i + 1})"
title="{group.name} (Ctrl+{i + 1})"
>
<span class="group-circle" aria-hidden="true">{i + 1}</span>
{#if group.hasNew}
<span class="group-badge" aria-label="New activity"></span>
{/if}
<GroupStatusDots
projects={group.projects ?? []}
groupNumber={i + 1}
isActive={getActiveGroupId() === group.id}
/>
</button>
{/each}

View file

@ -0,0 +1,62 @@
<script lang="ts">
import { getProjectAgentStatus } from './project-status';
import { statusToColor } from './status-colors';
interface Props {
projects: Array<{ id: string }>;
groupNumber: number;
isActive: boolean;
size?: number;
}
let { projects, groupNumber, isActive, size = 32 }: Props = $props();
function dotColor(projectId: string): string {
return statusToColor(getProjectAgentStatus(projectId));
}
</script>
<svg
viewBox="0 0 32 32"
class="group-dots"
width={size}
height={size}
role="img"
aria-label="Group {groupNumber} with {projects.length} projects"
>
<!-- Border circle -->
<circle
cx="16"
cy="16"
r="15"
fill="none"
stroke={isActive ? 'var(--accent, var(--ctp-blue))' : 'var(--ctp-surface1)'}
stroke-width="1"
/>
<!-- Number text -->
<text
x="16"
y="16"
text-anchor="middle"
dominant-baseline="central"
font-size="11"
font-weight="700"
fill={isActive ? 'var(--ctp-text)' : 'var(--ctp-subtext0)'}
>{groupNumber}</text>
<!-- Status dots -->
{#each projects as project, i}
{@const angle = (i / projects.length) * Math.PI * 2 - Math.PI / 2}
{@const cx = 16 + 12 * Math.cos(angle)}
{@const cy = 16 + 12 * Math.sin(angle)}
<circle cx={cx} cy={cy} r="2.5" fill={dotColor(project.id)} />
{/each}
</svg>
<style>
.group-dots {
display: block;
flex-shrink: 0;
}
</style>

View file

@ -0,0 +1,24 @@
/**
* Reads agent status for a project from the global agent store.
* Returns a ProjectStatus suitable for status dot coloring.
*
* Reading getSession() touches the _v version counter inside agent-store,
* so Svelte 5 will re-evaluate any reactive context that calls this function
* when sessions change.
*/
import { getSession } from './agent-store.svelte';
import type { ProjectStatus } from './status-colors';
export function getProjectAgentStatus(projectId: string): ProjectStatus {
const session = getSession(projectId);
if (!session) return 'inactive';
switch (session.status) {
case 'running': return 'running';
case 'done': return 'done';
case 'error': return 'error';
case 'idle': return 'inactive';
default: return 'inactive';
}
}

View file

@ -0,0 +1,14 @@
/**
* Pure function mapping project status to a CSS custom property color.
*/
export type ProjectStatus = 'inactive' | 'running' | 'done' | 'error';
export function statusToColor(status: ProjectStatus): string {
switch (status) {
case 'running': return 'var(--ctp-green)';
case 'done': return 'var(--ctp-peach)';
case 'error': return 'var(--ctp-red)';
default: return 'var(--ctp-overlay0)';
}
}