@echothink-ui/task 0.1.0

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.
Files changed (64) hide show
  1. package/README.md +5 -0
  2. package/dist/components/BackendThinkingChain.d.ts +2 -0
  3. package/dist/components/BlockingReasonPanel.d.ts +2 -0
  4. package/dist/components/DAGEdge.d.ts +2 -0
  5. package/dist/components/DAGLegend.d.ts +2 -0
  6. package/dist/components/DAGNode.d.ts +4 -0
  7. package/dist/components/DecisionRequiredPanel.d.ts +2 -0
  8. package/dist/components/HumanInterventionPanel.d.ts +2 -0
  9. package/dist/components/MobileTaskShell.d.ts +12 -0
  10. package/dist/components/TaskApprovalPanel.d.ts +2 -0
  11. package/dist/components/TaskCard.d.ts +2 -0
  12. package/dist/components/TaskDependencyList.d.ts +2 -0
  13. package/dist/components/TaskDetailPanel.d.ts +2 -0
  14. package/dist/components/TaskHandoffPanel.d.ts +2 -0
  15. package/dist/components/TaskProgressIndicator.d.ts +2 -0
  16. package/dist/components/TaskRetryPanel.d.ts +2 -0
  17. package/dist/components/TaskRunLog.d.ts +2 -0
  18. package/dist/components/TaskStatusBadge.d.ts +2 -0
  19. package/dist/components/TaskTable.d.ts +5 -0
  20. package/dist/components/TaskTimeline.d.ts +2 -0
  21. package/dist/components/TaskWaveDAG.d.ts +2 -0
  22. package/dist/components/TaskWaveHeader.d.ts +2 -0
  23. package/dist/components/TaskWaveTable.d.ts +2 -0
  24. package/dist/components/utils.d.ts +13 -0
  25. package/dist/index.cjs +2434 -0
  26. package/dist/index.cjs.map +1 -0
  27. package/dist/index.css +2402 -0
  28. package/dist/index.css.map +1 -0
  29. package/dist/index.d.ts +27 -0
  30. package/dist/index.js +2388 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/types.d.ts +249 -0
  33. package/package.json +45 -0
  34. package/src/components/BackendThinkingChain.tsx +129 -0
  35. package/src/components/BlockingReasonPanel.tsx +67 -0
  36. package/src/components/DAGEdge.tsx +97 -0
  37. package/src/components/DAGLegend.tsx +86 -0
  38. package/src/components/DAGNode.tsx +103 -0
  39. package/src/components/DecisionRequiredPanel.tsx +166 -0
  40. package/src/components/HumanInterventionPanel.tsx +82 -0
  41. package/src/components/MobileTaskShell.tsx +52 -0
  42. package/src/components/TaskApprovalPanel.tsx +159 -0
  43. package/src/components/TaskCard.tsx +71 -0
  44. package/src/components/TaskDependencyList.test.tsx +54 -0
  45. package/src/components/TaskDependencyList.tsx +105 -0
  46. package/src/components/TaskDetailPanel.test.tsx +49 -0
  47. package/src/components/TaskDetailPanel.tsx +139 -0
  48. package/src/components/TaskHandoffPanel.tsx +125 -0
  49. package/src/components/TaskProgressIndicator.tsx +70 -0
  50. package/src/components/TaskRetryPanel.test.tsx +29 -0
  51. package/src/components/TaskRetryPanel.tsx +103 -0
  52. package/src/components/TaskRunLog.tsx +156 -0
  53. package/src/components/TaskStatusBadge.tsx +29 -0
  54. package/src/components/TaskTable.tsx +294 -0
  55. package/src/components/TaskTimeline.tsx +98 -0
  56. package/src/components/TaskWaveDAG.tsx +202 -0
  57. package/src/components/TaskWaveHeader.tsx +82 -0
  58. package/src/components/TaskWaveTable.tsx +151 -0
  59. package/src/components/css.d.ts +1 -0
  60. package/src/components/utils.ts +116 -0
  61. package/src/index.test.tsx +316 -0
  62. package/src/index.tsx +90 -0
  63. package/src/styles.css +2889 -0
  64. package/src/types.ts +289 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,2434 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.tsx
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BackendThinkingChain: () => BackendThinkingChain,
34
+ BlockingReasonPanel: () => BlockingReasonPanel,
35
+ DAGEdge: () => DAGEdge,
36
+ DAGLegend: () => DAGLegend,
37
+ DAGNode: () => DAGNode,
38
+ DecisionRequiredPanel: () => DecisionRequiredPanel,
39
+ HumanInterventionPanel: () => HumanInterventionPanel,
40
+ MobileTaskShell: () => MobileTaskShell,
41
+ TaskApprovalPanel: () => TaskApprovalPanel,
42
+ TaskCard: () => TaskCard,
43
+ TaskComponentNames: () => TaskComponentNames,
44
+ TaskDependencyList: () => TaskDependencyList,
45
+ TaskDetailPanel: () => TaskDetailPanel,
46
+ TaskHandoffPanel: () => TaskHandoffPanel,
47
+ TaskProgressIndicator: () => TaskProgressIndicator,
48
+ TaskRetryPanel: () => TaskRetryPanel,
49
+ TaskRunLog: () => TaskRunLog,
50
+ TaskStatusBadge: () => TaskStatusBadge,
51
+ TaskTable: () => TaskTable,
52
+ TaskTimeline: () => TaskTimeline,
53
+ TaskWaveDAG: () => TaskWaveDAG,
54
+ TaskWaveHeader: () => TaskWaveHeader,
55
+ TaskWaveTable: () => TaskWaveTable
56
+ });
57
+ module.exports = __toCommonJS(index_exports);
58
+
59
+ // src/components/BackendThinkingChain.tsx
60
+ var React = __toESM(require("react"), 1);
61
+ var import_clsx = __toESM(require("clsx"), 1);
62
+ var import_core2 = require("@echothink-ui/core");
63
+
64
+ // src/components/utils.ts
65
+ var import_core = require("@echothink-ui/core");
66
+ function severityForStatus(status) {
67
+ if (status === "running" || status === "queued" || status === "in-progress") return "info";
68
+ if (status === "blocked" || status === "failed") return "danger";
69
+ if (status === "pending-approval" || status === "approval-required" || status === "warning") {
70
+ return "warning";
71
+ }
72
+ if (status === "succeeded" || status === "completed" || status === "synced") return "success";
73
+ return "neutral";
74
+ }
75
+ function severityForPriority(priority) {
76
+ if (priority === "critical") return "danger";
77
+ if (priority === "high") return "warning";
78
+ if (priority === "medium") return "info";
79
+ return "neutral";
80
+ }
81
+ function severityForRisk(riskLevel) {
82
+ if (riskLevel === "critical" || riskLevel === "high") return "danger";
83
+ if (riskLevel === "medium") return "warning";
84
+ if (riskLevel === "low") return "info";
85
+ return "neutral";
86
+ }
87
+ function labelForStatus(status) {
88
+ return (0, import_core.statusLabel)(status);
89
+ }
90
+ function formatDateTime(value) {
91
+ if (!value) return "-";
92
+ const date = value instanceof Date ? value : new Date(value);
93
+ if (Number.isNaN(date.valueOf())) return String(value);
94
+ return new Intl.DateTimeFormat(void 0, {
95
+ dateStyle: "medium",
96
+ timeStyle: "short"
97
+ }).format(date);
98
+ }
99
+ function formatDuration(durationMs) {
100
+ if (durationMs == null) return "-";
101
+ if (durationMs < 1e3) return `${durationMs}ms`;
102
+ const seconds = Math.round(durationMs / 1e3);
103
+ if (seconds < 60) return `${seconds}s`;
104
+ const minutes = Math.floor(seconds / 60);
105
+ const remainder = seconds % 60;
106
+ if (minutes < 60) return remainder ? `${minutes}m ${remainder}s` : `${minutes}m`;
107
+ const hours = Math.floor(minutes / 60);
108
+ const minuteRemainder = minutes % 60;
109
+ return minuteRemainder ? `${hours}h ${minuteRemainder}m` : `${hours}h`;
110
+ }
111
+ function clampProgress(value, total = 100) {
112
+ if (value == null || Number.isNaN(value)) return 0;
113
+ if (total <= 0) return 0;
114
+ const percent = total === 100 ? value : value / total * 100;
115
+ return Math.max(0, Math.min(100, Math.round(percent)));
116
+ }
117
+ function waveProgress(wave) {
118
+ if (!wave?.totalTasks) return 0;
119
+ const completed = (wave.statuses.completed ?? 0) + (wave.statuses.succeeded ?? 0);
120
+ return clampProgress(completed, wave.totalTasks);
121
+ }
122
+ function statusColor(status) {
123
+ switch (status) {
124
+ case "completed":
125
+ case "succeeded":
126
+ case "synced":
127
+ case "active":
128
+ return "var(--eth-color-success)";
129
+ case "running":
130
+ case "in-progress":
131
+ case "queued":
132
+ return "var(--eth-color-info)";
133
+ case "blocked":
134
+ case "failed":
135
+ case "approval-required":
136
+ return "var(--eth-color-danger)";
137
+ case "pending-approval":
138
+ case "warning":
139
+ return "var(--eth-color-warning)";
140
+ default:
141
+ return "var(--eth-color-border-strong)";
142
+ }
143
+ }
144
+
145
+ // src/components/BackendThinkingChain.tsx
146
+ var import_jsx_runtime = require("react/jsx-runtime");
147
+ var STREAMING_STATUSES = /* @__PURE__ */ new Set(["running", "in-progress"]);
148
+ function flattenSteps(steps) {
149
+ return steps.flatMap((step) => [step, ...flattenSteps(step.children ?? [])]);
150
+ }
151
+ function currentStreamingStepId(steps) {
152
+ const flattened = flattenSteps(steps);
153
+ for (let index = flattened.length - 1; index >= 0; index -= 1) {
154
+ if (STREAMING_STATUSES.has(flattened[index].status)) {
155
+ return flattened[index].id;
156
+ }
157
+ }
158
+ return flattened.at(-1)?.id;
159
+ }
160
+ function ThinkingStepItem({
161
+ step,
162
+ streamingStepId
163
+ }) {
164
+ const isStreaming = streamingStepId === step.id;
165
+ const hasChildren = Boolean(step.children?.length);
166
+ const [expanded, setExpanded] = React.useState(true);
167
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
168
+ "li",
169
+ {
170
+ className: (0, import_clsx.default)(
171
+ "eth-task-thinking-chain__item",
172
+ `eth-task-thinking-chain__item--${step.status}`,
173
+ step.redacted && "eth-task-thinking-chain__item--redacted"
174
+ ),
175
+ children: [
176
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "eth-task-thinking-chain__marker", "aria-hidden": "true" }),
177
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
178
+ "details",
179
+ {
180
+ className: "eth-task-thinking-chain__step",
181
+ open: expanded,
182
+ onToggle: (event) => {
183
+ setExpanded(event.currentTarget.open);
184
+ },
185
+ children: [
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", { className: "eth-task-thinking-chain__step-header", children: [
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "eth-task-thinking-chain__disclosure-icon", "aria-hidden": "true" }),
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "eth-task-thinking-chain__step-header-inner", children: [
189
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "eth-task-thinking-chain__step-heading", children: [
190
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "eth-task-thinking-chain__step-title", children: step.redacted ? "Restricted step" : step.title }),
191
+ hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "eth-task-thinking-chain__branch-count", children: [
192
+ step.children?.length,
193
+ " ",
194
+ step.children?.length === 1 ? "substep" : "substeps"
195
+ ] }) : null,
196
+ isStreaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "eth-task-thinking-chain__streaming", "aria-label": "Streaming update", children: [
197
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "eth-task-thinking-chain__pulse", "aria-hidden": "true" }),
198
+ "Streaming"
199
+ ] }) : null
200
+ ] }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "eth-task-thinking-chain__step-meta", children: [
202
+ step.redacted ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core2.Badge, { severity: "warning", children: "Redacted" }) : null,
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core2.StatusDot, { status: step.status, label: labelForStatus(step.status) }),
204
+ step.toolName ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core2.Tag, { children: step.toolName }) : null,
205
+ step.durationMs != null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "eth-task-thinking-chain__duration", children: formatDuration(step.durationMs) }) : null
206
+ ] })
207
+ ] })
208
+ ] }),
209
+ step.redacted ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "eth-task-thinking-chain__redacted", children: "Details are hidden by policy." }) : step.summary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "eth-task-thinking-chain__summary", children: step.summary }) : null,
210
+ hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "eth-task-thinking-chain__children", children: step.children?.map((child) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThinkingStepItem, { step: child, streamingStepId }, child.id)) }) : null
211
+ ]
212
+ }
213
+ )
214
+ ]
215
+ }
216
+ );
217
+ }
218
+ function BackendThinkingChain({
219
+ steps = [],
220
+ streaming,
221
+ className,
222
+ ...props
223
+ }) {
224
+ const streamingStepId = React.useMemo(
225
+ () => streaming ? currentStreamingStepId(steps) : void 0,
226
+ [steps, streaming]
227
+ );
228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
229
+ "section",
230
+ {
231
+ ...props,
232
+ className: (0, import_clsx.default)("eth-task-thinking-chain", className),
233
+ "aria-live": streaming ? "polite" : void 0,
234
+ "data-eth-component": "BackendThinkingChain",
235
+ children: steps.length ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "eth-task-thinking-chain__list", children: steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThinkingStepItem, { step, streamingStepId }, step.id)) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core2.EmptyState, { title: "No backend steps" })
236
+ }
237
+ );
238
+ }
239
+
240
+ // src/components/BlockingReasonPanel.tsx
241
+ var import_clsx2 = __toESM(require("clsx"), 1);
242
+ var import_core3 = require("@echothink-ui/core");
243
+ var import_jsx_runtime2 = require("react/jsx-runtime");
244
+ function BlockingReasonPanel({
245
+ blockers = [],
246
+ className,
247
+ title = "Blocking reasons",
248
+ description,
249
+ severity,
250
+ status,
251
+ ...props
252
+ }) {
253
+ const hasBlockers = blockers.length > 0;
254
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
255
+ import_core3.Surface,
256
+ {
257
+ ...props,
258
+ className: (0, import_clsx2.default)("eth-task-blocking-reason-panel", className),
259
+ title,
260
+ description,
261
+ status: status ?? (hasBlockers ? "blocked" : void 0),
262
+ severity: severity ?? (hasBlockers ? "danger" : void 0),
263
+ "data-eth-component": "BlockingReasonPanel",
264
+ children: hasBlockers ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "eth-task-blocking-reason-panel__list", "aria-label": "Active blocking reasons", children: blockers.map((blocker) => {
265
+ const resolveActions = blocker.resolveAction ? [
266
+ {
267
+ ...blocker.resolveAction,
268
+ intent: blocker.resolveAction.intent ?? "tertiary"
269
+ }
270
+ ] : void 0;
271
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "eth-task-blocking-reason-panel__item", children: [
272
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "eth-task-blocking-reason-panel__content", children: [
273
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("strong", { className: "eth-task-blocking-reason-panel__reason", children: blocker.reason }),
274
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "eth-task-blocking-reason-panel__meta", children: [
275
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core3.Badge, { severity: "danger", children: "Blocked" }),
276
+ blocker.ownerActor ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
277
+ "Owner: ",
278
+ blocker.ownerActor
279
+ ] }) : null
280
+ ] })
281
+ ] }),
282
+ resolveActions ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "eth-task-blocking-reason-panel__actions", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core3.ActionGroup, { actions: resolveActions }) }) : null
283
+ ] }, blocker.id);
284
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
285
+ import_core3.EmptyState,
286
+ {
287
+ title: "No blocking reasons",
288
+ description: "This task can continue without unblock actions."
289
+ }
290
+ )
291
+ }
292
+ );
293
+ }
294
+
295
+ // src/components/DAGEdge.tsx
296
+ var React2 = __toESM(require("react"), 1);
297
+ var import_clsx3 = __toESM(require("clsx"), 1);
298
+ var import_jsx_runtime3 = require("react/jsx-runtime");
299
+ function markerIdFromUseId(id) {
300
+ return `eth-dag-arrow-${id.replace(/[^a-zA-Z0-9_-]/g, "")}`;
301
+ }
302
+ function DAGEdge({
303
+ from,
304
+ to,
305
+ status,
306
+ label,
307
+ className,
308
+ "aria-label": ariaLabel,
309
+ ...props
310
+ }) {
311
+ const reactId = React2.useId();
312
+ const markerId = markerIdFromUseId(reactId);
313
+ const horizontal = Math.abs(to.x - from.x) >= Math.abs(to.y - from.y);
314
+ const controlA = horizontal ? { x: (from.x + to.x) / 2, y: from.y } : { x: from.x, y: (from.y + to.y) / 2 };
315
+ const controlB = horizontal ? { x: (from.x + to.x) / 2, y: to.y } : { x: to.x, y: (from.y + to.y) / 2 };
316
+ const d = `M ${from.x} ${from.y} C ${controlA.x} ${controlA.y}, ${controlB.x} ${controlB.y}, ${to.x} ${to.y}`;
317
+ const color = statusColor(status);
318
+ const midpoint = { x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 };
319
+ const labelText = label?.trim();
320
+ const labelWidth = labelText ? Math.max(56, labelText.length * 7 + 20) : 0;
321
+ const labelPosition = horizontal ? { x: midpoint.x, y: midpoint.y - 16 } : { x: midpoint.x + 44, y: midpoint.y };
322
+ const statusText = status ? labelForStatus(status) : "Neutral";
323
+ const accessibleLabel = typeof ariaLabel === "string" && ariaLabel.trim() ? ariaLabel : labelText ? `${labelText} dependency edge, ${statusText}` : `Dependency edge, ${statusText}`;
324
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
325
+ "g",
326
+ {
327
+ className: (0, import_clsx3.default)("eth-dag-edge", status && `eth-dag-edge--${status}`, className),
328
+ role: "group",
329
+ "aria-label": accessibleLabel,
330
+ children: [
331
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
332
+ "marker",
333
+ {
334
+ id: markerId,
335
+ markerWidth: "10",
336
+ markerHeight: "10",
337
+ refX: "9",
338
+ refY: "3",
339
+ orient: "auto",
340
+ markerUnits: "strokeWidth",
341
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M0,0 L0,6 L9,3 z", fill: color })
342
+ }
343
+ ) }),
344
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
345
+ "path",
346
+ {
347
+ ...props,
348
+ d,
349
+ fill: "none",
350
+ stroke: color,
351
+ strokeLinecap: "round",
352
+ strokeLinejoin: "round",
353
+ strokeWidth: 2,
354
+ markerEnd: `url(#${markerId})`,
355
+ vectorEffect: "non-scaling-stroke",
356
+ "aria-hidden": "true",
357
+ "data-eth-component": "DAGEdge"
358
+ }
359
+ ),
360
+ labelText ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { className: "eth-dag-edge__label", "aria-hidden": "true", children: [
361
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
362
+ "rect",
363
+ {
364
+ x: labelPosition.x - labelWidth / 2,
365
+ y: labelPosition.y - 11,
366
+ width: labelWidth,
367
+ height: 22,
368
+ rx: 0
369
+ }
370
+ ),
371
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
372
+ "text",
373
+ {
374
+ x: labelPosition.x,
375
+ y: labelPosition.y,
376
+ textAnchor: "middle",
377
+ dominantBaseline: "middle",
378
+ children: labelText
379
+ }
380
+ )
381
+ ] }) : null
382
+ ]
383
+ }
384
+ );
385
+ }
386
+
387
+ // src/components/DAGLegend.tsx
388
+ var import_clsx4 = __toESM(require("clsx"), 1);
389
+ var import_jsx_runtime4 = require("react/jsx-runtime");
390
+ var defaultStatuses = [
391
+ "not-started",
392
+ "queued",
393
+ "running",
394
+ "blocked",
395
+ "failed",
396
+ "pending-approval",
397
+ "completed"
398
+ ];
399
+ var defaultEdgeTypes = [
400
+ { id: "dependency", label: "Dependency", variant: "dependency" },
401
+ { id: "critical-path", label: "Critical path", status: "running", variant: "critical" },
402
+ {
403
+ id: "approval-gate",
404
+ label: "Approval gate",
405
+ status: "pending-approval",
406
+ variant: "conditional"
407
+ }
408
+ ];
409
+ function colorVariable(color) {
410
+ return { "--eth-dag-legend-color": color };
411
+ }
412
+ function DAGLegend({
413
+ statuses = defaultStatuses,
414
+ edgeTypes = defaultEdgeTypes,
415
+ className,
416
+ "aria-label": ariaLabel,
417
+ ...props
418
+ }) {
419
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
420
+ "div",
421
+ {
422
+ ...props,
423
+ className: (0, import_clsx4.default)("eth-dag-legend", className),
424
+ "data-eth-component": "DAGLegend",
425
+ role: "group",
426
+ "aria-label": ariaLabel ?? "DAG legend",
427
+ children: [
428
+ statuses.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "eth-dag-legend__group", children: [
429
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "eth-dag-legend__heading", children: "Node status" }),
430
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "eth-dag-legend__list", "aria-label": "DAG node status legend", children: statuses.map((status) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "eth-dag-legend__item", children: [
431
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
432
+ "span",
433
+ {
434
+ className: (0, import_clsx4.default)("eth-dag-legend__swatch", `eth-dag-legend__swatch--${status}`),
435
+ style: colorVariable(statusColor(status)),
436
+ "aria-hidden": "true"
437
+ }
438
+ ),
439
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "eth-dag-legend__label", children: labelForStatus(status) })
440
+ ] }, status)) })
441
+ ] }) : null,
442
+ edgeTypes.length ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "eth-dag-legend__group eth-dag-legend__group--edges", children: [
443
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "eth-dag-legend__heading", children: "Edge type" }),
444
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "eth-dag-legend__list", "aria-label": "DAG edge type legend", children: edgeTypes.map((edgeType) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("li", { className: "eth-dag-legend__item", children: [
445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
446
+ "span",
447
+ {
448
+ className: (0, import_clsx4.default)(
449
+ "eth-dag-legend__edge",
450
+ `eth-dag-legend__edge--${edgeType.variant ?? "dependency"}`
451
+ ),
452
+ style: colorVariable(statusColor(edgeType.status)),
453
+ "aria-hidden": "true"
454
+ }
455
+ ),
456
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "eth-dag-legend__label", children: edgeType.label })
457
+ ] }, edgeType.id)) })
458
+ ] }) : null
459
+ ]
460
+ }
461
+ );
462
+ }
463
+
464
+ // src/components/DAGNode.tsx
465
+ var import_clsx5 = __toESM(require("clsx"), 1);
466
+ var import_jsx_runtime5 = require("react/jsx-runtime");
467
+ var nodeWidth = 156;
468
+ var nodeHeight = 60;
469
+ function truncateLabel(label) {
470
+ return label.length > 20 ? `${label.slice(0, 19)}...` : label;
471
+ }
472
+ function DAGNode({ node, selected, onSelect, className, ...props }) {
473
+ const statusLabel2 = labelForStatus(node.status);
474
+ const x = node.x - nodeWidth / 2;
475
+ const y = node.y - nodeHeight / 2;
476
+ const handleSelect = () => onSelect?.(node);
477
+ const interactive = Boolean(onSelect);
478
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
479
+ "g",
480
+ {
481
+ ...props,
482
+ className: (0, import_clsx5.default)(
483
+ "eth-dag-node",
484
+ `eth-dag-node--${node.status}`,
485
+ interactive && "eth-dag-node--interactive",
486
+ selected && "eth-dag-node--selected",
487
+ className
488
+ ),
489
+ role: interactive ? "button" : "group",
490
+ tabIndex: interactive ? 0 : void 0,
491
+ "aria-label": `${node.label}, ${statusLabel2}`,
492
+ "aria-pressed": interactive ? Boolean(selected) : void 0,
493
+ onClick: interactive ? handleSelect : void 0,
494
+ onKeyDown: interactive ? (event) => {
495
+ if (event.key === "Enter" || event.key === " ") {
496
+ event.preventDefault();
497
+ handleSelect();
498
+ }
499
+ } : void 0,
500
+ "data-eth-component": "DAGNode",
501
+ children: [
502
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("title", { children: `${node.label} - ${statusLabel2}` }),
503
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
504
+ "rect",
505
+ {
506
+ className: "eth-dag-node__selection",
507
+ x: x - 3,
508
+ y: y - 3,
509
+ width: nodeWidth + 6,
510
+ height: nodeHeight + 6,
511
+ rx: 0
512
+ }
513
+ ),
514
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
515
+ "rect",
516
+ {
517
+ className: "eth-dag-node__body",
518
+ x,
519
+ y,
520
+ width: nodeWidth,
521
+ height: nodeHeight,
522
+ rx: 0,
523
+ fill: "var(--eth-color-layer-01)"
524
+ }
525
+ ),
526
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
527
+ "rect",
528
+ {
529
+ className: "eth-dag-node__status-strip",
530
+ x,
531
+ y,
532
+ width: 4,
533
+ height: nodeHeight,
534
+ fill: statusColor(node.status)
535
+ }
536
+ ),
537
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "eth-dag-node__port", cx: x, cy: node.y, r: 3 }),
538
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "eth-dag-node__port", cx: x + nodeWidth, cy: node.y, r: 3 }),
539
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
540
+ "circle",
541
+ {
542
+ className: "eth-dag-node__status-dot",
543
+ cx: x + 18,
544
+ cy: y + 18,
545
+ r: 3.5,
546
+ fill: statusColor(node.status)
547
+ }
548
+ ),
549
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
550
+ "text",
551
+ {
552
+ x: x + 28,
553
+ y: y + 22,
554
+ textAnchor: "start",
555
+ className: "eth-dag-node__label",
556
+ children: truncateLabel(node.label)
557
+ }
558
+ ),
559
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
560
+ "text",
561
+ {
562
+ x: x + 28,
563
+ y: y + 44,
564
+ textAnchor: "start",
565
+ className: "eth-dag-node__status",
566
+ children: statusLabel2
567
+ }
568
+ )
569
+ ]
570
+ }
571
+ );
572
+ }
573
+ var DAG_NODE_WIDTH = nodeWidth;
574
+ var DAG_NODE_HEIGHT = nodeHeight;
575
+
576
+ // src/components/DecisionRequiredPanel.tsx
577
+ var React3 = __toESM(require("react"), 1);
578
+ var import_clsx6 = __toESM(require("clsx"), 1);
579
+ var import_core4 = require("@echothink-ui/core");
580
+ var import_jsx_runtime6 = require("react/jsx-runtime");
581
+ function optionsFromActions(actions) {
582
+ return actions?.map((action) => ({
583
+ id: action.id,
584
+ label: action.label,
585
+ intent: action.intent,
586
+ action
587
+ })) ?? [];
588
+ }
589
+ function riskTitle(riskLevel) {
590
+ if (!riskLevel) return "Decision required";
591
+ return `${riskLevel.charAt(0).toUpperCase()}${riskLevel.slice(1)} risk decision`;
592
+ }
593
+ function optionIntent(option) {
594
+ return option.intent ?? "secondary";
595
+ }
596
+ function DecisionRequiredPanel({
597
+ title,
598
+ summary,
599
+ riskLevel,
600
+ evidence = [],
601
+ options,
602
+ onDecide,
603
+ decidedOptionId,
604
+ className,
605
+ description,
606
+ actions,
607
+ items,
608
+ severity,
609
+ status,
610
+ ...props
611
+ }) {
612
+ const titleId = React3.useId();
613
+ const evidenceId = React3.useId();
614
+ const optionsId = React3.useId();
615
+ const resolvedOptions = options?.map((option) => ({ ...option })) ?? (actions?.length ? optionsFromActions(actions) : items?.length ? items.map((item) => ({
616
+ id: item.id,
617
+ label: String(item.label),
618
+ description: item.description ? String(item.description) : void 0,
619
+ intent: item.status === "active" ? "primary" : "secondary"
620
+ })) : []);
621
+ const riskSeverity = riskLevel ? severityForRisk(riskLevel) : severity ?? "warning";
622
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
623
+ import_core4.Surface,
624
+ {
625
+ ...props,
626
+ role: "region",
627
+ "aria-labelledby": titleId,
628
+ className: (0, import_clsx6.default)(
629
+ "eth-task-decision-panel",
630
+ riskLevel && `eth-task-decision-panel--${riskLevel}`,
631
+ className
632
+ ),
633
+ title: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { id: titleId, children: title }),
634
+ severity: riskSeverity === "neutral" ? severity : riskSeverity,
635
+ status: status ?? "approval-required",
636
+ "data-eth-component": "DecisionRequiredPanel",
637
+ children: [
638
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_core4.InlineNotification, { severity: riskSeverity, title: riskTitle(riskLevel), children: description }),
639
+ summary ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "eth-task-decision-panel__summary", "aria-label": "Decision context", children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "eth-task-decision-panel__section-label", children: "Decision context" }),
641
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "eth-task-decision-panel__summary-body", children: summary })
642
+ ] }) : null,
643
+ evidence.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("section", { className: "eth-task-decision-panel__evidence", "aria-labelledby": evidenceId, children: [
644
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "eth-task-decision-panel__section-header", children: [
645
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { id: evidenceId, children: "Evidence" }),
646
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
647
+ evidence.length,
648
+ " source",
649
+ evidence.length === 1 ? "" : "s"
650
+ ] })
651
+ ] }),
652
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("ul", { children: evidence.map((item) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("li", { children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "eth-task-decision-panel__evidence-label", children: item.href ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { className: "eth-task-decision-panel__evidence-link", href: item.href, children: item.label }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: item.label }) }),
654
+ item.preview ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "eth-task-decision-panel__evidence-preview", children: item.preview }) : null
655
+ ] }, item.id)) })
656
+ ] }) : null,
657
+ resolvedOptions.length ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
658
+ "section",
659
+ {
660
+ className: "eth-task-decision-panel__options",
661
+ role: "group",
662
+ "aria-labelledby": optionsId,
663
+ children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "eth-task-decision-panel__section-header", children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { id: optionsId, children: "Decision options" }),
666
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { children: [
667
+ resolvedOptions.length,
668
+ " available"
669
+ ] })
670
+ ] }),
671
+ resolvedOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
672
+ "button",
673
+ {
674
+ type: "button",
675
+ className: (0, import_clsx6.default)(
676
+ "eth-task-decision-panel__option",
677
+ `eth-task-decision-panel__option--${optionIntent(option)}`
678
+ ),
679
+ disabled: option.action?.disabled,
680
+ "aria-pressed": decidedOptionId === option.id,
681
+ onClick: () => {
682
+ option.action?.onSelect?.();
683
+ onDecide?.(option.id);
684
+ },
685
+ children: [
686
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "eth-task-decision-panel__option-main", children: [
687
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "eth-task-decision-panel__option-label", children: option.label }),
688
+ option.description ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "eth-task-decision-panel__option-description", children: option.description }) : null
689
+ ] }),
690
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "eth-task-decision-panel__option-state", children: decidedOptionId === option.id ? "Selected" : "Choose" })
691
+ ]
692
+ },
693
+ option.id
694
+ ))
695
+ ]
696
+ }
697
+ ) : null,
698
+ decidedOptionId ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "eth-task-decision-panel__decision", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_core4.Tag, { children: [
699
+ "Decided: ",
700
+ decidedOptionId
701
+ ] }) }) : null
702
+ ]
703
+ }
704
+ );
705
+ }
706
+
707
+ // src/components/HumanInterventionPanel.tsx
708
+ var React4 = __toESM(require("react"), 1);
709
+ var import_clsx7 = __toESM(require("clsx"), 1);
710
+ var import_core5 = require("@echothink-ui/core");
711
+ var import_jsx_runtime7 = require("react/jsx-runtime");
712
+ function urgencySeverity(urgency) {
713
+ if (urgency === "high") return "danger";
714
+ if (urgency === "medium") return "warning";
715
+ return "info";
716
+ }
717
+ function urgencyLabel(urgency) {
718
+ if (urgency === "high") return "High urgency";
719
+ if (urgency === "medium") return "Medium urgency";
720
+ return "Low urgency";
721
+ }
722
+ function HumanInterventionPanel({
723
+ reason,
724
+ affectedTask,
725
+ actions,
726
+ urgency = "medium",
727
+ className,
728
+ title = "Human intervention required",
729
+ status = "approval-required",
730
+ severity,
731
+ role = "region",
732
+ "aria-labelledby": ariaLabelledBy,
733
+ ...props
734
+ }) {
735
+ const titleId = React4.useId();
736
+ const panelSeverity = severity ?? urgencySeverity(urgency);
737
+ const labelledBy = ariaLabelledBy ?? (title ? titleId : void 0);
738
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
739
+ import_core5.Surface,
740
+ {
741
+ ...props,
742
+ role,
743
+ "aria-labelledby": labelledBy,
744
+ className: (0, import_clsx7.default)("eth-task-human-intervention-panel", className),
745
+ title: title ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { id: titleId, children: title }) : void 0,
746
+ status,
747
+ severity: panelSeverity,
748
+ "data-eth-component": "HumanInterventionPanel",
749
+ children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core5.InlineNotification, { severity: panelSeverity, title: "Intervention requested", children: reason ?? "Review this task and choose the next action before the agent can continue." }),
751
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dl", { className: "eth-task-human-intervention-panel__details", "aria-label": "Intervention details", children: [
752
+ affectedTask ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
753
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: "Affected task" }),
754
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("dd", { children: [
755
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "eth-task-human-intervention-panel__task-title", children: affectedTask.title }),
756
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "eth-task-human-intervention-panel__task-id", children: affectedTask.id })
757
+ ] })
758
+ ] }) : null,
759
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
760
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dt", { children: "Urgency" }),
761
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("dd", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core5.Badge, { severity: panelSeverity, children: urgencyLabel(urgency) }) })
762
+ ] })
763
+ ] }),
764
+ actions?.length ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
765
+ "div",
766
+ {
767
+ className: "eth-task-human-intervention-panel__actions",
768
+ role: "group",
769
+ "aria-label": "Intervention actions",
770
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_core5.ActionGroup, { actions })
771
+ }
772
+ ) : null
773
+ ]
774
+ }
775
+ );
776
+ }
777
+
778
+ // src/components/TaskApprovalPanel.tsx
779
+ var React5 = __toESM(require("react"), 1);
780
+ var import_core6 = require("@echothink-ui/core");
781
+ var import_jsx_runtime8 = require("react/jsx-runtime");
782
+ function refEvidence(taskRef, diffRef, policyRef, evidence = []) {
783
+ const refs = [];
784
+ if (taskRef) refs.push({ id: "task-ref", label: "Task reference", preview: taskRef });
785
+ if (diffRef) refs.push({ id: "diff-ref", label: "Diff reference", preview: diffRef });
786
+ if (policyRef) refs.push({ id: "policy-ref", label: "Policy reference", preview: policyRef });
787
+ return [...refs, ...evidence];
788
+ }
789
+ function stateNotification(state) {
790
+ if (state === "approved") {
791
+ return {
792
+ severity: "success",
793
+ title: "Output approved",
794
+ description: "The approval has been recorded with the current audit metadata."
795
+ };
796
+ }
797
+ if (state === "rejected") {
798
+ return {
799
+ severity: "danger",
800
+ title: "Output rejected",
801
+ description: "The rejection reason and evidence references are retained for review."
802
+ };
803
+ }
804
+ if (state === "pending") {
805
+ return {
806
+ severity: "info",
807
+ title: "Decision pending",
808
+ description: "The approval decision is being prepared for audit capture."
809
+ };
810
+ }
811
+ return null;
812
+ }
813
+ function TaskApprovalPanel({
814
+ taskRef,
815
+ diffRef,
816
+ policyRef,
817
+ summary,
818
+ evidence,
819
+ onApprove,
820
+ onReject,
821
+ onRequestChanges,
822
+ state = "idle",
823
+ title = "Task approval required",
824
+ description,
825
+ footer,
826
+ ...props
827
+ }) {
828
+ const [reason, setReason] = React5.useState("");
829
+ const reasonId = React5.useId();
830
+ const notification = stateNotification(state);
831
+ const policy = policyRef ?? "Default task policy";
832
+ const isPending = state === "pending";
833
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("section", { className: "eth-task-approval-panel", "data-eth-component": "TaskApprovalPanel", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
834
+ DecisionRequiredPanel,
835
+ {
836
+ ...props,
837
+ title,
838
+ summary: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "eth-task-approval-panel__body", children: [
839
+ summary ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "eth-task-approval-panel__summary", children: summary }) : null,
840
+ notification ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
841
+ import_core6.InlineNotification,
842
+ {
843
+ className: "eth-task-approval-panel__state",
844
+ severity: notification.severity,
845
+ title: notification.title,
846
+ children: notification.description
847
+ }
848
+ ) : null,
849
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
850
+ import_core6.FormField,
851
+ {
852
+ id: reasonId,
853
+ label: "Decision reason",
854
+ helperText: "Stored with approval audit metadata.",
855
+ className: "eth-task-approval-panel__reason",
856
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
857
+ import_core6.Textarea,
858
+ {
859
+ value: reason,
860
+ rows: 4,
861
+ placeholder: "Summarize the approval rationale, rejection issue, or requested change.",
862
+ onChange: (event) => setReason(event.currentTarget.value)
863
+ }
864
+ )
865
+ }
866
+ ),
867
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("dl", { className: "eth-task-approval-panel__audit", children: [
868
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
869
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: "Task" }),
870
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: taskRef ?? "-" })
871
+ ] }),
872
+ diffRef ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: "Diff" }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: diffRef })
875
+ ] }) : null,
876
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dt", { children: "Policy" }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("dd", { children: policy })
879
+ ] })
880
+ ] })
881
+ ] }),
882
+ description: description ?? "Review the task output and choose an approval outcome.",
883
+ riskLevel: "high",
884
+ evidence: refEvidence(taskRef, diffRef, policyRef, evidence),
885
+ options: [],
886
+ footer: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "eth-task-approval-panel__footer", children: [
887
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
888
+ "div",
889
+ {
890
+ className: "eth-task-approval-panel__actions",
891
+ role: "group",
892
+ "aria-label": "Approval actions",
893
+ children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
895
+ import_core6.Button,
896
+ {
897
+ intent: "success",
898
+ disabled: isPending || !onApprove,
899
+ "aria-pressed": state === "approved",
900
+ onClick: () => onApprove?.(reason),
901
+ children: "Approve"
902
+ }
903
+ ),
904
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
905
+ import_core6.Button,
906
+ {
907
+ intent: "danger",
908
+ disabled: isPending || !onReject,
909
+ "aria-pressed": state === "rejected",
910
+ onClick: () => onReject?.(reason),
911
+ children: "Reject"
912
+ }
913
+ ),
914
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
915
+ import_core6.Button,
916
+ {
917
+ intent: "secondary",
918
+ disabled: isPending || !onRequestChanges,
919
+ onClick: () => onRequestChanges?.(reason),
920
+ children: "Request changes"
921
+ }
922
+ )
923
+ ]
924
+ }
925
+ ),
926
+ footer ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "eth-task-approval-panel__custom-footer", children: footer }) : null
927
+ ] })
928
+ }
929
+ ) });
930
+ }
931
+
932
+ // src/components/TaskCard.tsx
933
+ var import_clsx8 = __toESM(require("clsx"), 1);
934
+ var import_core7 = require("@echothink-ui/core");
935
+ var import_jsx_runtime9 = require("react/jsx-runtime");
936
+ function TaskCard({ task, actions, onSelect, className, ...props }) {
937
+ const cardActions = actions?.map((action) => ({
938
+ ...action,
939
+ intent: action.intent ?? "tertiary"
940
+ }));
941
+ const interactiveProps = onSelect ? {
942
+ role: "button",
943
+ tabIndex: 0,
944
+ onClick: () => onSelect(task),
945
+ onKeyDown: (event) => {
946
+ if (event.key === "Enter" || event.key === " ") {
947
+ event.preventDefault();
948
+ onSelect(task);
949
+ }
950
+ }
951
+ } : {};
952
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
953
+ import_core7.Surface,
954
+ {
955
+ ...props,
956
+ ...interactiveProps,
957
+ className: (0, import_clsx8.default)("eth-task-card", onSelect && "eth-task-card--selectable", className),
958
+ density: "compact",
959
+ title: task.title,
960
+ subtitle: task.id,
961
+ description: task.description,
962
+ status: task.status,
963
+ actions: cardActions,
964
+ metadata: [
965
+ {
966
+ label: "Assignee",
967
+ value: task.assignee ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "eth-task-card__assignee", children: task.assignee }) : "-"
968
+ },
969
+ {
970
+ label: "Due",
971
+ value: formatDateTime(task.dueAt)
972
+ },
973
+ {
974
+ label: "Priority",
975
+ value: task.priority ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_core7.Badge, { severity: severityForPriority(task.priority), children: task.priority }) : "-"
976
+ }
977
+ ],
978
+ "data-eth-component": "TaskCard",
979
+ children: task.tags?.length ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "eth-task-card__tags", "aria-label": "Task tags", children: task.tags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_core7.Tag, { children: tag }, tag)) }) : null
980
+ }
981
+ );
982
+ }
983
+
984
+ // src/components/TaskDependencyList.tsx
985
+ var React6 = __toESM(require("react"), 1);
986
+ var import_clsx10 = __toESM(require("clsx"), 1);
987
+ var import_core9 = require("@echothink-ui/core");
988
+
989
+ // src/components/TaskStatusBadge.tsx
990
+ var import_clsx9 = __toESM(require("clsx"), 1);
991
+ var import_core8 = require("@echothink-ui/core");
992
+ var import_jsx_runtime10 = require("react/jsx-runtime");
993
+ function TaskStatusBadge({
994
+ status,
995
+ label,
996
+ className,
997
+ title,
998
+ ...props
999
+ }) {
1000
+ const statusLabel2 = label ?? labelForStatus(status);
1001
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1002
+ import_core8.Badge,
1003
+ {
1004
+ ...props,
1005
+ className: (0, import_clsx9.default)("eth-task-status-badge", `eth-task-status-badge--${status}`, className),
1006
+ "data-eth-component": "TaskStatusBadge",
1007
+ "data-status": status,
1008
+ severity: severityForStatus(status),
1009
+ title: title ?? statusLabel2,
1010
+ children: [
1011
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "eth-task-status-badge__indicator", "aria-hidden": "true" }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "eth-task-status-badge__label", children: statusLabel2 })
1013
+ ]
1014
+ }
1015
+ );
1016
+ }
1017
+
1018
+ // src/components/TaskDependencyList.tsx
1019
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1020
+ var groupDescriptions = {
1021
+ "blocked-by": "Prerequisite work that must clear before this task can move forward.",
1022
+ blocks: "Downstream work waiting on this task."
1023
+ };
1024
+ function taskCountLabel(count) {
1025
+ return `${count} task${count === 1 ? "" : "s"}`;
1026
+ }
1027
+ function relationshipLabel(relationship) {
1028
+ return relationship === "blocks" ? "Dependent" : "Prerequisite";
1029
+ }
1030
+ function DependencyGroup({
1031
+ title,
1032
+ relationship,
1033
+ dependencies
1034
+ }) {
1035
+ const headingId = React6.useId();
1036
+ if (!dependencies.length) return null;
1037
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("section", { className: "eth-task-dependencies__group", "aria-labelledby": headingId, children: [
1038
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "eth-task-dependencies__group-header", children: [
1039
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "eth-task-dependencies__group-heading", children: [
1040
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h4", { id: headingId, children: title }),
1041
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { children: groupDescriptions[relationship] })
1042
+ ] }),
1043
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_core9.Badge, { children: taskCountLabel(dependencies.length) })
1044
+ ] }),
1045
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ul", { className: "eth-task-dependencies__list", children: dependencies.map((dependency) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1046
+ "li",
1047
+ {
1048
+ className: (0, import_clsx10.default)(
1049
+ "eth-task-dependencies__item",
1050
+ `eth-task-dependencies__item--${dependency.status}`
1051
+ ),
1052
+ "data-status": dependency.status,
1053
+ children: [
1054
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "eth-task-dependencies__item-main", children: [
1055
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "eth-task-dependencies__relationship", children: relationshipLabel(relationship) }),
1056
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "eth-task-dependencies__title", children: dependency.title }),
1057
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "eth-task-dependencies__id", children: dependency.id })
1058
+ ] }),
1059
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(TaskStatusBadge, { status: dependency.status })
1060
+ ]
1061
+ },
1062
+ dependency.id
1063
+ )) })
1064
+ ] });
1065
+ }
1066
+ function TaskDependencyList({
1067
+ dependencies = [],
1068
+ blocks = [],
1069
+ blockedBy = [],
1070
+ emptyTitle = "No dependencies",
1071
+ className,
1072
+ ...props
1073
+ }) {
1074
+ const derivedBlocks = dependencies.filter((dependency) => dependency.relationship === "blocks");
1075
+ const derivedBlockedBy = dependencies.filter(
1076
+ (dependency) => dependency.relationship !== "blocks"
1077
+ );
1078
+ const blockList = blocks.length ? blocks : derivedBlocks;
1079
+ const blockedByList = blockedBy.length ? blockedBy : derivedBlockedBy;
1080
+ const hasDependencies = blockList.length || blockedByList.length;
1081
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1082
+ "div",
1083
+ {
1084
+ ...props,
1085
+ className: (0, import_clsx10.default)("eth-task-dependencies", className),
1086
+ "data-eth-component": "TaskDependencyList",
1087
+ children: hasDependencies ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
1088
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1089
+ DependencyGroup,
1090
+ {
1091
+ title: "Blocked by",
1092
+ relationship: "blocked-by",
1093
+ dependencies: blockedByList
1094
+ }
1095
+ ),
1096
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(DependencyGroup, { title: "Blocks", relationship: "blocks", dependencies: blockList })
1097
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_core9.EmptyState, { title: emptyTitle })
1098
+ }
1099
+ );
1100
+ }
1101
+
1102
+ // src/components/TaskDetailPanel.tsx
1103
+ var import_clsx12 = __toESM(require("clsx"), 1);
1104
+ var import_core11 = require("@echothink-ui/core");
1105
+
1106
+ // src/components/TaskTimeline.tsx
1107
+ var import_clsx11 = __toESM(require("clsx"), 1);
1108
+ var import_core10 = require("@echothink-ui/core");
1109
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1110
+ var eventKindMeta = {
1111
+ status_change: { label: "Status", tone: "info" },
1112
+ comment: { label: "Comment", tone: "neutral" },
1113
+ assignment: { label: "Assignment", tone: "info" },
1114
+ attachment: { label: "Attachment", tone: "success" },
1115
+ system: { label: "System", tone: "neutral" }
1116
+ };
1117
+ function timestampMeta(value) {
1118
+ const date = value instanceof Date ? value : new Date(value);
1119
+ if (Number.isNaN(date.valueOf())) {
1120
+ const fallback = String(value);
1121
+ return { dateTime: fallback, label: fallback };
1122
+ }
1123
+ return {
1124
+ dateTime: date.toISOString(),
1125
+ label: formatDateTime(date)
1126
+ };
1127
+ }
1128
+ function TaskTimeline({
1129
+ events = [],
1130
+ className,
1131
+ role,
1132
+ "aria-label": ariaLabel,
1133
+ ...props
1134
+ }) {
1135
+ if (!events.length) {
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1137
+ "div",
1138
+ {
1139
+ ...props,
1140
+ className: (0, import_clsx11.default)("eth-task-timeline", className),
1141
+ "data-eth-component": "TaskTimeline",
1142
+ role: role ?? "region",
1143
+ "aria-label": ariaLabel ?? "Task timeline",
1144
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1145
+ import_core10.EmptyState,
1146
+ {
1147
+ title: "No task history",
1148
+ description: "Task activity and state changes appear here."
1149
+ }
1150
+ )
1151
+ }
1152
+ );
1153
+ }
1154
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1155
+ "div",
1156
+ {
1157
+ ...props,
1158
+ className: (0, import_clsx11.default)("eth-task-timeline", className),
1159
+ "data-eth-component": "TaskTimeline",
1160
+ role: role ?? "region",
1161
+ "aria-label": ariaLabel ?? "Task timeline",
1162
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("ol", { className: "eth-task-timeline__list", children: events.map((event) => {
1163
+ const kind = eventKindMeta[event.kind];
1164
+ const time = timestampMeta(event.timestamp);
1165
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1166
+ "li",
1167
+ {
1168
+ className: (0, import_clsx11.default)(
1169
+ "eth-task-timeline__event",
1170
+ `eth-task-timeline__event--${event.kind}`
1171
+ ),
1172
+ "data-kind": event.kind,
1173
+ children: [
1174
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("time", { className: "eth-task-timeline__time", dateTime: time.dateTime, children: time.label }),
1175
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "eth-task-timeline__rail", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "eth-task-timeline__marker" }) }),
1176
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("article", { className: "eth-task-timeline__body", children: [
1177
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("header", { className: "eth-task-timeline__event-header", children: [
1178
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_core10.Badge, { severity: kind.tone, children: kind.label }),
1179
+ event.actor ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "eth-task-timeline__actor", children: [
1180
+ "By ",
1181
+ event.actor
1182
+ ] }) : null
1183
+ ] }),
1184
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("strong", { className: "eth-task-timeline__summary", children: event.summary }),
1185
+ event.details ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "eth-task-timeline__details", children: event.details }) : null
1186
+ ] })
1187
+ ]
1188
+ },
1189
+ event.id
1190
+ );
1191
+ }) })
1192
+ }
1193
+ );
1194
+ }
1195
+
1196
+ // src/components/TaskDetailPanel.tsx
1197
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1198
+ function formatCount(count, singular, plural = `${singular}s`) {
1199
+ return `${count} ${count === 1 ? singular : plural}`;
1200
+ }
1201
+ function DetailSection({
1202
+ title,
1203
+ meta,
1204
+ open,
1205
+ children,
1206
+ className
1207
+ }) {
1208
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("details", { open, className: (0, import_clsx12.default)("eth-task-detail-panel__section", className), children: [
1209
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("summary", { className: "eth-task-detail-panel__summary", children: [
1210
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "eth-task-detail-panel__summary-label", children: title }),
1211
+ meta ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "eth-task-detail-panel__summary-meta", children: meta }) : null
1212
+ ] }),
1213
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "eth-task-detail-panel__section-body", children })
1214
+ ] });
1215
+ }
1216
+ function TaskDetailPanel({
1217
+ task,
1218
+ dependencies,
1219
+ events,
1220
+ actions,
1221
+ className,
1222
+ title,
1223
+ description,
1224
+ items,
1225
+ ...props
1226
+ }) {
1227
+ if (!task) {
1228
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1229
+ import_core11.Surface,
1230
+ {
1231
+ ...props,
1232
+ className: (0, import_clsx12.default)("eth-task-detail-panel", className),
1233
+ title: title ?? "Task detail",
1234
+ description,
1235
+ items,
1236
+ actions,
1237
+ "data-eth-component": "TaskDetailPanel",
1238
+ children: !items?.length ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_core11.EmptyState, { title: "No task selected" }) : null
1239
+ }
1240
+ );
1241
+ }
1242
+ const resolvedDependencies = dependencies ?? task.dependencies ?? [];
1243
+ const resolvedEvents = events ?? task.history ?? [];
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1245
+ import_core11.Surface,
1246
+ {
1247
+ ...props,
1248
+ className: (0, import_clsx12.default)("eth-task-detail-panel", className),
1249
+ title: title ?? task.title,
1250
+ subtitle: task.id,
1251
+ status: task.status,
1252
+ "data-eth-component": "TaskDetailPanel",
1253
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "eth-task-detail-panel__stack", children: [
1254
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DetailSection, { title: "Metadata", open: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("dl", { className: "eth-meta-grid eth-task-detail-panel__metadata", children: [
1255
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1256
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dt", { children: "Status" }),
1257
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dd", { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TaskStatusBadge, { status: task.status }) })
1258
+ ] }),
1259
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1260
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dt", { children: "Assignee" }),
1261
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dd", { children: task.assignee ?? "-" })
1262
+ ] }),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1264
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dt", { children: "Due" }),
1265
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dd", { children: formatDateTime(task.dueAt) })
1266
+ ] }),
1267
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1268
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dt", { children: "Priority" }),
1269
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("dd", { children: task.priority ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_core11.Badge, { severity: severityForPriority(task.priority), children: task.priority }) : "-" })
1270
+ ] })
1271
+ ] }) }),
1272
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DetailSection, { title: "Description", open: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "eth-task-detail-panel__description", children: task.description ?? description ?? "No description provided." }) }),
1273
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1274
+ DetailSection,
1275
+ {
1276
+ title: "Dependencies",
1277
+ meta: formatCount(resolvedDependencies.length, "dependency", "dependencies"),
1278
+ open: resolvedDependencies.length > 0,
1279
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TaskDependencyList, { dependencies: resolvedDependencies })
1280
+ }
1281
+ ),
1282
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1283
+ DetailSection,
1284
+ {
1285
+ title: "History",
1286
+ meta: formatCount(resolvedEvents.length, "event"),
1287
+ open: resolvedEvents.length > 0,
1288
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(TaskTimeline, { events: resolvedEvents })
1289
+ }
1290
+ ),
1291
+ actions?.length ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(DetailSection, { title: "Actions", meta: formatCount(actions.length, "action"), open: true, children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "eth-task-detail-panel__actions", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_core11.ActionGroup, { actions }) }) }) : null
1292
+ ] })
1293
+ }
1294
+ );
1295
+ }
1296
+
1297
+ // src/components/TaskHandoffPanel.tsx
1298
+ var React7 = __toESM(require("react"), 1);
1299
+ var import_clsx13 = __toESM(require("clsx"), 1);
1300
+ var import_core12 = require("@echothink-ui/core");
1301
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1302
+ function TaskHandoffPanel({
1303
+ assigneeOptions = [],
1304
+ currentAssigneeId,
1305
+ onHandoff,
1306
+ reasonRequired,
1307
+ className,
1308
+ title = "Task handoff",
1309
+ description,
1310
+ ...props
1311
+ }) {
1312
+ const [selectedId, setSelectedId] = React7.useState(
1313
+ currentAssigneeId ?? assigneeOptions[0]?.id ?? ""
1314
+ );
1315
+ const [reason, setReason] = React7.useState("");
1316
+ const targetId = React7.useId();
1317
+ const reasonId = React7.useId();
1318
+ const trimmedReason = reason.trim();
1319
+ const cannotSubmit = !selectedId || reasonRequired && !trimmedReason;
1320
+ const submitHandoff = (event) => {
1321
+ event?.preventDefault();
1322
+ if (cannotSubmit) return;
1323
+ onHandoff?.(selectedId, trimmedReason);
1324
+ };
1325
+ const handleReasonKeyDown = (event) => {
1326
+ if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
1327
+ event.preventDefault();
1328
+ submitHandoff();
1329
+ }
1330
+ };
1331
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1332
+ import_core12.Surface,
1333
+ {
1334
+ ...props,
1335
+ className: (0, import_clsx13.default)("eth-task-handoff-panel", className),
1336
+ title,
1337
+ description,
1338
+ "data-eth-component": "TaskHandoffPanel",
1339
+ children: assigneeOptions.length ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("form", { className: "eth-task-handoff-panel__body", onSubmit: submitHandoff, children: [
1340
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "eth-task-handoff-panel__target-field", children: [
1341
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { id: targetId, className: "eth-task-handoff-panel__target-heading", children: "Handoff target" }),
1342
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1343
+ "div",
1344
+ {
1345
+ className: "eth-task-handoff-panel__list",
1346
+ role: "group",
1347
+ "aria-labelledby": targetId,
1348
+ children: assigneeOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1349
+ "button",
1350
+ {
1351
+ type: "button",
1352
+ "aria-label": `Hand off to ${option.label} (${formatAssigneeKind(
1353
+ option.kind
1354
+ )})`,
1355
+ "aria-pressed": selectedId === option.id,
1356
+ className: (0, import_clsx13.default)(
1357
+ "eth-task-handoff-panel__option",
1358
+ selectedId === option.id && "eth-task-handoff-panel__option--selected"
1359
+ ),
1360
+ onClick: () => setSelectedId(option.id),
1361
+ children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "eth-task-handoff-panel__option-copy", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "eth-task-handoff-panel__option-label", children: option.label }) }),
1363
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "eth-task-handoff-panel__option-meta", children: [
1364
+ selectedId === option.id ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core12.Badge, { severity: "info", children: "Selected" }) : null,
1365
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core12.Badge, { severity: "neutral", children: formatAssigneeKind(option.kind) })
1366
+ ] })
1367
+ ]
1368
+ },
1369
+ option.id
1370
+ ))
1371
+ }
1372
+ )
1373
+ ] }),
1374
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1375
+ import_core12.FormField,
1376
+ {
1377
+ id: reasonId,
1378
+ label: "Handoff reason",
1379
+ required: reasonRequired,
1380
+ className: "eth-task-handoff-panel__reason-field",
1381
+ helperText: reasonRequired ? "A reason is required for this handoff." : void 0,
1382
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1383
+ import_core12.Textarea,
1384
+ {
1385
+ className: "eth-task-handoff-panel__reason",
1386
+ name: "handoff-reason",
1387
+ autoComplete: "off",
1388
+ placeholder: "Summarize context, blockers, and the next action...",
1389
+ value: reason,
1390
+ rows: 3,
1391
+ onKeyDown: handleReasonKeyDown,
1392
+ onChange: (event) => setReason(event.currentTarget.value)
1393
+ }
1394
+ )
1395
+ }
1396
+ ),
1397
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "eth-task-handoff-panel__actions", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core12.Button, { type: "submit", disabled: cannotSubmit, children: "Handoff" }) })
1398
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core12.EmptyState, { title: "No assignees available" })
1399
+ }
1400
+ );
1401
+ }
1402
+ function formatAssigneeKind(kind) {
1403
+ return kind.charAt(0).toUpperCase() + kind.slice(1);
1404
+ }
1405
+
1406
+ // src/components/TaskProgressIndicator.tsx
1407
+ var import_clsx14 = __toESM(require("clsx"), 1);
1408
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1409
+ var currentStepStatuses = /* @__PURE__ */ new Set(["active", "in-progress", "running"]);
1410
+ function TaskProgressIndicator({
1411
+ value,
1412
+ total = 100,
1413
+ steps,
1414
+ label = "Task progress",
1415
+ className,
1416
+ ...props
1417
+ }) {
1418
+ if (steps?.length) {
1419
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1420
+ "div",
1421
+ {
1422
+ ...props,
1423
+ className: (0, import_clsx14.default)("eth-task-progress eth-task-progress--steps", className),
1424
+ "data-eth-component": "TaskProgressIndicator",
1425
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ol", { className: "eth-task-progress__steps", "aria-label": label, children: steps.map((step, index) => {
1426
+ const statusLabel2 = labelForStatus(step.status);
1427
+ const isCurrentStep = currentStepStatuses.has(step.status);
1428
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1429
+ "li",
1430
+ {
1431
+ className: (0, import_clsx14.default)(
1432
+ "eth-task-progress__step",
1433
+ `eth-task-progress__step--${step.status}`
1434
+ ),
1435
+ "aria-current": isCurrentStep ? "step" : void 0,
1436
+ "data-status": step.status,
1437
+ children: [
1438
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "eth-task-progress__step-track", "aria-hidden": "true", children: [
1439
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "eth-task-progress__circle" }),
1440
+ index < steps.length - 1 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "eth-task-progress__connector" }) : null
1441
+ ] }),
1442
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "eth-task-progress__step-copy", children: [
1443
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "eth-task-progress__step-label", children: step.label }),
1444
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "eth-task-progress__step-status", children: statusLabel2 })
1445
+ ] })
1446
+ ]
1447
+ },
1448
+ step.id
1449
+ );
1450
+ }) })
1451
+ }
1452
+ );
1453
+ }
1454
+ const progressValue = clampProgress(value, total);
1455
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1456
+ "div",
1457
+ {
1458
+ ...props,
1459
+ className: (0, import_clsx14.default)("eth-task-progress eth-task-progress--bar", className),
1460
+ "data-eth-component": "TaskProgressIndicator",
1461
+ children: [
1462
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "eth-task-progress__label", children: [
1463
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: label }),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("strong", { children: [
1465
+ progressValue,
1466
+ "%"
1467
+ ] })
1468
+ ] }),
1469
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("progress", { value: progressValue, max: 100, "aria-label": label })
1470
+ ]
1471
+ }
1472
+ );
1473
+ }
1474
+
1475
+ // src/components/TaskRetryPanel.tsx
1476
+ var React8 = __toESM(require("react"), 1);
1477
+ var import_clsx15 = __toESM(require("clsx"), 1);
1478
+ var import_core13 = require("@echothink-ui/core");
1479
+ var import_icons = require("@echothink-ui/icons");
1480
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1481
+ var backoffLabels = {
1482
+ exponential: "Exponential",
1483
+ linear: "Linear"
1484
+ };
1485
+ function TaskRetryPanel({
1486
+ taskRef,
1487
+ failureSummary,
1488
+ retryPolicy,
1489
+ attemptCount = 0,
1490
+ onRetry,
1491
+ className,
1492
+ title = "Retry failed task",
1493
+ description,
1494
+ ...props
1495
+ }) {
1496
+ const [reason, setReason] = React8.useState("");
1497
+ const reasonId = React8.useId();
1498
+ const retriesExhausted = retryPolicy ? attemptCount >= retryPolicy.maxRetries : false;
1499
+ const attemptLabel = retryPolicy ? `${attemptCount} / ${retryPolicy.maxRetries}` : attemptCount;
1500
+ const backoffLabel = retryPolicy ? backoffLabels[retryPolicy.backoff] : "-";
1501
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1502
+ import_core13.Surface,
1503
+ {
1504
+ ...props,
1505
+ className: (0, import_clsx15.default)("eth-task-retry-panel", className),
1506
+ title,
1507
+ description,
1508
+ severity: "danger",
1509
+ "data-eth-component": "TaskRetryPanel",
1510
+ children: [
1511
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_core13.InlineNotification, { severity: "danger", title: "Task failed", children: failureSummary ?? "The task did not complete successfully." }),
1512
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dl", { className: "eth-task-retry-panel__policy", "aria-label": "Retry policy", children: [
1513
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
1514
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: "Task" }),
1515
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: taskRef ?? "-" })
1516
+ ] }),
1517
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1518
+ "div",
1519
+ {
1520
+ className: (0, import_clsx15.default)(
1521
+ "eth-task-retry-panel__attempts",
1522
+ retriesExhausted && "eth-task-retry-panel__attempts--exhausted"
1523
+ ),
1524
+ children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: "Attempts" }),
1526
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("dd", { children: [
1527
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: attemptLabel }),
1528
+ retriesExhausted ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "eth-task-retry-panel__policy-note", children: "Automatic retries exhausted" }) : null
1529
+ ] })
1530
+ ]
1531
+ }
1532
+ ),
1533
+ retryPolicy ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1534
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
1535
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: "Backoff" }),
1536
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: backoffLabel })
1537
+ ] }),
1538
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { children: [
1539
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dt", { children: "Interval" }),
1540
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("dd", { children: formatDuration(retryPolicy.intervalMs) })
1541
+ ] })
1542
+ ] }) : null
1543
+ ] }),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "eth-task-retry-panel__controls", children: [
1545
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1546
+ import_core13.FormField,
1547
+ {
1548
+ id: reasonId,
1549
+ label: "Retry reason",
1550
+ helperText: "Stored with retry audit metadata.",
1551
+ className: "eth-task-retry-panel__reason-field",
1552
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1553
+ import_core13.Textarea,
1554
+ {
1555
+ value: reason,
1556
+ rows: 3,
1557
+ placeholder: "Summarize what changed before retrying.",
1558
+ onChange: (event) => setReason(event.currentTarget.value)
1559
+ }
1560
+ )
1561
+ }
1562
+ ),
1563
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "eth-task-retry-panel__actions", children: [
1564
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1565
+ import_core13.Button,
1566
+ {
1567
+ icon: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_icons.RetryIcon, { size: 16 }),
1568
+ intent: "primary",
1569
+ onClick: () => onRetry?.(reason),
1570
+ children: "Retry task"
1571
+ }
1572
+ ),
1573
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { children: "Creates a new attempt and records this reason in the task audit trail." })
1574
+ ] })
1575
+ ] })
1576
+ ]
1577
+ }
1578
+ );
1579
+ }
1580
+
1581
+ // src/components/TaskRunLog.tsx
1582
+ var React9 = __toESM(require("react"), 1);
1583
+ var import_clsx16 = __toESM(require("clsx"), 1);
1584
+ var import_core14 = require("@echothink-ui/core");
1585
+ var import_icons2 = require("@echothink-ui/icons");
1586
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1587
+ var logLevels = ["error", "warn", "info", "debug"];
1588
+ function TaskRunLog({
1589
+ entries = [],
1590
+ streaming,
1591
+ onPauseToggle,
1592
+ onFilter,
1593
+ levelFilter,
1594
+ windowSize = 200,
1595
+ className,
1596
+ title = "Task run log",
1597
+ description,
1598
+ ...props
1599
+ }) {
1600
+ const [internalFilter, setInternalFilter] = React9.useState(logLevels);
1601
+ const activeFilter = levelFilter ?? internalFilter;
1602
+ const counts = React9.useMemo(
1603
+ () => logLevels.reduce(
1604
+ (summary, level) => ({
1605
+ ...summary,
1606
+ [level]: entries.filter((entry) => entry.level === level).length
1607
+ }),
1608
+ {}
1609
+ ),
1610
+ [entries]
1611
+ );
1612
+ const visibleEntries = React9.useMemo(
1613
+ () => entries.filter((entry) => activeFilter.includes(entry.level)).slice(-windowSize),
1614
+ [activeFilter, entries, windowSize]
1615
+ );
1616
+ const toggleLevel = (level) => {
1617
+ onFilter?.(level);
1618
+ if (!levelFilter) {
1619
+ setInternalFilter(
1620
+ (current) => current.includes(level) ? current.filter((candidate) => candidate !== level) : [...current, level]
1621
+ );
1622
+ }
1623
+ };
1624
+ const streamLabel = streaming ? "Streaming" : "Paused";
1625
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1626
+ import_core14.Surface,
1627
+ {
1628
+ ...props,
1629
+ className: (0, import_clsx16.default)("eth-task-run-log", className),
1630
+ title,
1631
+ description,
1632
+ "data-eth-component": "TaskRunLog",
1633
+ children: [
1634
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "eth-task-run-log__toolbar", role: "toolbar", "aria-label": "Task log controls", children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "eth-task-run-log__stream-state", "aria-live": "polite", children: [
1636
+ onPauseToggle ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1637
+ import_core14.IconButton,
1638
+ {
1639
+ label: streaming ? "Pause log stream" : "Resume log stream",
1640
+ icon: streaming ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons2.PauseIcon, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_icons2.PlayIcon, { size: 16 }),
1641
+ intent: "ghost",
1642
+ density: "compact",
1643
+ onClick: onPauseToggle
1644
+ }
1645
+ ) : null,
1646
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_core14.Badge, { severity: streaming ? "success" : "neutral", children: streamLabel }),
1647
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "eth-task-run-log__buffer-count", children: [
1648
+ visibleEntries.length,
1649
+ " of ",
1650
+ entries.length,
1651
+ " entries"
1652
+ ] })
1653
+ ] }),
1654
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("fieldset", { className: "eth-task-run-log__filters", children: [
1655
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("legend", { children: "Levels" }),
1656
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "eth-task-run-log__filter-options", children: logLevels.map((level) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1657
+ import_core14.Checkbox,
1658
+ {
1659
+ label: level,
1660
+ checked: activeFilter.includes(level),
1661
+ onChange: () => toggleLevel(level)
1662
+ },
1663
+ level
1664
+ )) })
1665
+ ] })
1666
+ ] }),
1667
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("dl", { className: "eth-task-run-log__summary", "aria-label": "Task log summary", children: [
1668
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
1669
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dt", { children: "Buffered" }),
1670
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dd", { children: entries.length })
1671
+ ] }),
1672
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
1673
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dt", { children: "Visible" }),
1674
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dd", { children: visibleEntries.length })
1675
+ ] }),
1676
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
1677
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dt", { children: "Warnings" }),
1678
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dd", { children: counts.warn })
1679
+ ] }),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
1681
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dt", { children: "Errors" }),
1682
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("dd", { children: counts.error })
1683
+ ] })
1684
+ ] }),
1685
+ visibleEntries.length ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1686
+ "div",
1687
+ {
1688
+ className: "eth-task-run-log__viewport",
1689
+ role: "log",
1690
+ "aria-label": "Task execution log entries",
1691
+ "aria-live": streaming ? "polite" : "off",
1692
+ "aria-relevant": "additions text",
1693
+ children: [
1694
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "eth-task-run-log__columns", "aria-hidden": "true", children: [
1695
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Time" }),
1696
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Level" }),
1697
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: "Message" })
1698
+ ] }),
1699
+ visibleEntries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
1700
+ "div",
1701
+ {
1702
+ className: (0, import_clsx16.default)("eth-task-run-log__entry", `eth-task-run-log__entry--${entry.level}`),
1703
+ children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("time", { className: "eth-task-run-log__time", dateTime: String(entry.timestamp), children: formatDateTime(entry.timestamp) }),
1705
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1706
+ "span",
1707
+ {
1708
+ className: (0, import_clsx16.default)(
1709
+ "eth-task-run-log__level",
1710
+ `eth-task-run-log__level--${entry.level}`
1711
+ ),
1712
+ children: entry.level.toUpperCase()
1713
+ }
1714
+ ),
1715
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1716
+ "span",
1717
+ {
1718
+ className: (0, import_clsx16.default)(
1719
+ "eth-task-run-log__message",
1720
+ entry.redacted && "eth-task-run-log__message--redacted"
1721
+ ),
1722
+ children: entry.redacted ? "[redacted]" : entry.message
1723
+ }
1724
+ )
1725
+ ]
1726
+ },
1727
+ entry.id
1728
+ ))
1729
+ ]
1730
+ }
1731
+ ) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_core14.EmptyState, { title: "No log entries" })
1732
+ ]
1733
+ }
1734
+ );
1735
+ }
1736
+
1737
+ // src/components/TaskTable.tsx
1738
+ var React10 = __toESM(require("react"), 1);
1739
+ var import_clsx17 = __toESM(require("clsx"), 1);
1740
+ var import_core15 = require("@echothink-ui/core");
1741
+ var import_data = require("@echothink-ui/data");
1742
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1743
+ var statusFilterOrder = [
1744
+ "not-started",
1745
+ "queued",
1746
+ "in-progress",
1747
+ "running",
1748
+ "approval-required",
1749
+ "pending-approval",
1750
+ "blocked",
1751
+ "failed",
1752
+ "completed",
1753
+ "succeeded",
1754
+ "synced"
1755
+ ];
1756
+ var priorityLabels = {
1757
+ critical: "Critical",
1758
+ high: "High",
1759
+ low: "Low",
1760
+ medium: "Medium"
1761
+ };
1762
+ function taskRowsFromItems(items) {
1763
+ return items?.map((item) => ({
1764
+ id: item.id,
1765
+ title: String(item.label),
1766
+ status: item.status ?? "not-started"
1767
+ })) ?? [];
1768
+ }
1769
+ function taskTableLabel(title) {
1770
+ return typeof title === "string" && title.trim() ? `${title} table` : "Task table";
1771
+ }
1772
+ function priorityCell(priority) {
1773
+ if (!priority) return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "eth-task-table__empty-value", children: "-" });
1774
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1775
+ import_core15.Badge,
1776
+ {
1777
+ severity: severityForPriority(priority),
1778
+ className: `eth-task-table__priority eth-task-table__priority--${priority}`,
1779
+ children: priorityLabels[priority]
1780
+ }
1781
+ );
1782
+ }
1783
+ function emptyCell(value) {
1784
+ if (value === void 0 || value === null || value === "") {
1785
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "eth-task-table__empty-value", children: "-" });
1786
+ }
1787
+ return value;
1788
+ }
1789
+ function taskStatusOptions(rows, activeStatus) {
1790
+ const counts = /* @__PURE__ */ new Map();
1791
+ for (const task of rows) {
1792
+ counts.set(task.status, (counts.get(task.status) ?? 0) + 1);
1793
+ }
1794
+ const activeOperationalStatus = activeStatus === "all" ? null : activeStatus;
1795
+ const activeStatusWithoutRows = Boolean(
1796
+ activeOperationalStatus && !counts.has(activeOperationalStatus)
1797
+ );
1798
+ const extraStatuses = activeOperationalStatus && activeStatusWithoutRows && !statusFilterOrder.includes(activeOperationalStatus) ? [activeOperationalStatus] : [];
1799
+ const sortedStatuses = [
1800
+ ...statusFilterOrder.filter(
1801
+ (status) => counts.has(status) || activeStatusWithoutRows && status === activeOperationalStatus
1802
+ ),
1803
+ ...Array.from(counts.keys()).filter((status) => !statusFilterOrder.includes(status)),
1804
+ ...extraStatuses
1805
+ ];
1806
+ return [
1807
+ { value: "all", label: `All statuses (${rows.length})` },
1808
+ ...sortedStatuses.map((status) => ({
1809
+ value: status,
1810
+ label: `${labelForStatus(status)} (${counts.get(status) ?? 0})`
1811
+ }))
1812
+ ];
1813
+ }
1814
+ var defaultColumns = [
1815
+ { key: "id", header: "ID", width: "5rem", sortable: true },
1816
+ {
1817
+ key: "title",
1818
+ header: "Task",
1819
+ width: "18rem",
1820
+ sortable: true,
1821
+ render: (task) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "eth-task-table__task", children: [
1822
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("strong", { className: "eth-task-table__task-title", children: task.title }),
1823
+ task.description ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "eth-task-table__task-description", children: task.description }) : null
1824
+ ] })
1825
+ },
1826
+ {
1827
+ key: "status",
1828
+ header: "Status",
1829
+ width: "12rem",
1830
+ sortable: true,
1831
+ render: (task) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TaskStatusBadge, { status: task.status })
1832
+ },
1833
+ {
1834
+ key: "priority",
1835
+ header: "Priority",
1836
+ width: "8rem",
1837
+ sortable: true,
1838
+ render: (task) => priorityCell(task.priority)
1839
+ },
1840
+ {
1841
+ key: "assignee",
1842
+ header: "Assignee",
1843
+ width: "9rem",
1844
+ render: (task) => emptyCell(task.assignee)
1845
+ },
1846
+ {
1847
+ key: "dueAt",
1848
+ header: "Due",
1849
+ width: "13rem",
1850
+ sortable: true,
1851
+ render: (task) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: task.dueAt ? "eth-task-table__date" : "eth-task-table__empty-value", children: formatDateTime(task.dueAt) })
1852
+ }
1853
+ ];
1854
+ function TaskTable({
1855
+ tasks,
1856
+ items,
1857
+ density = "default",
1858
+ selectable,
1859
+ selectedRows,
1860
+ onSelectionChange,
1861
+ rowActions,
1862
+ bulkActions,
1863
+ columns,
1864
+ showFilters,
1865
+ statusFilter,
1866
+ onStatusFilterChange,
1867
+ className,
1868
+ title,
1869
+ description,
1870
+ actions,
1871
+ subtitle: _subtitle,
1872
+ eyebrow: _eyebrow,
1873
+ status: _status,
1874
+ severity: _severity,
1875
+ loading: _loading,
1876
+ empty: _empty,
1877
+ error: _error,
1878
+ metadata: _metadata,
1879
+ footer: _footer,
1880
+ "aria-label": ariaLabel,
1881
+ ...sectionProps
1882
+ }) {
1883
+ const rows = tasks ?? taskRowsFromItems(items);
1884
+ const [internalStatusFilter, setInternalStatusFilter] = React10.useState("all");
1885
+ const effectiveStatusFilter = statusFilter ?? internalStatusFilter;
1886
+ const filterVisible = Boolean(showFilters) || statusFilter !== void 0 || Boolean(onStatusFilterChange);
1887
+ const filteredRows = filterVisible && effectiveStatusFilter !== "all" ? rows.filter((task) => task.status === effectiveStatusFilter) : rows;
1888
+ const statusOptions = React10.useMemo(
1889
+ () => taskStatusOptions(rows, effectiveStatusFilter),
1890
+ [effectiveStatusFilter, rows]
1891
+ );
1892
+ const resolvedColumns = columns ?? defaultColumns;
1893
+ const label = ariaLabel ?? taskTableLabel(title);
1894
+ const emptyTitle = rows.length && filterVisible ? "No matching tasks" : title ?? "No tasks";
1895
+ const emptyDescription = rows.length && filterVisible ? "No tasks match the selected status filter." : description ?? "There are no tasks to display.";
1896
+ const selectEnabled = selectable ?? Boolean(bulkActions?.length);
1897
+ const handleStatusFilterChange = (event) => {
1898
+ const nextStatus = event.currentTarget.value;
1899
+ if (statusFilter === void 0) setInternalStatusFilter(nextStatus);
1900
+ onStatusFilterChange?.(nextStatus);
1901
+ };
1902
+ const table = bulkActions?.length ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1903
+ import_data.BulkActionTable,
1904
+ {
1905
+ className: (0, import_clsx17.default)("eth-task-table", className),
1906
+ rows: filteredRows,
1907
+ columns: resolvedColumns,
1908
+ rowKey: "id",
1909
+ density,
1910
+ selectable: selectEnabled,
1911
+ selectedRows,
1912
+ onSelectionChange,
1913
+ rowActions,
1914
+ bulkActions,
1915
+ emptyState: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_core15.EmptyState, { title: emptyTitle, description: emptyDescription }),
1916
+ "aria-label": label,
1917
+ "data-eth-component": "TaskTable"
1918
+ }
1919
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1920
+ import_data.DataTable,
1921
+ {
1922
+ className: (0, import_clsx17.default)("eth-task-table", className),
1923
+ rows: filteredRows,
1924
+ columns: resolvedColumns,
1925
+ rowKey: "id",
1926
+ density,
1927
+ selectable,
1928
+ selectedRows,
1929
+ onSelectionChange,
1930
+ rowActions,
1931
+ emptyState: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_core15.EmptyState, { title: emptyTitle, description: emptyDescription }),
1932
+ "aria-label": label,
1933
+ "data-eth-component": "TaskTable"
1934
+ }
1935
+ );
1936
+ const hasHeader = Boolean(title || description || actions?.length);
1937
+ if (!hasHeader && !filterVisible) return table;
1938
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1939
+ "section",
1940
+ {
1941
+ ...sectionProps,
1942
+ className: "eth-task-table-shell",
1943
+ "aria-label": typeof title === "string" ? title : "Tasks",
1944
+ children: [
1945
+ hasHeader ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("header", { className: "eth-task-table-shell__header", children: [
1946
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "eth-task-table-shell__heading", children: [
1947
+ title ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("h2", { children: title }) : null,
1948
+ description ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: description }) : null
1949
+ ] }),
1950
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_core15.ActionGroup, { actions })
1951
+ ] }) : null,
1952
+ filterVisible ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "eth-task-table-shell__toolbar", "aria-label": "Task table filters", children: [
1953
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "eth-task-table-shell__filters", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1954
+ import_core15.Select,
1955
+ {
1956
+ className: "eth-task-table-shell__filter",
1957
+ density: "compact",
1958
+ labelText: "Status",
1959
+ options: statusOptions,
1960
+ value: effectiveStatusFilter,
1961
+ onChange: handleStatusFilterChange
1962
+ }
1963
+ ) }),
1964
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "eth-task-table-shell__summary", children: filteredRows.length === rows.length ? `${rows.length} ${rows.length === 1 ? "task" : "tasks"} shown` : `${filteredRows.length} of ${rows.length} tasks shown` })
1965
+ ] }) : null,
1966
+ table
1967
+ ]
1968
+ }
1969
+ );
1970
+ }
1971
+
1972
+ // src/components/TaskWaveDAG.tsx
1973
+ var React11 = __toESM(require("react"), 1);
1974
+ var import_clsx18 = __toESM(require("clsx"), 1);
1975
+ var import_core16 = require("@echothink-ui/core");
1976
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1977
+ var rankSpacing = DAG_NODE_WIDTH + 76;
1978
+ var rowSpacing = DAG_NODE_HEIGHT + 48;
1979
+ var originX = DAG_NODE_WIDTH / 2 + 28;
1980
+ var originY = DAG_NODE_HEIGHT / 2 + 48;
1981
+ var framePaddingX = 32;
1982
+ var framePaddingY = 28;
1983
+ function layoutNodes(nodes, edges, layout) {
1984
+ const ids = new Set(nodes.map((node) => node.id));
1985
+ const ranks = new Map(nodes.map((node) => [node.id, 0]));
1986
+ const validEdges = edges.filter((edge) => ids.has(edge.from) && ids.has(edge.to));
1987
+ for (let pass = 0; pass < nodes.length; pass += 1) {
1988
+ for (const edge of validEdges) {
1989
+ const fromRank = ranks.get(edge.from) ?? 0;
1990
+ const toRank = ranks.get(edge.to) ?? 0;
1991
+ if (toRank < fromRank + 1) ranks.set(edge.to, fromRank + 1);
1992
+ }
1993
+ }
1994
+ const groups = /* @__PURE__ */ new Map();
1995
+ for (const node of nodes) {
1996
+ const rank = ranks.get(node.id) ?? 0;
1997
+ groups.set(rank, [...groups.get(rank) ?? [], node]);
1998
+ }
1999
+ return nodes.map((node) => {
2000
+ if (typeof node.x === "number" && typeof node.y === "number") {
2001
+ return { ...node, x: node.x, y: node.y };
2002
+ }
2003
+ const rank = ranks.get(node.id) ?? 0;
2004
+ const group = groups.get(rank) ?? [];
2005
+ const index = group.findIndex((candidate) => candidate.id === node.id);
2006
+ const spread = Math.max(0, index) * rowSpacing + originY;
2007
+ const ranked = rank * rankSpacing + originX;
2008
+ return layout === "lr" ? { ...node, x: ranked, y: spread } : { ...node, x: spread, y: ranked };
2009
+ });
2010
+ }
2011
+ function frameFor(nodes) {
2012
+ if (!nodes.length) {
2013
+ return { x: 0, y: 0, width: 640, height: 180, viewBox: "0 0 640 180" };
2014
+ }
2015
+ const minX = Math.min(...nodes.map((node) => node.x - DAG_NODE_WIDTH / 2)) - framePaddingX;
2016
+ const minY = Math.min(...nodes.map((node) => node.y - DAG_NODE_HEIGHT / 2)) - framePaddingY;
2017
+ const maxX = Math.max(...nodes.map((node) => node.x + DAG_NODE_WIDTH / 2)) + framePaddingX;
2018
+ const maxY = Math.max(...nodes.map((node) => node.y + DAG_NODE_HEIGHT / 2)) + framePaddingY;
2019
+ const width = Math.max(360, maxX - minX);
2020
+ const height = Math.max(120, maxY - minY);
2021
+ return { x: minX, y: minY, width, height, viewBox: `${minX} ${minY} ${width} ${height}` };
2022
+ }
2023
+ function edgeEndpoints(from, to, layout) {
2024
+ if (layout === "lr") {
2025
+ return {
2026
+ from: { x: from.x + DAG_NODE_WIDTH / 2, y: from.y },
2027
+ to: { x: to.x - DAG_NODE_WIDTH / 2, y: to.y }
2028
+ };
2029
+ }
2030
+ return {
2031
+ from: { x: from.x, y: from.y + DAG_NODE_HEIGHT / 2 },
2032
+ to: { x: to.x, y: to.y - DAG_NODE_HEIGHT / 2 }
2033
+ };
2034
+ }
2035
+ function TaskWaveDAG({
2036
+ nodes = [],
2037
+ edges = [],
2038
+ layout = "lr",
2039
+ onNodeSelect,
2040
+ selectedNodeId,
2041
+ className,
2042
+ title,
2043
+ description,
2044
+ ...props
2045
+ }) {
2046
+ const positionedNodes = React11.useMemo(
2047
+ () => layoutNodes(nodes, edges, layout),
2048
+ [nodes, edges, layout]
2049
+ );
2050
+ const nodeMap = React11.useMemo(
2051
+ () => new Map(positionedNodes.map((node) => [node.id, node])),
2052
+ [positionedNodes]
2053
+ );
2054
+ const visibleStatuses = React11.useMemo(
2055
+ () => Array.from(new Set(positionedNodes.map((node) => node.status))),
2056
+ [positionedNodes]
2057
+ );
2058
+ const graphFrame = React11.useMemo(() => frameFor(positionedNodes), [positionedNodes]);
2059
+ const graphTitleId = React11.useId();
2060
+ const graphDescriptionId = React11.useId();
2061
+ const graphTitle = typeof title === "string" && title.trim() ? title : "Task wave dependency graph";
2062
+ const graphDescription = `${positionedNodes.length} nodes and ${edges.length} dependencies. Statuses: ${visibleStatuses.map((status) => status.replace(/-/g, " ")).join(", ") || "none"}.`;
2063
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2064
+ import_core16.Surface,
2065
+ {
2066
+ ...props,
2067
+ className: (0, import_clsx18.default)("eth-task-wave-dag", className),
2068
+ title: title ?? "Wave DAG",
2069
+ description,
2070
+ "data-eth-component": "TaskWaveDAG",
2071
+ children: positionedNodes.length ? /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2072
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "eth-task-wave-dag__viewport", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
2073
+ "svg",
2074
+ {
2075
+ className: "eth-dag",
2076
+ role: "img",
2077
+ "aria-labelledby": graphTitleId,
2078
+ "aria-describedby": graphDescriptionId,
2079
+ viewBox: graphFrame.viewBox,
2080
+ children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("title", { id: graphTitleId, children: graphTitle }),
2082
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("desc", { id: graphDescriptionId, children: graphDescription }),
2083
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2084
+ "rect",
2085
+ {
2086
+ className: "eth-dag__canvas",
2087
+ x: graphFrame.x,
2088
+ y: graphFrame.y,
2089
+ width: graphFrame.width,
2090
+ height: graphFrame.height,
2091
+ "aria-hidden": "true"
2092
+ }
2093
+ ),
2094
+ edges.map((edge, index) => {
2095
+ const from = nodeMap.get(edge.from);
2096
+ const to = nodeMap.get(edge.to);
2097
+ if (!from || !to) return null;
2098
+ const endpoints = edgeEndpoints(from, to, layout);
2099
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2100
+ DAGEdge,
2101
+ {
2102
+ from: endpoints.from,
2103
+ to: endpoints.to,
2104
+ status: edge.status ?? to.status,
2105
+ label: edge.label
2106
+ },
2107
+ `${edge.from}-${edge.to}-${index}`
2108
+ );
2109
+ }),
2110
+ positionedNodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
2111
+ DAGNode,
2112
+ {
2113
+ node,
2114
+ selected: node.id === selectedNodeId,
2115
+ onSelect: onNodeSelect
2116
+ },
2117
+ node.id
2118
+ ))
2119
+ ]
2120
+ }
2121
+ ) }),
2122
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(DAGLegend, { statuses: visibleStatuses.length ? visibleStatuses : void 0 }),
2123
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ol", { className: "eth-task-wave-dag__fallback", children: positionedNodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("li", { children: [
2124
+ node.label,
2125
+ ", ",
2126
+ node.status.replace(/-/g, " ")
2127
+ ] }, node.id)) })
2128
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_core16.EmptyState, { title: "No DAG nodes", description: "Add nodes and edges to render a wave graph." })
2129
+ }
2130
+ );
2131
+ }
2132
+
2133
+ // src/components/TaskWaveHeader.tsx
2134
+ var import_clsx19 = __toESM(require("clsx"), 1);
2135
+ var import_core17 = require("@echothink-ui/core");
2136
+ var import_jsx_runtime20 = require("react/jsx-runtime");
2137
+ var statusOrder = [
2138
+ "queued",
2139
+ "running",
2140
+ "in-progress",
2141
+ "pending-approval",
2142
+ "approval-required",
2143
+ "blocked",
2144
+ "failed",
2145
+ "completed",
2146
+ "succeeded"
2147
+ ];
2148
+ function TaskWaveHeader({
2149
+ wave,
2150
+ actions,
2151
+ className,
2152
+ title,
2153
+ description,
2154
+ ...props
2155
+ }) {
2156
+ const progress = waveProgress(wave);
2157
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
2158
+ import_core17.Surface,
2159
+ {
2160
+ ...props,
2161
+ className: (0, import_clsx19.default)("eth-task-wave-header", className),
2162
+ title: wave?.label ?? title ?? "Task wave",
2163
+ subtitle: wave?.id,
2164
+ description,
2165
+ actions,
2166
+ "data-eth-component": "TaskWaveHeader",
2167
+ children: wave ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
2168
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "eth-task-wave-header__summary", children: [
2169
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("strong", { children: [
2170
+ wave.totalTasks,
2171
+ " tasks"
2172
+ ] }),
2173
+ wave.startedAt ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { children: [
2174
+ "Started ",
2175
+ formatDateTime(wave.startedAt)
2176
+ ] }) : null,
2177
+ wave.eta ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("span", { children: [
2178
+ "ETA ",
2179
+ formatDateTime(wave.eta)
2180
+ ] }) : null
2181
+ ] }),
2182
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TaskProgressIndicator, { value: progress, label: "Wave progress" }),
2183
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "eth-task-wave-header__counts", "aria-label": "Task status counts", children: statusOrder.filter((status) => wave.statuses[status]).map((status) => {
2184
+ const label = labelForStatus(status);
2185
+ const count = wave.statuses[status] ?? 0;
2186
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
2187
+ import_core17.Badge,
2188
+ {
2189
+ severity: severityForStatus(status),
2190
+ className: (0, import_clsx19.default)(
2191
+ "eth-task-wave-header__count",
2192
+ `eth-task-wave-header__count--${status}`
2193
+ ),
2194
+ "aria-label": `${label}: ${count}`,
2195
+ children: [
2196
+ label,
2197
+ ": ",
2198
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "eth-task-wave-header__count-value", children: count })
2199
+ ]
2200
+ },
2201
+ status
2202
+ );
2203
+ }) })
2204
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(TaskProgressIndicator, { value: 0, label: "Wave progress" })
2205
+ }
2206
+ );
2207
+ }
2208
+
2209
+ // src/components/TaskWaveTable.tsx
2210
+ var React12 = __toESM(require("react"), 1);
2211
+ var import_clsx20 = __toESM(require("clsx"), 1);
2212
+ var import_core18 = require("@echothink-ui/core");
2213
+ var import_data2 = require("@echothink-ui/data");
2214
+ var import_jsx_runtime21 = require("react/jsx-runtime");
2215
+ function taskRowsFromItems2(items) {
2216
+ return items?.map((item, index) => ({
2217
+ id: item.id,
2218
+ title: String(item.label),
2219
+ status: item.status ?? "not-started",
2220
+ order: index + 1
2221
+ })) ?? [];
2222
+ }
2223
+ function dependencyCount(task) {
2224
+ if (typeof task.dependencyCount === "number") return task.dependencyCount;
2225
+ if (task.dependencyIds?.length) return task.dependencyIds.length;
2226
+ return task.dependencies?.length ?? 0;
2227
+ }
2228
+ function EmptyCell() {
2229
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "eth-task-wave-table__empty", children: "-" });
2230
+ }
2231
+ function TaskWaveTable({
2232
+ wave,
2233
+ tasks,
2234
+ items,
2235
+ onTaskSelect,
2236
+ rowActions,
2237
+ className,
2238
+ title,
2239
+ description,
2240
+ ..._surfaceProps
2241
+ }) {
2242
+ const rows = tasks ?? taskRowsFromItems2(items);
2243
+ const columns = React12.useMemo(
2244
+ () => [
2245
+ {
2246
+ key: "order",
2247
+ header: "Order",
2248
+ width: "4.5rem",
2249
+ render: (task, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "eth-task-wave-table__order", children: task.order ?? index + 1 })
2250
+ },
2251
+ {
2252
+ key: "title",
2253
+ header: "Task",
2254
+ width: "22rem",
2255
+ render: (task) => onTaskSelect ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2256
+ "button",
2257
+ {
2258
+ className: "eth-task-wave-table__task-button",
2259
+ type: "button",
2260
+ onClick: () => onTaskSelect(task),
2261
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "eth-task-wave-table__task-title", children: task.title })
2262
+ }
2263
+ ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "eth-task-wave-table__task-title", children: task.title })
2264
+ },
2265
+ {
2266
+ key: "status",
2267
+ header: "Status",
2268
+ width: "12rem",
2269
+ render: (task) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2270
+ import_core18.StatusDot,
2271
+ {
2272
+ className: "eth-task-wave-table__status",
2273
+ status: task.status,
2274
+ label: labelForStatus(task.status)
2275
+ }
2276
+ )
2277
+ },
2278
+ {
2279
+ key: "assignee",
2280
+ header: "Assignee",
2281
+ width: "8rem",
2282
+ render: (task) => task.assignee ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "eth-task-wave-table__assignee", children: task.assignee }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EmptyCell, {})
2283
+ },
2284
+ {
2285
+ key: "dependencies",
2286
+ header: "Dependencies",
2287
+ width: "7rem",
2288
+ align: "center",
2289
+ render: (task) => {
2290
+ const count = dependencyCount(task);
2291
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2292
+ import_core18.Badge,
2293
+ {
2294
+ className: "eth-task-wave-table__dependency-count",
2295
+ severity: count > 0 ? "info" : "neutral",
2296
+ children: count
2297
+ }
2298
+ );
2299
+ }
2300
+ },
2301
+ {
2302
+ key: "durationMs",
2303
+ header: "Duration",
2304
+ width: "7rem",
2305
+ render: (task) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2306
+ "span",
2307
+ {
2308
+ className: (0, import_clsx20.default)(
2309
+ "eth-task-wave-table__duration",
2310
+ task.durationMs == null && "eth-task-wave-table__empty"
2311
+ ),
2312
+ children: formatDuration(task.durationMs)
2313
+ }
2314
+ )
2315
+ }
2316
+ ],
2317
+ [onTaskSelect]
2318
+ );
2319
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2320
+ import_core18.Surface,
2321
+ {
2322
+ className: (0, import_clsx20.default)("eth-task-wave-table", className),
2323
+ title: wave?.label ?? title ?? "Task wave",
2324
+ subtitle: wave?.id,
2325
+ description,
2326
+ "data-eth-component": "TaskWaveTable",
2327
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
2328
+ import_data2.DataTable,
2329
+ {
2330
+ "aria-label": typeof (wave?.label ?? title) === "string" ? `${wave?.label ?? title} tasks` : "Wave tasks",
2331
+ className: "eth-task-wave-table__table",
2332
+ rows,
2333
+ columns,
2334
+ rowKey: "id",
2335
+ density: "compact",
2336
+ rowActions,
2337
+ emptyState: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_core18.EmptyState, { title: "No wave tasks" })
2338
+ }
2339
+ )
2340
+ }
2341
+ );
2342
+ }
2343
+
2344
+ // src/components/MobileTaskShell.tsx
2345
+ var import_clsx21 = __toESM(require("clsx"), 1);
2346
+ var import_jsx_runtime22 = require("react/jsx-runtime");
2347
+ function MobileTaskShell({
2348
+ title,
2349
+ subtitle,
2350
+ progress,
2351
+ main,
2352
+ footer,
2353
+ identity,
2354
+ children,
2355
+ className
2356
+ }) {
2357
+ const content = main ?? children;
2358
+ const hasHeader = title || subtitle || progress;
2359
+ const hasFooter = footer || identity;
2360
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2361
+ "section",
2362
+ {
2363
+ className: (0, import_clsx21.default)("eth-mobile-task-shell", className),
2364
+ "data-eth-component": "MobileTaskShell",
2365
+ children: [
2366
+ hasHeader ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("header", { className: "eth-mobile-task-shell__header", children: [
2367
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "eth-mobile-task-shell__title-block", children: [
2368
+ title ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("h1", { className: "eth-mobile-task-shell__title", children: title }) : null,
2369
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "eth-mobile-task-shell__subtitle", children: subtitle }) : null
2370
+ ] }),
2371
+ progress ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "eth-mobile-task-shell__progress", children: progress }) : null
2372
+ ] }) : null,
2373
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("main", { className: "eth-mobile-task-shell__main", children: content }),
2374
+ hasFooter ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("footer", { className: "eth-mobile-task-shell__footer", children: [
2375
+ footer ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "eth-mobile-task-shell__footer-main", children: footer }) : null,
2376
+ identity ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "eth-mobile-task-shell__identity", children: identity }) : null
2377
+ ] }) : null
2378
+ ]
2379
+ }
2380
+ );
2381
+ }
2382
+
2383
+ // src/index.tsx
2384
+ var TaskComponentNames = [
2385
+ "TaskCard",
2386
+ "TaskTable",
2387
+ "TaskDetailPanel",
2388
+ "TaskStatusBadge",
2389
+ "TaskProgressIndicator",
2390
+ "TaskDependencyList",
2391
+ "TaskTimeline",
2392
+ "TaskWaveTable",
2393
+ "TaskWaveHeader",
2394
+ "TaskWaveDAG",
2395
+ "DAGNode",
2396
+ "DAGEdge",
2397
+ "DAGLegend",
2398
+ "TaskApprovalPanel",
2399
+ "DecisionRequiredPanel",
2400
+ "BackendThinkingChain",
2401
+ "HumanInterventionPanel",
2402
+ "BlockingReasonPanel",
2403
+ "TaskHandoffPanel",
2404
+ "TaskRunLog",
2405
+ "TaskRetryPanel",
2406
+ "MobileTaskShell"
2407
+ ];
2408
+ // Annotate the CommonJS export names for ESM import in node:
2409
+ 0 && (module.exports = {
2410
+ BackendThinkingChain,
2411
+ BlockingReasonPanel,
2412
+ DAGEdge,
2413
+ DAGLegend,
2414
+ DAGNode,
2415
+ DecisionRequiredPanel,
2416
+ HumanInterventionPanel,
2417
+ MobileTaskShell,
2418
+ TaskApprovalPanel,
2419
+ TaskCard,
2420
+ TaskComponentNames,
2421
+ TaskDependencyList,
2422
+ TaskDetailPanel,
2423
+ TaskHandoffPanel,
2424
+ TaskProgressIndicator,
2425
+ TaskRetryPanel,
2426
+ TaskRunLog,
2427
+ TaskStatusBadge,
2428
+ TaskTable,
2429
+ TaskTimeline,
2430
+ TaskWaveDAG,
2431
+ TaskWaveHeader,
2432
+ TaskWaveTable
2433
+ });
2434
+ //# sourceMappingURL=index.cjs.map