@cadraos/sdk 0.3.11 → 0.3.12

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.
@@ -265,12 +265,9 @@ function useKanbanBoard(options) {
265
265
  ]);
266
266
  setBoard(nextBoard);
267
267
  setCards(cardList.cards);
268
- setSelectedCard((prev) => {
269
- if (!prev) return prev;
270
- const next = cardList.cards.find((c) => c.id === prev.id);
271
- if (!next) return prev;
272
- return next.run ? next : { ...next, run: prev.run };
273
- });
268
+ setSelectedCard(
269
+ (prev) => prev ? cardList.cards.find((c) => c.id === prev.id) ?? prev : prev
270
+ );
274
271
  } catch (err) {
275
272
  fail(err, "Failed to load board");
276
273
  } finally {
@@ -285,12 +282,9 @@ function useKanbanBoard(options) {
285
282
  ]);
286
283
  setBoard(nextBoard);
287
284
  setCards(cardList.cards);
288
- setSelectedCard((prev) => {
289
- if (!prev) return prev;
290
- const next = cardList.cards.find((c) => c.id === prev.id);
291
- if (!next) return prev;
292
- return next.run ? next : { ...next, run: prev.run };
293
- });
285
+ setSelectedCard(
286
+ (prev) => prev ? cardList.cards.find((c) => c.id === prev.id) ?? prev : prev
287
+ );
294
288
  } catch {
295
289
  }
296
290
  }, [adapter]);
@@ -311,7 +305,7 @@ function useKanbanBoard(options) {
311
305
  try {
312
306
  const full = await adapter.getCard(cardId);
313
307
  setSelectedCard(
314
- (prev) => prev == null || prev.id === cardId ? full : prev
308
+ (prev) => prev && prev.id === cardId ? full : prev
315
309
  );
316
310
  } catch (err) {
317
311
  if (!known) fail(err, "Failed to open card");
@@ -420,27 +414,6 @@ function useKanbanBoard(options) {
420
414
  },
421
415
  [refresh]
422
416
  );
423
- const deleteCard = react.useCallback(
424
- async (cardId) => {
425
- if (!adapter.deleteCard) return false;
426
- setError(null);
427
- const prevCards = cards;
428
- const prevSelected = selectedCard;
429
- setCards((prev) => prev.filter((c) => c.id !== cardId));
430
- setSelectedCard((prev) => prev && prev.id === cardId ? null : prev);
431
- try {
432
- await adapter.deleteCard(cardId);
433
- void silentRefresh();
434
- return true;
435
- } catch (err) {
436
- setCards(prevCards);
437
- setSelectedCard(prevSelected);
438
- fail(err, "Failed to delete card");
439
- return false;
440
- }
441
- },
442
- [adapter, cards, selectedCard, silentRefresh, fail]
443
- );
444
417
  return {
445
418
  board,
446
419
  lanes: board?.lanes ?? [],
@@ -456,7 +429,6 @@ function useKanbanBoard(options) {
456
429
  createCard,
457
430
  updateCard,
458
431
  transitionCard,
459
- deleteCard,
460
432
  handleRunTerminal
461
433
  };
462
434
  }
@@ -2750,9 +2722,6 @@ function KanbanCard({
2750
2722
  onClick,
2751
2723
  selected,
2752
2724
  dueState,
2753
- draggable,
2754
- onDragStart,
2755
- onDragEnd,
2756
2725
  className
2757
2726
  }) {
2758
2727
  const runStatus = card.run?.status;
@@ -2781,20 +2750,10 @@ function KanbanCard({
2781
2750
  onClick: () => onClick?.(card),
2782
2751
  "aria-pressed": selected,
2783
2752
  "aria-busy": isWorking,
2784
- draggable,
2785
- onDragStart: draggable ? (e) => {
2786
- e.dataTransfer.effectAllowed = "move";
2787
- e.dataTransfer.setData("text/plain", card.id);
2788
- onDragStart?.(card);
2789
- } : void 0,
2790
- onDragEnd: draggable ? () => onDragEnd?.() : void 0,
2791
2753
  className: cn9(
2792
- // Solid card: fill + border + a faint token top-sheen (adapts to theme),
2793
- // matching the host's cockpit cards (work-shell v2 visual pass).
2794
- "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",
2754
+ "relative w-full text-left rounded-lg border bg-card p-3 shadow-sm transition",
2795
2755
  "hover:shadow-md hover:border-primary/40",
2796
2756
  "focus:outline-none focus:ring-2 focus:ring-ring/50",
2797
- draggable && "cursor-grab active:cursor-grabbing",
2798
2757
  // The working card carries the SINGLE focal motion on the board (F11).
2799
2758
  isWorking ? "border-info/60 ring-2 ring-info/40 animate-pulse" : selected ? "border-primary/60 ring-2 ring-primary/30" : "border-border",
2800
2759
  className
@@ -2871,11 +2830,6 @@ function CapMeter({ count, cap, className }) {
2871
2830
  }
2872
2831
  );
2873
2832
  }
2874
- var AGENT_GROUPS = [
2875
- { kind: "cadra", label: "Cadra agents" },
2876
- { kind: "remote", label: "Your own agents" },
2877
- { kind: "human", label: "Teammates" }
2878
- ];
2879
2833
  function AgentKindIcon({ kind }) {
2880
2834
  const common = {
2881
2835
  viewBox: "0 0 24 24",
@@ -2918,55 +2872,17 @@ function OnlineDot({ online }) {
2918
2872
  }
2919
2873
  );
2920
2874
  }
2921
- function RemoteBadge() {
2922
- return /* @__PURE__ */ jsxRuntime.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" });
2923
- }
2924
2875
  function LaneAgentPicker({
2925
2876
  selected,
2926
2877
  options,
2927
2878
  onSelect,
2928
2879
  canEdit = true,
2929
2880
  lockedReason,
2930
- onEditAgent,
2931
2881
  className
2932
2882
  }) {
2933
2883
  const [open, setOpen] = react.useState(false);
2934
- const [placement, setPlacement] = react.useState(null);
2935
2884
  const rootRef = react.useRef(null);
2936
- const triggerRef = react.useRef(null);
2937
2885
  const editable = canEdit && !!onSelect && (options?.length ?? 0) > 0;
2938
- const openMenu = () => {
2939
- const el = triggerRef.current;
2940
- if (el && typeof window !== "undefined") {
2941
- const rect = el.getBoundingClientRect();
2942
- const vw = window.innerWidth || 0;
2943
- const vh = window.innerHeight || 0;
2944
- const margin = 8;
2945
- const gap = 4;
2946
- const desired = 288;
2947
- const width = Math.max(
2948
- Math.min(rect.width || 224, Math.max(vw - margin * 2, 160)),
2949
- 160
2950
- );
2951
- const left = Math.min(
2952
- Math.max(rect.left, margin),
2953
- Math.max(vw - width - margin, margin)
2954
- );
2955
- const spaceBelow = vh - rect.bottom - gap - margin;
2956
- const spaceAbove = rect.top - gap - margin;
2957
- const flipUp = spaceBelow < desired && spaceAbove > spaceBelow;
2958
- const maxHeight = Math.max(
2959
- Math.min(desired, flipUp ? spaceAbove : spaceBelow),
2960
- 120
2961
- );
2962
- setPlacement(
2963
- flipUp ? { bottom: Math.max(vh - rect.top + gap, margin), left, width, maxHeight } : { top: rect.bottom + gap, left, width, maxHeight }
2964
- );
2965
- } else {
2966
- setPlacement(null);
2967
- }
2968
- setOpen(true);
2969
- };
2970
2886
  react.useEffect(() => {
2971
2887
  if (!open) return;
2972
2888
  const onDocDown = (e) => {
@@ -3000,28 +2916,14 @@ function LaneAgentPicker({
3000
2916
  }
3001
2917
  ),
3002
2918
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: displayLabel }),
3003
- kind === "remote" && /* @__PURE__ */ jsxRuntime.jsx(RemoteBadge, {}),
3004
2919
  /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: selected?.online })
3005
2920
  ] });
3006
- const grouped = AGENT_GROUPS.map((group) => ({
3007
- ...group,
3008
- items: (options ?? []).filter((opt) => (opt.kind ?? "cadra") === group.kind)
3009
- })).filter((group) => group.items.length > 0);
3010
- const menuStyle = placement ? {
3011
- position: "fixed",
3012
- top: placement.top,
3013
- bottom: placement.bottom,
3014
- left: placement.left,
3015
- width: placement.width,
3016
- maxHeight: placement.maxHeight
3017
- } : void 0;
3018
2921
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: cn9("relative mt-2 w-full", className), children: [
3019
2922
  editable ? /* @__PURE__ */ jsxRuntime.jsxs(
3020
2923
  "button",
3021
2924
  {
3022
- ref: triggerRef,
3023
2925
  type: "button",
3024
- onClick: () => open ? setOpen(false) : openMenu(),
2926
+ onClick: () => setOpen((o) => !o),
3025
2927
  "aria-haspopup": "listbox",
3026
2928
  "aria-expanded": open,
3027
2929
  "aria-label": `Assigned agent: ${displayLabel}. Change`,
@@ -3099,106 +3001,47 @@ function LaneAgentPicker({
3099
3001
  ),
3100
3002
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: lockedReason })
3101
3003
  ] }),
