feat(electrobun): project status dots on group icons (SVG, per-project color by agent status)
This commit is contained in:
parent
41c3bc8e60
commit
ec12310801
4 changed files with 106 additions and 4 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
import SearchOverlay from "./SearchOverlay.svelte";
|
import SearchOverlay from "./SearchOverlay.svelte";
|
||||||
import SplashScreen from "./SplashScreen.svelte";
|
import SplashScreen from "./SplashScreen.svelte";
|
||||||
import ProjectWizard from "./ProjectWizard.svelte";
|
import ProjectWizard from "./ProjectWizard.svelte";
|
||||||
|
import GroupStatusDots from "./GroupStatusDots.svelte";
|
||||||
import { themeStore } from "./theme-store.svelte.ts";
|
import { themeStore } from "./theme-store.svelte.ts";
|
||||||
import { fontStore } from "./font-store.svelte.ts";
|
import { fontStore } from "./font-store.svelte.ts";
|
||||||
import { keybindingStore } from "./keybinding-store.svelte.ts";
|
import { keybindingStore } from "./keybinding-store.svelte.ts";
|
||||||
|
|
@ -381,10 +382,11 @@
|
||||||
aria-label="{group.name} (Ctrl+{i + 1})"
|
aria-label="{group.name} (Ctrl+{i + 1})"
|
||||||
title="{group.name} (Ctrl+{i + 1})"
|
title="{group.name} (Ctrl+{i + 1})"
|
||||||
>
|
>
|
||||||
<span class="group-circle" aria-hidden="true">{i + 1}</span>
|
<GroupStatusDots
|
||||||
{#if group.hasNew}
|
projects={group.projects ?? []}
|
||||||
<span class="group-badge" aria-label="New activity"></span>
|
groupNumber={i + 1}
|
||||||
{/if}
|
isActive={getActiveGroupId() === group.id}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
|
|
||||||
62
ui-electrobun/src/mainview/GroupStatusDots.svelte
Normal file
62
ui-electrobun/src/mainview/GroupStatusDots.svelte
Normal 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>
|
||||||
24
ui-electrobun/src/mainview/project-status.ts
Normal file
24
ui-electrobun/src/mainview/project-status.ts
Normal 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
14
ui-electrobun/src/mainview/status-colors.ts
Normal file
14
ui-electrobun/src/mainview/status-colors.ts
Normal 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)';
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue