feat: project SubWorker activity in sidebar
This commit is contained in:
@@ -128,7 +128,7 @@ runtime_id?: string | null,
|
||||
/**
|
||||
* Producer-owned monotonic revision for this Worker subject.
|
||||
*/
|
||||
subject_revision: number, state: SubscriptionWorkerState, workspace_id?: string | null, display_name?: string | null, profile?: string | null, repository_id?: string | null, working_directory_id?: SubscriptionWorkdirId | null, };
|
||||
subject_revision: number, state: SubscriptionWorkerState, has_running_internal_workers: boolean, workspace_id?: string | null, display_name?: string | null, profile?: string | null, repository_id?: string | null, working_directory_id?: SubscriptionWorkdirId | null, };
|
||||
|
||||
export type SubscriptionWorkdir = { working_directory_id: SubscriptionWorkdirId, repository_id: string, state: string, primary_worker_id?: SubscriptionWorkerId | null, };
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<style>
|
||||
.spinner {
|
||||
display: inline-flex;
|
||||
color: var(--accent);
|
||||
color: var(--spinner-color, var(--accent));
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import Spinner from '$lib/workspace/console/Spinner.svelte';
|
||||
import { workerConsoleHref } from '$lib/workspace/console/model';
|
||||
import {
|
||||
workspaceWorkersStore,
|
||||
@@ -76,10 +77,12 @@
|
||||
aria-current={currentPath === href ? 'page' : undefined}
|
||||
>
|
||||
<span class="worker-status-indicator">
|
||||
{#if worker.state === 'idle'}
|
||||
{#if worker.state === 'running'}
|
||||
<span class="worker-status-spinner"><Spinner label="Running" /></span>
|
||||
{:else if worker.has_running_internal_workers}
|
||||
<span class="worker-status-spinner is-subworker"><Spinner label="SubWorker running" /></span>
|
||||
{:else if worker.state === 'idle'}
|
||||
<span class="worker-status-dot" aria-label="Idle"></span>
|
||||
{:else if worker.state === 'running'}
|
||||
<span class="worker-status-spinner" aria-label="Running"></span>
|
||||
{/if}
|
||||
</span>
|
||||
<span class="worker-nav-label">{worker.display_name || worker.label}</span>
|
||||
|
||||
@@ -267,12 +267,17 @@
|
||||
background: var(--success);
|
||||
}
|
||||
.worker-status-spinner {
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
border: 0.125rem solid color-mix(in oklch, var(--accent) 25%, transparent);
|
||||
border-top-color: var(--accent);
|
||||
border-radius: 50%;
|
||||
animation: worker-status-spin 0.8s linear infinite;
|
||||
--spinner-color: var(--success);
|
||||
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 0.75rem;
|
||||
font-size: 0.7rem;
|
||||
line-height: 1;
|
||||
}
|
||||
.worker-status-spinner.is-subworker {
|
||||
--spinner-color: var(--tui-magenta);
|
||||
}
|
||||
.worker-nav-label {
|
||||
grid-column: 2;
|
||||
@@ -339,16 +344,6 @@
|
||||
.worker-overflow-toggle[aria-expanded="true"] .worker-overflow-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
@keyframes worker-status-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.worker-status-spinner {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.sidebar-frame,
|
||||
|
||||
@@ -20,6 +20,7 @@ function worker(runtimeId: string, workerId: string, revision: number): Subscrip
|
||||
runtime_id: runtimeId,
|
||||
subject_revision: revision,
|
||||
state: 'idle',
|
||||
has_running_internal_workers: false,
|
||||
workspace_id: 'workspace-test',
|
||||
display_name: null,
|
||||
profile: null,
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { Worker } from './types';
|
||||
export type SidebarWorker = Worker & {
|
||||
repository_id: string | null;
|
||||
working_directory_id: string | null;
|
||||
has_running_internal_workers: boolean;
|
||||
};
|
||||
|
||||
export type WorkspaceWorkersState = {
|
||||
@@ -96,6 +97,7 @@ function projectWorker(worker: SubscriptionWorker): SidebarWorker {
|
||||
},
|
||||
repository_id: worker.repository_id ?? null,
|
||||
working_directory_id: worker.working_directory_id ?? null,
|
||||
has_running_internal_workers: worker.has_running_internal_workers,
|
||||
working_directory: null,
|
||||
diagnostics: [],
|
||||
};
|
||||
|
||||
@@ -28,6 +28,62 @@ Deno.test("Console spinner wraps a reusable timed sequence loop", async () => {
|
||||
assert(spinner.includes("SequenceLoop"), "Spinner should wrap SequenceLoop");
|
||||
});
|
||||
|
||||
Deno.test("sidebar running status reuses the green symbol spinner", async () => {
|
||||
const sidebar = await Deno.readTextFile(
|
||||
new URL(
|
||||
"../src/lib/workspace/sidebar/WorkersNavSection.svelte",
|
||||
import.meta.url,
|
||||
),
|
||||
);
|
||||
const sidebarCss = await Deno.readTextFile(
|
||||
new URL(
|
||||
"../src/lib/workspace/sidebar/sidebar.css",
|
||||
import.meta.url,
|
||||
),
|
||||
);
|
||||
|
||||
assert(
|
||||
sidebar.includes(
|
||||
"import Spinner from '$lib/workspace/console/Spinner.svelte'",
|
||||
),
|
||||
"Workers sidebar should import the reusable symbol Spinner",
|
||||
);
|
||||
assert(
|
||||
sidebar.includes('<Spinner label="Running" />'),
|
||||
"running Workers should render the reusable symbol Spinner",
|
||||
);
|
||||
assert(
|
||||
sidebarCss.includes("--spinner-color: var(--success)"),
|
||||
"sidebar spinner should use the green success token",
|
||||
);
|
||||
assert(
|
||||
sidebar.indexOf("worker.state === 'running'") <
|
||||
sidebar.indexOf("worker.has_running_internal_workers"),
|
||||
"parent running state should keep the green Spinner priority",
|
||||
);
|
||||
assert(
|
||||
sidebar.indexOf("worker.has_running_internal_workers") <
|
||||
sidebar.indexOf("worker.state === 'idle'"),
|
||||
"SubWorker activity should replace the idle dot with the purple Spinner",
|
||||
);
|
||||
assert(
|
||||
sidebar.includes("worker.has_running_internal_workers"),
|
||||
"idle parents should render SubWorker activity from the Workspace projection",
|
||||
);
|
||||
assert(
|
||||
sidebar.includes('<Spinner label="SubWorker running" />'),
|
||||
"running SubWorkers should use the reusable symbol Spinner",
|
||||
);
|
||||
assert(
|
||||
sidebarCss.includes("--spinner-color: var(--tui-magenta)"),
|
||||
"SubWorker spinner should use the purple TUI token",
|
||||
);
|
||||
assert(
|
||||
!sidebarCss.includes("@keyframes worker-status-spin"),
|
||||
"legacy rotating ring spinner should be removed",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("running status is Composer-side above mini Tasks", async () => {
|
||||
const page = await Deno.readTextFile(
|
||||
new URL(
|
||||
|
||||
Reference in New Issue
Block a user