ui: move workers list to page

This commit is contained in:
2026-07-10 12:06:10 +09:00
parent a3cb59af43
commit 47e03bd54b
10 changed files with 322 additions and 877 deletions
@@ -1,9 +1,7 @@
<script lang="ts">
import { workerConsoleHref } from '$lib/workspace-console/model';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
let workspaceId = $derived(data.workspace?.workspace_id ?? data.workspaceId);
</script>
<svelte:head>
@@ -39,95 +37,48 @@
{/if}
</section>
<section class="grid runtime">
<div class="card">
<h2>Hosts</h2>
{#if data.hosts}
{#if data.hosts.items.length === 0}
<p>No local Hosts are visible.</p>
{:else}
<div class="stack">
{#each data.hosts.items as host}
<article class="runtime-card">
<div class="runtime-heading">
<strong>{host.label}</strong>
<span class:warn={host.status !== 'available'}>{host.status}</span>
<section class="card">
<h2>Hosts</h2>
{#if data.hosts}
{#if data.hosts.items.length === 0}
<p>No local Hosts are visible.</p>
{:else}
<div class="stack">
{#each data.hosts.items as host}
<article class="runtime-card">
<div class="runtime-heading">
<strong>{host.label}</strong>
<span class:warn={host.status !== 'available'}>{host.status}</span>
</div>
<dl>
<div>
<dt>ID</dt>
<dd><code>{host.host_id}</code></dd>
</div>
<dl>
<div>
<dt>ID</dt>
<dd><code>{host.host_id}</code></dd>
</div>
<div>
<dt>Kind</dt>
<dd>{host.kind}</dd>
</div>
<div>
<dt>Runtime</dt>
<dd><code>{host.runtime_id}</code></dd>
</div>
<div>
<dt>Scope</dt>
<dd>{host.capabilities.workspace_scope}</dd>
</div>
<div>
<dt>Platform</dt>
<dd>{host.capabilities.os} / {host.capabilities.arch}</dd>
</div>
</dl>
</article>
{/each}
</div>
{/if}
{:else if data.hostsError}
<p class="error">{data.hostsError}</p>
{:else}
<p>Waiting for <code>/api/hosts</code></p>
<div>
<dt>Kind</dt>
<dd>{host.kind}</dd>
</div>
<div>
<dt>Runtime</dt>
<dd><code>{host.runtime_id}</code></dd>
</div>
<div>
<dt>Scope</dt>
<dd>{host.capabilities.workspace_scope}</dd>
</div>
<div>
<dt>Platform</dt>
<dd>{host.capabilities.os} / {host.capabilities.arch}</dd>
</div>
</dl>
</article>
{/each}
</div>
{/if}
</div>
<div class="card">
<h2>Workers</h2>
{#if data.workers}
{#if data.workers.items.length === 0}
<p>No local Workers are visible.</p>
{:else}
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Worker</th>
<th>Host</th>
<th>State</th>
<th>Workspace</th>
<th>Implementation</th>
<th>Attach</th>
</tr>
</thead>
<tbody>
{#each data.workers.items as worker}
<tr>
<td>
<strong>{worker.label}</strong>
{#if worker.role || worker.profile}
<small>{worker.role ?? 'role unknown'} / {worker.profile ?? 'profile unknown'}</small>
{/if}
</td>
<td><code>{worker.host_id}</code></td>
<td>{worker.state} · {worker.status}</td>
<td>{worker.workspace.visibility} · {worker.workspace.identity}</td>
<td>{worker.implementation.kind}</td>
<td><a class="inline-link" href={workerConsoleHref(worker, workspaceId)}>Open Console</a></td>
</tr>
{/each}
</tbody>
</table>
</div>
{/if}
{:else if data.workersError}
<p class="error">{data.workersError}</p>
{:else}
<p>Waiting for <code>/api/workers</code></p>
{/if}
</div>
{:else if data.hostsError}
<p class="error">{data.hostsError}</p>
{:else}
<p>Waiting for <code>/api/hosts</code></p>
{/if}
</section>
@@ -1,19 +1,14 @@
import { loadJson, workspaceApiPath } from "$lib/workspace-api/http";
import type { Host, ListResponse, Worker } from "$lib/workspace-sidebar/types";
import type { Host, ListResponse } from "$lib/workspace-sidebar/types";
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ fetch, params }) => {
const apiPath = (path: string) => workspaceApiPath(params.workspaceId, path);
const [hosts, workers] = await Promise.all([
loadJson<ListResponse<Host>>(fetch, apiPath("/hosts")),
loadJson<ListResponse<Worker>>(fetch, apiPath("/workers")),
]);
const hosts = await loadJson<ListResponse<Host>>(fetch, apiPath("/hosts"));
return {
workspaceId: params.workspaceId,
hosts: hosts.data,
hostsError: hosts.error,
workers: workers.data,
workersError: workers.error,
};
};
@@ -0,0 +1,78 @@
<script lang="ts">
import { workerConsoleHref } from '$lib/workspace-console/model';
import type { Worker } from '$lib/workspace-sidebar/types';
import type { PageProps } from './$types';
let { data }: PageProps = $props();
function workerStatus(worker: Worker): string {
return `${worker.state} · ${worker.status}`;
}
function workerProfile(worker: Worker): string {
return worker.profile ?? worker.role ?? 'unknown';
}
function workerDirectory(worker: Worker): string {
const directory = worker.working_directory;
if (!directory) return '—';
const selector = directory.requested_selector ?? 'HEAD';
const commit = directory.resolved_commit ? directory.resolved_commit.slice(0, 12) : null;
return commit ? `${directory.repository_id} · ${selector} · ${commit}` : `${directory.repository_id} · ${selector}`;
}
</script>
<svelte:head>
<title>Workers · Yoi Workspace</title>
<meta name="description" content="Workspace Workers" />
</svelte:head>
<section class="workers-page" aria-labelledby="workers-heading">
<header class="workers-page-header">
<div>
<h1 id="workers-heading">Workers</h1>
<p>Workers running or persisted for this workspace.</p>
</div>
<a class="section-action" href={`/w/${data.workspaceId}/workers/new`}>New Worker</a>
</header>
{#if data.workersError}
<p class="section-state error">{data.workersError}</p>
{:else if !data.workers}
<p class="section-state">Loading Workers…</p>
{:else if data.workers.items.length === 0}
<p class="section-state">No Workers are visible.</p>
{:else}
<div class="table-wrap workers-table-wrap">
<table class="workers-table">
<thead>
<tr>
<th>Worker</th>
<th>Runtime</th>
<th>Profile</th>
<th>Status</th>
<th>Working directory</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{#each data.workers.items as worker}
<tr>
<td>
<strong>{worker.label}</strong>
<small><code>{worker.worker_id}</code></small>
</td>
<td><code>{worker.runtime_id}</code></td>
<td>{workerProfile(worker)}</td>
<td>{workerStatus(worker)}</td>
<td>{workerDirectory(worker)}</td>
<td>
<a class="inline-link" href={workerConsoleHref(worker, data.workspaceId)}>Open Console</a>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
{/if}
</section>
@@ -0,0 +1,16 @@
import { loadJson, workspaceApiPath } from "$lib/workspace-api/http";
import type { ListResponse, Worker } from "$lib/workspace-sidebar/types";
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ fetch, params }) => {
const workers = await loadJson<ListResponse<Worker>>(
fetch,
workspaceApiPath(params.workspaceId, "/workers"),
);
return {
workspaceId: params.workspaceId,
workers: workers.data,
workersError: workers.error,
};
};