3102
- editable && open && /* @__PURE__ */ jsxRuntime.jsxs(
3103
- "div",
3004
+ editable && open && /* @__PURE__ */ jsxRuntime.jsx(
3005
+ "ul",
3104
3006
  {
3105
- style: menuStyle,
3007
+ role: "listbox",
3008
+ "aria-label": "Assign an agent",
3106
3009
  className: cn9(
3107
- "flex flex-col overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
3108
- menuStyle ? "z-50" : "absolute z-20 mt-1 max-h-64 w-full"
3010
+ "absolute z-20 mt-1 max-h-64 w-full overflow-y-auto rounded-lg border border-border",
3011
+ "bg-popover p-1 shadow-lg"
3109
3012
  ),
3110
- children: [
3111
- /* @__PURE__ */ jsxRuntime.jsx(
3112
- "ul",
3113
- {
3114
- role: "listbox",
3115
- "aria-label": "Assign an agent",
3116
- className: "min-h-0 flex-1 overflow-y-auto p-1",
3117
- children: grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("li", { role: "presentation", children: [
3118
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: group.label }),
3119
- /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "group", "aria-label": group.label, children: group.items.map((opt) => {
3120
- const isSelected = opt.id === selected?.id;
3121
- const optKind = opt.kind ?? "cadra";
3122
- return /* @__PURE__ */ jsxRuntime.jsx("li", { role: "option", "aria-selected": isSelected, children: /* @__PURE__ */ jsxRuntime.jsxs(
3123
- "button",
3124
- {
3125
- type: "button",
3126
- onClick: () => {
3127
- onSelect(opt.id);
3128
- setOpen(false);
3129
- },
3130
- className: cn9(
3131
- "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium",
3132
- "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
3133
- isSelected ? "text-primary" : "text-foreground"
3134
- ),
3135
- children: [
3136
- /* @__PURE__ */ jsxRuntime.jsx(
3137
- "span",
3138
- {
3139
- className: cn9(
3140
- "grid h-5 w-5 shrink-0 place-items-center rounded-full",
3141
- optKind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
3142
- ),
3143
- children: /* @__PURE__ */ jsxRuntime.jsx(AgentKindIcon, { kind: optKind })
3144
- }
3145
- ),
3146
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }),
3147
- optKind === "remote" && /* @__PURE__ */ jsxRuntime.jsx(RemoteBadge, {}),
3148
- /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: opt.online }),
3149
- isSelected && /* @__PURE__ */ jsxRuntime.jsx(
3150
- "svg",
3151
- {
3152
- viewBox: "0 0 24 24",
3153
- fill: "none",
3154
- stroke: "currentColor",
3155
- strokeWidth: 2,
3156
- strokeLinecap: "round",
3157
- strokeLinejoin: "round",
3158
- "aria-hidden": true,
3159
- className: "h-3.5 w-3.5 shrink-0",
3160
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" })
3161
- }
3162
- )
3163
- ]
3164
- }
3165
- ) }, opt.id);
3166
- }) })
3167
- ] }, group.kind))
3168
- }
3169
- ),
3170
- onEditAgent && selected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border p-1", children: /* @__PURE__ */ jsxRuntime.jsxs(
3013
+ children: options.map((opt) => {
3014
+ const isSelected = opt.id === selected?.id;
3015
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { role: "option", "aria-selected": isSelected, children: /* @__PURE__ */ jsxRuntime.jsxs(
3171
3016
  "button",
3172
3017
  {
3173
3018
  type: "button",
3174
3019
  onClick: () => {
3175
- onEditAgent(selected.id);
3020
+ onSelect(opt.id);
3176
3021
  setOpen(false);
3177
3022
  },
3178
3023
  className: cn9(
3179
- "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium text-foreground",
3180
- "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50"
3024
+ "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium",
3025
+ "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
3026
+ isSelected ? "text-primary" : "text-foreground"
3181
3027
  ),
3182
3028
  children: [
3183
3029
  /* @__PURE__ */ jsxRuntime.jsx(
3184
- "svg",
3030
+ "span",
3185
3031
  {
3186
- viewBox: "0 0 24 24",
3187
- fill: "none",
3188
- stroke: "currentColor",
3189
- strokeWidth: 2,
3190
- strokeLinecap: "round",
3191
- strokeLinejoin: "round",
3192
- "aria-hidden": true,
3193
- className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
3194
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" })
3032
+ className: cn9(
3033
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
3034
+ (opt.kind ?? "cadra") === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
3035
+ ),
3036
+ children: /* @__PURE__ */ jsxRuntime.jsx(AgentKindIcon, { kind: opt.kind ?? "cadra" })
3195
3037
  }
3196
3038
  ),
3197
- "Edit this agent"
3039
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }),
3040
+ /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: opt.online })
3198
3041
  ]
3199
3042
  }
