mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
feat(ui): improve Workboard task details
Make Workboard cards compact by moving expanded task/run metadata, proof, diagnostics, worker logs, automation, protocol state, events, and operator notes into a detail drawer. Keep execution state simple and safe: active, linked, and archived cards avoid duplicate start paths; stale task cache is ignored when session lifecycle is authoritative; recent proof/events stay visible; dispatcher capacity distinguishes unclaimed review cards from claimed cards.
This commit is contained in:
@@ -80,6 +80,64 @@ describe("dispatchAndStartWorkboardCards", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not let review cards consume an agent running slot", async () => {
|
||||
const store = new WorkboardStore(createMemoryStore());
|
||||
await store.create({
|
||||
title: "Waiting for operator review",
|
||||
status: "review",
|
||||
priority: "normal",
|
||||
agentId: "codex-main",
|
||||
});
|
||||
const ready = await store.create({
|
||||
title: "Next ready card",
|
||||
status: "ready",
|
||||
priority: "high",
|
||||
agentId: "codex-main",
|
||||
});
|
||||
const run = vi.fn().mockResolvedValue({ runId: "run-next" });
|
||||
|
||||
const result = await dispatchAndStartWorkboardCards({
|
||||
store,
|
||||
subagent: { run },
|
||||
options: { now: 10, maxStarts: 3 },
|
||||
});
|
||||
|
||||
expect(result.started).toEqual([
|
||||
expect.objectContaining({
|
||||
cardId: ready.id,
|
||||
runId: "run-next",
|
||||
}),
|
||||
]);
|
||||
expect(run).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("keeps claimed review cards in the owner running slot", async () => {
|
||||
const store = new WorkboardStore(createMemoryStore());
|
||||
const review = await store.create({
|
||||
title: "Claimed operator review",
|
||||
status: "review",
|
||||
priority: "normal",
|
||||
agentId: "codex-main",
|
||||
});
|
||||
await store.claim(review.id, { ownerId: "codex-main", token: "review-token" });
|
||||
await store.create({
|
||||
title: "Next ready card",
|
||||
status: "ready",
|
||||
priority: "high",
|
||||
agentId: "codex-main",
|
||||
});
|
||||
const run = vi.fn().mockResolvedValue({ runId: "run-next" });
|
||||
|
||||
const result = await dispatchAndStartWorkboardCards({
|
||||
store,
|
||||
subagent: { run },
|
||||
options: { now: 10, maxStarts: 3 },
|
||||
});
|
||||
|
||||
expect(result.started).toEqual([]);
|
||||
expect(run).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("blocks a card when worker start fails after claim", async () => {
|
||||
const store = new WorkboardStore(createMemoryStore());
|
||||
const card = await store.create({ title: "Fail worker", status: "ready" });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import type { PluginRuntime } from "openclaw/plugin-sdk/plugin-runtime";
|
||||
import { WorkboardStore, type WorkboardDispatchResult } from "./store.js";
|
||||
import type { WorkboardCard, WorkboardExecution, WorkboardStatus } from "./types.js";
|
||||
import type { WorkboardCard, WorkboardExecution } from "./types.js";
|
||||
|
||||
const DEFAULT_DISPATCH_MAX_STARTS = 3;
|
||||
const DEFAULT_DISPATCH_OWNER = "workboard-dispatcher";
|
||||
@@ -126,10 +126,13 @@ function selectStartableCards(cards: WorkboardCard[], limit: number): WorkboardC
|
||||
if (limit <= 0) {
|
||||
return [];
|
||||
}
|
||||
const activeStatuses = new Set<WorkboardStatus>(["running", "review"]);
|
||||
const runningByOwner = new Map<string, number>();
|
||||
for (const card of cards) {
|
||||
if (!activeStatuses.has(card.status) || cardIsArchived(card)) {
|
||||
const consumesOwnerSlot =
|
||||
card.status === "running" ||
|
||||
Boolean(card.metadata?.claim) ||
|
||||
card.execution?.status === "running";
|
||||
if (!consumesOwnerSlot || cardIsArchived(card)) {
|
||||
continue;
|
||||
}
|
||||
const owner = card.agentId ?? DEFAULT_DISPATCH_OWNER;
|
||||
|
||||
@@ -977,7 +977,7 @@ export function createWorkboardTools(params: {
|
||||
name: "workboard_dispatch",
|
||||
label: "Workboard Dispatch",
|
||||
description:
|
||||
"Nudge Workboard dependency promotion and reclaim expired claims or timed-out runs.",
|
||||
"Run one Workboard dispatcher pass: promote unblocked cards, reclaim expired claims, and block timed-out runs.",
|
||||
parameters: Type.Object({}, { additionalProperties: false }),
|
||||
execute: async () => {
|
||||
const result = await store.dispatch();
|
||||
|
||||
29
ui/src/i18n/.i18n/ar.meta.json
generated
29
ui/src/i18n/.i18n/ar.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.990Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.090Z",
|
||||
"locale": "ar",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/de.meta.json
generated
29
ui/src/i18n/.i18n/de.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.583Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.623Z",
|
||||
"locale": "de",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/es.meta.json
generated
29
ui/src/i18n/.i18n/es.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.664Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.724Z",
|
||||
"locale": "es",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/fa.meta.json
generated
29
ui/src/i18n/.i18n/fa.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.719Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.893Z",
|
||||
"locale": "fa",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/fr.meta.json
generated
29
ui/src/i18n/.i18n/fr.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.907Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.001Z",
|
||||
"locale": "fr",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/id.meta.json
generated
29
ui/src/i18n/.i18n/id.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.305Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.448Z",
|
||||
"locale": "id",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/it.meta.json
generated
29
ui/src/i18n/.i18n/it.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.068Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.183Z",
|
||||
"locale": "it",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/ja-JP.meta.json
generated
29
ui/src/i18n/.i18n/ja-JP.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.743Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.818Z",
|
||||
"locale": "ja-JP",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/ko.meta.json
generated
29
ui/src/i18n/.i18n/ko.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.823Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.911Z",
|
||||
"locale": "ko",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/nl.meta.json
generated
29
ui/src/i18n/.i18n/nl.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.639Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.801Z",
|
||||
"locale": "nl",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/pl.meta.json
generated
29
ui/src/i18n/.i18n/pl.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.382Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.535Z",
|
||||
"locale": "pl",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/pt-BR.meta.json
generated
29
ui/src/i18n/.i18n/pt-BR.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.498Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.531Z",
|
||||
"locale": "pt-BR",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/th.meta.json
generated
29
ui/src/i18n/.i18n/th.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.469Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.623Z",
|
||||
"locale": "th",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/tr.meta.json
generated
29
ui/src/i18n/.i18n/tr.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.146Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.272Z",
|
||||
"locale": "tr",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/uk.meta.json
generated
29
ui/src/i18n/.i18n/uk.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.227Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.362Z",
|
||||
"locale": "uk",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/vi.meta.json
generated
29
ui/src/i18n/.i18n/vi.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:06.555Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:31.711Z",
|
||||
"locale": "vi",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/zh-CN.meta.json
generated
29
ui/src/i18n/.i18n/zh-CN.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.323Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.090Z",
|
||||
"locale": "zh-CN",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
29
ui/src/i18n/.i18n/zh-TW.meta.json
generated
29
ui/src/i18n/.i18n/zh-TW.meta.json
generated
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-31T23:28:05.410Z",
|
||||
"fallbackKeys": [
|
||||
"workboard.detailAddNote",
|
||||
"workboard.detailAutomation",
|
||||
"workboard.detailAutomationBoard",
|
||||
"workboard.detailAutomationSkills",
|
||||
"workboard.detailAutomationSummary",
|
||||
"workboard.detailAutomationTenant",
|
||||
"workboard.detailAutomationWorkspace",
|
||||
"workboard.detailDiagnostics",
|
||||
"workboard.detailNoNotes",
|
||||
"workboard.detailNotePlaceholder",
|
||||
"workboard.detailOperatorNotes",
|
||||
"workboard.detailProof",
|
||||
"workboard.detailRun",
|
||||
"workboard.detailTask",
|
||||
"workboard.detailTitle",
|
||||
"workboard.detailUpdated",
|
||||
"workboard.detailUpdatedValue",
|
||||
"workboard.detailWorkerLogs",
|
||||
"workboard.detailWorkerProtocol",
|
||||
"workboard.viewDetails"
|
||||
],
|
||||
"generatedAt": "2026-06-01T02:32:30.440Z",
|
||||
"locale": "zh-TW",
|
||||
"model": "claude-opus-4-8",
|
||||
"provider": "anthropic",
|
||||
"sourceHash": "21d07eb018c81914c212ea2dff7f7174db30e25cf1d70dac9b269cf982d7cd8b",
|
||||
"totalKeys": 1284,
|
||||
"sourceHash": "59589c3b29f7126995ed4763e88b763702a7c20b1791b4e16c62b394bca02d45",
|
||||
"totalKeys": 1304,
|
||||
"translatedKeys": 1284,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
20
ui/src/i18n/locales/ar.ts
generated
20
ui/src/i18n/locales/ar.ts
generated
@@ -507,6 +507,26 @@ export const ar: TranslationMap = {
|
||||
newCardHelp: "أضف العمل إلى قائمة الانتظار لجلسة وكيل.",
|
||||
archiveCard: "أرشفة البطاقة",
|
||||
deleteCard: "حذف البطاقة",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "فتح الجلسة",
|
||||
openLinkedSession: "فتح الجلسة المرتبطة",
|
||||
defaultAgent: "الوكيل الافتراضي",
|
||||
|
||||
20
ui/src/i18n/locales/de.ts
generated
20
ui/src/i18n/locales/de.ts
generated
@@ -512,6 +512,26 @@ export const de: TranslationMap = {
|
||||
newCardHelp: "Arbeit für eine Agentensitzung in die Warteschlange einreihen.",
|
||||
archiveCard: "Karte archivieren",
|
||||
deleteCard: "Karte löschen",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Sitzung öffnen",
|
||||
openLinkedSession: "Verknüpfte Sitzung öffnen",
|
||||
defaultAgent: "Standard-Agent",
|
||||
|
||||
@@ -506,6 +506,26 @@ export const en: TranslationMap = {
|
||||
newCardHelp: "Queue work for an agent session.",
|
||||
archiveCard: "Archive card",
|
||||
deleteCard: "Delete card",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Open session",
|
||||
openLinkedSession: "Open linked session",
|
||||
defaultAgent: "Default agent",
|
||||
|
||||
20
ui/src/i18n/locales/es.ts
generated
20
ui/src/i18n/locales/es.ts
generated
@@ -509,6 +509,26 @@ export const es: TranslationMap = {
|
||||
newCardHelp: "Pon trabajo en cola para una sesión de agente.",
|
||||
archiveCard: "Archivar tarjeta",
|
||||
deleteCard: "Eliminar tarjeta",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Abrir sesión",
|
||||
openLinkedSession: "Abrir sesión vinculada",
|
||||
defaultAgent: "Agente predeterminado",
|
||||
|
||||
20
ui/src/i18n/locales/fa.ts
generated
20
ui/src/i18n/locales/fa.ts
generated
@@ -509,6 +509,26 @@ export const fa: TranslationMap = {
|
||||
newCardHelp: "کار را برای یک نشست عامل در صف قرار دهید.",
|
||||
archiveCard: "بایگانی کارت",
|
||||
deleteCard: "حذف کارت",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "باز کردن نشست",
|
||||
openLinkedSession: "باز کردن نشست پیوندشده",
|
||||
defaultAgent: "عامل پیشفرض",
|
||||
|
||||
20
ui/src/i18n/locales/fr.ts
generated
20
ui/src/i18n/locales/fr.ts
generated
@@ -511,6 +511,26 @@ export const fr: TranslationMap = {
|
||||
newCardHelp: "Mettez du travail en file d’attente pour une session d’agent.",
|
||||
archiveCard: "Archiver la carte",
|
||||
deleteCard: "Supprimer la carte",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Ouvrir la session",
|
||||
openLinkedSession: "Ouvrir la session liée",
|
||||
defaultAgent: "Agent par défaut",
|
||||
|
||||
20
ui/src/i18n/locales/id.ts
generated
20
ui/src/i18n/locales/id.ts
generated
@@ -508,6 +508,26 @@ export const id: TranslationMap = {
|
||||
newCardHelp: "Antrekan pekerjaan untuk sesi agen.",
|
||||
archiveCard: "Arsipkan kartu",
|
||||
deleteCard: "Hapus kartu",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Buka sesi",
|
||||
openLinkedSession: "Buka sesi tertaut",
|
||||
defaultAgent: "Agen default",
|
||||
|
||||
20
ui/src/i18n/locales/it.ts
generated
20
ui/src/i18n/locales/it.ts
generated
@@ -510,6 +510,26 @@ export const it: TranslationMap = {
|
||||
newCardHelp: "Metti in coda il lavoro per una sessione dell'agente.",
|
||||
archiveCard: "Archivia scheda",
|
||||
deleteCard: "Elimina scheda",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Apri sessione",
|
||||
openLinkedSession: "Apri sessione collegata",
|
||||
defaultAgent: "Agente predefinito",
|
||||
|
||||
20
ui/src/i18n/locales/ja-JP.ts
generated
20
ui/src/i18n/locales/ja-JP.ts
generated
@@ -511,6 +511,26 @@ export const ja_JP: TranslationMap = {
|
||||
newCardHelp: "エージェントセッションの作業をキューに追加します。",
|
||||
archiveCard: "カードをアーカイブ",
|
||||
deleteCard: "カードを削除",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "セッションを開く",
|
||||
openLinkedSession: "リンクされたセッションを開く",
|
||||
defaultAgent: "デフォルトエージェント",
|
||||
|
||||
20
ui/src/i18n/locales/ko.ts
generated
20
ui/src/i18n/locales/ko.ts
generated
@@ -507,6 +507,26 @@ export const ko: TranslationMap = {
|
||||
newCardHelp: "에이전트 세션을 위한 작업을 대기열에 추가합니다.",
|
||||
archiveCard: "카드 보관",
|
||||
deleteCard: "카드 삭제",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "세션 열기",
|
||||
openLinkedSession: "연결된 세션 열기",
|
||||
defaultAgent: "기본 에이전트",
|
||||
|
||||
20
ui/src/i18n/locales/nl.ts
generated
20
ui/src/i18n/locales/nl.ts
generated
@@ -510,6 +510,26 @@ export const nl: TranslationMap = {
|
||||
newCardHelp: "Zet werk in de wachtrij voor een agentsessie.",
|
||||
archiveCard: "Kaart archiveren",
|
||||
deleteCard: "Kaart verwijderen",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Sessie openen",
|
||||
openLinkedSession: "Gekoppelde sessie openen",
|
||||
defaultAgent: "Standaardagent",
|
||||
|
||||
20
ui/src/i18n/locales/pl.ts
generated
20
ui/src/i18n/locales/pl.ts
generated
@@ -509,6 +509,26 @@ export const pl: TranslationMap = {
|
||||
newCardHelp: "Dodaj zadanie do kolejki dla sesji agenta.",
|
||||
archiveCard: "Archiwizuj kartę",
|
||||
deleteCard: "Usuń kartę",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Otwórz sesję",
|
||||
openLinkedSession: "Otwórz powiązaną sesję",
|
||||
defaultAgent: "Domyślny agent",
|
||||
|
||||
20
ui/src/i18n/locales/pt-BR.ts
generated
20
ui/src/i18n/locales/pt-BR.ts
generated
@@ -508,6 +508,26 @@ export const pt_BR: TranslationMap = {
|
||||
newCardHelp: "Enfileire trabalho para uma sessão de agente.",
|
||||
archiveCard: "Arquivar cartão",
|
||||
deleteCard: "Excluir cartão",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Abrir sessão",
|
||||
openLinkedSession: "Abrir sessão vinculada",
|
||||
defaultAgent: "Agente padrão",
|
||||
|
||||
20
ui/src/i18n/locales/th.ts
generated
20
ui/src/i18n/locales/th.ts
generated
@@ -506,6 +506,26 @@ export const th: TranslationMap = {
|
||||
newCardHelp: "จัดคิวงานสำหรับเซสชันของเอเจนต์",
|
||||
archiveCard: "เก็บถาวรการ์ด",
|
||||
deleteCard: "ลบการ์ด",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "เปิดเซสชัน",
|
||||
openLinkedSession: "เปิดเซสชันที่ลิงก์ไว้",
|
||||
defaultAgent: "เอเจนต์เริ่มต้น",
|
||||
|
||||
20
ui/src/i18n/locales/tr.ts
generated
20
ui/src/i18n/locales/tr.ts
generated
@@ -511,6 +511,26 @@ export const tr: TranslationMap = {
|
||||
newCardHelp: "Bir ajan oturumu için işi kuyruğa alın.",
|
||||
archiveCard: "Kartı arşivle",
|
||||
deleteCard: "Kartı sil",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Oturumu aç",
|
||||
openLinkedSession: "Bağlantılı oturumu aç",
|
||||
defaultAgent: "Varsayılan ajan",
|
||||
|
||||
20
ui/src/i18n/locales/uk.ts
generated
20
ui/src/i18n/locales/uk.ts
generated
@@ -510,6 +510,26 @@ export const uk: TranslationMap = {
|
||||
newCardHelp: "Поставте роботу в чергу для сесії агента.",
|
||||
archiveCard: "Архівувати картку",
|
||||
deleteCard: "Видалити картку",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Відкрити сесію",
|
||||
openLinkedSession: "Відкрити пов’язану сесію",
|
||||
defaultAgent: "Агент за замовчуванням",
|
||||
|
||||
20
ui/src/i18n/locales/vi.ts
generated
20
ui/src/i18n/locales/vi.ts
generated
@@ -509,6 +509,26 @@ export const vi: TranslationMap = {
|
||||
newCardHelp: "Đưa công việc vào hàng đợi cho một phiên agent.",
|
||||
archiveCard: "Lưu trữ thẻ",
|
||||
deleteCard: "Xóa thẻ",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "Mở phiên",
|
||||
openLinkedSession: "Mở phiên được liên kết",
|
||||
defaultAgent: "Agent mặc định",
|
||||
|
||||
20
ui/src/i18n/locales/zh-CN.ts
generated
20
ui/src/i18n/locales/zh-CN.ts
generated
@@ -505,6 +505,26 @@ export const zh_CN: TranslationMap = {
|
||||
newCardHelp: "为代理会话排队工作。",
|
||||
archiveCard: "归档卡片",
|
||||
deleteCard: "删除卡片",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "打开会话",
|
||||
openLinkedSession: "打开关联会话",
|
||||
defaultAgent: "默认代理",
|
||||
|
||||
20
ui/src/i18n/locales/zh-TW.ts
generated
20
ui/src/i18n/locales/zh-TW.ts
generated
@@ -505,6 +505,26 @@ export const zh_TW: TranslationMap = {
|
||||
newCardHelp: "為代理程式工作階段排入工作。",
|
||||
archiveCard: "封存卡片",
|
||||
deleteCard: "刪除卡片",
|
||||
viewDetails: "View details",
|
||||
detailTitle: "Card details",
|
||||
detailTask: "Gateway task",
|
||||
detailRun: "Run",
|
||||
detailUpdated: "Updated",
|
||||
detailProof: "Proof",
|
||||
detailDiagnostics: "Diagnostics",
|
||||
detailWorkerLogs: "Worker logs",
|
||||
detailWorkerProtocol: "Worker protocol",
|
||||
detailAutomation: "Automation",
|
||||
detailUpdatedValue: "Updated: {time}",
|
||||
detailAutomationTenant: "Tenant: {tenant}",
|
||||
detailAutomationBoard: "Board: {board}",
|
||||
detailAutomationSkills: "Skills: {skills}",
|
||||
detailAutomationWorkspace: "Workspace: {workspace}",
|
||||
detailAutomationSummary: "Summary: {summary}",
|
||||
detailOperatorNotes: "Operator notes",
|
||||
detailNoNotes: "No operator notes yet.",
|
||||
detailNotePlaceholder: "Add a decision, blocker, or proof note...",
|
||||
detailAddNote: "Add note",
|
||||
openSession: "開啟工作階段",
|
||||
openLinkedSession: "開啟連結的工作階段",
|
||||
defaultAgent: "預設代理程式",
|
||||
|
||||
@@ -502,6 +502,142 @@
|
||||
font-size: 0.86rem;
|
||||
}
|
||||
|
||||
.workboard-detail-drawer {
|
||||
position: fixed;
|
||||
inset: 0 0 0 auto;
|
||||
z-index: 900;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: min(460px, 100vw);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.workboard-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
pointer-events: auto;
|
||||
border-left: 1px solid color-mix(in srgb, var(--border) 86%, transparent);
|
||||
background: color-mix(in srgb, var(--panel) 98%, var(--bg) 2%);
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
.workboard-detail__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.workboard-detail__header h2 {
|
||||
margin: 8px 0 0;
|
||||
font-size: 1.04rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.workboard-detail__section {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
||||
}
|
||||
|
||||
.workboard-detail__section:first-of-type {
|
||||
padding-top: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.workboard-detail__section h3 {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 0.74rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.workboard-detail__section p {
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.45;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.workboard-detail__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workboard-detail__row {
|
||||
min-width: 0;
|
||||
padding: 8px;
|
||||
border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
|
||||
border-radius: 7px;
|
||||
background: color-mix(in srgb, var(--bg) 74%, transparent);
|
||||
}
|
||||
|
||||
.workboard-detail__row span {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 650;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.workboard-detail__row strong {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
color: var(--text);
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workboard-detail__list {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.workboard-detail__list li {
|
||||
min-width: 0;
|
||||
padding: 7px 8px;
|
||||
border-radius: 7px;
|
||||
background: color-mix(in srgb, var(--bg) 68%, transparent);
|
||||
color: var(--text);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.workboard textarea.input.workboard-detail__note {
|
||||
width: 100%;
|
||||
min-height: 84px;
|
||||
padding: 9px 10px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.workboard-detail__actions {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: auto;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
||||
}
|
||||
|
||||
.workboard-detail__actions > .btn {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@media (max-width: 860px) {
|
||||
.workboard-toolbar {
|
||||
display: flex;
|
||||
@@ -530,4 +666,19 @@
|
||||
.workboard-board {
|
||||
grid-auto-columns: minmax(260px, 82vw);
|
||||
}
|
||||
|
||||
.workboard-detail-drawer {
|
||||
inset: auto 0 0;
|
||||
width: 100vw;
|
||||
max-height: 82vh;
|
||||
}
|
||||
|
||||
.workboard-detail {
|
||||
border-top: 1px solid color-mix(in srgb, var(--border) 86%, transparent);
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.workboard-detail__grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import type { GatewaySessionRow } from "../types.ts";
|
||||
import {
|
||||
addWorkboardCardComment,
|
||||
archiveWorkboardCard,
|
||||
captureSessionToWorkboard,
|
||||
createWorkboardCard,
|
||||
@@ -323,6 +324,36 @@ describe("workboard controller", () => {
|
||||
expect(state.editingCardId).toBeNull();
|
||||
});
|
||||
|
||||
it("adds operator notes to a selected detail card without opening the edit draft", async () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
state.cards = [sampleCard];
|
||||
state.detailCardId = sampleCard.id;
|
||||
state.detailCommentBody = "Need one more proof run.";
|
||||
const updated = {
|
||||
...sampleCard,
|
||||
metadata: {
|
||||
comments: [{ id: "comment-1", body: "Need one more proof run.", createdAt: 2 }],
|
||||
},
|
||||
} satisfies WorkboardCard;
|
||||
const client = createClient({ "workboard.cards.comment": { card: updated } });
|
||||
|
||||
await addWorkboardCardComment({
|
||||
host,
|
||||
client: client as never,
|
||||
cardId: sampleCard.id,
|
||||
body: state.detailCommentBody,
|
||||
});
|
||||
|
||||
expect(client.request).toHaveBeenCalledWith("workboard.cards.comment", {
|
||||
id: "card-1",
|
||||
body: "Need one more proof run.",
|
||||
});
|
||||
expect(state.cards[0]?.metadata?.comments?.[0]?.body).toBe("Need one more proof run.");
|
||||
expect(state.detailCommentBody).toBe("");
|
||||
expect(state.draftOpen).toBe(false);
|
||||
});
|
||||
|
||||
it("captures existing sessions as linked workboard cards", async () => {
|
||||
const host = {};
|
||||
const session = {
|
||||
|
||||
@@ -347,6 +347,8 @@ export type WorkboardUiState = {
|
||||
draftSessionKey: string;
|
||||
draftTemplateId: WorkboardTemplateId | "";
|
||||
draftCommentBody: string;
|
||||
detailCardId: string | null;
|
||||
detailCommentBody: string;
|
||||
busyCardId: string | null;
|
||||
draggedCardId: string | null;
|
||||
syncingCardIds: Set<string>;
|
||||
@@ -389,6 +391,8 @@ function createDefaultState(): WorkboardUiState {
|
||||
draftSessionKey: "",
|
||||
draftTemplateId: "",
|
||||
draftCommentBody: "",
|
||||
detailCardId: null,
|
||||
detailCommentBody: "",
|
||||
busyCardId: null,
|
||||
draggedCardId: null,
|
||||
syncingCardIds: new Set(),
|
||||
@@ -1680,27 +1684,34 @@ export async function saveWorkboardCardDraft(params: {
|
||||
export async function addWorkboardCardComment(params: {
|
||||
host: WorkboardHost;
|
||||
client: GatewayBrowserClient | null;
|
||||
cardId?: string;
|
||||
body?: string;
|
||||
requestUpdate?: () => void;
|
||||
}) {
|
||||
const state = getWorkboardState(params.host);
|
||||
const body = state.draftCommentBody.trim();
|
||||
if (!state.editingCardId || !params.client || !body) {
|
||||
const cardId = params.cardId ?? state.editingCardId;
|
||||
const body = (params.body ?? state.draftCommentBody).trim();
|
||||
if (!cardId || !params.client || !body) {
|
||||
return;
|
||||
}
|
||||
state.loading = true;
|
||||
state.busyCardId = cardId;
|
||||
state.error = null;
|
||||
params.requestUpdate?.();
|
||||
try {
|
||||
const payload = await params.client.request("workboard.cards.comment", {
|
||||
id: state.editingCardId,
|
||||
id: cardId,
|
||||
body,
|
||||
});
|
||||
replaceCard(state, normalizeCardPayload(payload));
|
||||
state.draftCommentBody = "";
|
||||
if (params.body === undefined) {
|
||||
state.draftCommentBody = "";
|
||||
} else if (state.detailCardId === cardId) {
|
||||
state.detailCommentBody = "";
|
||||
}
|
||||
} catch (error) {
|
||||
state.error = formatError(error);
|
||||
} finally {
|
||||
state.loading = false;
|
||||
state.busyCardId = null;
|
||||
params.requestUpdate?.();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { getWorkboardState } from "../controllers/workboard.ts";
|
||||
import type { GatewayBrowserClient } from "../gateway.ts";
|
||||
import { renderWorkboard } from "./workboard.ts";
|
||||
|
||||
type WorkboardRenderProps = Parameters<typeof renderWorkboard>[0];
|
||||
|
||||
describe("renderWorkboard", () => {
|
||||
it("renders board columns and preloaded cards", () => {
|
||||
const now = Date.now();
|
||||
@@ -93,7 +95,7 @@ describe("renderWorkboard", () => {
|
||||
expect(container.textContent).not.toContain("Invalid Date");
|
||||
});
|
||||
|
||||
it("opens linked cards from the card surface without hijacking action buttons", () => {
|
||||
it("opens card details from the card surface without hijacking action buttons", () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
const onOpenSession = vi.fn();
|
||||
@@ -137,7 +139,31 @@ describe("renderWorkboard", () => {
|
||||
|
||||
const card = container.querySelector<HTMLElement>(".workboard-card");
|
||||
card?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
expect(onOpenSession).toHaveBeenCalledWith("agent:main:dashboard:1");
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [
|
||||
{
|
||||
key: "agent:main:dashboard:1",
|
||||
kind: "direct",
|
||||
displayName: "Dashboard session",
|
||||
updatedAt: 2,
|
||||
hasActiveRun: true,
|
||||
status: "running",
|
||||
},
|
||||
],
|
||||
onOpenSession,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain(
|
||||
"Inspect a running task",
|
||||
);
|
||||
expect(onOpenSession).not.toHaveBeenCalled();
|
||||
|
||||
onOpenSession.mockClear();
|
||||
container
|
||||
@@ -146,7 +172,7 @@ describe("renderWorkboard", () => {
|
||||
expect(onOpenSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows Codex and Claude execution actions for unlinked cards", () => {
|
||||
it("keeps cards compact and puts model-specific execution actions in details", () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
state.loaded = true;
|
||||
@@ -180,21 +206,36 @@ describe("renderWorkboard", () => {
|
||||
const startButtons = [
|
||||
...container.querySelectorAll<HTMLButtonElement>(".workboard-card__start"),
|
||||
];
|
||||
expect(startButtons.map((button) => button.textContent?.trim())).toEqual([
|
||||
expect(startButtons.map((button) => button.textContent?.trim())).toEqual(["Start"]);
|
||||
expect(startButtons.map((button) => button.title)).toEqual(["Run default agent"]);
|
||||
expect(container.querySelector(".workboard-card")?.getAttribute("role")).toBe("button");
|
||||
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
|
||||
const detailStartButtons = [
|
||||
...container.querySelectorAll<HTMLButtonElement>(".workboard-detail .workboard-card__start"),
|
||||
];
|
||||
expect(detailStartButtons.map((button) => button.textContent?.trim())).toEqual([
|
||||
"Start",
|
||||
"codex",
|
||||
"claude",
|
||||
"codex",
|
||||
"claude",
|
||||
]);
|
||||
expect(startButtons.map((button) => button.title)).toEqual([
|
||||
"Run default agent",
|
||||
"Run codex",
|
||||
"Run claude",
|
||||
"Open codex",
|
||||
"Open claude",
|
||||
]);
|
||||
expect(container.querySelector(".workboard-card")?.getAttribute("role")).toBeNull();
|
||||
});
|
||||
|
||||
it("hides autonomous model override actions for non-admin operators", () => {
|
||||
@@ -232,16 +273,33 @@ describe("renderWorkboard", () => {
|
||||
const startButtons = [
|
||||
...container.querySelectorAll<HTMLButtonElement>(".workboard-card__start"),
|
||||
];
|
||||
expect(startButtons.map((button) => button.textContent?.trim())).toEqual([
|
||||
expect(startButtons.map((button) => button.textContent?.trim())).toEqual(["Start"]);
|
||||
expect(startButtons.map((button) => button.title)).toEqual(["Run default agent"]);
|
||||
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
canModelOverride: false,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
const detailStartButtons = [
|
||||
...container.querySelectorAll<HTMLButtonElement>(".workboard-detail .workboard-card__start"),
|
||||
];
|
||||
expect(detailStartButtons.map((button) => button.textContent?.trim())).toEqual([
|
||||
"Start",
|
||||
"codex",
|
||||
"claude",
|
||||
]);
|
||||
expect(startButtons.map((button) => button.title)).toEqual([
|
||||
"Run default agent",
|
||||
"Open codex",
|
||||
"Open claude",
|
||||
]);
|
||||
});
|
||||
|
||||
it("renders linked Gateway task status on cards", () => {
|
||||
@@ -321,33 +379,42 @@ describe("renderWorkboard", () => {
|
||||
progressSummary: "Still running according to stale cache.",
|
||||
});
|
||||
const container = document.createElement("div");
|
||||
const props = {
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [
|
||||
{
|
||||
key: "agent:main:subagent:workboard-default-card-1",
|
||||
kind: "direct",
|
||||
displayName: "Finished session",
|
||||
updatedAt: 2,
|
||||
hasActiveRun: false,
|
||||
status: "done",
|
||||
},
|
||||
],
|
||||
onOpenSession: () => undefined,
|
||||
onRequestUpdate: () => undefined,
|
||||
} satisfies WorkboardRenderProps;
|
||||
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [
|
||||
{
|
||||
key: "agent:main:subagent:workboard-default-card-1",
|
||||
kind: "direct",
|
||||
displayName: "Finished session",
|
||||
updatedAt: 2,
|
||||
hasActiveRun: false,
|
||||
status: "done",
|
||||
},
|
||||
],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
expect(container.textContent).toContain("Done");
|
||||
expect(container.textContent).toContain("Finished session");
|
||||
expect(container.textContent).not.toContain("Task running");
|
||||
expect(container.textContent).not.toContain("Still running according to stale cache.");
|
||||
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("Finished session");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).not.toContain(
|
||||
"Still running according to stale cache.",
|
||||
);
|
||||
});
|
||||
|
||||
it("shows stop controls without start controls for active task-only cards", () => {
|
||||
@@ -375,24 +442,33 @@ describe("renderWorkboard", () => {
|
||||
progressSummary: "Worker is active.",
|
||||
});
|
||||
const container = document.createElement("div");
|
||||
const props = {
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
onRequestUpdate: () => undefined,
|
||||
};
|
||||
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
expect(container.textContent).toContain("Task running");
|
||||
expect(container.querySelector('button[title="Stop session"]')).not.toBeNull();
|
||||
expect(container.querySelectorAll<HTMLButtonElement>(".workboard-card__start")).toHaveLength(0);
|
||||
expect(container.querySelector(".workboard-card")?.getAttribute("role")).toBeNull();
|
||||
expect(container.querySelector(".workboard-card")?.getAttribute("role")).toBe("button");
|
||||
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain(
|
||||
"Worker is active.",
|
||||
);
|
||||
expect(container.querySelectorAll<HTMLButtonElement>(".workboard-card__start")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("hides write controls for read-only operators", () => {
|
||||
@@ -434,6 +510,7 @@ describe("renderWorkboard", () => {
|
||||
container.querySelector<HTMLButtonElement>(".workboard-toolbar__actions .btn.primary"),
|
||||
).toBeNull();
|
||||
expect(container.querySelector(".workboard-card")?.getAttribute("draggable")).toBe("false");
|
||||
expect(container.querySelector(".workboard-card")?.getAttribute("role")).toBe("button");
|
||||
});
|
||||
|
||||
it("offers start controls when a linked session no longer exists", () => {
|
||||
@@ -469,7 +546,7 @@ describe("renderWorkboard", () => {
|
||||
);
|
||||
|
||||
expect(container.textContent).toContain("Session missing");
|
||||
expect(container.querySelectorAll<HTMLButtonElement>(".workboard-card__start")).toHaveLength(5);
|
||||
expect(container.querySelectorAll<HTMLButtonElement>(".workboard-card__start")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("opens a modal for new cards", () => {
|
||||
@@ -560,27 +637,41 @@ describe("renderWorkboard", () => {
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
events: [
|
||||
{ id: "event-1", kind: "created", at: 1, toStatus: "todo" },
|
||||
{ id: "event-2", kind: "moved", at: 2, fromStatus: "todo", toStatus: "review" },
|
||||
{ id: "event-1", kind: "moved", at: 1, fromStatus: "triage", toStatus: "backlog" },
|
||||
{ id: "event-2", kind: "moved", at: 2, fromStatus: "backlog", toStatus: "todo" },
|
||||
{ id: "event-3", kind: "moved", at: 3, fromStatus: "todo", toStatus: "scheduled" },
|
||||
{ id: "event-4", kind: "moved", at: 4, fromStatus: "scheduled", toStatus: "ready" },
|
||||
{ id: "event-5", kind: "moved", at: 5, fromStatus: "ready", toStatus: "running" },
|
||||
{ id: "event-6", kind: "moved", at: 6, fromStatus: "running", toStatus: "review" },
|
||||
{ id: "event-7", kind: "moved", at: 7, fromStatus: "review", toStatus: "done" },
|
||||
],
|
||||
},
|
||||
];
|
||||
const container = document.createElement("div");
|
||||
const props = {
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
onRequestUpdate: () => undefined,
|
||||
};
|
||||
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
expect(container.querySelector(".workboard-events")?.textContent).toContain("Moved to Review");
|
||||
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("Moved to Done");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).not.toContain(
|
||||
"Moved to Backlog",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders card metadata badges and hides archived cards", () => {
|
||||
@@ -603,7 +694,26 @@ describe("renderWorkboard", () => {
|
||||
failureCount: 1,
|
||||
comments: [{ id: "comment-1", body: "Needs owner check", createdAt: 3 }],
|
||||
links: [{ id: "link-1", type: "relates_to", url: "https://example.com", createdAt: 4 }],
|
||||
proof: [{ id: "proof-1", status: "passed", command: "pnpm test", createdAt: 5 }],
|
||||
workerProtocol: {
|
||||
state: "blocked",
|
||||
detail: "Worker asked for owner input.",
|
||||
updatedAt: 12,
|
||||
},
|
||||
automation: {
|
||||
tenant: "ops",
|
||||
boardId: "quality",
|
||||
skills: ["review", "test"],
|
||||
workspace: { kind: "worktree", path: "/tmp/workboard", branch: "proof" },
|
||||
dispatchCount: 3,
|
||||
summary: "Ready for review.",
|
||||
},
|
||||
proof: Array.from({ length: 7 }, (_, index) => ({
|
||||
id: `proof-${index + 1}`,
|
||||
status: "passed",
|
||||
command: `pnpm test ${index + 1}`,
|
||||
url: `https://example.com/proof-${index + 1}`,
|
||||
createdAt: 5 + index,
|
||||
})),
|
||||
stale: { detectedAt: 6, reason: "No recent activity." },
|
||||
},
|
||||
},
|
||||
@@ -635,13 +745,83 @@ describe("renderWorkboard", () => {
|
||||
);
|
||||
|
||||
expect(container.textContent).toContain("Plugin");
|
||||
expect(container.textContent).toContain("1 attempts");
|
||||
expect(container.textContent).toContain("1 failed");
|
||||
expect(container.textContent).toContain("1 comments");
|
||||
expect(container.textContent).toContain("1 links");
|
||||
expect(container.textContent).toContain("1 proof");
|
||||
expect(container.textContent).toContain("7 proof");
|
||||
expect(container.textContent).toContain("stale");
|
||||
expect(container.textContent).not.toContain("Archived task");
|
||||
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("1 attempts");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("1 links");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).not.toContain("pnpm test 1");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("pnpm test 7");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain(
|
||||
"https://example.com/proof-7",
|
||||
);
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("Worker protocol");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain(
|
||||
"Worker asked for owner input.",
|
||||
);
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("Automation");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain("Tenant: ops");
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain(
|
||||
"Skills: review, test",
|
||||
);
|
||||
expect(container.querySelector(".workboard-detail")?.textContent).toContain(
|
||||
"Workspace: worktree /tmp/workboard proof",
|
||||
);
|
||||
});
|
||||
|
||||
it("does not render details for archived selected cards", () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
state.loaded = true;
|
||||
state.detailCardId = "card-1";
|
||||
state.cards = [
|
||||
{
|
||||
id: "card-1",
|
||||
title: "Archived selected task",
|
||||
status: "todo",
|
||||
priority: "normal",
|
||||
labels: [],
|
||||
position: 1000,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
metadata: { archivedAt: 2 },
|
||||
},
|
||||
];
|
||||
const container = document.createElement("div");
|
||||
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
|
||||
expect(container.querySelector(".workboard-detail")).toBeNull();
|
||||
expect(container.querySelectorAll<HTMLButtonElement>(".workboard-card__start")).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("shows stale lifecycle on executed linked cards", () => {
|
||||
@@ -854,6 +1034,107 @@ describe("renderWorkboard", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("locks edit-modal actions while a comment request is in flight", () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
state.loaded = true;
|
||||
state.draftOpen = true;
|
||||
state.editingCardId = "card-1";
|
||||
state.draftTitle = "Rename me";
|
||||
state.draftCommentBody = "Ship after CI";
|
||||
state.busyCardId = "card-1";
|
||||
state.cards = [
|
||||
{
|
||||
id: "card-1",
|
||||
title: "Rename me",
|
||||
status: "todo",
|
||||
priority: "normal",
|
||||
labels: [],
|
||||
position: 1000,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
];
|
||||
const container = document.createElement("div");
|
||||
|
||||
render(
|
||||
renderWorkboard({
|
||||
host,
|
||||
client: null,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
|
||||
const buttons = [...container.querySelectorAll<HTMLButtonElement>("button")];
|
||||
expect(buttons.find((button) => button.textContent?.includes("Create"))?.disabled).toBe(true);
|
||||
expect(buttons.find((button) => button.textContent?.includes("Save"))?.disabled).toBe(true);
|
||||
});
|
||||
|
||||
it("adds operator notes from the details drawer", async () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
state.loaded = true;
|
||||
state.cards = [
|
||||
{
|
||||
id: "card-1",
|
||||
title: "Investigate proof gap",
|
||||
status: "review",
|
||||
priority: "normal",
|
||||
labels: [],
|
||||
position: 1000,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
];
|
||||
const request = vi.fn(async () => ({
|
||||
card: {
|
||||
...state.cards[0],
|
||||
metadata: {
|
||||
comments: [{ id: "comment-1", body: "Need Linux proof.", createdAt: 2 }],
|
||||
},
|
||||
},
|
||||
}));
|
||||
const props = {
|
||||
host,
|
||||
client: { request } as unknown as GatewayBrowserClient,
|
||||
connected: true,
|
||||
pluginEnabled: true,
|
||||
agentsList: null,
|
||||
sessions: [],
|
||||
onOpenSession: () => undefined,
|
||||
onRequestUpdate: () => undefined,
|
||||
};
|
||||
const container = document.createElement("div");
|
||||
|
||||
render(renderWorkboard(props), container);
|
||||
container
|
||||
.querySelector<HTMLButtonElement>('button[title="View details"]')
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
render(renderWorkboard(props), container);
|
||||
|
||||
const note = container.querySelector<HTMLTextAreaElement>(".workboard-detail__note");
|
||||
note!.value = "Need Linux proof.";
|
||||
note!.dispatchEvent(new InputEvent("input", { bubbles: true }));
|
||||
render(renderWorkboard(props), container);
|
||||
[...container.querySelectorAll<HTMLButtonElement>(".workboard-detail button")]
|
||||
.find((button) => button.textContent?.includes("Add note"))
|
||||
?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(request).toHaveBeenCalledWith("workboard.cards.comment", {
|
||||
id: "card-1",
|
||||
body: "Need Linux proof.",
|
||||
});
|
||||
expect(state.detailCommentBody).toBe("");
|
||||
expect(state.cards[0]?.metadata?.comments?.[0]?.body).toBe("Need Linux proof.");
|
||||
});
|
||||
|
||||
it("archives cards from the card action", async () => {
|
||||
const host = {};
|
||||
const state = getWorkboardState(host);
|
||||
|
||||
@@ -185,47 +185,20 @@ function renderEvents(card: WorkboardCard) {
|
||||
`;
|
||||
}
|
||||
|
||||
function renderMetadataBadges(card: WorkboardCard) {
|
||||
function renderCompactBadges(card: WorkboardCard, task?: WorkboardTaskSummary) {
|
||||
const metadata = card.metadata;
|
||||
const badges = [
|
||||
metadata?.templateId ? t(`workboard.template.${metadata.templateId}`) : null,
|
||||
card.taskId ? t("workboard.badgeTaskLinked") : null,
|
||||
metadata?.attempts?.length
|
||||
? t("workboard.badgeAttempts", { count: String(metadata.attempts.length) })
|
||||
: null,
|
||||
(task ?? card.taskId) ? t("workboard.badgeTaskLinked") : null,
|
||||
metadata?.failureCount
|
||||
? t("workboard.badgeFailures", { count: String(metadata.failureCount) })
|
||||
: null,
|
||||
metadata?.comments?.length
|
||||
? t("workboard.badgeComments", { count: String(metadata.comments.length) })
|
||||
: null,
|
||||
metadata?.links?.length
|
||||
? t("workboard.badgeLinks", { count: String(metadata.links.length) })
|
||||
: null,
|
||||
metadata?.proof?.length
|
||||
? t("workboard.badgeProof", { count: String(metadata.proof.length) })
|
||||
: null,
|
||||
metadata?.artifacts?.length
|
||||
? t("workboard.badgeArtifacts", { count: String(metadata.artifacts.length) })
|
||||
: null,
|
||||
metadata?.attachments?.length
|
||||
? t("workboard.badgeAttachments", { count: String(metadata.attachments.length) })
|
||||
: null,
|
||||
metadata?.workerLogs?.length
|
||||
? t("workboard.badgeWorkerLogs", { count: String(metadata.workerLogs.length) })
|
||||
: null,
|
||||
metadata?.workerProtocol?.state
|
||||
? t("workboard.badgeWorkerProtocol", { state: metadata.workerProtocol.state })
|
||||
: null,
|
||||
metadata?.automation?.tenant
|
||||
? t("workboard.badgeTenant", { tenant: metadata.automation.tenant })
|
||||
: null,
|
||||
metadata?.automation?.skills?.length
|
||||
? t("workboard.badgeSkills", { count: String(metadata.automation.skills.length) })
|
||||
: null,
|
||||
metadata?.automation?.dispatchCount
|
||||
? t("workboard.badgeDispatches", { count: String(metadata.automation.dispatchCount) })
|
||||
: null,
|
||||
metadata?.claim ? t("workboard.badgeClaimed", { owner: metadata.claim.ownerId }) : null,
|
||||
metadata?.diagnostics?.length
|
||||
? t("workboard.badgeDiagnostics", { count: String(metadata.diagnostics.length) })
|
||||
@@ -326,16 +299,9 @@ function isCardActionTarget(event: Event): boolean {
|
||||
: false;
|
||||
}
|
||||
|
||||
function openCardSession(
|
||||
props: Pick<WorkboardProps, "onOpenSession">,
|
||||
card: WorkboardCard,
|
||||
): boolean {
|
||||
const sessionKey = card.sessionKey ?? card.execution?.sessionKey;
|
||||
if (!sessionKey) {
|
||||
return false;
|
||||
}
|
||||
props.onOpenSession(sessionKey);
|
||||
return true;
|
||||
function openCardDetails(state: WorkboardUiState, card: WorkboardCard) {
|
||||
state.detailCardId = card.id;
|
||||
state.detailCommentBody = "";
|
||||
}
|
||||
|
||||
function resetDraft(state: WorkboardUiState) {
|
||||
@@ -395,6 +361,7 @@ function renderCardModal(props: WorkboardProps) {
|
||||
? (state.cards.find((card) => card.id === state.editingCardId) ?? null)
|
||||
: null;
|
||||
const comments = editingCard?.metadata?.comments ?? [];
|
||||
const draftCommentBusy = editing && state.busyCardId === state.editingCardId;
|
||||
return html`
|
||||
<div
|
||||
class="workboard-modal"
|
||||
@@ -413,6 +380,9 @@ function renderCardModal(props: WorkboardProps) {
|
||||
aria-labelledby="workboard-card-modal-title"
|
||||
@submit=${(event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
if (state.loading || draftCommentBusy) {
|
||||
return;
|
||||
}
|
||||
void saveWorkboardCardDraft({
|
||||
host: props.host,
|
||||
client: props.client,
|
||||
@@ -601,7 +571,7 @@ function renderCardModal(props: WorkboardProps) {
|
||||
<button
|
||||
class="btn"
|
||||
type="button"
|
||||
?disabled=${state.loading || !state.draftCommentBody.trim()}
|
||||
?disabled=${state.loading || draftCommentBusy || !state.draftCommentBody.trim()}
|
||||
@click=${() => {
|
||||
void addWorkboardCardComment({
|
||||
host: props.host,
|
||||
@@ -617,7 +587,10 @@ function renderCardModal(props: WorkboardProps) {
|
||||
`
|
||||
: nothing}
|
||||
<div class="workboard-modal__actions">
|
||||
<button class="btn primary" ?disabled=${state.loading || !state.draftTitle.trim()}>
|
||||
<button
|
||||
class="btn primary"
|
||||
?disabled=${state.loading || draftCommentBusy || !state.draftTitle.trim()}
|
||||
>
|
||||
${editing ? t("common.save") : t("common.create")}
|
||||
</button>
|
||||
<button
|
||||
@@ -714,6 +687,18 @@ function taskIsActive(task: WorkboardTaskSummary | undefined): boolean {
|
||||
return task?.status === "queued" || task?.status === "running";
|
||||
}
|
||||
|
||||
function cardCanStart(
|
||||
state: WorkboardUiState,
|
||||
sessions: readonly GatewaySessionRow[],
|
||||
card: WorkboardCard,
|
||||
): boolean {
|
||||
const task = state.tasksByCardId.get(card.id);
|
||||
const session = findWorkboardSession(card, sessions);
|
||||
const activeTask = taskIsActive(task);
|
||||
const linkedSessionKey = card.sessionKey ?? card.execution?.sessionKey;
|
||||
return !activeTask && (!linkedSessionKey || !session);
|
||||
}
|
||||
|
||||
function renderLifecycle(
|
||||
card: WorkboardCard,
|
||||
sessions: readonly GatewaySessionRow[],
|
||||
@@ -797,6 +782,272 @@ function renderStartExecutionControls(props: WorkboardProps, card: WorkboardCard
|
||||
`;
|
||||
}
|
||||
|
||||
function renderDetailRow(label: string, value: unknown) {
|
||||
if (typeof value !== "string" && typeof value !== "number") {
|
||||
return nothing;
|
||||
}
|
||||
const text = String(value).trim();
|
||||
if (!text) {
|
||||
return nothing;
|
||||
}
|
||||
return html`
|
||||
<div class="workboard-detail__row">
|
||||
<span>${label}</span>
|
||||
<strong>${text}</strong>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderDetailList(
|
||||
title: string,
|
||||
values: readonly string[],
|
||||
empty: string | typeof nothing = nothing,
|
||||
) {
|
||||
const entries = values
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean)
|
||||
.slice(-6);
|
||||
if (entries.length === 0) {
|
||||
return empty;
|
||||
}
|
||||
return html`
|
||||
<section class="workboard-detail__section">
|
||||
<h3>${title}</h3>
|
||||
<ol class="workboard-detail__list">
|
||||
${entries.map((entry) => html`<li>${entry}</li>`)}
|
||||
</ol>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderCardDetailsPanel(props: WorkboardProps) {
|
||||
const state = getWorkboardState(props.host);
|
||||
const card = state.detailCardId
|
||||
? (state.cards.find((entry) => entry.id === state.detailCardId) ?? null)
|
||||
: null;
|
||||
if (!card || card.metadata?.archivedAt) {
|
||||
return nothing;
|
||||
}
|
||||
const task = state.tasksByCardId.get(card.id);
|
||||
const lifecycle = getWorkboardLifecycle(card, props.sessions, task);
|
||||
const formatted = formatLifecycle(lifecycle);
|
||||
const taskIsAuthoritative = task ? taskMatchesLifecycle(task, lifecycle) : false;
|
||||
const linkedSessionKey = card.sessionKey ?? card.execution?.sessionKey;
|
||||
const writable = canMutate(props);
|
||||
const comments = card.metadata?.comments ?? [];
|
||||
const attempts = card.metadata?.attempts ?? [];
|
||||
const links = card.metadata?.links ?? [];
|
||||
const proof = card.metadata?.proof ?? [];
|
||||
const artifacts = card.metadata?.artifacts ?? [];
|
||||
const attachments = card.metadata?.attachments ?? [];
|
||||
const diagnostics = card.metadata?.diagnostics ?? [];
|
||||
const workerLogs = card.metadata?.workerLogs ?? [];
|
||||
const workerProtocol = card.metadata?.workerProtocol;
|
||||
const automation = card.metadata?.automation;
|
||||
const events = (card.events ?? []).slice(-6).toReversed();
|
||||
const busy = state.busyCardId === card.id;
|
||||
const showStartControls = writable && cardCanStart(state, props.sessions, card);
|
||||
return html`
|
||||
<aside class="workboard-detail-drawer" aria-label=${t("workboard.detailTitle")}>
|
||||
<div class="workboard-detail">
|
||||
<header class="workboard-detail__header">
|
||||
<div>
|
||||
<span class="workboard-card__priority">${card.priority}</span>
|
||||
<h2>${card.title}</h2>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn--icon workboard-card__icon"
|
||||
type="button"
|
||||
title=${t("common.cancel")}
|
||||
@click=${() => {
|
||||
state.detailCardId = null;
|
||||
state.detailCommentBody = "";
|
||||
props.onRequestUpdate?.();
|
||||
}}
|
||||
>
|
||||
${icons.x}
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<section class="workboard-detail__section">
|
||||
<div class="workboard-card__lifecycle">
|
||||
<span class="workboard-lifecycle workboard-lifecycle--${formatted.tone}">
|
||||
${formatted.label}
|
||||
</span>
|
||||
<span class="workboard-card__lifecycle-detail">
|
||||
${task && taskIsAuthoritative
|
||||
? taskDetail(task)
|
||||
: (lifecycle.session?.displayName ?? formatted.detail)}
|
||||
</span>
|
||||
</div>
|
||||
<div class="workboard-detail__grid">
|
||||
${renderDetailRow(t("workboard.fieldStatus"), formatStatusLabel(card.status))}
|
||||
${renderDetailRow(
|
||||
t("workboard.fieldAgent"),
|
||||
card.agentId ?? t("workboard.defaultAgent"),
|
||||
)}
|
||||
${renderDetailRow(t("workboard.detailTask"), task?.taskId ?? card.taskId)}
|
||||
${renderDetailRow(t("workboard.fieldSession"), linkedSessionKey)}
|
||||
${renderDetailRow(t("workboard.detailRun"), card.runId ?? card.execution?.runId)}
|
||||
${renderDetailRow(t("workboard.detailUpdated"), formatTime(card.updatedAt))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
${card.notes
|
||||
? html`
|
||||
<section class="workboard-detail__section">
|
||||
<h3>${t("workboard.fieldNotes")}</h3>
|
||||
<p>${card.notes}</p>
|
||||
</section>
|
||||
`
|
||||
: nothing}
|
||||
${renderDetailList(t("workboard.fieldLabels"), card.labels)}
|
||||
${renderDetailList(
|
||||
t("workboard.badgeAttempts", { count: String(attempts.length) }),
|
||||
attempts.map((entry) =>
|
||||
[entry.status, entry.model, entry.sessionKey, entry.error].filter(Boolean).join(" - "),
|
||||
),
|
||||
)}
|
||||
${renderDetailList(
|
||||
t("workboard.badgeLinks", { count: String(links.length) }),
|
||||
links.map((entry) =>
|
||||
[entry.type, entry.title, entry.targetCardId, entry.url].filter(Boolean).join(" - "),
|
||||
),
|
||||
)}
|
||||
${renderDetailList(
|
||||
t("workboard.detailProof"),
|
||||
proof.map((entry) =>
|
||||
[entry.status, entry.label, entry.command, entry.url, entry.note]
|
||||
.filter(Boolean)
|
||||
.join(" - "),
|
||||
),
|
||||
)}
|
||||
${renderDetailList(
|
||||
t("workboard.badgeArtifacts", { count: String(artifacts.length) }),
|
||||
artifacts.map((entry) =>
|
||||
[entry.label, entry.url, entry.path, entry.mimeType].filter(Boolean).join(" - "),
|
||||
),
|
||||
)}
|
||||
${renderDetailList(
|
||||
t("workboard.badgeAttachments", { count: String(attachments.length) }),
|
||||
attachments.map((entry) =>
|
||||
[entry.fileName, entry.mimeType, entry.note].filter(Boolean).join(" - "),
|
||||
),
|
||||
)}
|
||||
${renderDetailList(
|
||||
t("workboard.detailDiagnostics"),
|
||||
diagnostics.map((entry) => `${entry.severity}: ${entry.title}`),
|
||||
)}
|
||||
${renderDetailList(
|
||||
t("workboard.detailWorkerLogs"),
|
||||
workerLogs.map((entry) => `${entry.level}: ${entry.message}`),
|
||||
)}
|
||||
${workerProtocol
|
||||
? renderDetailList(t("workboard.detailWorkerProtocol"), [
|
||||
workerProtocol.state,
|
||||
workerProtocol.detail ?? "",
|
||||
workerProtocol.updatedAt
|
||||
? t("workboard.detailUpdatedValue", { time: formatTime(workerProtocol.updatedAt) })
|
||||
: "",
|
||||
])
|
||||
: nothing}
|
||||
${automation
|
||||
? renderDetailList(t("workboard.detailAutomation"), [
|
||||
automation.tenant
|
||||
? t("workboard.detailAutomationTenant", { tenant: automation.tenant })
|
||||
: "",
|
||||
automation.boardId
|
||||
? t("workboard.detailAutomationBoard", { board: automation.boardId })
|
||||
: "",
|
||||
automation.skills?.length
|
||||
? t("workboard.detailAutomationSkills", { skills: automation.skills.join(", ") })
|
||||
: "",
|
||||
automation.workspace
|
||||
? t("workboard.detailAutomationWorkspace", {
|
||||
workspace: [
|
||||
automation.workspace.kind,
|
||||
automation.workspace.path,
|
||||
automation.workspace.branch,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" "),
|
||||
})
|
||||
: "",
|
||||
automation.dispatchCount
|
||||
? t("workboard.badgeDispatches", { count: String(automation.dispatchCount) })
|
||||
: "",
|
||||
automation.lastDispatchAt
|
||||
? t("workboard.detailUpdatedValue", { time: formatTime(automation.lastDispatchAt) })
|
||||
: "",
|
||||
automation.summary
|
||||
? t("workboard.detailAutomationSummary", { summary: automation.summary })
|
||||
: "",
|
||||
])
|
||||
: nothing}
|
||||
${renderDetailList(
|
||||
t("workboard.eventsLabel"),
|
||||
events.map((event) => `${formatEventLabel(event)} ${formatTime(event.at)}`),
|
||||
)}
|
||||
|
||||
<section class="workboard-detail__section">
|
||||
<h3>${t("workboard.detailOperatorNotes")}</h3>
|
||||
${comments.length
|
||||
? html`
|
||||
<ol class="workboard-detail__list">
|
||||
${comments.slice(-6).map((comment) => html`<li>${comment.body}</li>`)}
|
||||
</ol>
|
||||
`
|
||||
: html`<p>${t("workboard.detailNoNotes")}</p>`}
|
||||
${writable
|
||||
? html`
|
||||
<textarea
|
||||
class="input workboard-detail__note"
|
||||
maxlength="2000"
|
||||
placeholder=${t("workboard.detailNotePlaceholder")}
|
||||
.value=${state.detailCommentBody}
|
||||
@input=${(event: InputEvent) => {
|
||||
state.detailCommentBody = (event.currentTarget as HTMLTextAreaElement).value;
|
||||
props.onRequestUpdate?.();
|
||||
}}
|
||||
></textarea>
|
||||
<button
|
||||
class="btn"
|
||||
type="button"
|
||||
?disabled=${busy || !state.detailCommentBody.trim()}
|
||||
@click=${() =>
|
||||
addWorkboardCardComment({
|
||||
host: props.host,
|
||||
client: props.client,
|
||||
cardId: card.id,
|
||||
body: state.detailCommentBody,
|
||||
requestUpdate: props.onRequestUpdate,
|
||||
})}
|
||||
>
|
||||
${icons.plus} ${t("workboard.detailAddNote")}
|
||||
</button>
|
||||
`
|
||||
: nothing}
|
||||
</section>
|
||||
|
||||
<div class="workboard-detail__actions">
|
||||
${linkedSessionKey
|
||||
? html`
|
||||
<button
|
||||
class="btn"
|
||||
type="button"
|
||||
@click=${() => props.onOpenSession(linkedSessionKey)}
|
||||
>
|
||||
${icons.messageSquare} ${t("workboard.openSession")}
|
||||
</button>
|
||||
`
|
||||
: nothing}
|
||||
${showStartControls ? renderStartExecutionControls(props, card) : nothing}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderDispatchSummary(state: WorkboardUiState) {
|
||||
const summary = state.lastDispatchSummary;
|
||||
if (!summary) {
|
||||
@@ -836,30 +1087,30 @@ function renderCard(props: WorkboardProps, card: WorkboardCard) {
|
||||
session?.hasActiveRun === true ||
|
||||
(session?.hasActiveRun !== false && session?.status === "running");
|
||||
const linkedSessionKey = card.sessionKey ?? card.execution?.sessionKey;
|
||||
const openable = Boolean(linkedSessionKey);
|
||||
const writable = canMutate(props);
|
||||
const showStartControls = writable && !activeTask && (!linkedSessionKey || !session);
|
||||
const showStartControls = writable && cardCanStart(state, props.sessions, card);
|
||||
return html`
|
||||
<article
|
||||
class="workboard-card priority-${card.priority} ${busy
|
||||
? "workboard-card--busy"
|
||||
: ""} ${openable ? "workboard-card--openable" : ""}"
|
||||
role=${openable ? "button" : nothing}
|
||||
tabindex=${openable ? 0 : nothing}
|
||||
title=${openable ? t("workboard.openLinkedSession") : nothing}
|
||||
: ""} workboard-card--openable"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title=${t("workboard.viewDetails")}
|
||||
draggable=${writable ? "true" : "false"}
|
||||
@click=${(event: MouseEvent) => {
|
||||
if (!isCardActionTarget(event)) {
|
||||
openCardSession(props, card);
|
||||
openCardDetails(state, card);
|
||||
props.onRequestUpdate?.();
|
||||
}
|
||||
}}
|
||||
@keydown=${(event: KeyboardEvent) => {
|
||||
if (isCardActionTarget(event) || (event.key !== "Enter" && event.key !== " ")) {
|
||||
return;
|
||||
}
|
||||
if (openCardSession(props, card)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
openCardDetails(state, card);
|
||||
props.onRequestUpdate?.();
|
||||
event.preventDefault();
|
||||
}}
|
||||
@dragstart=${(event: DragEvent) => {
|
||||
if (!writable) {
|
||||
@@ -889,7 +1140,7 @@ function renderCard(props: WorkboardProps, card: WorkboardCard) {
|
||||
${card.labels.map((label) => html`<span>${label}</span>`)}
|
||||
</div>`
|
||||
: nothing}
|
||||
${renderMetadataBadges(card)}
|
||||
${renderCompactBadges(card, task)}
|
||||
<div class="workboard-card__meta">
|
||||
${card.agentId
|
||||
? html`<span>${card.agentId}</span>`
|
||||
@@ -898,6 +1149,16 @@ function renderCard(props: WorkboardProps, card: WorkboardCard) {
|
||||
</div>
|
||||
${renderEvents(card)}
|
||||
<div class="workboard-card__actions">
|
||||
<button
|
||||
class="btn btn--icon workboard-card__icon"
|
||||
title=${t("workboard.viewDetails")}
|
||||
@click=${() => {
|
||||
openCardDetails(state, card);
|
||||
props.onRequestUpdate?.();
|
||||
}}
|
||||
>
|
||||
${icons.panelRightOpen}
|
||||
</button>
|
||||
${writable
|
||||
? html`
|
||||
<button
|
||||
@@ -959,7 +1220,7 @@ function renderCard(props: WorkboardProps, card: WorkboardCard) {
|
||||
</button>
|
||||
`
|
||||
: nothing}
|
||||
${showStartControls ? renderStartExecutionControls(props, card) : nothing}
|
||||
${showStartControls ? renderStartExecutionButton(props, card, null, "autonomous") : nothing}
|
||||
${writable
|
||||
? html`
|
||||
<button
|
||||
@@ -1155,7 +1416,7 @@ export function renderWorkboard(props: WorkboardProps) {
|
||||
</div>
|
||||
</div>
|
||||
${state.error ? html`<div class="callout danger">${state.error}</div>` : nothing}
|
||||
${renderDispatchSummary(state)} ${renderCardModal(props)}
|
||||
${renderDispatchSummary(state)} ${renderCardModal(props)} ${renderCardDetailsPanel(props)}
|
||||
<div class="workboard-board">
|
||||
${state.statuses.map((status) => renderColumn(props, status, byStatus.get(status) ?? []))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user