@flanksource/clicky-ui 0.3.35 → 0.3.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai.cjs +2 -1
- package/dist/ai.cjs.map +1 -1
- package/dist/ai.js +2 -1
- package/dist/ai.js.map +1 -1
- package/dist/data/TaskHeaderExtras.cjs +35 -0
- package/dist/data/TaskHeaderExtras.cjs.map +1 -0
- package/dist/data/TaskHeaderExtras.d.ts +27 -0
- package/dist/data/TaskHeaderExtras.d.ts.map +1 -0
- package/dist/data/TaskHeaderExtras.js +35 -0
- package/dist/data/TaskHeaderExtras.js.map +1 -0
- package/dist/data/TaskManager.cjs +18 -6
- package/dist/data/TaskManager.cjs.map +1 -1
- package/dist/data/TaskManager.d.ts +13 -1
- package/dist/data/TaskManager.d.ts.map +1 -1
- package/dist/data/TaskManager.js +18 -6
- package/dist/data/TaskManager.js.map +1 -1
- package/dist/data/TaskProcessDetails.cjs +4 -1
- package/dist/data/TaskProcessDetails.cjs.map +1 -1
- package/dist/data/TaskProcessDetails.d.ts.map +1 -1
- package/dist/data/TaskProcessDetails.js +4 -1
- package/dist/data/TaskProcessDetails.js.map +1 -1
- package/dist/data/TaskProcessMetadata.cjs +50 -0
- package/dist/data/TaskProcessMetadata.cjs.map +1 -0
- package/dist/data/TaskProcessMetadata.d.ts +20 -0
- package/dist/data/TaskProcessMetadata.d.ts.map +1 -0
- package/dist/data/TaskProcessMetadata.js +50 -0
- package/dist/data/TaskProcessMetadata.js.map +1 -0
- package/dist/data/TaskProgress.cjs +22 -31
- package/dist/data/TaskProgress.cjs.map +1 -1
- package/dist/data/TaskProgress.d.ts +14 -1
- package/dist/data/TaskProgress.d.ts.map +1 -1
- package/dist/data/TaskProgress.js +22 -31
- package/dist/data/TaskProgress.js.map +1 -1
- package/dist/data/TaskSnapshot.cjs.map +1 -1
- package/dist/data/TaskSnapshot.d.ts +22 -0
- package/dist/data/TaskSnapshot.d.ts.map +1 -1
- package/dist/data/TaskSnapshot.js.map +1 -1
- package/dist/data/TaskStreamTabs.cjs +78 -0
- package/dist/data/TaskStreamTabs.cjs.map +1 -0
- package/dist/data/TaskStreamTabs.d.ts +35 -0
- package/dist/data/TaskStreamTabs.d.ts.map +1 -0
- package/dist/data/TaskStreamTabs.js +78 -0
- package/dist/data/TaskStreamTabs.js.map +1 -0
- package/dist/data/ai/SessionInspector.approvals-model.cjs +9 -0
- package/dist/data/ai/SessionInspector.approvals-model.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.approvals-model.d.ts +17 -0
- package/dist/data/ai/SessionInspector.approvals-model.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.approvals-model.js +9 -0
- package/dist/data/ai/SessionInspector.approvals-model.js.map +1 -0
- package/dist/data/ai/SessionInspector.approvals.cjs +7 -11
- package/dist/data/ai/SessionInspector.approvals.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.approvals.d.ts +2 -10
- package/dist/data/ai/SessionInspector.approvals.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.approvals.js +4 -8
- package/dist/data/ai/SessionInspector.approvals.js.map +1 -1
- package/dist/data/ai/SessionInspector.cjs +2 -2
- package/dist/data/ai/SessionInspector.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.d.ts +1 -1
- package/dist/data/ai/SessionInspector.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.js +1 -1
- package/dist/data/ai/SessionInspector.js.map +1 -1
- package/dist/data/ai/SessionInspector.panel-parts.cjs +0 -13
- package/dist/data/ai/SessionInspector.panel-parts.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.panel-parts.d.ts +4 -5
- package/dist/data/ai/SessionInspector.panel-parts.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.panel-parts.js +1 -14
- package/dist/data/ai/SessionInspector.panel-parts.js.map +1 -1
- package/dist/data/ai/SessionInspector.panel-values.cjs +18 -0
- package/dist/data/ai/SessionInspector.panel-values.cjs.map +1 -0
- package/dist/data/ai/SessionInspector.panel-values.d.ts +13 -0
- package/dist/data/ai/SessionInspector.panel-values.d.ts.map +1 -0
- package/dist/data/ai/SessionInspector.panel-values.js +18 -0
- package/dist/data/ai/SessionInspector.panel-values.js.map +1 -0
- package/dist/data/ai/SessionInspector.panels.cjs +27 -26
- package/dist/data/ai/SessionInspector.panels.cjs.map +1 -1
- package/dist/data/ai/SessionInspector.panels.d.ts.map +1 -1
- package/dist/data/ai/SessionInspector.panels.js +2 -1
- package/dist/data/ai/SessionInspector.panels.js.map +1 -1
- package/dist/data/ai/index.d.ts +2 -1
- package/dist/data/ai/index.d.ts.map +1 -1
- package/dist/data/task-metadata.cjs +36 -0
- package/dist/data/task-metadata.cjs.map +1 -0
- package/dist/data/task-metadata.d.ts +39 -0
- package/dist/data/task-metadata.d.ts.map +1 -0
- package/dist/data/task-metadata.js +36 -0
- package/dist/data/task-metadata.js.map +1 -0
- package/dist/data/task-streams.cjs +39 -0
- package/dist/data/task-streams.cjs.map +1 -0
- package/dist/data/task-streams.d.ts +60 -0
- package/dist/data/task-streams.d.ts.map +1 -0
- package/dist/data/task-streams.js +39 -0
- package/dist/data/task-streams.js.map +1 -0
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/data.cjs +14 -0
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.ts +6 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +14 -0
- package/dist/data.js.map +1 -1
- package/dist/hooks/use-task-run.cjs +13 -15
- package/dist/hooks/use-task-run.cjs.map +1 -1
- package/dist/hooks/use-task-run.d.ts.map +1 -1
- package/dist/hooks/use-task-run.js +14 -16
- package/dist/hooks/use-task-run.js.map +1 -1
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/ai.cjs
CHANGED
|
@@ -68,6 +68,7 @@ const SessionViewer_model = require("./data/ai/SessionViewer.model.cjs");
|
|
|
68
68
|
const types$1 = require("./data/ai/PromptPicker/types.cjs");
|
|
69
69
|
const context = require("./data/ai/context.cjs");
|
|
70
70
|
const chatWindowContext = require("./data/ai/chat-window-context.cjs");
|
|
71
|
+
const SessionInspector_approvalsModel = require("./data/ai/SessionInspector.approvals-model.cjs");
|
|
71
72
|
const promptRuntime = require("./data/ai/PromptPicker/prompt-runtime.cjs");
|
|
72
73
|
const SessionViewer_input = require("./data/ai/SessionViewer.input.cjs");
|
|
73
74
|
const useRuntimeFamilies = require("./data/ai/runtime-profiles/use-runtime-families.cjs");
|
|
@@ -173,7 +174,6 @@ exports.SandboxCreateWizard = SandboxCreateWizard.SandboxCreateWizard;
|
|
|
173
174
|
exports.SchemaBrowser = ToolSchemaBrowser.ToolSchemaBrowser;
|
|
174
175
|
exports.ToolSchemaBrowser = ToolSchemaBrowser.ToolSchemaBrowser;
|
|
175
176
|
exports.SessionApprovalsPanel = SessionInspector_approvals.SessionApprovalsPanel;
|
|
176
|
-
exports.pendingApprovalRequests = SessionInspector_approvals.pendingApprovalRequests;
|
|
177
177
|
exports.SessionChatComposer = SessionChatComposer.SessionChatComposer;
|
|
178
178
|
exports.SessionContextMeter = SessionViewer_header.SessionContextMeter;
|
|
179
179
|
exports.SessionInspector = SessionInspector.SessionInspector;
|
|
@@ -236,6 +236,7 @@ exports.parseContextPrefix = context.parseContextPrefix;
|
|
|
236
236
|
exports.serializeContext = context.serializeContext;
|
|
237
237
|
exports.nextPromptId = chatWindowContext.nextPromptId;
|
|
238
238
|
exports.useChatWindowManager = chatWindowContext.useChatWindowManager;
|
|
239
|
+
exports.pendingApprovalRequests = SessionInspector_approvalsModel.pendingApprovalRequests;
|
|
239
240
|
exports.promptPreviewText = promptRuntime.promptPreviewText;
|
|
240
241
|
exports.promptRuntimeValueToPayload = promptRuntime.promptRuntimeValueToPayload;
|
|
241
242
|
exports.specToPromptRuntimeValue = promptRuntime.specToPromptRuntimeValue;
|
package/dist/ai.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ai.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/ai.js
CHANGED
|
@@ -45,7 +45,7 @@ import { SPEC_RUNTIME_MODES, familyForModel, modeOptionFor, runtimeModeFromModel
|
|
|
45
45
|
import { SandboxConnectionsSection } from "./data/ai/SandboxConnectionsSection.js";
|
|
46
46
|
import { SandboxCreateWizard } from "./data/ai/SandboxCreateWizard.js";
|
|
47
47
|
import { ToolSchemaBrowser, ToolSchemaBrowser as ToolSchemaBrowser2 } from "./data/ai/ToolSchemaBrowser.js";
|
|
48
|
-
import { SessionApprovalsPanel
|
|
48
|
+
import { SessionApprovalsPanel } from "./data/ai/SessionInspector.approvals.js";
|
|
49
49
|
import { SessionChatComposer } from "./data/ai/SessionChatComposer.js";
|
|
50
50
|
import { SessionContextMeter } from "./data/ai/SessionViewer.header.js";
|
|
51
51
|
import { SessionInspector } from "./data/ai/SessionInspector.js";
|
|
@@ -66,6 +66,7 @@ import { getSessionAction, getSessionMetadata, normalizeMessages, normalizeSessi
|
|
|
66
66
|
import { isValidPromptSpecDetail } from "./data/ai/PromptPicker/types.js";
|
|
67
67
|
import { makeContextId, parseContextPrefix, serializeContext } from "./data/ai/context.js";
|
|
68
68
|
import { nextPromptId, useChatWindowManager } from "./data/ai/chat-window-context.js";
|
|
69
|
+
import { pendingApprovalRequests } from "./data/ai/SessionInspector.approvals-model.js";
|
|
69
70
|
import { promptPreviewText, promptRuntimeValueToPayload, specToPromptRuntimeValue } from "./data/ai/PromptPicker/prompt-runtime.js";
|
|
70
71
|
import { questionsFromToolInput, summarizeToolInput, toolDiff, toolInputParams } from "./data/ai/SessionViewer.input.js";
|
|
71
72
|
import { useRuntimeFamilies } from "./data/ai/runtime-profiles/use-runtime-families.js";
|
package/dist/ai.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ai.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const utils = require("../lib/utils.cjs");
|
|
5
|
+
const taskProcessDetails = require("./task-process-details.cjs");
|
|
6
|
+
const taskMetadata = require("./task-metadata.cjs");
|
|
7
|
+
function MetadataChip({ label, value, tone }) {
|
|
8
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9
|
+
"span",
|
|
10
|
+
{
|
|
11
|
+
className: utils.cn(
|
|
12
|
+
"inline-flex shrink-0 items-baseline gap-1 rounded-full px-1.5 py-px text-[10px] leading-4",
|
|
13
|
+
tone === "active" ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"
|
|
14
|
+
),
|
|
15
|
+
title: `${label}: ${value}`,
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-70", children: label }),
|
|
18
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-32 truncate font-medium", children: value })
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
const ACTIVE_STATES = /* @__PURE__ */ new Set(["running", "active", "busy", "working", "in-progress", "starting"]);
|
|
24
|
+
const STATE_KEYS = /* @__PURE__ */ new Set(["state", "status", "phase"]);
|
|
25
|
+
function chipTone(key, value) {
|
|
26
|
+
return STATE_KEYS.has(key) && ACTIVE_STATES.has(value.toLowerCase()) ? "active" : void 0;
|
|
27
|
+
}
|
|
28
|
+
function TaskMetadataChips({ task }) {
|
|
29
|
+
const details = taskProcessDetails.isTaskProcessDetails(task.details) ? task.details : void 0;
|
|
30
|
+
const entries = taskMetadata.metadataEntries(details == null ? void 0 : details.metadata);
|
|
31
|
+
if (entries.length === 0) return null;
|
|
32
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 shrink items-center gap-1 overflow-hidden", children: entries.map(({ key, text }) => /* @__PURE__ */ jsxRuntime.jsx(MetadataChip, { label: key, value: text, ...chipTone(key, text) ? { tone: "active" } : {} }, key)) });
|
|
33
|
+
}
|
|
34
|
+
exports.TaskMetadataChips = TaskMetadataChips;
|
|
35
|
+
//# sourceMappingURL=TaskHeaderExtras.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskHeaderExtras.cjs","sources":["../../src/data/TaskHeaderExtras.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { cn } from \"../lib/utils\";\nimport { isTaskProcessDetails } from \"./task-process-details\";\nimport { metadataEntries } from \"./task-metadata\";\nimport type { TaskSnapshot } from \"./TaskSnapshot\";\n\n/**\n * Contributes host-owned content into a task's header row — the line carrying\n * its name, beside the duration and controls. It exists for the same reason\n * `TaskExtraTabs` does: what is worth knowing about a task at a glance is often\n * something only the host can say. An agent row wants its turn state and the\n * thread it is working; a build row wants neither.\n *\n * Return `null` for tasks it has nothing to add for, and the default renderer\n * takes over — so a host can enrich one kind of task without having to\n * re-implement the default for every other kind.\n */\nexport type TaskHeaderExtra = (task: TaskSnapshot, group: TaskSnapshot) => ReactNode;\n\n/** One metadata value, sized and coloured to sit on a header row. */\nfunction MetadataChip({ label, value, tone }: { label: string; value: string; tone?: \"active\" }) {\n return (\n <span\n className={cn(\n \"inline-flex shrink-0 items-baseline gap-1 rounded-full px-1.5 py-px text-[10px] leading-4\",\n tone === \"active\" ? \"bg-primary/10 text-primary\" : \"bg-muted text-muted-foreground\",\n )}\n title={`${label}: ${value}`}\n >\n <span className=\"opacity-70\">{label}</span>\n <span className=\"max-w-32 truncate font-medium\">{value}</span>\n </span>\n );\n}\n\n/**\n * States that read as \"this is doing something right now\", and so earn the\n * accent that makes a busy row findable in a long list. Matched case-insensitively\n * against a `state`/`status`/`phase` value, because the producer names its own\n * states and this only decides how loud to render them.\n */\nconst ACTIVE_STATES = new Set([\"running\", \"active\", \"busy\", \"working\", \"in-progress\", \"starting\"]);\nconst STATE_KEYS = new Set([\"state\", \"status\", \"phase\"]);\n\nfunction chipTone(key: string, value: string): \"active\" | undefined {\n return STATE_KEYS.has(key) && ACTIVE_STATES.has(value.toLowerCase()) ? \"active\" : undefined;\n}\n\n/**\n * The default header content: the scalar values of a process's metadata, as\n * chips. Structured values are deliberately left out — they are shown in full,\n * with their structure, in the expanded process details, and a header row is not\n * the place to flatten them.\n *\n * A task with no process details, or none worth a chip, renders nothing, so a\n * plain task row looks exactly as it did before.\n */\nexport function TaskMetadataChips({ task }: { task: TaskSnapshot }) {\n const details = isTaskProcessDetails(task.details) ? task.details : undefined;\n const entries = metadataEntries(details?.metadata);\n if (entries.length === 0) return null;\n return (\n <div className=\"flex min-w-0 shrink items-center gap-1 overflow-hidden\">\n {entries.map(({ key, text }) => (\n <MetadataChip key={key} label={key} value={text} {...(chipTone(key, text) ? { tone: \"active\" as const } : {})} />\n ))}\n </div>\n );\n}\n"],"names":["jsxs","cn","jsx","isTaskProcessDetails","metadataEntries"],"mappings":";;;;;;AAqBA,SAAS,aAAa,EAAE,OAAO,OAAO,QAA2D;AAC/F,SACEA,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA,SAAS,WAAW,+BAA+B;AAAA,MAAA;AAAA,MAErD,OAAO,GAAG,KAAK,KAAK,KAAK;AAAA,MAEzB,UAAA;AAAA,QAAAC,2BAAAA,IAAC,QAAA,EAAK,WAAU,cAAc,UAAA,OAAM;AAAA,QACpCA,2BAAAA,IAAC,QAAA,EAAK,WAAU,iCAAiC,UAAA,MAAA,CAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG7D;AAQA,MAAM,gBAAgB,oBAAI,IAAI,CAAC,WAAW,UAAU,QAAQ,WAAW,eAAe,UAAU,CAAC;AACjG,MAAM,aAAa,oBAAI,IAAI,CAAC,SAAS,UAAU,OAAO,CAAC;AAEvD,SAAS,SAAS,KAAa,OAAqC;AAClE,SAAO,WAAW,IAAI,GAAG,KAAK,cAAc,IAAI,MAAM,YAAA,CAAa,IAAI,WAAW;AACpF;AAWO,SAAS,kBAAkB,EAAE,QAAgC;AAClE,QAAM,UAAUC,mBAAAA,qBAAqB,KAAK,OAAO,IAAI,KAAK,UAAU;AACpE,QAAM,UAAUC,aAAAA,gBAAgB,mCAAS,QAAQ;AACjD,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SACEF,2BAAAA,IAAC,OAAA,EAAI,WAAU,0DACZ,UAAA,QAAQ,IAAI,CAAC,EAAE,KAAK,KAAA,MACnBA,2BAAAA,IAAC,gBAAuB,OAAO,KAAK,OAAO,MAAO,GAAI,SAAS,KAAK,IAAI,IAAI,EAAE,MAAM,SAAA,IAAsB,CAAA,EAAC,GAAxF,GAA4F,CAChH,EAAA,CACH;AAEJ;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { TaskSnapshot } from './TaskSnapshot';
|
|
3
|
+
/**
|
|
4
|
+
* Contributes host-owned content into a task's header row — the line carrying
|
|
5
|
+
* its name, beside the duration and controls. It exists for the same reason
|
|
6
|
+
* `TaskExtraTabs` does: what is worth knowing about a task at a glance is often
|
|
7
|
+
* something only the host can say. An agent row wants its turn state and the
|
|
8
|
+
* thread it is working; a build row wants neither.
|
|
9
|
+
*
|
|
10
|
+
* Return `null` for tasks it has nothing to add for, and the default renderer
|
|
11
|
+
* takes over — so a host can enrich one kind of task without having to
|
|
12
|
+
* re-implement the default for every other kind.
|
|
13
|
+
*/
|
|
14
|
+
export type TaskHeaderExtra = (task: TaskSnapshot, group: TaskSnapshot) => ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* The default header content: the scalar values of a process's metadata, as
|
|
17
|
+
* chips. Structured values are deliberately left out — they are shown in full,
|
|
18
|
+
* with their structure, in the expanded process details, and a header row is not
|
|
19
|
+
* the place to flatten them.
|
|
20
|
+
*
|
|
21
|
+
* A task with no process details, or none worth a chip, renders nothing, so a
|
|
22
|
+
* plain task row looks exactly as it did before.
|
|
23
|
+
*/
|
|
24
|
+
export declare function TaskMetadataChips({ task }: {
|
|
25
|
+
task: TaskSnapshot;
|
|
26
|
+
}): import("react").JSX.Element | null;
|
|
27
|
+
//# sourceMappingURL=TaskHeaderExtras.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskHeaderExtras.d.ts","sourceRoot":"","sources":["../../src/data/TaskHeaderExtras.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,KAAK,SAAS,CAAC;AA+BrF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,sCAWjE"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { isTaskProcessDetails } from "./task-process-details.js";
|
|
4
|
+
import { metadataEntries } from "./task-metadata.js";
|
|
5
|
+
function MetadataChip({ label, value, tone }) {
|
|
6
|
+
return /* @__PURE__ */ jsxs(
|
|
7
|
+
"span",
|
|
8
|
+
{
|
|
9
|
+
className: cn(
|
|
10
|
+
"inline-flex shrink-0 items-baseline gap-1 rounded-full px-1.5 py-px text-[10px] leading-4",
|
|
11
|
+
tone === "active" ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"
|
|
12
|
+
),
|
|
13
|
+
title: `${label}: ${value}`,
|
|
14
|
+
children: [
|
|
15
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-70", children: label }),
|
|
16
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-32 truncate font-medium", children: value })
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const ACTIVE_STATES = /* @__PURE__ */ new Set(["running", "active", "busy", "working", "in-progress", "starting"]);
|
|
22
|
+
const STATE_KEYS = /* @__PURE__ */ new Set(["state", "status", "phase"]);
|
|
23
|
+
function chipTone(key, value) {
|
|
24
|
+
return STATE_KEYS.has(key) && ACTIVE_STATES.has(value.toLowerCase()) ? "active" : void 0;
|
|
25
|
+
}
|
|
26
|
+
function TaskMetadataChips({ task }) {
|
|
27
|
+
const details = isTaskProcessDetails(task.details) ? task.details : void 0;
|
|
28
|
+
const entries = metadataEntries(details == null ? void 0 : details.metadata);
|
|
29
|
+
if (entries.length === 0) return null;
|
|
30
|
+
return /* @__PURE__ */ jsx("div", { className: "flex min-w-0 shrink items-center gap-1 overflow-hidden", children: entries.map(({ key, text }) => /* @__PURE__ */ jsx(MetadataChip, { label: key, value: text, ...chipTone(key, text) ? { tone: "active" } : {} }, key)) });
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
TaskMetadataChips
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=TaskHeaderExtras.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskHeaderExtras.js","sources":["../../src/data/TaskHeaderExtras.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport { cn } from \"../lib/utils\";\nimport { isTaskProcessDetails } from \"./task-process-details\";\nimport { metadataEntries } from \"./task-metadata\";\nimport type { TaskSnapshot } from \"./TaskSnapshot\";\n\n/**\n * Contributes host-owned content into a task's header row — the line carrying\n * its name, beside the duration and controls. It exists for the same reason\n * `TaskExtraTabs` does: what is worth knowing about a task at a glance is often\n * something only the host can say. An agent row wants its turn state and the\n * thread it is working; a build row wants neither.\n *\n * Return `null` for tasks it has nothing to add for, and the default renderer\n * takes over — so a host can enrich one kind of task without having to\n * re-implement the default for every other kind.\n */\nexport type TaskHeaderExtra = (task: TaskSnapshot, group: TaskSnapshot) => ReactNode;\n\n/** One metadata value, sized and coloured to sit on a header row. */\nfunction MetadataChip({ label, value, tone }: { label: string; value: string; tone?: \"active\" }) {\n return (\n <span\n className={cn(\n \"inline-flex shrink-0 items-baseline gap-1 rounded-full px-1.5 py-px text-[10px] leading-4\",\n tone === \"active\" ? \"bg-primary/10 text-primary\" : \"bg-muted text-muted-foreground\",\n )}\n title={`${label}: ${value}`}\n >\n <span className=\"opacity-70\">{label}</span>\n <span className=\"max-w-32 truncate font-medium\">{value}</span>\n </span>\n );\n}\n\n/**\n * States that read as \"this is doing something right now\", and so earn the\n * accent that makes a busy row findable in a long list. Matched case-insensitively\n * against a `state`/`status`/`phase` value, because the producer names its own\n * states and this only decides how loud to render them.\n */\nconst ACTIVE_STATES = new Set([\"running\", \"active\", \"busy\", \"working\", \"in-progress\", \"starting\"]);\nconst STATE_KEYS = new Set([\"state\", \"status\", \"phase\"]);\n\nfunction chipTone(key: string, value: string): \"active\" | undefined {\n return STATE_KEYS.has(key) && ACTIVE_STATES.has(value.toLowerCase()) ? \"active\" : undefined;\n}\n\n/**\n * The default header content: the scalar values of a process's metadata, as\n * chips. Structured values are deliberately left out — they are shown in full,\n * with their structure, in the expanded process details, and a header row is not\n * the place to flatten them.\n *\n * A task with no process details, or none worth a chip, renders nothing, so a\n * plain task row looks exactly as it did before.\n */\nexport function TaskMetadataChips({ task }: { task: TaskSnapshot }) {\n const details = isTaskProcessDetails(task.details) ? task.details : undefined;\n const entries = metadataEntries(details?.metadata);\n if (entries.length === 0) return null;\n return (\n <div className=\"flex min-w-0 shrink items-center gap-1 overflow-hidden\">\n {entries.map(({ key, text }) => (\n <MetadataChip key={key} label={key} value={text} {...(chipTone(key, text) ? { tone: \"active\" as const } : {})} />\n ))}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;AAqBA,SAAS,aAAa,EAAE,OAAO,OAAO,QAA2D;AAC/F,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,WAAW,+BAA+B;AAAA,MAAA;AAAA,MAErD,OAAO,GAAG,KAAK,KAAK,KAAK;AAAA,MAEzB,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,cAAc,UAAA,OAAM;AAAA,QACpC,oBAAC,QAAA,EAAK,WAAU,iCAAiC,UAAA,MAAA,CAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAG7D;AAQA,MAAM,gBAAgB,oBAAI,IAAI,CAAC,WAAW,UAAU,QAAQ,WAAW,eAAe,UAAU,CAAC;AACjG,MAAM,aAAa,oBAAI,IAAI,CAAC,SAAS,UAAU,OAAO,CAAC;AAEvD,SAAS,SAAS,KAAa,OAAqC;AAClE,SAAO,WAAW,IAAI,GAAG,KAAK,cAAc,IAAI,MAAM,YAAA,CAAa,IAAI,WAAW;AACpF;AAWO,SAAS,kBAAkB,EAAE,QAAgC;AAClE,QAAM,UAAU,qBAAqB,KAAK,OAAO,IAAI,KAAK,UAAU;AACpE,QAAM,UAAU,gBAAgB,mCAAS,QAAQ;AACjD,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SACE,oBAAC,OAAA,EAAI,WAAU,0DACZ,UAAA,QAAQ,IAAI,CAAC,EAAE,KAAK,KAAA,MACnB,oBAAC,gBAAuB,OAAO,KAAK,OAAO,MAAO,GAAI,SAAS,KAAK,IAAI,IAAI,EAAE,MAAM,SAAA,IAAsB,CAAA,EAAC,GAAxF,GAA4F,CAChH,EAAA,CACH;AAEJ;"}
|
|
@@ -19,7 +19,9 @@ function TaskManager({
|
|
|
19
19
|
pollMs,
|
|
20
20
|
className,
|
|
21
21
|
selectedId,
|
|
22
|
-
onSelectRun
|
|
22
|
+
onSelectRun,
|
|
23
|
+
extraTabs,
|
|
24
|
+
headerExtra
|
|
23
25
|
}) {
|
|
24
26
|
const [kindFilter, setKindFilter] = react.useState(kind ?? "");
|
|
25
27
|
const [statusFilter, setStatusFilter] = react.useState("");
|
|
@@ -95,7 +97,9 @@ function TaskManager({
|
|
|
95
97
|
pollMs,
|
|
96
98
|
selectedId,
|
|
97
99
|
onSelectRun,
|
|
98
|
-
runsQueryKey
|
|
100
|
+
runsQueryKey,
|
|
101
|
+
extraTabs,
|
|
102
|
+
headerExtra
|
|
99
103
|
},
|
|
100
104
|
run.id
|
|
101
105
|
)) })
|
|
@@ -126,7 +130,9 @@ function RunRow({
|
|
|
126
130
|
pollMs,
|
|
127
131
|
selectedId,
|
|
128
132
|
onSelectRun,
|
|
129
|
-
runsQueryKey
|
|
133
|
+
runsQueryKey,
|
|
134
|
+
extraTabs,
|
|
135
|
+
headerExtra
|
|
130
136
|
}) {
|
|
131
137
|
const [localOpen, setLocalOpen] = react.useState(false);
|
|
132
138
|
const controlled = onSelectRun !== void 0;
|
|
@@ -195,7 +201,9 @@ function RunRow({
|
|
|
195
201
|
runId: run.id,
|
|
196
202
|
basePath,
|
|
197
203
|
pollMs,
|
|
198
|
-
runsQueryKey
|
|
204
|
+
runsQueryKey,
|
|
205
|
+
extraTabs,
|
|
206
|
+
headerExtra
|
|
199
207
|
}
|
|
200
208
|
)
|
|
201
209
|
] });
|
|
@@ -204,7 +212,9 @@ function ExpandedRun({
|
|
|
204
212
|
runId,
|
|
205
213
|
basePath,
|
|
206
214
|
pollMs,
|
|
207
|
-
runsQueryKey
|
|
215
|
+
runsQueryKey,
|
|
216
|
+
extraTabs,
|
|
217
|
+
headerExtra
|
|
208
218
|
}) {
|
|
209
219
|
const { snapshots, status } = useTaskRun.useTaskRun({ id: runId, basePath, pollMs });
|
|
210
220
|
const apiBase = basePath ?? "/api/v1";
|
|
@@ -262,7 +272,9 @@ function ExpandedRun({
|
|
|
262
272
|
compact: true,
|
|
263
273
|
onControl: control,
|
|
264
274
|
onTaskControl: controlTask,
|
|
265
|
-
metricsBaseUrl: `${apiBase}/tasks/metrics
|
|
275
|
+
metricsBaseUrl: `${apiBase}/tasks/metrics/`,
|
|
276
|
+
...extraTabs ? { extraTabs } : {},
|
|
277
|
+
...headerExtra ? { headerExtra } : {}
|
|
266
278
|
}
|
|
267
279
|
) });
|
|
268
280
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskManager.cjs","sources":["../../src/data/TaskManager.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { hashKey, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type { QueryKey } from \"@tanstack/react-query\";\nimport { cn } from \"../lib/utils\";\nimport { useTaskRun, useTaskRuns } from \"../hooks/use-task-run\";\nimport { Icon } from \"./Icon\";\nimport { ProgressBar } from \"./ProgressBar\";\nimport { Timestamp } from \"./cells/Timestamp\";\nimport { TaskProgress } from \"./TaskProgress\";\nimport { taskWorkProgress } from \"./task-work-progress\";\nimport type { TaskControlAction, TaskRunMeta, TaskSnapshot } from \"./TaskSnapshot\";\nimport { taskQueryKeys } from \"./task-query-keys\";\nimport { taskSegments, taskStatusBg, taskStatusColor, taskStatusIcon } from \"./task-status\";\n\n// TaskManager is the generic clicky-ui task-manager view: it lists every run\n// (TaskRunMeta) from GET {basePath}/tasks with kind/status filters, and expands\n// each row into a live <TaskProgress> for that run id. Runs are grouped by kind,\n// which applications use as their stable task identity.\n//\n// Requires a react-query QueryClientProvider in the host app (like\n// TimeseriesPanel and CacheBrowser): control actions run through useMutation,\n// and the listing is mirrored into the query cache so sibling views can read it.\n\nexport interface TaskManagerProps {\n /** Base path of the clicky task API, e.g. \"/api/v1\". */\n basePath?: string;\n /** Restrict the listing to a single kind (also hides the kind filter). */\n kind?: string;\n /** Restrict the listing to runs whose labels contain every key/value pair. */\n labels?: Record<string, string>;\n pollMs?: number;\n className?: string;\n /**\n * Currently-selected (expanded) run id, e.g. driven from the URL. When set,\n * the matching run renders expanded and selection is controlled — otherwise\n * each row manages its own expand/collapse state.\n */\n selectedId?: string;\n /**\n * Called when a run row is toggled: the new selection id, or null when the\n * open row is collapsed. Pair with selectedId for URL-driven deep links.\n */\n onSelectRun?: (id: string | null) => void;\n}\n\nexport function TaskManager({\n basePath,\n kind,\n labels,\n pollMs,\n className,\n selectedId,\n onSelectRun,\n}: TaskManagerProps) {\n const [kindFilter, setKindFilter] = useState<string>(kind ?? \"\");\n const [statusFilter, setStatusFilter] = useState<string>(\"\");\n const queryClient = useQueryClient();\n const apiBase = basePath ?? \"/api/v1\";\n const activeKind = kind ?? (kindFilter || undefined);\n const { runs, runsKey, status } = useTaskRuns({\n basePath: apiBase,\n kind: activeKind,\n labels,\n status: statusFilter || undefined,\n pollMs,\n });\n const runsQueryKey = useMemo(\n () => taskQueryKeys.runs({\n basePath: apiBase,\n kind: activeKind,\n status: statusFilter || undefined,\n labels,\n }),\n [activeKind, apiBase, labels, statusFilter],\n );\n\n // Changing a filter re-subscribes the transport but leaves the previous\n // listing on screen until the next SSE/poll frame. Publishing that listing\n // under the new key would hand cache consumers rows that do not match their\n // filters, so mirror only once the delivered listing's own key agrees.\n useEffect(() => {\n if (!runsKey || hashKey(runsKey) !== hashKey(runsQueryKey)) return;\n queryClient.setQueryData(runsQueryKey, runs);\n }, [queryClient, runs, runsKey, runsQueryKey]);\n\n const kinds = useMemo(() => {\n const set = new Set<string>();\n for (const r of runs) if (r.kind) set.add(r.kind);\n return [...set].sort();\n }, [runs]);\n const applications = useMemo(() => groupRunsByApplication(runs), [runs]);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {!kind && (\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value)}\n >\n <option value=\"\">All kinds</option>\n {kinds.map((k) => (\n <option key={k} value={k}>\n {k}\n </option>\n ))}\n </select>\n )}\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n >\n <option value=\"\">Any status</option>\n <option value=\"running\">Running</option>\n <option value=\"pending\">Pending</option>\n <option value=\"success\">Success</option>\n <option value=\"failed\">Failed</option>\n <option value=\"warning\">Warning</option>\n <option value=\"canceled\">Canceled</option>\n </select>\n <span className=\"text-xs text-muted-foreground\">{status}</span>\n </div>\n\n {runs.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground\">\n No background tasks.\n </div>\n ) : (\n <div className=\"space-y-3\">\n {applications.map((application) => (\n <section key={application.kind}>\n <h3 className=\"mb-1 px-1 text-xs font-semibold text-muted-foreground\">\n {applicationLabel(application.kind)}\n </h3>\n <div className=\"overflow-hidden rounded-lg border bg-card\">\n {application.runs.map((run) => (\n <RunRow\n key={run.id}\n run={run}\n basePath={basePath}\n pollMs={pollMs}\n selectedId={selectedId}\n onSelectRun={onSelectRun}\n runsQueryKey={runsQueryKey}\n />\n ))}\n </div>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction groupRunsByApplication(runs: TaskRunMeta[]) {\n const grouped = new Map<string, TaskRunMeta[]>();\n for (const run of runs) {\n const application = run.kind || \"other\";\n grouped.set(application, [...(grouped.get(application) ?? []), run]);\n }\n return [...grouped].map(([kind, applicationRuns]) => ({ kind, runs: applicationRuns }));\n}\n\nfunction applicationLabel(kind: string): string {\n const names: Record<string, string> = {\n makerprint: \"MakerPrint\",\n openscad: \"OpenSCAD\",\n orca: \"Orca\",\n snapmaker: \"Snapmaker\",\n other: \"Other\",\n };\n return names[kind] ?? kind;\n}\n\nfunction RunRow({\n run,\n basePath,\n pollMs,\n selectedId,\n onSelectRun,\n runsQueryKey,\n}: {\n run: TaskRunMeta;\n basePath: string | undefined;\n pollMs: number | undefined;\n selectedId: string | undefined;\n onSelectRun: ((id: string | null) => void) | undefined;\n runsQueryKey: QueryKey;\n}) {\n const [localOpen, setLocalOpen] = useState(false);\n // Controlled by selectedId when a selection handler is wired; otherwise the\n // row owns its expand state (backward compatible for router-less consumers).\n const controlled = onSelectRun !== undefined;\n const open = controlled ? selectedId === run.id : localOpen;\n const toggle = () => {\n if (controlled) onSelectRun(open ? null : run.id);\n else setLocalOpen((v) => !v);\n };\n const isTerminal = run.status !== \"running\" && run.status !== \"pending\";\n const progress = taskWorkProgress(run);\n\n return (\n <div className=\"border-b last:border-0\">\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-4 px-4 py-3 text-left hover:bg-muted/50\"\n onClick={toggle}\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <Icon\n icon={taskStatusIcon(run.status)}\n className={cn(taskStatusColor(run.status), run.status === \"running\" && \"animate-spin\")}\n />\n <span className=\"truncate font-medium\">{run.name}</span>\n {run.kind && (\n <span className=\"rounded-full bg-muted px-1.5 py-0 text-[10px] text-muted-foreground\">\n {run.kind}\n </span>\n )}\n {isTerminal && (\n <span\n className={cn(\n \"rounded-full px-2 py-0.5 text-xs\",\n taskStatusColor(run.status),\n taskStatusBg(run.status),\n )}\n >\n {run.status}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground\">\n {run.startedAt && <Timestamp value={run.startedAt} format=\"relative\" />}\n <span>\n {progress.label}\n </span>\n {Object.entries(run.labels ?? {}).map(([k, v]) => (\n <span key={k} className=\"font-mono\">\n {k}={v}\n </span>\n ))}\n </div>\n </div>\n <div className=\"w-40 shrink-0\">\n <ProgressBar\n segments={taskSegments(progress.counts)}\n total={progress.total || 1}\n height=\"h-1.5\"\n />\n </div>\n </button>\n {open && (\n <ExpandedRun\n runId={run.id}\n basePath={basePath}\n pollMs={pollMs}\n runsQueryKey={runsQueryKey}\n />\n )}\n </div>\n );\n}\n\nfunction ExpandedRun({\n runId,\n basePath,\n pollMs,\n runsQueryKey,\n}: {\n runId: string;\n basePath: string | undefined;\n pollMs: number | undefined;\n runsQueryKey: QueryKey;\n}) {\n const { snapshots, status } = useTaskRun({ id: runId, basePath, pollMs });\n const apiBase = basePath ?? \"/api/v1\";\n const queryClient = useQueryClient();\n const pendingControls = useRef(new Map<string, Promise<void>>());\n const cachedSnapshots = useRef<TaskSnapshot[] | undefined>(undefined);\n const runQueryKey = useMemo(\n () => taskQueryKeys.run({ basePath: apiBase, runId }),\n [apiBase, runId],\n );\n const controlMutation = useMutation({\n mutationKey: taskQueryKeys.control({ basePath: apiBase, runId }),\n mutationFn: postTaskControl,\n onSuccess: async () => {\n await Promise.all([\n queryClient.invalidateQueries({ queryKey: runsQueryKey, exact: true, refetchType: \"none\" }),\n queryClient.invalidateQueries({ queryKey: runQueryKey, exact: true, refetchType: \"none\" }),\n ]);\n },\n });\n\n useEffect(() => {\n const previous = cachedSnapshots.current;\n if (previous?.length === snapshots.length && previous.every((snapshot, index) => snapshot === snapshots[index])) {\n return;\n }\n cachedSnapshots.current = snapshots;\n queryClient.setQueryData(runQueryKey, snapshots);\n }, [queryClient, runQueryKey, snapshots]);\n\n const mutateControl = useCallback((request: TaskControlRequest) => {\n const key = `${request.url}:${request.action}`;\n const pending = pendingControls.current.get(key);\n if (pending) return pending;\n controlMutation.reset();\n const mutation = controlMutation.mutateAsync(request).finally(() => {\n if (pendingControls.current.get(key) === mutation) pendingControls.current.delete(key);\n });\n pendingControls.current.set(key, mutation);\n return mutation;\n }, [controlMutation.mutateAsync, controlMutation.reset]);\n const control = (action: TaskControlAction, group: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/control`,\n action,\n target: group,\n });\n const controlTask = (action: TaskControlAction, task: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/tasks/${encodeURIComponent(task.id)}/control`,\n action,\n target: task,\n });\n return (\n <div className=\"border-t bg-muted/30 px-4 py-3\">\n <TaskProgress\n snapshots={snapshots}\n connectionStatus={status}\n compact\n onControl={control}\n onTaskControl={controlTask}\n metricsBaseUrl={`${apiBase}/tasks/metrics/`}\n />\n </div>\n );\n}\n\ninterface TaskControlRequest {\n url: string;\n action: TaskControlAction;\n target: TaskSnapshot;\n}\n\nasync function postTaskControl({ url, action, target }: TaskControlRequest) {\n const context = `Failed to ${action} ${target.type} \"${target.name}\"`;\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ action }),\n });\n } catch (cause) {\n throw new Error(`${context}: ${cause instanceof Error ? cause.message : \"request failed\"}`, { cause });\n }\n if (!response.ok) {\n const detail = (await response.text()).trim() || `HTTP ${response.status}`;\n throw new Error(`${context}: ${detail}`);\n }\n}\n"],"names":["useState","useQueryClient","useTaskRuns","useMemo","taskQueryKeys","useEffect","hashKey","cn","jsxs","jsx","taskWorkProgress","Icon","taskStatusIcon","taskStatusColor","taskStatusBg","Timestamp","ProgressBar","taskSegments","useTaskRun","useRef","useMutation","useCallback","TaskProgress"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TaskManager.cjs","sources":["../../src/data/TaskManager.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { hashKey, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type { QueryKey } from \"@tanstack/react-query\";\nimport { cn } from \"../lib/utils\";\nimport { useTaskRun, useTaskRuns } from \"../hooks/use-task-run\";\nimport { Icon } from \"./Icon\";\nimport { ProgressBar } from \"./ProgressBar\";\nimport { Timestamp } from \"./cells/Timestamp\";\nimport { TaskProgress } from \"./TaskProgress\";\nimport { taskWorkProgress } from \"./task-work-progress\";\nimport type { TaskControlAction, TaskRunMeta, TaskSnapshot } from \"./TaskSnapshot\";\nimport type { TaskHeaderExtra } from \"./TaskHeaderExtras\";\nimport type { TaskExtraTabs } from \"./TaskStreamTabs\";\nimport { taskQueryKeys } from \"./task-query-keys\";\nimport { taskSegments, taskStatusBg, taskStatusColor, taskStatusIcon } from \"./task-status\";\n\n// TaskManager is the generic clicky-ui task-manager view: it lists every run\n// (TaskRunMeta) from GET {basePath}/tasks with kind/status filters, and expands\n// each row into a live <TaskProgress> for that run id. Runs are grouped by kind,\n// which applications use as their stable task identity.\n//\n// Requires a react-query QueryClientProvider in the host app (like\n// TimeseriesPanel and CacheBrowser): control actions run through useMutation,\n// and the listing is mirrored into the query cache so sibling views can read it.\n\nexport interface TaskManagerProps {\n /** Base path of the clicky task API, e.g. \"/api/v1\". */\n basePath?: string;\n /** Restrict the listing to a single kind (also hides the kind filter). */\n kind?: string;\n /** Restrict the listing to runs whose labels contain every key/value pair. */\n labels?: Record<string, string>;\n pollMs?: number;\n className?: string;\n /**\n * Currently-selected (expanded) run id, e.g. driven from the URL. When set,\n * the matching run renders expanded and selection is controlled — otherwise\n * each row manages its own expand/collapse state.\n */\n selectedId?: string;\n /**\n * Called when a run row is toggled: the new selection id, or null when the\n * open row is collapsed. Pair with selectedId for URL-driven deep links.\n */\n onSelectRun?: (id: string | null) => void;\n /**\n * Contributes host-owned output panes to each task, alongside its own\n * stdout/stderr — an agent's session transcript, say.\n */\n extraTabs?: TaskExtraTabs;\n /**\n * Replaces the content beside a task's name in its header row. Returning null\n * for a task falls back to the default metadata chips.\n */\n headerExtra?: TaskHeaderExtra;\n}\n\nexport function TaskManager({\n basePath,\n kind,\n labels,\n pollMs,\n className,\n selectedId,\n onSelectRun,\n extraTabs,\n headerExtra,\n}: TaskManagerProps) {\n const [kindFilter, setKindFilter] = useState<string>(kind ?? \"\");\n const [statusFilter, setStatusFilter] = useState<string>(\"\");\n const queryClient = useQueryClient();\n const apiBase = basePath ?? \"/api/v1\";\n const activeKind = kind ?? (kindFilter || undefined);\n const { runs, runsKey, status } = useTaskRuns({\n basePath: apiBase,\n kind: activeKind,\n labels,\n status: statusFilter || undefined,\n pollMs,\n });\n const runsQueryKey = useMemo(\n () => taskQueryKeys.runs({\n basePath: apiBase,\n kind: activeKind,\n status: statusFilter || undefined,\n labels,\n }),\n [activeKind, apiBase, labels, statusFilter],\n );\n\n // Changing a filter re-subscribes the transport but leaves the previous\n // listing on screen until the next SSE/poll frame. Publishing that listing\n // under the new key would hand cache consumers rows that do not match their\n // filters, so mirror only once the delivered listing's own key agrees.\n useEffect(() => {\n if (!runsKey || hashKey(runsKey) !== hashKey(runsQueryKey)) return;\n queryClient.setQueryData(runsQueryKey, runs);\n }, [queryClient, runs, runsKey, runsQueryKey]);\n\n const kinds = useMemo(() => {\n const set = new Set<string>();\n for (const r of runs) if (r.kind) set.add(r.kind);\n return [...set].sort();\n }, [runs]);\n const applications = useMemo(() => groupRunsByApplication(runs), [runs]);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {!kind && (\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value)}\n >\n <option value=\"\">All kinds</option>\n {kinds.map((k) => (\n <option key={k} value={k}>\n {k}\n </option>\n ))}\n </select>\n )}\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n >\n <option value=\"\">Any status</option>\n <option value=\"running\">Running</option>\n <option value=\"pending\">Pending</option>\n <option value=\"success\">Success</option>\n <option value=\"failed\">Failed</option>\n <option value=\"warning\">Warning</option>\n <option value=\"canceled\">Canceled</option>\n </select>\n <span className=\"text-xs text-muted-foreground\">{status}</span>\n </div>\n\n {runs.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground\">\n No background tasks.\n </div>\n ) : (\n <div className=\"space-y-3\">\n {applications.map((application) => (\n <section key={application.kind}>\n <h3 className=\"mb-1 px-1 text-xs font-semibold text-muted-foreground\">\n {applicationLabel(application.kind)}\n </h3>\n <div className=\"overflow-hidden rounded-lg border bg-card\">\n {application.runs.map((run) => (\n <RunRow\n key={run.id}\n run={run}\n basePath={basePath}\n pollMs={pollMs}\n selectedId={selectedId}\n onSelectRun={onSelectRun}\n runsQueryKey={runsQueryKey}\n extraTabs={extraTabs}\n headerExtra={headerExtra}\n />\n ))}\n </div>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction groupRunsByApplication(runs: TaskRunMeta[]) {\n const grouped = new Map<string, TaskRunMeta[]>();\n for (const run of runs) {\n const application = run.kind || \"other\";\n grouped.set(application, [...(grouped.get(application) ?? []), run]);\n }\n return [...grouped].map(([kind, applicationRuns]) => ({ kind, runs: applicationRuns }));\n}\n\nfunction applicationLabel(kind: string): string {\n const names: Record<string, string> = {\n makerprint: \"MakerPrint\",\n openscad: \"OpenSCAD\",\n orca: \"Orca\",\n snapmaker: \"Snapmaker\",\n other: \"Other\",\n };\n return names[kind] ?? kind;\n}\n\nfunction RunRow({\n run,\n basePath,\n pollMs,\n selectedId,\n onSelectRun,\n runsQueryKey,\n extraTabs,\n headerExtra,\n}: {\n run: TaskRunMeta;\n basePath: string | undefined;\n pollMs: number | undefined;\n selectedId: string | undefined;\n onSelectRun: ((id: string | null) => void) | undefined;\n runsQueryKey: QueryKey;\n extraTabs: TaskExtraTabs | undefined;\n headerExtra: TaskHeaderExtra | undefined;\n}) {\n const [localOpen, setLocalOpen] = useState(false);\n // Controlled by selectedId when a selection handler is wired; otherwise the\n // row owns its expand state (backward compatible for router-less consumers).\n const controlled = onSelectRun !== undefined;\n const open = controlled ? selectedId === run.id : localOpen;\n const toggle = () => {\n if (controlled) onSelectRun(open ? null : run.id);\n else setLocalOpen((v) => !v);\n };\n const isTerminal = run.status !== \"running\" && run.status !== \"pending\";\n const progress = taskWorkProgress(run);\n\n return (\n <div className=\"border-b last:border-0\">\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-4 px-4 py-3 text-left hover:bg-muted/50\"\n onClick={toggle}\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <Icon\n icon={taskStatusIcon(run.status)}\n className={cn(taskStatusColor(run.status), run.status === \"running\" && \"animate-spin\")}\n />\n <span className=\"truncate font-medium\">{run.name}</span>\n {run.kind && (\n <span className=\"rounded-full bg-muted px-1.5 py-0 text-[10px] text-muted-foreground\">\n {run.kind}\n </span>\n )}\n {isTerminal && (\n <span\n className={cn(\n \"rounded-full px-2 py-0.5 text-xs\",\n taskStatusColor(run.status),\n taskStatusBg(run.status),\n )}\n >\n {run.status}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground\">\n {run.startedAt && <Timestamp value={run.startedAt} format=\"relative\" />}\n <span>\n {progress.label}\n </span>\n {Object.entries(run.labels ?? {}).map(([k, v]) => (\n <span key={k} className=\"font-mono\">\n {k}={v}\n </span>\n ))}\n </div>\n </div>\n <div className=\"w-40 shrink-0\">\n <ProgressBar\n segments={taskSegments(progress.counts)}\n total={progress.total || 1}\n height=\"h-1.5\"\n />\n </div>\n </button>\n {open && (\n <ExpandedRun\n runId={run.id}\n basePath={basePath}\n pollMs={pollMs}\n runsQueryKey={runsQueryKey}\n extraTabs={extraTabs}\n headerExtra={headerExtra}\n />\n )}\n </div>\n );\n}\n\nfunction ExpandedRun({\n runId,\n basePath,\n pollMs,\n runsQueryKey,\n extraTabs,\n headerExtra,\n}: {\n runId: string;\n basePath: string | undefined;\n pollMs: number | undefined;\n runsQueryKey: QueryKey;\n extraTabs: TaskExtraTabs | undefined;\n headerExtra: TaskHeaderExtra | undefined;\n}) {\n const { snapshots, status } = useTaskRun({ id: runId, basePath, pollMs });\n const apiBase = basePath ?? \"/api/v1\";\n const queryClient = useQueryClient();\n const pendingControls = useRef(new Map<string, Promise<void>>());\n const cachedSnapshots = useRef<TaskSnapshot[] | undefined>(undefined);\n const runQueryKey = useMemo(\n () => taskQueryKeys.run({ basePath: apiBase, runId }),\n [apiBase, runId],\n );\n const controlMutation = useMutation({\n mutationKey: taskQueryKeys.control({ basePath: apiBase, runId }),\n mutationFn: postTaskControl,\n onSuccess: async () => {\n await Promise.all([\n queryClient.invalidateQueries({ queryKey: runsQueryKey, exact: true, refetchType: \"none\" }),\n queryClient.invalidateQueries({ queryKey: runQueryKey, exact: true, refetchType: \"none\" }),\n ]);\n },\n });\n\n useEffect(() => {\n const previous = cachedSnapshots.current;\n if (previous?.length === snapshots.length && previous.every((snapshot, index) => snapshot === snapshots[index])) {\n return;\n }\n cachedSnapshots.current = snapshots;\n queryClient.setQueryData(runQueryKey, snapshots);\n }, [queryClient, runQueryKey, snapshots]);\n\n const mutateControl = useCallback((request: TaskControlRequest) => {\n const key = `${request.url}:${request.action}`;\n const pending = pendingControls.current.get(key);\n if (pending) return pending;\n controlMutation.reset();\n const mutation = controlMutation.mutateAsync(request).finally(() => {\n if (pendingControls.current.get(key) === mutation) pendingControls.current.delete(key);\n });\n pendingControls.current.set(key, mutation);\n return mutation;\n }, [controlMutation.mutateAsync, controlMutation.reset]);\n const control = (action: TaskControlAction, group: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/control`,\n action,\n target: group,\n });\n const controlTask = (action: TaskControlAction, task: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/tasks/${encodeURIComponent(task.id)}/control`,\n action,\n target: task,\n });\n return (\n <div className=\"border-t bg-muted/30 px-4 py-3\">\n <TaskProgress\n snapshots={snapshots}\n connectionStatus={status}\n compact\n onControl={control}\n onTaskControl={controlTask}\n metricsBaseUrl={`${apiBase}/tasks/metrics/`}\n {...(extraTabs ? { extraTabs } : {})}\n {...(headerExtra ? { headerExtra } : {})}\n />\n </div>\n );\n}\n\ninterface TaskControlRequest {\n url: string;\n action: TaskControlAction;\n target: TaskSnapshot;\n}\n\nasync function postTaskControl({ url, action, target }: TaskControlRequest) {\n const context = `Failed to ${action} ${target.type} \"${target.name}\"`;\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ action }),\n });\n } catch (cause) {\n throw new Error(`${context}: ${cause instanceof Error ? cause.message : \"request failed\"}`, { cause });\n }\n if (!response.ok) {\n const detail = (await response.text()).trim() || `HTTP ${response.status}`;\n throw new Error(`${context}: ${detail}`);\n }\n}\n"],"names":["useState","useQueryClient","useTaskRuns","useMemo","taskQueryKeys","useEffect","hashKey","cn","jsxs","jsx","taskWorkProgress","Icon","taskStatusIcon","taskStatusColor","taskStatusBg","Timestamp","ProgressBar","taskSegments","useTaskRun","useRef","useMutation","useCallback","TaskProgress"],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { TaskHeaderExtra } from './TaskHeaderExtras';
|
|
2
|
+
import { TaskExtraTabs } from './TaskStreamTabs';
|
|
1
3
|
export interface TaskManagerProps {
|
|
2
4
|
/** Base path of the clicky task API, e.g. "/api/v1". */
|
|
3
5
|
basePath?: string;
|
|
@@ -18,6 +20,16 @@ export interface TaskManagerProps {
|
|
|
18
20
|
* open row is collapsed. Pair with selectedId for URL-driven deep links.
|
|
19
21
|
*/
|
|
20
22
|
onSelectRun?: (id: string | null) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Contributes host-owned output panes to each task, alongside its own
|
|
25
|
+
* stdout/stderr — an agent's session transcript, say.
|
|
26
|
+
*/
|
|
27
|
+
extraTabs?: TaskExtraTabs;
|
|
28
|
+
/**
|
|
29
|
+
* Replaces the content beside a task's name in its header row. Returning null
|
|
30
|
+
* for a task falls back to the default metadata chips.
|
|
31
|
+
*/
|
|
32
|
+
headerExtra?: TaskHeaderExtra;
|
|
21
33
|
}
|
|
22
|
-
export declare function TaskManager({ basePath, kind, labels, pollMs, className, selectedId, onSelectRun, }: TaskManagerProps): import("react").JSX.Element;
|
|
34
|
+
export declare function TaskManager({ basePath, kind, labels, pollMs, className, selectedId, onSelectRun, extraTabs, headerExtra, }: TaskManagerProps): import("react").JSX.Element;
|
|
23
35
|
//# sourceMappingURL=TaskManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskManager.d.ts","sourceRoot":"","sources":["../../src/data/TaskManager.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TaskManager.d.ts","sourceRoot":"","sources":["../../src/data/TaskManager.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAatD,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,MAAM,EACN,SAAS,EACT,UAAU,EACV,WAAW,EACX,SAAS,EACT,WAAW,GACZ,EAAE,gBAAgB,+BAwGlB"}
|
package/dist/data/TaskManager.js
CHANGED
|
@@ -17,7 +17,9 @@ function TaskManager({
|
|
|
17
17
|
pollMs,
|
|
18
18
|
className,
|
|
19
19
|
selectedId,
|
|
20
|
-
onSelectRun
|
|
20
|
+
onSelectRun,
|
|
21
|
+
extraTabs,
|
|
22
|
+
headerExtra
|
|
21
23
|
}) {
|
|
22
24
|
const [kindFilter, setKindFilter] = useState(kind ?? "");
|
|
23
25
|
const [statusFilter, setStatusFilter] = useState("");
|
|
@@ -93,7 +95,9 @@ function TaskManager({
|
|
|
93
95
|
pollMs,
|
|
94
96
|
selectedId,
|
|
95
97
|
onSelectRun,
|
|
96
|
-
runsQueryKey
|
|
98
|
+
runsQueryKey,
|
|
99
|
+
extraTabs,
|
|
100
|
+
headerExtra
|
|
97
101
|
},
|
|
98
102
|
run.id
|
|
99
103
|
)) })
|
|
@@ -124,7 +128,9 @@ function RunRow({
|
|
|
124
128
|
pollMs,
|
|
125
129
|
selectedId,
|
|
126
130
|
onSelectRun,
|
|
127
|
-
runsQueryKey
|
|
131
|
+
runsQueryKey,
|
|
132
|
+
extraTabs,
|
|
133
|
+
headerExtra
|
|
128
134
|
}) {
|
|
129
135
|
const [localOpen, setLocalOpen] = useState(false);
|
|
130
136
|
const controlled = onSelectRun !== void 0;
|
|
@@ -193,7 +199,9 @@ function RunRow({
|
|
|
193
199
|
runId: run.id,
|
|
194
200
|
basePath,
|
|
195
201
|
pollMs,
|
|
196
|
-
runsQueryKey
|
|
202
|
+
runsQueryKey,
|
|
203
|
+
extraTabs,
|
|
204
|
+
headerExtra
|
|
197
205
|
}
|
|
198
206
|
)
|
|
199
207
|
] });
|
|
@@ -202,7 +210,9 @@ function ExpandedRun({
|
|
|
202
210
|
runId,
|
|
203
211
|
basePath,
|
|
204
212
|
pollMs,
|
|
205
|
-
runsQueryKey
|
|
213
|
+
runsQueryKey,
|
|
214
|
+
extraTabs,
|
|
215
|
+
headerExtra
|
|
206
216
|
}) {
|
|
207
217
|
const { snapshots, status } = useTaskRun({ id: runId, basePath, pollMs });
|
|
208
218
|
const apiBase = basePath ?? "/api/v1";
|
|
@@ -260,7 +270,9 @@ function ExpandedRun({
|
|
|
260
270
|
compact: true,
|
|
261
271
|
onControl: control,
|
|
262
272
|
onTaskControl: controlTask,
|
|
263
|
-
metricsBaseUrl: `${apiBase}/tasks/metrics
|
|
273
|
+
metricsBaseUrl: `${apiBase}/tasks/metrics/`,
|
|
274
|
+
...extraTabs ? { extraTabs } : {},
|
|
275
|
+
...headerExtra ? { headerExtra } : {}
|
|
264
276
|
}
|
|
265
277
|
) });
|
|
266
278
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TaskManager.js","sources":["../../src/data/TaskManager.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { hashKey, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type { QueryKey } from \"@tanstack/react-query\";\nimport { cn } from \"../lib/utils\";\nimport { useTaskRun, useTaskRuns } from \"../hooks/use-task-run\";\nimport { Icon } from \"./Icon\";\nimport { ProgressBar } from \"./ProgressBar\";\nimport { Timestamp } from \"./cells/Timestamp\";\nimport { TaskProgress } from \"./TaskProgress\";\nimport { taskWorkProgress } from \"./task-work-progress\";\nimport type { TaskControlAction, TaskRunMeta, TaskSnapshot } from \"./TaskSnapshot\";\nimport { taskQueryKeys } from \"./task-query-keys\";\nimport { taskSegments, taskStatusBg, taskStatusColor, taskStatusIcon } from \"./task-status\";\n\n// TaskManager is the generic clicky-ui task-manager view: it lists every run\n// (TaskRunMeta) from GET {basePath}/tasks with kind/status filters, and expands\n// each row into a live <TaskProgress> for that run id. Runs are grouped by kind,\n// which applications use as their stable task identity.\n//\n// Requires a react-query QueryClientProvider in the host app (like\n// TimeseriesPanel and CacheBrowser): control actions run through useMutation,\n// and the listing is mirrored into the query cache so sibling views can read it.\n\nexport interface TaskManagerProps {\n /** Base path of the clicky task API, e.g. \"/api/v1\". */\n basePath?: string;\n /** Restrict the listing to a single kind (also hides the kind filter). */\n kind?: string;\n /** Restrict the listing to runs whose labels contain every key/value pair. */\n labels?: Record<string, string>;\n pollMs?: number;\n className?: string;\n /**\n * Currently-selected (expanded) run id, e.g. driven from the URL. When set,\n * the matching run renders expanded and selection is controlled — otherwise\n * each row manages its own expand/collapse state.\n */\n selectedId?: string;\n /**\n * Called when a run row is toggled: the new selection id, or null when the\n * open row is collapsed. Pair with selectedId for URL-driven deep links.\n */\n onSelectRun?: (id: string | null) => void;\n}\n\nexport function TaskManager({\n basePath,\n kind,\n labels,\n pollMs,\n className,\n selectedId,\n onSelectRun,\n}: TaskManagerProps) {\n const [kindFilter, setKindFilter] = useState<string>(kind ?? \"\");\n const [statusFilter, setStatusFilter] = useState<string>(\"\");\n const queryClient = useQueryClient();\n const apiBase = basePath ?? \"/api/v1\";\n const activeKind = kind ?? (kindFilter || undefined);\n const { runs, runsKey, status } = useTaskRuns({\n basePath: apiBase,\n kind: activeKind,\n labels,\n status: statusFilter || undefined,\n pollMs,\n });\n const runsQueryKey = useMemo(\n () => taskQueryKeys.runs({\n basePath: apiBase,\n kind: activeKind,\n status: statusFilter || undefined,\n labels,\n }),\n [activeKind, apiBase, labels, statusFilter],\n );\n\n // Changing a filter re-subscribes the transport but leaves the previous\n // listing on screen until the next SSE/poll frame. Publishing that listing\n // under the new key would hand cache consumers rows that do not match their\n // filters, so mirror only once the delivered listing's own key agrees.\n useEffect(() => {\n if (!runsKey || hashKey(runsKey) !== hashKey(runsQueryKey)) return;\n queryClient.setQueryData(runsQueryKey, runs);\n }, [queryClient, runs, runsKey, runsQueryKey]);\n\n const kinds = useMemo(() => {\n const set = new Set<string>();\n for (const r of runs) if (r.kind) set.add(r.kind);\n return [...set].sort();\n }, [runs]);\n const applications = useMemo(() => groupRunsByApplication(runs), [runs]);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {!kind && (\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value)}\n >\n <option value=\"\">All kinds</option>\n {kinds.map((k) => (\n <option key={k} value={k}>\n {k}\n </option>\n ))}\n </select>\n )}\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n >\n <option value=\"\">Any status</option>\n <option value=\"running\">Running</option>\n <option value=\"pending\">Pending</option>\n <option value=\"success\">Success</option>\n <option value=\"failed\">Failed</option>\n <option value=\"warning\">Warning</option>\n <option value=\"canceled\">Canceled</option>\n </select>\n <span className=\"text-xs text-muted-foreground\">{status}</span>\n </div>\n\n {runs.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground\">\n No background tasks.\n </div>\n ) : (\n <div className=\"space-y-3\">\n {applications.map((application) => (\n <section key={application.kind}>\n <h3 className=\"mb-1 px-1 text-xs font-semibold text-muted-foreground\">\n {applicationLabel(application.kind)}\n </h3>\n <div className=\"overflow-hidden rounded-lg border bg-card\">\n {application.runs.map((run) => (\n <RunRow\n key={run.id}\n run={run}\n basePath={basePath}\n pollMs={pollMs}\n selectedId={selectedId}\n onSelectRun={onSelectRun}\n runsQueryKey={runsQueryKey}\n />\n ))}\n </div>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction groupRunsByApplication(runs: TaskRunMeta[]) {\n const grouped = new Map<string, TaskRunMeta[]>();\n for (const run of runs) {\n const application = run.kind || \"other\";\n grouped.set(application, [...(grouped.get(application) ?? []), run]);\n }\n return [...grouped].map(([kind, applicationRuns]) => ({ kind, runs: applicationRuns }));\n}\n\nfunction applicationLabel(kind: string): string {\n const names: Record<string, string> = {\n makerprint: \"MakerPrint\",\n openscad: \"OpenSCAD\",\n orca: \"Orca\",\n snapmaker: \"Snapmaker\",\n other: \"Other\",\n };\n return names[kind] ?? kind;\n}\n\nfunction RunRow({\n run,\n basePath,\n pollMs,\n selectedId,\n onSelectRun,\n runsQueryKey,\n}: {\n run: TaskRunMeta;\n basePath: string | undefined;\n pollMs: number | undefined;\n selectedId: string | undefined;\n onSelectRun: ((id: string | null) => void) | undefined;\n runsQueryKey: QueryKey;\n}) {\n const [localOpen, setLocalOpen] = useState(false);\n // Controlled by selectedId when a selection handler is wired; otherwise the\n // row owns its expand state (backward compatible for router-less consumers).\n const controlled = onSelectRun !== undefined;\n const open = controlled ? selectedId === run.id : localOpen;\n const toggle = () => {\n if (controlled) onSelectRun(open ? null : run.id);\n else setLocalOpen((v) => !v);\n };\n const isTerminal = run.status !== \"running\" && run.status !== \"pending\";\n const progress = taskWorkProgress(run);\n\n return (\n <div className=\"border-b last:border-0\">\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-4 px-4 py-3 text-left hover:bg-muted/50\"\n onClick={toggle}\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <Icon\n icon={taskStatusIcon(run.status)}\n className={cn(taskStatusColor(run.status), run.status === \"running\" && \"animate-spin\")}\n />\n <span className=\"truncate font-medium\">{run.name}</span>\n {run.kind && (\n <span className=\"rounded-full bg-muted px-1.5 py-0 text-[10px] text-muted-foreground\">\n {run.kind}\n </span>\n )}\n {isTerminal && (\n <span\n className={cn(\n \"rounded-full px-2 py-0.5 text-xs\",\n taskStatusColor(run.status),\n taskStatusBg(run.status),\n )}\n >\n {run.status}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground\">\n {run.startedAt && <Timestamp value={run.startedAt} format=\"relative\" />}\n <span>\n {progress.label}\n </span>\n {Object.entries(run.labels ?? {}).map(([k, v]) => (\n <span key={k} className=\"font-mono\">\n {k}={v}\n </span>\n ))}\n </div>\n </div>\n <div className=\"w-40 shrink-0\">\n <ProgressBar\n segments={taskSegments(progress.counts)}\n total={progress.total || 1}\n height=\"h-1.5\"\n />\n </div>\n </button>\n {open && (\n <ExpandedRun\n runId={run.id}\n basePath={basePath}\n pollMs={pollMs}\n runsQueryKey={runsQueryKey}\n />\n )}\n </div>\n );\n}\n\nfunction ExpandedRun({\n runId,\n basePath,\n pollMs,\n runsQueryKey,\n}: {\n runId: string;\n basePath: string | undefined;\n pollMs: number | undefined;\n runsQueryKey: QueryKey;\n}) {\n const { snapshots, status } = useTaskRun({ id: runId, basePath, pollMs });\n const apiBase = basePath ?? \"/api/v1\";\n const queryClient = useQueryClient();\n const pendingControls = useRef(new Map<string, Promise<void>>());\n const cachedSnapshots = useRef<TaskSnapshot[] | undefined>(undefined);\n const runQueryKey = useMemo(\n () => taskQueryKeys.run({ basePath: apiBase, runId }),\n [apiBase, runId],\n );\n const controlMutation = useMutation({\n mutationKey: taskQueryKeys.control({ basePath: apiBase, runId }),\n mutationFn: postTaskControl,\n onSuccess: async () => {\n await Promise.all([\n queryClient.invalidateQueries({ queryKey: runsQueryKey, exact: true, refetchType: \"none\" }),\n queryClient.invalidateQueries({ queryKey: runQueryKey, exact: true, refetchType: \"none\" }),\n ]);\n },\n });\n\n useEffect(() => {\n const previous = cachedSnapshots.current;\n if (previous?.length === snapshots.length && previous.every((snapshot, index) => snapshot === snapshots[index])) {\n return;\n }\n cachedSnapshots.current = snapshots;\n queryClient.setQueryData(runQueryKey, snapshots);\n }, [queryClient, runQueryKey, snapshots]);\n\n const mutateControl = useCallback((request: TaskControlRequest) => {\n const key = `${request.url}:${request.action}`;\n const pending = pendingControls.current.get(key);\n if (pending) return pending;\n controlMutation.reset();\n const mutation = controlMutation.mutateAsync(request).finally(() => {\n if (pendingControls.current.get(key) === mutation) pendingControls.current.delete(key);\n });\n pendingControls.current.set(key, mutation);\n return mutation;\n }, [controlMutation.mutateAsync, controlMutation.reset]);\n const control = (action: TaskControlAction, group: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/control`,\n action,\n target: group,\n });\n const controlTask = (action: TaskControlAction, task: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/tasks/${encodeURIComponent(task.id)}/control`,\n action,\n target: task,\n });\n return (\n <div className=\"border-t bg-muted/30 px-4 py-3\">\n <TaskProgress\n snapshots={snapshots}\n connectionStatus={status}\n compact\n onControl={control}\n onTaskControl={controlTask}\n metricsBaseUrl={`${apiBase}/tasks/metrics/`}\n />\n </div>\n );\n}\n\ninterface TaskControlRequest {\n url: string;\n action: TaskControlAction;\n target: TaskSnapshot;\n}\n\nasync function postTaskControl({ url, action, target }: TaskControlRequest) {\n const context = `Failed to ${action} ${target.type} \"${target.name}\"`;\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ action }),\n });\n } catch (cause) {\n throw new Error(`${context}: ${cause instanceof Error ? cause.message : \"request failed\"}`, { cause });\n }\n if (!response.ok) {\n const detail = (await response.text()).trim() || `HTTP ${response.status}`;\n throw new Error(`${context}: ${detail}`);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TaskManager.js","sources":["../../src/data/TaskManager.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { hashKey, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type { QueryKey } from \"@tanstack/react-query\";\nimport { cn } from \"../lib/utils\";\nimport { useTaskRun, useTaskRuns } from \"../hooks/use-task-run\";\nimport { Icon } from \"./Icon\";\nimport { ProgressBar } from \"./ProgressBar\";\nimport { Timestamp } from \"./cells/Timestamp\";\nimport { TaskProgress } from \"./TaskProgress\";\nimport { taskWorkProgress } from \"./task-work-progress\";\nimport type { TaskControlAction, TaskRunMeta, TaskSnapshot } from \"./TaskSnapshot\";\nimport type { TaskHeaderExtra } from \"./TaskHeaderExtras\";\nimport type { TaskExtraTabs } from \"./TaskStreamTabs\";\nimport { taskQueryKeys } from \"./task-query-keys\";\nimport { taskSegments, taskStatusBg, taskStatusColor, taskStatusIcon } from \"./task-status\";\n\n// TaskManager is the generic clicky-ui task-manager view: it lists every run\n// (TaskRunMeta) from GET {basePath}/tasks with kind/status filters, and expands\n// each row into a live <TaskProgress> for that run id. Runs are grouped by kind,\n// which applications use as their stable task identity.\n//\n// Requires a react-query QueryClientProvider in the host app (like\n// TimeseriesPanel and CacheBrowser): control actions run through useMutation,\n// and the listing is mirrored into the query cache so sibling views can read it.\n\nexport interface TaskManagerProps {\n /** Base path of the clicky task API, e.g. \"/api/v1\". */\n basePath?: string;\n /** Restrict the listing to a single kind (also hides the kind filter). */\n kind?: string;\n /** Restrict the listing to runs whose labels contain every key/value pair. */\n labels?: Record<string, string>;\n pollMs?: number;\n className?: string;\n /**\n * Currently-selected (expanded) run id, e.g. driven from the URL. When set,\n * the matching run renders expanded and selection is controlled — otherwise\n * each row manages its own expand/collapse state.\n */\n selectedId?: string;\n /**\n * Called when a run row is toggled: the new selection id, or null when the\n * open row is collapsed. Pair with selectedId for URL-driven deep links.\n */\n onSelectRun?: (id: string | null) => void;\n /**\n * Contributes host-owned output panes to each task, alongside its own\n * stdout/stderr — an agent's session transcript, say.\n */\n extraTabs?: TaskExtraTabs;\n /**\n * Replaces the content beside a task's name in its header row. Returning null\n * for a task falls back to the default metadata chips.\n */\n headerExtra?: TaskHeaderExtra;\n}\n\nexport function TaskManager({\n basePath,\n kind,\n labels,\n pollMs,\n className,\n selectedId,\n onSelectRun,\n extraTabs,\n headerExtra,\n}: TaskManagerProps) {\n const [kindFilter, setKindFilter] = useState<string>(kind ?? \"\");\n const [statusFilter, setStatusFilter] = useState<string>(\"\");\n const queryClient = useQueryClient();\n const apiBase = basePath ?? \"/api/v1\";\n const activeKind = kind ?? (kindFilter || undefined);\n const { runs, runsKey, status } = useTaskRuns({\n basePath: apiBase,\n kind: activeKind,\n labels,\n status: statusFilter || undefined,\n pollMs,\n });\n const runsQueryKey = useMemo(\n () => taskQueryKeys.runs({\n basePath: apiBase,\n kind: activeKind,\n status: statusFilter || undefined,\n labels,\n }),\n [activeKind, apiBase, labels, statusFilter],\n );\n\n // Changing a filter re-subscribes the transport but leaves the previous\n // listing on screen until the next SSE/poll frame. Publishing that listing\n // under the new key would hand cache consumers rows that do not match their\n // filters, so mirror only once the delivered listing's own key agrees.\n useEffect(() => {\n if (!runsKey || hashKey(runsKey) !== hashKey(runsQueryKey)) return;\n queryClient.setQueryData(runsQueryKey, runs);\n }, [queryClient, runs, runsKey, runsQueryKey]);\n\n const kinds = useMemo(() => {\n const set = new Set<string>();\n for (const r of runs) if (r.kind) set.add(r.kind);\n return [...set].sort();\n }, [runs]);\n const applications = useMemo(() => groupRunsByApplication(runs), [runs]);\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n <div className=\"flex flex-wrap items-center gap-2\">\n {!kind && (\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={kindFilter}\n onChange={(e) => setKindFilter(e.target.value)}\n >\n <option value=\"\">All kinds</option>\n {kinds.map((k) => (\n <option key={k} value={k}>\n {k}\n </option>\n ))}\n </select>\n )}\n <select\n className=\"rounded-md border bg-background px-2 py-1 text-xs\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n >\n <option value=\"\">Any status</option>\n <option value=\"running\">Running</option>\n <option value=\"pending\">Pending</option>\n <option value=\"success\">Success</option>\n <option value=\"failed\">Failed</option>\n <option value=\"warning\">Warning</option>\n <option value=\"canceled\">Canceled</option>\n </select>\n <span className=\"text-xs text-muted-foreground\">{status}</span>\n </div>\n\n {runs.length === 0 ? (\n <div className=\"rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground\">\n No background tasks.\n </div>\n ) : (\n <div className=\"space-y-3\">\n {applications.map((application) => (\n <section key={application.kind}>\n <h3 className=\"mb-1 px-1 text-xs font-semibold text-muted-foreground\">\n {applicationLabel(application.kind)}\n </h3>\n <div className=\"overflow-hidden rounded-lg border bg-card\">\n {application.runs.map((run) => (\n <RunRow\n key={run.id}\n run={run}\n basePath={basePath}\n pollMs={pollMs}\n selectedId={selectedId}\n onSelectRun={onSelectRun}\n runsQueryKey={runsQueryKey}\n extraTabs={extraTabs}\n headerExtra={headerExtra}\n />\n ))}\n </div>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nfunction groupRunsByApplication(runs: TaskRunMeta[]) {\n const grouped = new Map<string, TaskRunMeta[]>();\n for (const run of runs) {\n const application = run.kind || \"other\";\n grouped.set(application, [...(grouped.get(application) ?? []), run]);\n }\n return [...grouped].map(([kind, applicationRuns]) => ({ kind, runs: applicationRuns }));\n}\n\nfunction applicationLabel(kind: string): string {\n const names: Record<string, string> = {\n makerprint: \"MakerPrint\",\n openscad: \"OpenSCAD\",\n orca: \"Orca\",\n snapmaker: \"Snapmaker\",\n other: \"Other\",\n };\n return names[kind] ?? kind;\n}\n\nfunction RunRow({\n run,\n basePath,\n pollMs,\n selectedId,\n onSelectRun,\n runsQueryKey,\n extraTabs,\n headerExtra,\n}: {\n run: TaskRunMeta;\n basePath: string | undefined;\n pollMs: number | undefined;\n selectedId: string | undefined;\n onSelectRun: ((id: string | null) => void) | undefined;\n runsQueryKey: QueryKey;\n extraTabs: TaskExtraTabs | undefined;\n headerExtra: TaskHeaderExtra | undefined;\n}) {\n const [localOpen, setLocalOpen] = useState(false);\n // Controlled by selectedId when a selection handler is wired; otherwise the\n // row owns its expand state (backward compatible for router-less consumers).\n const controlled = onSelectRun !== undefined;\n const open = controlled ? selectedId === run.id : localOpen;\n const toggle = () => {\n if (controlled) onSelectRun(open ? null : run.id);\n else setLocalOpen((v) => !v);\n };\n const isTerminal = run.status !== \"running\" && run.status !== \"pending\";\n const progress = taskWorkProgress(run);\n\n return (\n <div className=\"border-b last:border-0\">\n <button\n type=\"button\"\n className=\"flex w-full items-center justify-between gap-4 px-4 py-3 text-left hover:bg-muted/50\"\n onClick={toggle}\n >\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <Icon\n icon={taskStatusIcon(run.status)}\n className={cn(taskStatusColor(run.status), run.status === \"running\" && \"animate-spin\")}\n />\n <span className=\"truncate font-medium\">{run.name}</span>\n {run.kind && (\n <span className=\"rounded-full bg-muted px-1.5 py-0 text-[10px] text-muted-foreground\">\n {run.kind}\n </span>\n )}\n {isTerminal && (\n <span\n className={cn(\n \"rounded-full px-2 py-0.5 text-xs\",\n taskStatusColor(run.status),\n taskStatusBg(run.status),\n )}\n >\n {run.status}\n </span>\n )}\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground\">\n {run.startedAt && <Timestamp value={run.startedAt} format=\"relative\" />}\n <span>\n {progress.label}\n </span>\n {Object.entries(run.labels ?? {}).map(([k, v]) => (\n <span key={k} className=\"font-mono\">\n {k}={v}\n </span>\n ))}\n </div>\n </div>\n <div className=\"w-40 shrink-0\">\n <ProgressBar\n segments={taskSegments(progress.counts)}\n total={progress.total || 1}\n height=\"h-1.5\"\n />\n </div>\n </button>\n {open && (\n <ExpandedRun\n runId={run.id}\n basePath={basePath}\n pollMs={pollMs}\n runsQueryKey={runsQueryKey}\n extraTabs={extraTabs}\n headerExtra={headerExtra}\n />\n )}\n </div>\n );\n}\n\nfunction ExpandedRun({\n runId,\n basePath,\n pollMs,\n runsQueryKey,\n extraTabs,\n headerExtra,\n}: {\n runId: string;\n basePath: string | undefined;\n pollMs: number | undefined;\n runsQueryKey: QueryKey;\n extraTabs: TaskExtraTabs | undefined;\n headerExtra: TaskHeaderExtra | undefined;\n}) {\n const { snapshots, status } = useTaskRun({ id: runId, basePath, pollMs });\n const apiBase = basePath ?? \"/api/v1\";\n const queryClient = useQueryClient();\n const pendingControls = useRef(new Map<string, Promise<void>>());\n const cachedSnapshots = useRef<TaskSnapshot[] | undefined>(undefined);\n const runQueryKey = useMemo(\n () => taskQueryKeys.run({ basePath: apiBase, runId }),\n [apiBase, runId],\n );\n const controlMutation = useMutation({\n mutationKey: taskQueryKeys.control({ basePath: apiBase, runId }),\n mutationFn: postTaskControl,\n onSuccess: async () => {\n await Promise.all([\n queryClient.invalidateQueries({ queryKey: runsQueryKey, exact: true, refetchType: \"none\" }),\n queryClient.invalidateQueries({ queryKey: runQueryKey, exact: true, refetchType: \"none\" }),\n ]);\n },\n });\n\n useEffect(() => {\n const previous = cachedSnapshots.current;\n if (previous?.length === snapshots.length && previous.every((snapshot, index) => snapshot === snapshots[index])) {\n return;\n }\n cachedSnapshots.current = snapshots;\n queryClient.setQueryData(runQueryKey, snapshots);\n }, [queryClient, runQueryKey, snapshots]);\n\n const mutateControl = useCallback((request: TaskControlRequest) => {\n const key = `${request.url}:${request.action}`;\n const pending = pendingControls.current.get(key);\n if (pending) return pending;\n controlMutation.reset();\n const mutation = controlMutation.mutateAsync(request).finally(() => {\n if (pendingControls.current.get(key) === mutation) pendingControls.current.delete(key);\n });\n pendingControls.current.set(key, mutation);\n return mutation;\n }, [controlMutation.mutateAsync, controlMutation.reset]);\n const control = (action: TaskControlAction, group: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/control`,\n action,\n target: group,\n });\n const controlTask = (action: TaskControlAction, task: TaskSnapshot) =>\n mutateControl({\n url: `${apiBase}/tasks/${encodeURIComponent(runId)}/tasks/${encodeURIComponent(task.id)}/control`,\n action,\n target: task,\n });\n return (\n <div className=\"border-t bg-muted/30 px-4 py-3\">\n <TaskProgress\n snapshots={snapshots}\n connectionStatus={status}\n compact\n onControl={control}\n onTaskControl={controlTask}\n metricsBaseUrl={`${apiBase}/tasks/metrics/`}\n {...(extraTabs ? { extraTabs } : {})}\n {...(headerExtra ? { headerExtra } : {})}\n />\n </div>\n );\n}\n\ninterface TaskControlRequest {\n url: string;\n action: TaskControlAction;\n target: TaskSnapshot;\n}\n\nasync function postTaskControl({ url, action, target }: TaskControlRequest) {\n const context = `Failed to ${action} ${target.type} \"${target.name}\"`;\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ action }),\n });\n } catch (cause) {\n throw new Error(`${context}: ${cause instanceof Error ? cause.message : \"request failed\"}`, { cause });\n }\n if (!response.ok) {\n const detail = (await response.text()).trim() || `HTTP ${response.status}`;\n throw new Error(`${context}: ${detail}`);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ const DiagnosticsTree = require("./diagnostics/DiagnosticsTree.cjs");
|
|
|
6
6
|
const TaskResourceReadings = require("./TaskResourceReadings.cjs");
|
|
7
7
|
const taskProcessDetails = require("./task-process-details.cjs");
|
|
8
8
|
const TaskExecDetails = require("./TaskExecDetails.cjs");
|
|
9
|
+
const TaskProcessMetadata = require("./TaskProcessMetadata.cjs");
|
|
9
10
|
const UiActivity = require("../icons/components/UiActivity.cjs");
|
|
10
11
|
const UiDatabase = require("../icons/components/UiDatabase.cjs");
|
|
11
12
|
const UiFile = require("../icons/components/UiFile.cjs");
|
|
@@ -18,6 +19,7 @@ function TaskProcessDetailsView({
|
|
|
18
19
|
const metricBase = metricsBaseUrl ?? "/api/v1/tasks/metrics/";
|
|
19
20
|
const restartLabel = `${details.restarts} ${details.restarts === 1 ? "restart" : "restarts"}`;
|
|
20
21
|
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "mb-3 space-y-3 rounded-md border bg-muted/20 p-3", children: [
|
|
22
|
+
/* @__PURE__ */ jsxRuntime.jsx(TaskProcessMetadata.TaskMetadata, { ...details.metadata !== void 0 ? { metadata: details.metadata } : {} }),
|
|
21
23
|
/* @__PURE__ */ jsxRuntime.jsx(TaskExecDetails.TaskCommandLine, { command: details.command, ...details.args ? { args: details.args } : {} }),
|
|
22
24
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground", children: [
|
|
23
25
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-foreground", children: [
|
|
@@ -27,7 +29,8 @@ function TaskProcessDetailsView({
|
|
|
27
29
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: details.status }),
|
|
28
30
|
/* @__PURE__ */ jsxRuntime.jsx(TaskResourceReadings.TaskResourceReadings, { details, ...connectionStatus ? { connectionStatus } : {} }),
|
|
29
31
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: restartLabel }),
|
|
30
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: details.restartPolicy })
|
|
32
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: details.restartPolicy }),
|
|
33
|
+
/* @__PURE__ */ jsxRuntime.jsx(TaskProcessMetadata.TaskProcessFacts, { details })
|
|
31
34
|
] }),
|
|
32
35
|
Object.keys(details.metrics).length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2 xl:grid-cols-4", children: [
|
|
33
36
|
details.metrics.cpu && /* @__PURE__ */ jsxRuntime.jsx(
|