3200
- ) })
3201
- ]
3043
+ ) }, opt.id);
3044
+ })
3202
3045
  }
3203
3046
  )
3204
3047
  ] });
@@ -3223,12 +3066,6 @@ function KanbanColumn({
3223
3066
  count,
3224
3067
  agentPicker,
3225
3068
  canEditHandlers,
3226
- draggableCards,
3227
- dragActive,
3228
- dropAllowed,
3229
- onCardDragStart,
3230
- onCardDragEnd,
3231
- onLaneDrop,
3232
3069
  className
3233
3070
  }) {
3234
3071
  const headerCount = count ?? cards.length;
@@ -3239,21 +3076,9 @@ function KanbanColumn({
3239
3076
  return /* @__PURE__ */ jsxRuntime.jsxs(
3240
3077
  "div",
3241
3078
  {
3242
- onDragOver: dropAllowed ? (e) => {
3243
- e.preventDefault();
3244
- e.dataTransfer.dropEffect = "move";
3245
- } : void 0,
3246
- onDrop: dropAllowed && onLaneDrop ? (e) => {
3247
- e.preventDefault();
3248
- onLaneDrop(lane);
3249
- } : void 0,
3250
3079
  className: cn9(
3251
- "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",
3080
+ "flex w-[85vw] max-w-sm snap-start shrink-0 flex-col rounded-xl bg-muted/40 border border-border sm:w-72 sm:max-w-none",
3252
3081
  isScheduled && "border-border",
3253
- // MV4 drag affordance: allowed targets glow; other lanes dim so the
3254
- // valid drop zones read at a glance.
3255
- dragActive && dropAllowed && "border-primary/60 ring-2 ring-primary/40 bg-primary/5",
3256
- dragActive && !dropAllowed && "opacity-55",
3257
3082
  className
3258
3083
  ),
3259
3084
  children: [
@@ -3322,10 +3147,7 @@ function KanbanColumn({
3322
3147
  card,
3323
3148
  onClick: onCardClick,
3324
3149
  selected: card.id === selectedCardId,
3325
- dueState: dueStateFor(card, lane, now),
3326
- draggable: draggableCards,
3327
- onDragStart: onCardDragStart,
3328
- onDragEnd: onCardDragEnd
3150
+ dueState: dueStateFor(card, lane, now)
3329
3151
  },
3330
3152
  card.id
3331
3153
  )),
@@ -3485,8 +3307,6 @@ function KanbanBoard({
3485
3307
  laneAgentPicker,
3486
3308
  scheduleStatus,
3487
3309
  focusCardId,
3488
- renderCardDrawer,
3489
- enableDrag,
3490
3310
  className
3491
3311
  }) {
3492
3312
  const {
@@ -3501,7 +3321,6 @@ function KanbanBoard({
3501
3321
  createCard,
3502
3322
  updateCard,
3503
3323
  transitionCard,
3504
- deleteCard,
3505
3324
  handleRunTerminal
3506
3325
  } = useKanbanBoard({ adapter, onError, onCardCreated, pollIntervalMs });
3507
3326
  const lastFocusedRef = react.useRef(void 0);
@@ -3546,44 +3365,6 @@ function KanbanBoard({
3546
3365
  (cardId, toLane) => transitionCard({ cardId, targetLane: toLane }),
3547
3366
  [transitionCard]
3548
3367
  );
3549
- const [dragging, setDragging] = react.useState(null);
3550
- const draggingRef = react.useRef(null);
3551
- const dropInFlightRef = react.useRef(null);
3552
- const allowedDropLanes = react.useMemo(() => {
3553
- if (!dragging || !board?.transitions) return /* @__PURE__ */ new Set();
3554
- return new Set(
3555
- board.transitions.filter((t) => t.fromLane === dragging.lane && t.manualAllowed).map((t) => t.toLane)
3556
- );
3557
- }, [dragging, board?.transitions]);
3558
- const handleCardDragStart = react.useCallback((card) => {
3559
- const d = { id: card.id, lane: card.lane };
3560
- draggingRef.current = d;
3561
- setDragging(d);
3562
- }, []);
3563
- const handleCardDragEnd = react.useCallback(() => {
3564
- draggingRef.current = null;
3565
- setDragging(null);
3566
- }, []);
3567
- const handleLaneDrop = react.useCallback(
3568
- (lane) => {
3569
- const cur = draggingRef.current;
3570
- draggingRef.current = null;
3571
- setDragging(null);
3572
- if (!cur || lane.key === cur.lane) return;
3573
- if (dropInFlightRef.current === cur.id) return;
3574
- const allowed = (board?.transitions ?? []).some(
3575
- (t) => t.fromLane === cur.lane && t.toLane === lane.key && t.manualAllowed
3576
- );
3577
- if (!allowed) return;
3578
- dropInFlightRef.current = cur.id;
3579
- void Promise.resolve(transitionCard({ cardId: cur.id, targetLane: lane.key })).finally(
3580
- () => {
3581
- if (dropInFlightRef.current === cur.id) dropInFlightRef.current = null;
3582
- }
3583
- );
3584
- },
3585
- [board?.transitions, transitionCard]
3586
- );
3587
3368
  const initialLaneKey = lanes.find((l) => !l.terminal)?.key ?? lanes[0]?.key;
3588
3369
  const selectedLane = selectedCard ? lanes.find((l) => l.key === selectedCard.lane) : void 0;
3589
3370
  const canAdvanceSelected = !!selectedLane && !selectedLane.terminal;
@@ -3604,32 +3385,12 @@ function KanbanBoard({
3604
3385
  showCreate: enableCreate && lane.key === initialLaneKey,
3605
3386
  count: laneCounts?.[lane.key],
3606
3387
  agentPicker: laneAgentPicker?.(lane),
3607
- canEditHandlers,
3608
- draggableCards: enableDrag && enableAdvance,
3609
- dragActive: !!dragging,
3610
- dropAllowed: !!dragging && dragging.lane !== lane.key && allowedDropLanes.has(lane.key),
3611
- onCardDragStart: handleCardDragStart,
3612
- onCardDragEnd: handleCardDragEnd,
3613
- onLaneDrop: handleLaneDrop
3388
+ canEditHandlers
3614
3389
  },
3615
3390
  lane.id ?? lane.key
3616
3391
  ))
3617
3392
  ] }),
3618
- selectedCard && (renderCardDrawer ? renderCardDrawer({
3619
- card: selectedCard,
3620
- adapter,
3621
- lanes,
3622
- transitions: enableAdvance ? board?.transitions : void 0,
3623
- canAdvance: canAdvanceSelected,
3624
- onClose: closeCard,
3625
- onRetry: onRetryCard ? handleRetry : void 0,
3626
- onUpdate: enableEdit ? handleUpdate : void 0,
3627
- onAdvance: enableAdvance ? handleAdvance : void 0,
3628
- onTransition: enableAdvance ? handleTransition : void 0,
3629
- onDelete: adapter.deleteCard ? deleteCard : void 0,
3630
- onRunTerminal: handleRunTerminal,
3631
- onError
3632
- }) : /* @__PURE__ */ jsxRuntime.jsx(
3393
+ selectedCard && /* @__PURE__ */ jsxRuntime.jsx(
3633
3394
  CardDrawer,
3634
3395
  {
3635
3396
  card: selectedCard,
@@ -3645,7 +3406,7 @@ function KanbanBoard({
3645
3406
  onRunTerminal: handleRunTerminal,
3646
3407
  onError
3647
3408
  }
3648
- ))
3409
+ )
3649
3410
  ] })
3650
3411
  ] });
3651
3412
  }
@@ -3670,7 +3431,6 @@ function composeAdapter(source, boardConfigUuid, version, cardSource) {
3670
3431
  transitionCard(input) {
3671
3432
  return cardSource.transitionCard(input);
3672
3433
  },
3673
- ...cardSource.deleteCard ? { deleteCard: (id) => cardSource.deleteCard(id) } : {},
3674
3434
  ...cardSource.createRunStream ? { createRunStream: (id) => cardSource.createRunStream(id) } : {}
3675
3435
  };
3676
3436
  }