@cadraos/sdk 0.3.8 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/board-host/index.d.cts +74 -6
- package/dist/board-host/index.d.ts +74 -6
- package/dist/board-host/server/index.d.cts +81 -6
- package/dist/board-host/server/index.d.ts +81 -6
- package/dist/chat/index.cjs +2 -47
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +8 -9
- package/dist/chat/index.d.ts +8 -9
- package/dist/chat/index.js +2 -47
- package/dist/chat/index.js.map +1 -1
- package/dist/index.cjs +0 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +0 -43
- package/dist/index.js.map +1 -1
- package/dist/kanban/engine/index.cjs +113 -31
- package/dist/kanban/engine/index.cjs.map +1 -1
- package/dist/kanban/engine/index.d.cts +112 -6
- package/dist/kanban/engine/index.d.ts +112 -6
- package/dist/kanban/engine/index.js +113 -31
- package/dist/kanban/engine/index.js.map +1 -1
- package/dist/kanban/index.cjs +807 -158
- package/dist/kanban/index.cjs.map +1 -1
- package/dist/kanban/index.d.cts +382 -11
- package/dist/kanban/index.d.ts +382 -11
- package/dist/kanban/index.js +802 -159
- package/dist/kanban/index.js.map +1 -1
- package/dist/teams/index.cjs +0 -43
- package/dist/teams/index.cjs.map +1 -1
- package/dist/teams/index.d.cts +7 -0
- package/dist/teams/index.d.ts +7 -0
- package/dist/teams/index.js +0 -43
- package/dist/teams/index.js.map +1 -1
- package/package.json +1 -1
package/dist/kanban/index.js
CHANGED
|
@@ -16,6 +16,10 @@ function mapHandler(h) {
|
|
|
16
16
|
type: h.type,
|
|
17
17
|
agentUuid: h.agent_uuid,
|
|
18
18
|
teamUuid: h.team_uuid,
|
|
19
|
+
// FINDING-6: carry the remote_agent binding through the wire parse. The
|
|
20
|
+
// engine's remote_agent dispatch reads `remoteAgentUuid`; dropping it here
|
|
21
|
+
// made every published remote_agent lane dispatch-dead.
|
|
22
|
+
remoteAgentUuid: h.remote_agent_uuid,
|
|
19
23
|
agentRef: h.agent_ref,
|
|
20
24
|
webhook: h.webhook ? {
|
|
21
25
|
url: h.webhook.url,
|
|
@@ -294,7 +298,7 @@ function useKanbanBoard(options) {
|
|
|
294
298
|
try {
|
|
295
299
|
const full = await adapter.getCard(cardId);
|
|
296
300
|
setSelectedCard(
|
|
297
|
-
(prev) => prev
|
|
301
|
+
(prev) => prev == null || prev.id === cardId ? full : prev
|
|
298
302
|
);
|
|
299
303
|
} catch (err) {
|
|
300
304
|
if (!known) fail(err, "Failed to open card");
|
|
@@ -403,6 +407,27 @@ function useKanbanBoard(options) {
|
|
|
403
407
|
},
|
|
404
408
|
[refresh]
|
|
405
409
|
);
|
|
410
|
+
const deleteCard = useCallback(
|
|
411
|
+
async (cardId) => {
|
|
412
|
+
if (!adapter.deleteCard) return false;
|
|
413
|
+
setError(null);
|
|
414
|
+
const prevCards = cards;
|
|
415
|
+
const prevSelected = selectedCard;
|
|
416
|
+
setCards((prev) => prev.filter((c) => c.id !== cardId));
|
|
417
|
+
setSelectedCard((prev) => prev && prev.id === cardId ? null : prev);
|
|
418
|
+
try {
|
|
419
|
+
await adapter.deleteCard(cardId);
|
|
420
|
+
void silentRefresh();
|
|
421
|
+
return true;
|
|
422
|
+
} catch (err) {
|
|
423
|
+
setCards(prevCards);
|
|
424
|
+
setSelectedCard(prevSelected);
|
|
425
|
+
fail(err, "Failed to delete card");
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
[adapter, cards, selectedCard, silentRefresh, fail]
|
|
430
|
+
);
|
|
406
431
|
return {
|
|
407
432
|
board,
|
|
408
433
|
lanes: board?.lanes ?? [],
|
|
@@ -418,6 +443,7 @@ function useKanbanBoard(options) {
|
|
|
418
443
|
createCard,
|
|
419
444
|
updateCard,
|
|
420
445
|
transitionCard,
|
|
446
|
+
deleteCard,
|
|
421
447
|
handleRunTerminal
|
|
422
448
|
};
|
|
423
449
|
}
|
|
@@ -2293,35 +2319,40 @@ function useCardRun(options) {
|
|
|
2293
2319
|
function cn9(...classes) {
|
|
2294
2320
|
return classes.filter(Boolean).join(" ");
|
|
2295
2321
|
}
|
|
2296
|
-
function
|
|
2297
|
-
switch (
|
|
2298
|
-
case "
|
|
2299
|
-
return
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
case "
|
|
2309
|
-
return
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
case "cancelled":
|
|
2314
|
-
return {
|
|
2315
|
-
label: "Cancelled",
|
|
2316
|
-
className: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
|
|
2317
|
-
};
|
|
2322
|
+
function badgeVariantClass(variant) {
|
|
2323
|
+
switch (variant) {
|
|
2324
|
+
case "default":
|
|
2325
|
+
return "bg-primary text-primary-foreground";
|
|
2326
|
+
case "secondary":
|
|
2327
|
+
return "border border-border bg-secondary text-secondary-foreground";
|
|
2328
|
+
case "outline":
|
|
2329
|
+
return "border border-border text-foreground";
|
|
2330
|
+
case "info":
|
|
2331
|
+
return "border border-info/30 bg-info/15 text-info";
|
|
2332
|
+
case "success":
|
|
2333
|
+
return "border border-success/30 bg-success/15 text-success";
|
|
2334
|
+
case "warning":
|
|
2335
|
+
return "border border-warning/40 bg-warning/15 text-warning";
|
|
2336
|
+
case "destructive":
|
|
2337
|
+
return "border border-destructive/40 bg-destructive/15 text-destructive";
|
|
2338
|
+
case "neutral":
|
|
2318
2339
|
default:
|
|
2319
|
-
return
|
|
2320
|
-
label: "Pending",
|
|
2321
|
-
className: "bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300"
|
|
2322
|
-
};
|
|
2340
|
+
return "border border-border bg-secondary text-muted-foreground";
|
|
2323
2341
|
}
|
|
2324
2342
|
}
|
|
2343
|
+
function runStatusTone(status) {
|
|
2344
|
+
const map = {
|
|
2345
|
+
running: { label: "Running", variant: "info" },
|
|
2346
|
+
completed: { label: "Completed", variant: "success" },
|
|
2347
|
+
failed: { label: "Failed", variant: "destructive" },
|
|
2348
|
+
cancelled: { label: "Cancelled", variant: "neutral" }
|
|
2349
|
+
};
|
|
2350
|
+
const { label, variant } = map[status ?? ""] ?? {
|
|
2351
|
+
label: "Pending",
|
|
2352
|
+
variant: "neutral"
|
|
2353
|
+
};
|
|
2354
|
+
return { label, variant, className: badgeVariantClass(variant) };
|
|
2355
|
+
}
|
|
2325
2356
|
function CardDrawer({
|
|
2326
2357
|
card,
|
|
2327
2358
|
adapter,
|
|
@@ -2440,11 +2471,11 @@ function CardDrawer({
|
|
|
2440
2471
|
role: "dialog",
|
|
2441
2472
|
"aria-label": `Card: ${card.title}`,
|
|
2442
2473
|
className: cn9(
|
|
2443
|
-
"flex h-full w-full sm:max-w-sm flex-col bg-
|
|
2474
|
+
"flex h-full w-full sm:max-w-sm flex-col bg-card border-l border-border shadow-xl",
|
|
2444
2475
|
className
|
|
2445
2476
|
),
|
|
2446
2477
|
children: [
|
|
2447
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-
|
|
2478
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-border px-4 py-3", children: [
|
|
2448
2479
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2449
2480
|
isEditing ? /* @__PURE__ */ jsx(
|
|
2450
2481
|
"input",
|
|
@@ -2454,14 +2485,14 @@ function CardDrawer({
|
|
|
2454
2485
|
onChange: (e) => setEditTitle(e.target.value),
|
|
2455
2486
|
"aria-label": "Card title",
|
|
2456
2487
|
placeholder: "Card title",
|
|
2457
|
-
className: "w-full rounded-md border border-
|
|
2488
|
+
className: "w-full rounded-md border border-input bg-background px-2 py-1 text-base font-semibold text-foreground focus:outline-none focus:ring-2 focus:ring-ring/50"
|
|
2458
2489
|
}
|
|
2459
2490
|
) : /* @__PURE__ */ jsx(
|
|
2460
2491
|
"h2",
|
|
2461
2492
|
{
|
|
2462
2493
|
className: cn9(
|
|
2463
|
-
"text-base font-semibold text-
|
|
2464
|
-
onUpdate && "cursor-text rounded hover:bg-
|
|
2494
|
+
"text-base font-semibold text-foreground truncate",
|
|
2495
|
+
onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
|
|
2465
2496
|
),
|
|
2466
2497
|
onClick: onUpdate ? () => setIsEditing(true) : void 0,
|
|
2467
2498
|
title: onUpdate ? "Click to edit" : void 0,
|
|
@@ -2469,7 +2500,7 @@ function CardDrawer({
|
|
|
2469
2500
|
}
|
|
2470
2501
|
),
|
|
2471
2502
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
|
|
2472
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-
|
|
2503
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: card.lane }),
|
|
2473
2504
|
/* @__PURE__ */ jsx(
|
|
2474
2505
|
"span",
|
|
2475
2506
|
{
|
|
@@ -2489,7 +2520,7 @@ function CardDrawer({
|
|
|
2489
2520
|
type: "button",
|
|
2490
2521
|
onClick: () => setIsEditing(true),
|
|
2491
2522
|
"aria-label": "Edit card",
|
|
2492
|
-
className: "rounded p-1 text-
|
|
2523
|
+
className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2493
2524
|
children: /* @__PURE__ */ jsxs(
|
|
2494
2525
|
"svg",
|
|
2495
2526
|
{
|
|
@@ -2515,7 +2546,7 @@ function CardDrawer({
|
|
|
2515
2546
|
type: "button",
|
|
2516
2547
|
onClick: onClose,
|
|
2517
2548
|
"aria-label": "Close",
|
|
2518
|
-
className: "rounded p-1 text-
|
|
2549
|
+
className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
2519
2550
|
children: /* @__PURE__ */ jsxs(
|
|
2520
2551
|
"svg",
|
|
2521
2552
|
{
|
|
@@ -2537,8 +2568,8 @@ function CardDrawer({
|
|
|
2537
2568
|
)
|
|
2538
2569
|
] })
|
|
2539
2570
|
] }),
|
|
2540
|
-
isEditing ? /* @__PURE__ */ jsxs("div", { className: "border-b border-
|
|
2541
|
-
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-
|
|
2571
|
+
isEditing ? /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-4 py-3", children: [
|
|
2572
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Description" }),
|
|
2542
2573
|
/* @__PURE__ */ jsx(
|
|
2543
2574
|
"textarea",
|
|
2544
2575
|
{
|
|
@@ -2547,7 +2578,7 @@ function CardDrawer({
|
|
|
2547
2578
|
"aria-label": "Card description",
|
|
2548
2579
|
rows: 4,
|
|
2549
2580
|
placeholder: "Add a description\u2026",
|
|
2550
|
-
className: "w-full resize-y rounded-md border border-
|
|
2581
|
+
className: "w-full resize-y rounded-md border border-input bg-background px-2 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring/50"
|
|
2551
2582
|
}
|
|
2552
2583
|
),
|
|
2553
2584
|
/* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
|
|
@@ -2557,7 +2588,7 @@ function CardDrawer({
|
|
|
2557
2588
|
type: "button",
|
|
2558
2589
|
onClick: handleCancel,
|
|
2559
2590
|
disabled: isSaving,
|
|
2560
|
-
className: "rounded-md px-3 py-1.5 text-sm font-medium text-
|
|
2591
|
+
className: "rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground hover:bg-muted disabled:opacity-50",
|
|
2561
2592
|
children: "Cancel"
|
|
2562
2593
|
}
|
|
2563
2594
|
),
|
|
@@ -2567,26 +2598,27 @@ function CardDrawer({
|
|
|
2567
2598
|
type: "button",
|
|
2568
2599
|
onClick: () => void handleSave(),
|
|
2569
2600
|
disabled: isSaving,
|
|
2570
|
-
className: "rounded-md bg-
|
|
2601
|
+
className: "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:opacity-50",
|
|
2571
2602
|
children: isSaving ? "Saving\u2026" : "Save"
|
|
2572
2603
|
}
|
|
2573
2604
|
)
|
|
2574
2605
|
] })
|
|
2575
|
-
] }) : (card.description || onUpdate) && /* @__PURE__ */ jsx("div", { className: "border-b border-
|
|
2606
|
+
] }) : (card.description || onUpdate) && /* @__PURE__ */ jsx("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
2576
2607
|
"p",
|
|
2577
2608
|
{
|
|
2578
2609
|
className: cn9(
|
|
2579
2610
|
"text-sm whitespace-pre-wrap",
|
|
2580
|
-
card.description ? "text-
|
|
2581
|
-
onUpdate && "cursor-text rounded hover:bg-
|
|
2611
|
+
card.description ? "text-muted-foreground" : "italic text-muted-foreground/70",
|
|
2612
|
+
onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
|
|
2582
2613
|
),
|
|
2583
2614
|
onClick: onUpdate ? () => setIsEditing(true) : void 0,
|
|
2584
2615
|
title: onUpdate ? "Click to edit" : void 0,
|
|
2585
2616
|
children: card.description || "Add a description\u2026"
|
|
2586
2617
|
}
|
|
2587
2618
|
) }),
|
|
2588
|
-
showTransitionButtons ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 border-b border-
|
|
2589
|
-
isHumanGate &&
|
|
2619
|
+
showTransitionButtons ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 border-b border-border px-4 py-2", children: [
|
|
2620
|
+
isHumanGate && // Human gate = needs-you → warning (amber is reserved for this, F3).
|
|
2621
|
+
/* @__PURE__ */ jsx("span", { className: "mr-auto text-xs font-medium text-warning", children: "Awaiting your approval" }),
|
|
2590
2622
|
manualEdges.map((t, i) => {
|
|
2591
2623
|
const toLabel = laneByKey.get(t.toLane)?.label ?? t.toLane;
|
|
2592
2624
|
const isPrimary = isHumanGate ? i === 0 : false;
|
|
@@ -2599,26 +2631,27 @@ function CardDrawer({
|
|
|
2599
2631
|
onClick: () => void handleTransition(t.toLane),
|
|
2600
2632
|
disabled: pendingLane !== null,
|
|
2601
2633
|
className: cn9(
|
|
2602
|
-
|
|
2603
|
-
|
|
2634
|
+
// ≥44px touch (M12/F5) — density kept via padding, not height.
|
|
2635
|
+
"inline-flex min-h-11 items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:opacity-50",
|
|
2636
|
+
isPrimary ? "border border-border bg-secondary text-secondary-foreground hover:bg-muted" : "border border-border text-foreground hover:bg-muted"
|
|
2604
2637
|
),
|
|
2605
2638
|
children: busy ? "Moving\u2026" : label
|
|
2606
2639
|
},
|
|
2607
2640
|
t.id
|
|
2608
2641
|
);
|
|
2609
2642
|
})
|
|
2610
|
-
] }) : onAdvance && canAdvance && !isEditing && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end border-b border-
|
|
2643
|
+
] }) : onAdvance && canAdvance && !isEditing && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx(
|
|
2611
2644
|
"button",
|
|
2612
2645
|
{
|
|
2613
2646
|
type: "button",
|
|
2614
2647
|
onClick: () => void handleAdvance(),
|
|
2615
2648
|
disabled: isAdvancing,
|
|
2616
|
-
className: "rounded-md border border-
|
|
2649
|
+
className: "inline-flex min-h-11 items-center justify-center rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:opacity-50",
|
|
2617
2650
|
children: isAdvancing ? "Advancing\u2026" : "Advance \u2192"
|
|
2618
2651
|
}
|
|
2619
2652
|
) }),
|
|
2620
2653
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
|
|
2621
|
-
!hasExecution ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-
|
|
2654
|
+
!hasExecution ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No run yet for this card." }) }) : /* @__PURE__ */ jsx(
|
|
2622
2655
|
MessageList,
|
|
2623
2656
|
{
|
|
2624
2657
|
messages: run.messages,
|
|
@@ -2631,14 +2664,14 @@ function CardDrawer({
|
|
|
2631
2664
|
className: "flex-1"
|
|
2632
2665
|
}
|
|
2633
2666
|
),
|
|
2634
|
-
failed && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-
|
|
2635
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm text-
|
|
2667
|
+
failed && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-destructive/40 bg-destructive/10 px-4 py-3", children: [
|
|
2668
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: run.error ?? "Run failed." }),
|
|
2636
2669
|
onRetry && /* @__PURE__ */ jsx(
|
|
2637
2670
|
"button",
|
|
2638
2671
|
{
|
|
2639
2672
|
type: "button",
|
|
2640
2673
|
onClick: () => onRetry(card),
|
|
2641
|
-
className: "shrink-0 rounded-md bg-
|
|
2674
|
+
className: "inline-flex min-h-11 shrink-0 items-center justify-center rounded-md border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
|
|
2642
2675
|
children: "Retry"
|
|
2643
2676
|
}
|
|
2644
2677
|
)
|
|
@@ -2680,10 +2713,54 @@ function Spinner({ className }) {
|
|
|
2680
2713
|
}
|
|
2681
2714
|
);
|
|
2682
2715
|
}
|
|
2683
|
-
function
|
|
2716
|
+
function ClockIcon2() {
|
|
2717
|
+
return /* @__PURE__ */ jsxs(
|
|
2718
|
+
"svg",
|
|
2719
|
+
{
|
|
2720
|
+
viewBox: "0 0 24 24",
|
|
2721
|
+
fill: "none",
|
|
2722
|
+
stroke: "currentColor",
|
|
2723
|
+
strokeWidth: "2",
|
|
2724
|
+
strokeLinecap: "round",
|
|
2725
|
+
strokeLinejoin: "round",
|
|
2726
|
+
"aria-hidden": true,
|
|
2727
|
+
className: "h-2.5 w-2.5",
|
|
2728
|
+
children: [
|
|
2729
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
2730
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
2731
|
+
]
|
|
2732
|
+
}
|
|
2733
|
+
);
|
|
2734
|
+
}
|
|
2735
|
+
function KanbanCard({
|
|
2736
|
+
card,
|
|
2737
|
+
onClick,
|
|
2738
|
+
selected,
|
|
2739
|
+
dueState,
|
|
2740
|
+
draggable,
|
|
2741
|
+
onDragStart,
|
|
2742
|
+
onDragEnd,
|
|
2743
|
+
className
|
|
2744
|
+
}) {
|
|
2684
2745
|
const runStatus = card.run?.status;
|
|
2685
|
-
const
|
|
2686
|
-
const
|
|
2746
|
+
const isWorking = runStatus === "running";
|
|
2747
|
+
const failed = runStatus === "failed";
|
|
2748
|
+
let chip = null;
|
|
2749
|
+
if (failed) {
|
|
2750
|
+
chip = { label: "Failed", variant: "destructive" };
|
|
2751
|
+
} else if (dueState === "ready") {
|
|
2752
|
+
chip = { label: "Ready", variant: "success" };
|
|
2753
|
+
} else if (runStatus === "completed") {
|
|
2754
|
+
const t = runStatusTone(runStatus);
|
|
2755
|
+
chip = { label: t.label, variant: t.variant };
|
|
2756
|
+
} else if (dueState === "due") {
|
|
2757
|
+
chip = { label: "Due", variant: "neutral", icon: "clock" };
|
|
2758
|
+
} else if (dueState === "waiting") {
|
|
2759
|
+
chip = { label: "Waiting", variant: "neutral" };
|
|
2760
|
+
} else if (runStatus) {
|
|
2761
|
+
const t = runStatusTone(runStatus);
|
|
2762
|
+
chip = { label: t.label, variant: t.variant };
|
|
2763
|
+
}
|
|
2687
2764
|
return /* @__PURE__ */ jsxs(
|
|
2688
2765
|
"button",
|
|
2689
2766
|
{
|
|
@@ -2691,47 +2768,438 @@ function KanbanCard({ card, onClick, selected, className }) {
|
|
|
2691
2768
|
onClick: () => onClick?.(card),
|
|
2692
2769
|
"aria-pressed": selected,
|
|
2693
2770
|
"aria-busy": isWorking,
|
|
2771
|
+
draggable,
|
|
2772
|
+
onDragStart: draggable ? (e) => {
|
|
2773
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2774
|
+
e.dataTransfer.setData("text/plain", card.id);
|
|
2775
|
+
onDragStart?.(card);
|
|
2776
|
+
} : void 0,
|
|
2777
|
+
onDragEnd: draggable ? () => onDragEnd?.() : void 0,
|
|
2694
2778
|
className: cn9(
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
"
|
|
2698
|
-
|
|
2779
|
+
// Solid card: fill + border + a faint token top-sheen (adapts to theme),
|
|
2780
|
+
// matching the host's cockpit cards (work-shell v2 visual pass).
|
|
2781
|
+
"relative w-full text-left rounded-xl border bg-card bg-gradient-to-b from-foreground/[0.03] to-transparent p-3 shadow-sm motion-safe:transition-shadow",
|
|
2782
|
+
"hover:shadow-md hover:border-primary/40",
|
|
2783
|
+
"focus:outline-none focus:ring-2 focus:ring-ring/50",
|
|
2784
|
+
draggable && "cursor-grab active:cursor-grabbing",
|
|
2785
|
+
// The working card carries the SINGLE focal motion on the board (F11).
|
|
2786
|
+
isWorking ? "border-info/60 ring-2 ring-info/40 animate-pulse" : selected ? "border-primary/60 ring-2 ring-primary/30" : "border-border",
|
|
2699
2787
|
className
|
|
2700
2788
|
),
|
|
2701
2789
|
children: [
|
|
2702
|
-
isWorking && /* @__PURE__ */ jsxs(
|
|
2703
|
-
"span",
|
|
2704
|
-
{
|
|
2705
|
-
"aria-hidden": true,
|
|
2706
|
-
className: "pointer-events-none absolute -top-1 -right-1 flex h-3 w-3",
|
|
2707
|
-
children: [
|
|
2708
|
-
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }),
|
|
2709
|
-
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-3 w-3 rounded-full bg-amber-500" })
|
|
2710
|
-
]
|
|
2711
|
-
}
|
|
2712
|
-
),
|
|
2713
2790
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
2714
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-
|
|
2715
|
-
isWorking ? /* @__PURE__ */ jsxs(
|
|
2716
|
-
/* @__PURE__ */ jsx(Spinner, {}),
|
|
2717
|
-
"Working"
|
|
2718
|
-
] }) : tone && /* @__PURE__ */ jsx(
|
|
2791
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground line-clamp-2", children: card.title }),
|
|
2792
|
+
isWorking ? /* @__PURE__ */ jsxs(
|
|
2719
2793
|
"span",
|
|
2720
2794
|
{
|
|
2721
2795
|
className: cn9(
|
|
2722
|
-
"shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
|
|
2723
|
-
|
|
2796
|
+
"shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
|
|
2797
|
+
badgeVariantClass("info")
|
|
2724
2798
|
),
|
|
2725
|
-
children:
|
|
2799
|
+
children: [
|
|
2800
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
2801
|
+
"Working"
|
|
2802
|
+
]
|
|
2803
|
+
}
|
|
2804
|
+
) : chip && /* @__PURE__ */ jsxs(
|
|
2805
|
+
"span",
|
|
2806
|
+
{
|
|
2807
|
+
className: cn9(
|
|
2808
|
+
"shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
|
|
2809
|
+
badgeVariantClass(chip.variant)
|
|
2810
|
+
),
|
|
2811
|
+
children: [
|
|
2812
|
+
chip.icon === "clock" && /* @__PURE__ */ jsx(ClockIcon2, {}),
|
|
2813
|
+
chip.label
|
|
2814
|
+
]
|
|
2726
2815
|
}
|
|
2727
2816
|
)
|
|
2728
2817
|
] }),
|
|
2729
|
-
card.description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-
|
|
2730
|
-
card.type && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("span", { className: "inline-block rounded bg-
|
|
2818
|
+
card.description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: card.description }),
|
|
2819
|
+
card.type && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("span", { className: "inline-block rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: card.type }) })
|
|
2731
2820
|
]
|
|
2732
2821
|
}
|
|
2733
2822
|
);
|
|
2734
2823
|
}
|
|
2824
|
+
function CapMeter({ count, cap, className }) {
|
|
2825
|
+
const pct = cap > 0 ? Math.min(100, Math.round(count / cap * 100)) : 0;
|
|
2826
|
+
return /* @__PURE__ */ jsxs(
|
|
2827
|
+
"div",
|
|
2828
|
+
{
|
|
2829
|
+
className: cn9(
|
|
2830
|
+
"mt-2 flex items-center gap-2 text-[11px] text-muted-foreground",
|
|
2831
|
+
className
|
|
2832
|
+
),
|
|
2833
|
+
children: [
|
|
2834
|
+
/* @__PURE__ */ jsxs("span", { className: "tabular-nums font-medium", children: [
|
|
2835
|
+
count,
|
|
2836
|
+
"/",
|
|
2837
|
+
cap
|
|
2838
|
+
] }),
|
|
2839
|
+
/* @__PURE__ */ jsx(
|
|
2840
|
+
"span",
|
|
2841
|
+
{
|
|
2842
|
+
role: "progressbar",
|
|
2843
|
+
"aria-valuemin": 0,
|
|
2844
|
+
"aria-valuemax": cap,
|
|
2845
|
+
"aria-valuenow": count,
|
|
2846
|
+
"aria-label": `${count} of ${cap} used`,
|
|
2847
|
+
className: "h-1 flex-1 overflow-hidden rounded-full bg-secondary",
|
|
2848
|
+
children: /* @__PURE__ */ jsx(
|
|
2849
|
+
"span",
|
|
2850
|
+
{
|
|
2851
|
+
className: "block h-full rounded-full bg-primary",
|
|
2852
|
+
style: { width: `${pct}%` }
|
|
2853
|
+
}
|
|
2854
|
+
)
|
|
2855
|
+
}
|
|
2856
|
+
)
|
|
2857
|
+
]
|
|
2858
|
+
}
|
|
2859
|
+
);
|
|
2860
|
+
}
|
|
2861
|
+
var AGENT_GROUPS = [
|
|
2862
|
+
{ kind: "cadra", label: "Cadra agents" },
|
|
2863
|
+
{ kind: "remote", label: "Your own agents" },
|
|
2864
|
+
{ kind: "human", label: "Teammates" }
|
|
2865
|
+
];
|
|
2866
|
+
function AgentKindIcon({ kind }) {
|
|
2867
|
+
const common = {
|
|
2868
|
+
viewBox: "0 0 24 24",
|
|
2869
|
+
fill: "none",
|
|
2870
|
+
stroke: "currentColor",
|
|
2871
|
+
strokeWidth: 2,
|
|
2872
|
+
strokeLinecap: "round",
|
|
2873
|
+
strokeLinejoin: "round",
|
|
2874
|
+
className: "h-3.5 w-3.5",
|
|
2875
|
+
"aria-hidden": true
|
|
2876
|
+
};
|
|
2877
|
+
if (kind === "remote") {
|
|
2878
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2879
|
+
/* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
|
|
2880
|
+
/* @__PURE__ */ jsx("path", { d: "M8 21h8M12 17v4" })
|
|
2881
|
+
] });
|
|
2882
|
+
}
|
|
2883
|
+
if (kind === "human") {
|
|
2884
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2885
|
+
/* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
|
|
2886
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "7", r: "4" })
|
|
2887
|
+
] });
|
|
2888
|
+
}
|
|
2889
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2890
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "10", rx: "2" }),
|
|
2891
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "2" }),
|
|
2892
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v4M8 16h.01M16 16h.01" })
|
|
2893
|
+
] });
|
|
2894
|
+
}
|
|
2895
|
+
function OnlineDot({ online }) {
|
|
2896
|
+
if (online === void 0) return null;
|
|
2897
|
+
return /* @__PURE__ */ jsx(
|
|
2898
|
+
"span",
|
|
2899
|
+
{
|
|
2900
|
+
"aria-label": online ? "connected" : "offline",
|
|
2901
|
+
className: cn9(
|
|
2902
|
+
"h-1.5 w-1.5 shrink-0 rounded-full",
|
|
2903
|
+
online ? "bg-success" : "bg-muted-foreground"
|
|
2904
|
+
)
|
|
2905
|
+
}
|
|
2906
|
+
);
|
|
2907
|
+
}
|
|
2908
|
+
function RemoteBadge() {
|
|
2909
|
+
return /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full border border-accent/40 bg-accent/15 px-1.5 py-px text-[10px] font-semibold leading-4 text-accent", children: "Remote" });
|
|
2910
|
+
}
|
|
2911
|
+
function LaneAgentPicker({
|
|
2912
|
+
selected,
|
|
2913
|
+
options,
|
|
2914
|
+
onSelect,
|
|
2915
|
+
canEdit = true,
|
|
2916
|
+
lockedReason,
|
|
2917
|
+
onEditAgent,
|
|
2918
|
+
className
|
|
2919
|
+
}) {
|
|
2920
|
+
const [open, setOpen] = useState(false);
|
|
2921
|
+
const [placement, setPlacement] = useState(null);
|
|
2922
|
+
const rootRef = useRef(null);
|
|
2923
|
+
const triggerRef = useRef(null);
|
|
2924
|
+
const editable = canEdit && !!onSelect && (options?.length ?? 0) > 0;
|
|
2925
|
+
const openMenu = () => {
|
|
2926
|
+
const el = triggerRef.current;
|
|
2927
|
+
if (el && typeof window !== "undefined") {
|
|
2928
|
+
const rect = el.getBoundingClientRect();
|
|
2929
|
+
const vw = window.innerWidth || 0;
|
|
2930
|
+
const vh = window.innerHeight || 0;
|
|
2931
|
+
const margin = 8;
|
|
2932
|
+
const gap = 4;
|
|
2933
|
+
const desired = 288;
|
|
2934
|
+
const width = Math.max(
|
|
2935
|
+
Math.min(rect.width || 224, Math.max(vw - margin * 2, 160)),
|
|
2936
|
+
160
|
|
2937
|
+
);
|
|
2938
|
+
const left = Math.min(
|
|
2939
|
+
Math.max(rect.left, margin),
|
|
2940
|
+
Math.max(vw - width - margin, margin)
|
|
2941
|
+
);
|
|
2942
|
+
const spaceBelow = vh - rect.bottom - gap - margin;
|
|
2943
|
+
const spaceAbove = rect.top - gap - margin;
|
|
2944
|
+
const flipUp = spaceBelow < desired && spaceAbove > spaceBelow;
|
|
2945
|
+
const maxHeight = Math.max(
|
|
2946
|
+
Math.min(desired, flipUp ? spaceAbove : spaceBelow),
|
|
2947
|
+
120
|
|
2948
|
+
);
|
|
2949
|
+
setPlacement(
|
|
2950
|
+
flipUp ? { bottom: Math.max(vh - rect.top + gap, margin), left, width, maxHeight } : { top: rect.bottom + gap, left, width, maxHeight }
|
|
2951
|
+
);
|
|
2952
|
+
} else {
|
|
2953
|
+
setPlacement(null);
|
|
2954
|
+
}
|
|
2955
|
+
setOpen(true);
|
|
2956
|
+
};
|
|
2957
|
+
useEffect(() => {
|
|
2958
|
+
if (!open) return;
|
|
2959
|
+
const onDocDown = (e) => {
|
|
2960
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) {
|
|
2961
|
+
setOpen(false);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
const onKey = (e) => {
|
|
2965
|
+
if (e.key === "Escape") setOpen(false);
|
|
2966
|
+
};
|
|
2967
|
+
document.addEventListener("mousedown", onDocDown);
|
|
2968
|
+
document.addEventListener("touchstart", onDocDown);
|
|
2969
|
+
document.addEventListener("keydown", onKey);
|
|
2970
|
+
return () => {
|
|
2971
|
+
document.removeEventListener("mousedown", onDocDown);
|
|
2972
|
+
document.removeEventListener("touchstart", onDocDown);
|
|
2973
|
+
document.removeEventListener("keydown", onKey);
|
|
2974
|
+
};
|
|
2975
|
+
}, [open]);
|
|
2976
|
+
const kind = selected?.kind ?? "cadra";
|
|
2977
|
+
const displayLabel = selected?.label ?? "Unassigned";
|
|
2978
|
+
const face = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2979
|
+
/* @__PURE__ */ jsx(
|
|
2980
|
+
"span",
|
|
2981
|
+
{
|
|
2982
|
+
className: cn9(
|
|
2983
|
+
"grid h-5 w-5 shrink-0 place-items-center rounded-full",
|
|
2984
|
+
kind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
|
|
2985
|
+
),
|
|
2986
|
+
children: /* @__PURE__ */ jsx(AgentKindIcon, { kind })
|
|
2987
|
+
}
|
|
2988
|
+
),
|
|
2989
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: displayLabel }),
|
|
2990
|
+
kind === "remote" && /* @__PURE__ */ jsx(RemoteBadge, {}),
|
|
2991
|
+
/* @__PURE__ */ jsx(OnlineDot, { online: selected?.online })
|
|
2992
|
+
] });
|
|
2993
|
+
const grouped = AGENT_GROUPS.map((group) => ({
|
|
2994
|
+
...group,
|
|
2995
|
+
items: (options ?? []).filter((opt) => (opt.kind ?? "cadra") === group.kind)
|
|
2996
|
+
})).filter((group) => group.items.length > 0);
|
|
2997
|
+
const menuStyle = placement ? {
|
|
2998
|
+
position: "fixed",
|
|
2999
|
+
top: placement.top,
|
|
3000
|
+
bottom: placement.bottom,
|
|
3001
|
+
left: placement.left,
|
|
3002
|
+
width: placement.width,
|
|
3003
|
+
maxHeight: placement.maxHeight
|
|
3004
|
+
} : void 0;
|
|
3005
|
+
return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn9("relative mt-2 w-full", className), children: [
|
|
3006
|
+
editable ? /* @__PURE__ */ jsxs(
|
|
3007
|
+
"button",
|
|
3008
|
+
{
|
|
3009
|
+
ref: triggerRef,
|
|
3010
|
+
type: "button",
|
|
3011
|
+
onClick: () => open ? setOpen(false) : openMenu(),
|
|
3012
|
+
"aria-haspopup": "listbox",
|
|
3013
|
+
"aria-expanded": open,
|
|
3014
|
+
"aria-label": `Assigned agent: ${displayLabel}. Change`,
|
|
3015
|
+
className: cn9(
|
|
3016
|
+
"flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-card px-2",
|
|
3017
|
+
"text-xs font-semibold text-foreground",
|
|
3018
|
+
"hover:border-primary/50 focus:outline-none focus:ring-2 focus:ring-ring/50"
|
|
3019
|
+
),
|
|
3020
|
+
children: [
|
|
3021
|
+
face,
|
|
3022
|
+
/* @__PURE__ */ jsx(
|
|
3023
|
+
"svg",
|
|
3024
|
+
{
|
|
3025
|
+
viewBox: "0 0 24 24",
|
|
3026
|
+
fill: "none",
|
|
3027
|
+
stroke: "currentColor",
|
|
3028
|
+
strokeWidth: 2,
|
|
3029
|
+
strokeLinecap: "round",
|
|
3030
|
+
strokeLinejoin: "round",
|
|
3031
|
+
"aria-hidden": true,
|
|
3032
|
+
className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
|
|
3033
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" })
|
|
3034
|
+
}
|
|
3035
|
+
)
|
|
3036
|
+
]
|
|
3037
|
+
}
|
|
3038
|
+
) : /* @__PURE__ */ jsxs(
|
|
3039
|
+
"div",
|
|
3040
|
+
{
|
|
3041
|
+
role: "group",
|
|
3042
|
+
"aria-label": `Assigned agent: ${displayLabel}`,
|
|
3043
|
+
className: cn9(
|
|
3044
|
+
"flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-muted/60 px-2",
|
|
3045
|
+
"text-xs font-semibold text-muted-foreground"
|
|
3046
|
+
),
|
|
3047
|
+
children: [
|
|
3048
|
+
face,
|
|
3049
|
+
/* @__PURE__ */ jsxs(
|
|
3050
|
+
"svg",
|
|
3051
|
+
{
|
|
3052
|
+
viewBox: "0 0 24 24",
|
|
3053
|
+
fill: "none",
|
|
3054
|
+
stroke: "currentColor",
|
|
3055
|
+
strokeWidth: 2,
|
|
3056
|
+
strokeLinecap: "round",
|
|
3057
|
+
strokeLinejoin: "round",
|
|
3058
|
+
"aria-hidden": true,
|
|
3059
|
+
className: "h-3.5 w-3.5 shrink-0",
|
|
3060
|
+
children: [
|
|
3061
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
|
|
3062
|
+
/* @__PURE__ */ jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
|
|
3063
|
+
]
|
|
3064
|
+
}
|
|
3065
|
+
)
|
|
3066
|
+
]
|
|
3067
|
+
}
|
|
3068
|
+
),
|
|
3069
|
+
!editable && lockedReason && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
3070
|
+
/* @__PURE__ */ jsxs(
|
|
3071
|
+
"svg",
|
|
3072
|
+
{
|
|
3073
|
+
viewBox: "0 0 24 24",
|
|
3074
|
+
fill: "none",
|
|
3075
|
+
stroke: "currentColor",
|
|
3076
|
+
strokeWidth: 2,
|
|
3077
|
+
strokeLinecap: "round",
|
|
3078
|
+
strokeLinejoin: "round",
|
|
3079
|
+
"aria-hidden": true,
|
|
3080
|
+
className: "h-3 w-3 shrink-0",
|
|
3081
|
+
children: [
|
|
3082
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
|
|
3083
|
+
/* @__PURE__ */ jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
|
|
3084
|
+
]
|
|
3085
|
+
}
|
|
3086
|
+
),
|
|
3087
|
+
/* @__PURE__ */ jsx("span", { children: lockedReason })
|
|
3088
|
+
] }),
|
|
3089
|
+
editable && open && /* @__PURE__ */ jsxs(
|
|
3090
|
+
"div",
|
|
3091
|
+
{
|
|
3092
|
+
style: menuStyle,
|
|
3093
|
+
className: cn9(
|
|
3094
|
+
"flex flex-col overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
|
|
3095
|
+
menuStyle ? "z-50" : "absolute z-20 mt-1 max-h-64 w-full"
|
|
3096
|
+
),
|
|
3097
|
+
children: [
|
|
3098
|
+
/* @__PURE__ */ jsx(
|
|
3099
|
+
"ul",
|
|
3100
|
+
{
|
|
3101
|
+
role: "listbox",
|
|
3102
|
+
"aria-label": "Assign an agent",
|
|
3103
|
+
className: "min-h-0 flex-1 overflow-y-auto p-1",
|
|
3104
|
+
children: grouped.map((group) => /* @__PURE__ */ jsxs("li", { role: "presentation", children: [
|
|
3105
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: group.label }),
|
|
3106
|
+
/* @__PURE__ */ jsx("ul", { role: "group", "aria-label": group.label, children: group.items.map((opt) => {
|
|
3107
|
+
const isSelected = opt.id === selected?.id;
|
|
3108
|
+
const optKind = opt.kind ?? "cadra";
|
|
3109
|
+
return /* @__PURE__ */ jsx("li", { role: "option", "aria-selected": isSelected, children: /* @__PURE__ */ jsxs(
|
|
3110
|
+
"button",
|
|
3111
|
+
{
|
|
3112
|
+
type: "button",
|
|
3113
|
+
onClick: () => {
|
|
3114
|
+
onSelect(opt.id);
|
|
3115
|
+
setOpen(false);
|
|
3116
|
+
},
|
|
3117
|
+
className: cn9(
|
|
3118
|
+
"flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium",
|
|
3119
|
+
"hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
|
|
3120
|
+
isSelected ? "text-primary" : "text-foreground"
|
|
3121
|
+
),
|
|
3122
|
+
children: [
|
|
3123
|
+
/* @__PURE__ */ jsx(
|
|
3124
|
+
"span",
|
|
3125
|
+
{
|
|
3126
|
+
className: cn9(
|
|
3127
|
+
"grid h-5 w-5 shrink-0 place-items-center rounded-full",
|
|
3128
|
+
optKind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
|
|
3129
|
+
),
|
|
3130
|
+
children: /* @__PURE__ */ jsx(AgentKindIcon, { kind: optKind })
|
|
3131
|
+
}
|
|
3132
|
+
),
|
|
3133
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }),
|
|
3134
|
+
optKind === "remote" && /* @__PURE__ */ jsx(RemoteBadge, {}),
|
|
3135
|
+
/* @__PURE__ */ jsx(OnlineDot, { online: opt.online }),
|
|
3136
|
+
isSelected && /* @__PURE__ */ jsx(
|
|
3137
|
+
"svg",
|
|
3138
|
+
{
|
|
3139
|
+
viewBox: "0 0 24 24",
|
|
3140
|
+
fill: "none",
|
|
3141
|
+
stroke: "currentColor",
|
|
3142
|
+
strokeWidth: 2,
|
|
3143
|
+
strokeLinecap: "round",
|
|
3144
|
+
strokeLinejoin: "round",
|
|
3145
|
+
"aria-hidden": true,
|
|
3146
|
+
className: "h-3.5 w-3.5 shrink-0",
|
|
3147
|
+
children: /* @__PURE__ */ jsx("path", { d: "M20 6L9 17l-5-5" })
|
|
3148
|
+
}
|
|
3149
|
+
)
|
|
3150
|
+
]
|
|
3151
|
+
}
|
|
3152
|
+
) }, opt.id);
|
|
3153
|
+
}) })
|
|
3154
|
+
] }, group.kind))
|
|
3155
|
+
}
|
|
3156
|
+
),
|
|
3157
|
+
onEditAgent && selected && /* @__PURE__ */ jsx("div", { className: "border-t border-border p-1", children: /* @__PURE__ */ jsxs(
|
|
3158
|
+
"button",
|
|
3159
|
+
{
|
|
3160
|
+
type: "button",
|
|
3161
|
+
onClick: () => {
|
|
3162
|
+
onEditAgent(selected.id);
|
|
3163
|
+
setOpen(false);
|
|
3164
|
+
},
|
|
3165
|
+
className: cn9(
|
|
3166
|
+
"flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium text-foreground",
|
|
3167
|
+
"hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50"
|
|
3168
|
+
),
|
|
3169
|
+
children: [
|
|
3170
|
+
/* @__PURE__ */ jsx(
|
|
3171
|
+
"svg",
|
|
3172
|
+
{
|
|
3173
|
+
viewBox: "0 0 24 24",
|
|
3174
|
+
fill: "none",
|
|
3175
|
+
stroke: "currentColor",
|
|
3176
|
+
strokeWidth: 2,
|
|
3177
|
+
strokeLinecap: "round",
|
|
3178
|
+
strokeLinejoin: "round",
|
|
3179
|
+
"aria-hidden": true,
|
|
3180
|
+
className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
|
|
3181
|
+
children: /* @__PURE__ */ jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" })
|
|
3182
|
+
}
|
|
3183
|
+
),
|
|
3184
|
+
"Edit this agent"
|
|
3185
|
+
]
|
|
3186
|
+
}
|
|
3187
|
+
) })
|
|
3188
|
+
]
|
|
3189
|
+
}
|
|
3190
|
+
)
|
|
3191
|
+
] });
|
|
3192
|
+
}
|
|
3193
|
+
function dueStateFor(card, lane, now) {
|
|
3194
|
+
const rs = card.run?.status;
|
|
3195
|
+
if (rs === "completed" && !lane.terminal) return "ready";
|
|
3196
|
+
if (card.nextDueAt) {
|
|
3197
|
+
const t = Date.parse(card.nextDueAt);
|
|
3198
|
+
if (!Number.isNaN(t) && t <= now) return "due";
|
|
3199
|
+
}
|
|
3200
|
+
if (lane.trigger === "scheduled" && !rs) return "waiting";
|
|
3201
|
+
return void 0;
|
|
3202
|
+
}
|
|
2735
3203
|
function KanbanColumn({
|
|
2736
3204
|
lane,
|
|
2737
3205
|
cards,
|
|
@@ -2739,29 +3207,100 @@ function KanbanColumn({
|
|
|
2739
3207
|
selectedCardId,
|
|
2740
3208
|
onCreateCard,
|
|
2741
3209
|
showCreate,
|
|
3210
|
+
count,
|
|
3211
|
+
agentPicker,
|
|
3212
|
+
canEditHandlers,
|
|
3213
|
+
draggableCards,
|
|
3214
|
+
dragActive,
|
|
3215
|
+
dropAllowed,
|
|
3216
|
+
onCardDragStart,
|
|
3217
|
+
onCardDragEnd,
|
|
3218
|
+
onLaneDrop,
|
|
2742
3219
|
className
|
|
2743
3220
|
}) {
|
|
3221
|
+
const headerCount = count ?? cards.length;
|
|
3222
|
+
const isScheduled = lane.trigger === "scheduled";
|
|
3223
|
+
const showCapMeter = count !== void 0 && typeof lane.cap === "number" && lane.cap > 0;
|
|
3224
|
+
const showAgentBadge = lane.handler?.type === "cadra_agent" && !agentPicker;
|
|
3225
|
+
const now = Date.now();
|
|
2744
3226
|
return /* @__PURE__ */ jsxs(
|
|
2745
3227
|
"div",
|
|
2746
3228
|
{
|
|
3229
|
+
onDragOver: dropAllowed ? (e) => {
|
|
3230
|
+
e.preventDefault();
|
|
3231
|
+
e.dataTransfer.dropEffect = "move";
|
|
3232
|
+
} : void 0,
|
|
3233
|
+
onDrop: dropAllowed && onLaneDrop ? (e) => {
|
|
3234
|
+
e.preventDefault();
|
|
3235
|
+
onLaneDrop(lane);
|
|
3236
|
+
} : void 0,
|
|
2747
3237
|
className: cn9(
|
|
2748
|
-
"flex w-[85vw] max-w-sm snap-start shrink-0 flex-col rounded-xl bg-
|
|
3238
|
+
"flex w-[85vw] max-w-sm snap-start shrink-0 flex-col rounded-xl bg-muted/40 border border-border transition sm:w-72 sm:max-w-none",
|
|
3239
|
+
isScheduled && "border-border",
|
|
3240
|
+
// MV4 drag affordance: allowed targets glow; other lanes dim so the
|
|
3241
|
+
// valid drop zones read at a glance.
|
|
3242
|
+
dragActive && dropAllowed && "border-primary/60 ring-2 ring-primary/40 bg-primary/5",
|
|
3243
|
+
dragActive && !dropAllowed && "opacity-55",
|
|
2749
3244
|
className
|
|
2750
3245
|
),
|
|
2751
3246
|
children: [
|
|
2752
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
2753
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2
|
|
2754
|
-
/* @__PURE__ */
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
3247
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2.5", children: [
|
|
3248
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
3249
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
3250
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground truncate", children: lane.label }),
|
|
3251
|
+
showAgentBadge && /* @__PURE__ */ jsx(
|
|
3252
|
+
"span",
|
|
3253
|
+
{
|
|
3254
|
+
title: lane.handler?.agentRef ? `Agent: ${lane.handler.agentRef}` : "Agent lane",
|
|
3255
|
+
className: cn9(
|
|
3256
|
+
// A static lane marker — NOT a run state. Uses neutral tokens,
|
|
3257
|
+
// never `info` (which means "running" in the F3 vocabulary).
|
|
3258
|
+
"rounded px-1.5 py-0.5 text-[10px] font-medium",
|
|
3259
|
+
badgeVariantClass("neutral")
|
|
3260
|
+
),
|
|
3261
|
+
children: "agent"
|
|
3262
|
+
}
|
|
3263
|
+
)
|
|
3264
|
+
] }),
|
|
3265
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-secondary px-2 py-0.5 text-[11px] font-medium text-muted-foreground tabular-nums", children: headerCount })
|
|
2763
3266
|
] }),
|
|
2764
|
-
/* @__PURE__ */ jsx("
|
|
3267
|
+
isScheduled && /* @__PURE__ */ jsx("div", { className: "mt-2 flex items-center gap-2 flex-wrap", children: /* @__PURE__ */ jsxs(
|
|
3268
|
+
"span",
|
|
3269
|
+
{
|
|
3270
|
+
className: cn9(
|
|
3271
|
+
"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-semibold",
|
|
3272
|
+
badgeVariantClass("neutral")
|
|
3273
|
+
),
|
|
3274
|
+
children: [
|
|
3275
|
+
/* @__PURE__ */ jsxs(
|
|
3276
|
+
"svg",
|
|
3277
|
+
{
|
|
3278
|
+
viewBox: "0 0 24 24",
|
|
3279
|
+
fill: "none",
|
|
3280
|
+
stroke: "currentColor",
|
|
3281
|
+
strokeWidth: "2",
|
|
3282
|
+
strokeLinecap: "round",
|
|
3283
|
+
strokeLinejoin: "round",
|
|
3284
|
+
"aria-hidden": true,
|
|
3285
|
+
className: "h-3 w-3",
|
|
3286
|
+
children: [
|
|
3287
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
3288
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
3289
|
+
]
|
|
3290
|
+
}
|
|
3291
|
+
),
|
|
3292
|
+
"runs on schedule"
|
|
3293
|
+
]
|
|
3294
|
+
}
|
|
3295
|
+
) }),
|
|
3296
|
+
showCapMeter && /* @__PURE__ */ jsx(CapMeter, { count: headerCount, cap: lane.cap }),
|
|
3297
|
+
agentPicker && /* @__PURE__ */ jsx(
|
|
3298
|
+
LaneAgentPicker,
|
|
3299
|
+
{
|
|
3300
|
+
...agentPicker,
|
|
3301
|
+
canEdit: agentPicker.canEdit ?? canEditHandlers
|
|
3302
|
+
}
|
|
3303
|
+
)
|
|
2765
3304
|
] }),
|
|
2766
3305
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-2 overflow-y-auto p-2", children: [
|
|
2767
3306
|
cards.map((card) => /* @__PURE__ */ jsx(
|
|
@@ -2769,21 +3308,26 @@ function KanbanColumn({
|
|
|
2769
3308
|
{
|
|
2770
3309
|
card,
|
|
2771
3310
|
onClick: onCardClick,
|
|
2772
|
-
selected: card.id === selectedCardId
|
|
3311
|
+
selected: card.id === selectedCardId,
|
|
3312
|
+
dueState: dueStateFor(card, lane, now),
|
|
3313
|
+
draggable: draggableCards,
|
|
3314
|
+
onDragStart: onCardDragStart,
|
|
3315
|
+
onDragEnd: onCardDragEnd
|
|
2773
3316
|
},
|
|
2774
3317
|
card.id
|
|
2775
3318
|
)),
|
|
2776
|
-
cards.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-1 py-4 text-center text-xs text-
|
|
3319
|
+
cards.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-1 py-4 text-center text-xs text-muted-foreground", children: "No cards" }),
|
|
2777
3320
|
showCreate && onCreateCard && /* @__PURE__ */ jsx(
|
|
2778
3321
|
"button",
|
|
2779
3322
|
{
|
|
2780
3323
|
type: "button",
|
|
2781
3324
|
onClick: () => onCreateCard(lane),
|
|
2782
3325
|
className: cn9(
|
|
2783
|
-
|
|
2784
|
-
"
|
|
2785
|
-
"
|
|
2786
|
-
"
|
|
3326
|
+
// ≥44px touch (M12/F5) — density kept via padding, not height.
|
|
3327
|
+
"mt-1 inline-flex min-h-11 w-full items-center justify-center rounded-lg border border-dashed border-border px-3 py-2",
|
|
3328
|
+
"text-sm font-medium text-muted-foreground",
|
|
3329
|
+
"hover:border-primary/50 hover:text-foreground transition",
|
|
3330
|
+
"focus:outline-none focus:ring-2 focus:ring-ring/40"
|
|
2787
3331
|
),
|
|
2788
3332
|
children: "+ New card"
|
|
2789
3333
|
}
|
|
@@ -2798,14 +3342,14 @@ function SkeletonCard() {
|
|
|
2798
3342
|
"div",
|
|
2799
3343
|
{
|
|
2800
3344
|
className: cn9(
|
|
2801
|
-
"w-full rounded-lg border border-
|
|
2802
|
-
"bg-
|
|
3345
|
+
"w-full rounded-lg border border-border",
|
|
3346
|
+
"bg-card p-3 shadow-sm"
|
|
2803
3347
|
),
|
|
2804
3348
|
"aria-hidden": "true",
|
|
2805
3349
|
children: [
|
|
2806
|
-
/* @__PURE__ */ jsx("div", { className: "h-3.5 w-3/4 rounded bg-
|
|
2807
|
-
/* @__PURE__ */ jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-
|
|
2808
|
-
/* @__PURE__ */ jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-
|
|
3350
|
+
/* @__PURE__ */ jsx("div", { className: "h-3.5 w-3/4 rounded bg-muted" }),
|
|
3351
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-muted" }),
|
|
3352
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-muted" })
|
|
2809
3353
|
]
|
|
2810
3354
|
}
|
|
2811
3355
|
);
|
|
@@ -2816,14 +3360,14 @@ function SkeletonColumn({ cardCount }) {
|
|
|
2816
3360
|
{
|
|
2817
3361
|
className: cn9(
|
|
2818
3362
|
"flex w-72 shrink-0 flex-col rounded-xl",
|
|
2819
|
-
"bg-
|
|
2820
|
-
"border border-
|
|
3363
|
+
"bg-muted/40",
|
|
3364
|
+
"border border-border"
|
|
2821
3365
|
),
|
|
2822
3366
|
"aria-hidden": "true",
|
|
2823
3367
|
children: [
|
|
2824
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-
|
|
2825
|
-
/* @__PURE__ */ jsx("div", { className: "h-3.5 w-24 rounded bg-
|
|
2826
|
-
/* @__PURE__ */ jsx("div", { className: "h-4 w-6 rounded-full bg-
|
|
3368
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-border", children: [
|
|
3369
|
+
/* @__PURE__ */ jsx("div", { className: "h-3.5 w-24 rounded bg-muted" }),
|
|
3370
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-6 rounded-full bg-muted" })
|
|
2827
3371
|
] }),
|
|
2828
3372
|
/* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-2 p-2", children: Array.from({ length: cardCount }).map((_, i) => (
|
|
2829
3373
|
// eslint-disable-next-line react/no-array-index-key
|
|
@@ -2852,6 +3396,67 @@ function KanbanBoardSkeleton({
|
|
|
2852
3396
|
}
|
|
2853
3397
|
);
|
|
2854
3398
|
}
|
|
3399
|
+
function formatCountdown(nextTickAt) {
|
|
3400
|
+
if (!nextTickAt) return null;
|
|
3401
|
+
const target = Date.parse(nextTickAt);
|
|
3402
|
+
if (Number.isNaN(target)) return null;
|
|
3403
|
+
const ms = target - Date.now();
|
|
3404
|
+
if (ms <= 0) return "any moment";
|
|
3405
|
+
const mins = Math.round(ms / 6e4);
|
|
3406
|
+
if (mins < 1) return "under a minute";
|
|
3407
|
+
if (mins < 60) return `${mins}m`;
|
|
3408
|
+
const hrs = Math.floor(mins / 60);
|
|
3409
|
+
const rem = mins % 60;
|
|
3410
|
+
return rem > 0 ? `${hrs}h ${rem}m` : `${hrs}h`;
|
|
3411
|
+
}
|
|
3412
|
+
var DOT_TONE = {
|
|
3413
|
+
"on-schedule": "bg-success",
|
|
3414
|
+
due: "bg-info",
|
|
3415
|
+
paused: "bg-muted-foreground"
|
|
3416
|
+
};
|
|
3417
|
+
var DEFAULT_LABEL = {
|
|
3418
|
+
"on-schedule": "On schedule",
|
|
3419
|
+
due: "Running now",
|
|
3420
|
+
paused: "Paused"
|
|
3421
|
+
};
|
|
3422
|
+
function ScheduleStatus({
|
|
3423
|
+
nextTickAt,
|
|
3424
|
+
dueByLane,
|
|
3425
|
+
label,
|
|
3426
|
+
tone = "on-schedule",
|
|
3427
|
+
className
|
|
3428
|
+
}) {
|
|
3429
|
+
const countdown = formatCountdown(nextTickAt);
|
|
3430
|
+
const totalDue = dueByLane ? Object.values(dueByLane).reduce((a, b) => a + b, 0) : 0;
|
|
3431
|
+
return /* @__PURE__ */ jsxs(
|
|
3432
|
+
"div",
|
|
3433
|
+
{
|
|
3434
|
+
className: cn9(
|
|
3435
|
+
"flex items-center gap-2 text-xs text-muted-foreground",
|
|
3436
|
+
className
|
|
3437
|
+
),
|
|
3438
|
+
children: [
|
|
3439
|
+
/* @__PURE__ */ jsx(
|
|
3440
|
+
"span",
|
|
3441
|
+
{
|
|
3442
|
+
"aria-hidden": true,
|
|
3443
|
+
className: cn9("h-2 w-2 shrink-0 rounded-full", DOT_TONE[tone])
|
|
3444
|
+
}
|
|
3445
|
+
),
|
|
3446
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: label ?? DEFAULT_LABEL[tone] }),
|
|
3447
|
+
countdown && /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
|
|
3448
|
+
"\xB7 next run in ",
|
|
3449
|
+
countdown
|
|
3450
|
+
] }),
|
|
3451
|
+
totalDue > 0 && /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
|
|
3452
|
+
"\xB7 ",
|
|
3453
|
+
totalDue,
|
|
3454
|
+
" due"
|
|
3455
|
+
] })
|
|
3456
|
+
]
|
|
3457
|
+
}
|
|
3458
|
+
);
|
|
3459
|
+
}
|
|
2855
3460
|
function KanbanBoard({
|
|
2856
3461
|
adapter,
|
|
2857
3462
|
onCreateCard,
|
|
@@ -2862,6 +3467,13 @@ function KanbanBoard({
|
|
|
2862
3467
|
enableEdit = true,
|
|
2863
3468
|
enableAdvance = true,
|
|
2864
3469
|
pollIntervalMs,
|
|
3470
|
+
laneCounts,
|
|
3471
|
+
canEditHandlers,
|
|
3472
|
+
laneAgentPicker,
|
|
3473
|
+
scheduleStatus,
|
|
3474
|
+
focusCardId,
|
|
3475
|
+
renderCardDrawer,
|
|
3476
|
+
enableDrag,
|
|
2865
3477
|
className
|
|
2866
3478
|
}) {
|
|
2867
3479
|
const {
|
|
@@ -2876,8 +3488,19 @@ function KanbanBoard({
|
|
|
2876
3488
|
createCard,
|
|
2877
3489
|
updateCard,
|
|
2878
3490
|
transitionCard,
|
|
3491
|
+
deleteCard,
|
|
2879
3492
|
handleRunTerminal
|
|
2880
3493
|
} = useKanbanBoard({ adapter, onError, onCardCreated, pollIntervalMs });
|
|
3494
|
+
const lastFocusedRef = useRef(void 0);
|
|
3495
|
+
useEffect(() => {
|
|
3496
|
+
if (!focusCardId) {
|
|
3497
|
+
lastFocusedRef.current = void 0;
|
|
3498
|
+
return;
|
|
3499
|
+
}
|
|
3500
|
+
if (lastFocusedRef.current === focusCardId) return;
|
|
3501
|
+
lastFocusedRef.current = focusCardId;
|
|
3502
|
+
void openCard(focusCardId);
|
|
3503
|
+
}, [focusCardId]);
|
|
2881
3504
|
const handleCardClick = useCallback(
|
|
2882
3505
|
(card) => {
|
|
2883
3506
|
void openCard(card.id);
|
|
@@ -2910,14 +3533,53 @@ function KanbanBoard({
|
|
|
2910
3533
|
(cardId, toLane) => transitionCard({ cardId, targetLane: toLane }),
|
|
2911
3534
|
[transitionCard]
|
|
2912
3535
|
);
|
|
3536
|
+
const [dragging, setDragging] = useState(null);
|
|
3537
|
+
const draggingRef = useRef(null);
|
|
3538
|
+
const dropInFlightRef = useRef(null);
|
|
3539
|
+
const allowedDropLanes = useMemo(() => {
|
|
3540
|
+
if (!dragging || !board?.transitions) return /* @__PURE__ */ new Set();
|
|
3541
|
+
return new Set(
|
|
3542
|
+
board.transitions.filter((t) => t.fromLane === dragging.lane && t.manualAllowed).map((t) => t.toLane)
|
|
3543
|
+
);
|
|
3544
|
+
}, [dragging, board?.transitions]);
|
|
3545
|
+
const handleCardDragStart = useCallback((card) => {
|
|
3546
|
+
const d = { id: card.id, lane: card.lane };
|
|
3547
|
+
draggingRef.current = d;
|
|
3548
|
+
setDragging(d);
|
|
3549
|
+
}, []);
|
|
3550
|
+
const handleCardDragEnd = useCallback(() => {
|
|
3551
|
+
draggingRef.current = null;
|
|
3552
|
+
setDragging(null);
|
|
3553
|
+
}, []);
|
|
3554
|
+
const handleLaneDrop = useCallback(
|
|
3555
|
+
(lane) => {
|
|
3556
|
+
const cur = draggingRef.current;
|
|
3557
|
+
draggingRef.current = null;
|
|
3558
|
+
setDragging(null);
|
|
3559
|
+
if (!cur || lane.key === cur.lane) return;
|
|
3560
|
+
if (dropInFlightRef.current === cur.id) return;
|
|
3561
|
+
const allowed = (board?.transitions ?? []).some(
|
|
3562
|
+
(t) => t.fromLane === cur.lane && t.toLane === lane.key && t.manualAllowed
|
|
3563
|
+
);
|
|
3564
|
+
if (!allowed) return;
|
|
3565
|
+
dropInFlightRef.current = cur.id;
|
|
3566
|
+
void Promise.resolve(transitionCard({ cardId: cur.id, targetLane: lane.key })).finally(
|
|
3567
|
+
() => {
|
|
3568
|
+
if (dropInFlightRef.current === cur.id) dropInFlightRef.current = null;
|
|
3569
|
+
}
|
|
3570
|
+
);
|
|
3571
|
+
},
|
|
3572
|
+
[board?.transitions, transitionCard]
|
|
3573
|
+
);
|
|
2913
3574
|
const initialLaneKey = lanes.find((l) => !l.terminal)?.key ?? lanes[0]?.key;
|
|
2914
3575
|
const selectedLane = selectedCard ? lanes.find((l) => l.key === selectedCard.lane) : void 0;
|
|
2915
3576
|
const canAdvanceSelected = !!selectedLane && !selectedLane.terminal;
|
|
2916
3577
|
return /* @__PURE__ */ jsxs("div", { className: cn9("flex h-full w-full flex-col", className), children: [
|
|
2917
|
-
error && /* @__PURE__ */ jsx("div", { className: "m-3 rounded-md border border-
|
|
3578
|
+
error && /* @__PURE__ */ jsx("div", { className: "m-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
|
|
3579
|
+
scheduleStatus && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx(ScheduleStatus, { ...scheduleStatus }) }),
|
|
2918
3580
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
2919
3581
|
lanes.length === 0 && isLoading ? /* @__PURE__ */ jsx(KanbanBoardSkeleton, { className: "flex-1 overflow-x-auto p-4" }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-start gap-4 overflow-x-auto p-4 snap-x snap-mandatory no-scrollbar sm:snap-none", children: [
|
|
2920
|
-
lanes.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-
|
|
3582
|
+
lanes.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No lanes configured." }),
|
|
2921
3583
|
lanes.map((lane) => /* @__PURE__ */ jsx(
|
|
2922
3584
|
KanbanColumn,
|
|
2923
3585
|
{
|
|
@@ -2926,12 +3588,35 @@ function KanbanBoard({
|
|
|
2926
3588
|
onCardClick: handleCardClick,
|
|
2927
3589
|
selectedCardId: selectedCard?.id,
|
|
2928
3590
|
onCreateCard: handleCreate,
|
|
2929
|
-
showCreate: enableCreate && lane.key === initialLaneKey
|
|
3591
|
+
showCreate: enableCreate && lane.key === initialLaneKey,
|
|
3592
|
+
count: laneCounts?.[lane.key],
|
|
3593
|
+
agentPicker: laneAgentPicker?.(lane),
|
|
3594
|
+
canEditHandlers,
|
|
3595
|
+
draggableCards: enableDrag && enableAdvance,
|
|
3596
|
+
dragActive: !!dragging,
|
|
3597
|
+
dropAllowed: !!dragging && dragging.lane !== lane.key && allowedDropLanes.has(lane.key),
|
|
3598
|
+
onCardDragStart: handleCardDragStart,
|
|
3599
|
+
onCardDragEnd: handleCardDragEnd,
|
|
3600
|
+
onLaneDrop: handleLaneDrop
|
|
2930
3601
|
},
|
|
2931
3602
|
lane.id ?? lane.key
|
|
2932
3603
|
))
|
|
2933
3604
|
] }),
|
|
2934
|
-
selectedCard &&
|
|
3605
|
+
selectedCard && (renderCardDrawer ? renderCardDrawer({
|
|
3606
|
+
card: selectedCard,
|
|
3607
|
+
adapter,
|
|
3608
|
+
lanes,
|
|
3609
|
+
transitions: enableAdvance ? board?.transitions : void 0,
|
|
3610
|
+
canAdvance: canAdvanceSelected,
|
|
3611
|
+
onClose: closeCard,
|
|
3612
|
+
onRetry: onRetryCard ? handleRetry : void 0,
|
|
3613
|
+
onUpdate: enableEdit ? handleUpdate : void 0,
|
|
3614
|
+
onAdvance: enableAdvance ? handleAdvance : void 0,
|
|
3615
|
+
onTransition: enableAdvance ? handleTransition : void 0,
|
|
3616
|
+
onDelete: adapter.deleteCard ? deleteCard : void 0,
|
|
3617
|
+
onRunTerminal: handleRunTerminal,
|
|
3618
|
+
onError
|
|
3619
|
+
}) : /* @__PURE__ */ jsx(
|
|
2935
3620
|
CardDrawer,
|
|
2936
3621
|
{
|
|
2937
3622
|
card: selectedCard,
|
|
@@ -2947,7 +3632,7 @@ function KanbanBoard({
|
|
|
2947
3632
|
onRunTerminal: handleRunTerminal,
|
|
2948
3633
|
onError
|
|
2949
3634
|
}
|
|
2950
|
-
)
|
|
3635
|
+
))
|
|
2951
3636
|
] })
|
|
2952
3637
|
] });
|
|
2953
3638
|
}
|
|
@@ -2972,6 +3657,7 @@ function composeAdapter(source, boardConfigUuid, version, cardSource) {
|
|
|
2972
3657
|
transitionCard(input) {
|
|
2973
3658
|
return cardSource.transitionCard(input);
|
|
2974
3659
|
},
|
|
3660
|
+
...cardSource.deleteCard ? { deleteCard: (id) => cardSource.deleteCard(id) } : {},
|
|
2975
3661
|
...cardSource.createRunStream ? { createRunStream: (id) => cardSource.createRunStream(id) } : {}
|
|
2976
3662
|
};
|
|
2977
3663
|
}
|
|
@@ -3668,15 +4354,6 @@ var ExecutionStreamControllerImpl = class {
|
|
|
3668
4354
|
sseClient = null;
|
|
3669
4355
|
status = "connecting";
|
|
3670
4356
|
reconnectAttempts = 0;
|
|
3671
|
-
// CAD-39 / webchat dual-plane create-connect race. `hasConnected` flips true on
|
|
3672
|
-
// the first successful SSE open; `notFoundRetries` bounds retries of a 404
|
|
3673
|
-
// encountered BEFORE we ever connect (the run row is persisted slightly AFTER
|
|
3674
|
-
// the client opens the stream — the norm on the webchat path where msg-api owns
|
|
3675
|
-
// the executionId and cadra persists the agent_executions row via an async
|
|
3676
|
-
// worker callback). See handleConnectionError.
|
|
3677
|
-
hasConnected = false;
|
|
3678
|
-
notFoundRetries = 0;
|
|
3679
|
-
maxConnectRaceRetries = 3;
|
|
3680
4357
|
seenEventIds = /* @__PURE__ */ new Set();
|
|
3681
4358
|
_lastEventId = null;
|
|
3682
4359
|
activityTimeoutId = null;
|
|
@@ -3816,8 +4493,6 @@ var ExecutionStreamControllerImpl = class {
|
|
|
3816
4493
|
if (connected) {
|
|
3817
4494
|
this.status = "connected";
|
|
3818
4495
|
this.reconnectAttempts = 0;
|
|
3819
|
-
this.hasConnected = true;
|
|
3820
|
-
this.notFoundRetries = 0;
|
|
3821
4496
|
this.resetActivityTimeout();
|
|
3822
4497
|
}
|
|
3823
4498
|
this.emit("connectionChange", { connected, usingFallback: false });
|
|
@@ -4107,38 +4782,6 @@ var ExecutionStreamControllerImpl = class {
|
|
|
4107
4782
|
return;
|
|
4108
4783
|
}
|
|
4109
4784
|
const msg = error instanceof Error ? error.message : String(error);
|
|
4110
|
-
const isNotFound = /\b404\b/.test(msg);
|
|
4111
|
-
const isAuthFailure = /\b40[13]\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg);
|
|
4112
|
-
if (isNotFound && !isAuthFailure && !this.hasConnected) {
|
|
4113
|
-
if (this.notFoundRetries < this.maxConnectRaceRetries) {
|
|
4114
|
-
this.notFoundRetries++;
|
|
4115
|
-
this.status = "reconnecting";
|
|
4116
|
-
const delay = Math.min(
|
|
4117
|
-
this.config.reconnectBaseDelayMs * this.notFoundRetries,
|
|
4118
|
-
this.config.reconnectMaxDelayMs
|
|
4119
|
-
);
|
|
4120
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
4121
|
-
if (this.status === "closed") {
|
|
4122
|
-
return;
|
|
4123
|
-
}
|
|
4124
|
-
try {
|
|
4125
|
-
await this.connectSSE();
|
|
4126
|
-
} catch (retryError) {
|
|
4127
|
-
await this.handleConnectionError(retryError);
|
|
4128
|
-
}
|
|
4129
|
-
return;
|
|
4130
|
-
}
|
|
4131
|
-
if (this.pollingEnabled) {
|
|
4132
|
-
if (this.sseClient) {
|
|
4133
|
-
this.sseClient.disconnect();
|
|
4134
|
-
this.sseClient = null;
|
|
4135
|
-
}
|
|
4136
|
-
this.status = "fallback_polling";
|
|
4137
|
-
this.emit("connectionChange", { connected: false, usingFallback: true });
|
|
4138
|
-
this.startPolling();
|
|
4139
|
-
return;
|
|
4140
|
-
}
|
|
4141
|
-
}
|
|
4142
4785
|
if (/\b4\d\d\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg)) {
|
|
4143
4786
|
if (this.sseClient) {
|
|
4144
4787
|
this.sseClient.disconnect();
|
|
@@ -6186,6 +6829,6 @@ function createDefaultKanbanAdapter(config) {
|
|
|
6186
6829
|
};
|
|
6187
6830
|
}
|
|
6188
6831
|
|
|
6189
|
-
export { AgentBoard, BoardConfigClient, CadraLifecycleSource, CardDrawer, KanbanBoard, KanbanCard as KanbanCardTile, KanbanColumn, createDefaultKanbanAdapter, mapDefinitionToBoard, toResolvedLifecycle, useCardRun, useKanbanBoard };
|
|
6832
|
+
export { AgentBoard, BoardConfigClient, CadraLifecycleSource, CapMeter, CardDrawer, KanbanBoard, KanbanCard as KanbanCardTile, KanbanColumn, LaneAgentPicker, ScheduleStatus, badgeVariantClass, cn9 as cn, createDefaultKanbanAdapter, mapDefinitionToBoard, runStatusTone, toResolvedLifecycle, useCardRun, useKanbanBoard };
|
|
6190
6833
|
//# sourceMappingURL=index.js.map
|
|
6191
6834
|
//# sourceMappingURL=index.js.map
|