@invana/ui 0.0.23 → 0.0.25

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/index.cjs CHANGED
@@ -3115,27 +3115,214 @@ function ChatSessionMessage({
3115
3115
  footer
3116
3116
  ] });
3117
3117
  }
3118
+ var NONE = Object.freeze([]);
3119
+ function same(a, b) {
3120
+ if (a.length !== b.length) return false;
3121
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3122
+ return true;
3123
+ }
3124
+ function useOverflowItems({
3125
+ count,
3126
+ enabled = true,
3127
+ pinnedIndex,
3128
+ reserve = 32
3129
+ }) {
3130
+ const containerEl = React23__namespace.useRef(null);
3131
+ const itemEls = React23__namespace.useRef([]);
3132
+ const widths = React23__namespace.useRef([]);
3133
+ const itemRefs = React23__namespace.useRef([]);
3134
+ const [hidden, setHidden] = React23__namespace.useState(NONE);
3135
+ const fit = React23__namespace.useCallback(() => {
3136
+ const el = containerEl.current;
3137
+ if (!el || !enabled) {
3138
+ setHidden((prev) => prev === NONE ? prev : NONE);
3139
+ return;
3140
+ }
3141
+ const gap = Number.parseFloat(getComputedStyle(el).columnGap) || 0;
3142
+ for (let i = 0; i < count; i++) {
3143
+ const item = itemEls.current[i];
3144
+ if (item && item.offsetParent !== null) widths.current[i] = item.offsetWidth + gap;
3145
+ }
3146
+ const budget = el.clientWidth;
3147
+ if (budget <= 0) return;
3148
+ let total = 0;
3149
+ for (let i = 0; i < count; i++) total += widths.current[i] ?? 0;
3150
+ if (total <= budget + gap) {
3151
+ setHidden((prev) => prev === NONE ? prev : NONE);
3152
+ return;
3153
+ }
3154
+ const next = [];
3155
+ let used = pinnedIndex != null ? widths.current[pinnedIndex] ?? 0 : 0;
3156
+ for (let i = 0; i < count; i++) {
3157
+ if (i === pinnedIndex) continue;
3158
+ const w = widths.current[i] ?? 0;
3159
+ if (used + w <= budget - reserve) used += w;
3160
+ else next.push(i);
3161
+ }
3162
+ setHidden((prev) => same(prev, next) ? prev : next);
3163
+ }, [count, enabled, pinnedIndex, reserve]);
3164
+ React23__namespace.useLayoutEffect(() => {
3165
+ widths.current = [];
3166
+ itemEls.current.length = count;
3167
+ setHidden((prev) => prev === NONE ? prev : NONE);
3168
+ }, [count]);
3169
+ React23__namespace.useLayoutEffect(() => {
3170
+ fit();
3171
+ if (!enabled || typeof ResizeObserver === "undefined") return;
3172
+ const observer = new ResizeObserver(() => fit());
3173
+ if (containerEl.current) observer.observe(containerEl.current);
3174
+ for (const item of itemEls.current) if (item) observer.observe(item);
3175
+ return () => observer.disconnect();
3176
+ }, [fit, enabled, hidden]);
3177
+ const containerRef = React23__namespace.useCallback((el) => {
3178
+ containerEl.current = el;
3179
+ }, []);
3180
+ const itemRef = React23__namespace.useCallback((index) => {
3181
+ let fn = itemRefs.current[index];
3182
+ if (!fn) {
3183
+ fn = (el) => {
3184
+ itemEls.current[index] = el;
3185
+ };
3186
+ itemRefs.current[index] = fn;
3187
+ }
3188
+ return fn;
3189
+ }, []);
3190
+ const isHidden = React23__namespace.useCallback(
3191
+ (index) => hidden.includes(index),
3192
+ [hidden]
3193
+ );
3194
+ return { containerRef, itemRef, hiddenIndices: hidden, isHidden };
3195
+ }
3196
+ var VARIANT = {
3197
+ /** The capsule: a filled, ringed chip. Toolbars, rails, header actions. */
3198
+ nav: {
3199
+ item: "rounded-control ring-1 ring-transparent",
3200
+ hover: "hover:bg-primary/10 hover:text-primary hover:ring-primary/25",
3201
+ open: "data-[state=open]:bg-primary/15 data-[state=open]:text-primary data-[state=open]:ring-primary/25",
3202
+ active: "bg-primary/15 text-primary ring-primary/25",
3203
+ strip: ""
3204
+ },
3205
+ /** The panel tab: accented fill with a rule under it, merging into the body. */
3206
+ underline: {
3207
+ item: "h-full rounded-none px-3 py-2",
3208
+ hover: "hover:text-foreground",
3209
+ open: "data-[state=open]:text-foreground",
3210
+ active: "bg-primary/10 text-primary border-b-2 border-primary mb-[-1px]",
3211
+ strip: "items-stretch"
3212
+ },
3213
+ /**
3214
+ * The workbook tab: boxed on top / left / right with an *open bottom*, so it
3215
+ * punches through the strip's own rule — the classic folder-tab notch.
3216
+ */
3217
+ folder: {
3218
+ item: "h-full rounded-none px-3 py-2 gap-1.5",
3219
+ hover: "hover:text-foreground",
3220
+ open: "data-[state=open]:text-foreground",
3221
+ active: "mb-[-1px] rounded-t-md border border-b-0 border-primary text-primary",
3222
+ strip: "items-stretch"
3223
+ }
3224
+ };
3225
+ var keyOf = (item) => item.key ?? item.name;
3118
3226
  var NavItems = ({
3119
3227
  items,
3120
3228
  orientation = "horizontal",
3121
3229
  tooltipSide = orientation === "vertical" ? "right" : "bottom",
3122
- iconClassName = orientation === "vertical" ? "w-5 h-5" : "w-4 h-4 flex-shrink-0"
3230
+ iconClassName = orientation === "vertical" ? "w-5 h-5" : "w-4 h-4 flex-shrink-0",
3231
+ variant = "nav",
3232
+ activeKey,
3233
+ onActiveChange,
3234
+ selectionMode = "none",
3235
+ panelId,
3236
+ overflow = false,
3237
+ overflowLabel = "More",
3238
+ className
3123
3239
  }) => {
3124
3240
  const isVertical = orientation === "vertical";
3125
- const [activeItem, setActiveItem] = React23__namespace.default.useState(null);
3126
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: items.map((item) => {
3127
- const isActive = activeItem === item.name;
3128
- const padding = item.label ? "px-3 py-1.5" : "px-2 py-2";
3241
+ const isTabs = selectionMode === "tabs";
3242
+ const V = VARIANT[variant];
3243
+ const [internalActive, setInternalActive] = React23__namespace.default.useState(
3244
+ null
3245
+ );
3246
+ const isControlled = activeKey !== void 0;
3247
+ const currentKey = isControlled ? activeKey : internalActive;
3248
+ const activeIndex = items.findIndex((item) => keyOf(item) === currentKey);
3249
+ const nodes = React23__namespace.default.useRef({});
3250
+ const pendingFocus = React23__namespace.default.useRef(null);
3251
+ React23__namespace.default.useEffect(() => {
3252
+ const key = pendingFocus.current;
3253
+ if (!key) return;
3254
+ const node = nodes.current[key];
3255
+ if (!node) return;
3256
+ pendingFocus.current = null;
3257
+ node.focus();
3258
+ });
3259
+ const { containerRef, itemRef, hiddenIndices, isHidden } = useOverflowItems({
3260
+ count: items.length,
3261
+ enabled: overflow && !isVertical,
3262
+ pinnedIndex: activeIndex >= 0 ? activeIndex : void 0
3263
+ });
3264
+ const select = React23__namespace.default.useCallback(
3265
+ (item) => {
3266
+ if (item.disabled) return;
3267
+ const key = keyOf(item);
3268
+ item.onClick?.();
3269
+ if (!isControlled) {
3270
+ setInternalActive((prev) => isTabs ? key : prev === key ? null : key);
3271
+ }
3272
+ onActiveChange?.(key);
3273
+ },
3274
+ [isControlled, isTabs, onActiveChange]
3275
+ );
3276
+ const step = (from, delta) => {
3277
+ if (items.length === 0) return -1;
3278
+ let i = from < 0 ? delta > 0 ? -1 : 0 : from;
3279
+ for (let n = 0; n < items.length; n++) {
3280
+ i = (i + delta + items.length) % items.length;
3281
+ if (!items[i]?.disabled) return i;
3282
+ }
3283
+ return -1;
3284
+ };
3285
+ const edge = (delta) => {
3286
+ const start = delta > 0 ? -1 : items.length;
3287
+ return step(start, delta);
3288
+ };
3289
+ const handleKeyDown = (event) => {
3290
+ if (!isTabs) return;
3291
+ const next = isVertical ? "ArrowDown" : "ArrowRight";
3292
+ const prev = isVertical ? "ArrowUp" : "ArrowLeft";
3293
+ let target = -1;
3294
+ if (event.key === next) target = step(activeIndex, 1);
3295
+ else if (event.key === prev) target = step(activeIndex, -1);
3296
+ else if (event.key === "Home") target = edge(1);
3297
+ else if (event.key === "End") target = edge(-1);
3298
+ else return;
3299
+ const item = items[target];
3300
+ if (!item) return;
3301
+ event.preventDefault();
3302
+ pendingFocus.current = keyOf(item);
3303
+ select(item);
3304
+ };
3305
+ const renderItem = (item, index) => {
3306
+ const key = keyOf(item);
3307
+ const isActive = key === currentKey;
3129
3308
  const hasMenu = !item.href && Boolean(item.menuItems?.length);
3130
- const isInteractive = Boolean(item.href || item.onClick || hasMenu);
3131
- const itemClass = `relative inline-flex border-0 items-center justify-center gap-2
3132
- whitespace-nowrap rounded-control transition-colors
3133
- ring-1 ring-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
3134
- ${padding}
3135
- ${isInteractive ? "cursor-pointer hover:bg-primary/10 hover:text-primary hover:ring-primary/25" : ""}
3136
- ${hasMenu ? "data-[state=open]:bg-primary/15 data-[state=open]:text-primary data-[state=open]:ring-primary/25" : ""}
3137
- ${isActive ? `bg-primary/15 text-primary ring-primary/25 ${item.activeClass || ""}` : ""}
3138
- ${item.className || ""}`;
3309
+ const asCaret = hasMenu && item.menuTrigger === "caret";
3310
+ const isInteractive = Boolean(
3311
+ item.href || item.onClick || hasMenu || isTabs
3312
+ );
3313
+ const padding = variant === "nav" ? item.label ? "px-3 py-1.5" : "px-2 py-2" : "";
3314
+ const itemClass = cn(
3315
+ `relative inline-flex border-0 items-center justify-center gap-2
3316
+ whitespace-nowrap transition-colors
3317
+ ring-1 ring-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`,
3318
+ V.item,
3319
+ padding,
3320
+ isInteractive && !item.disabled && `cursor-pointer ${V.hover}`,
3321
+ hasMenu && !asCaret && V.open,
3322
+ item.disabled && "pointer-events-none opacity-50",
3323
+ isActive && cn(V.active, item.activeClass),
3324
+ item.className
3325
+ );
3139
3326
  const icon = item.icon && /* @__PURE__ */ jsxRuntime.jsx(
3140
3327
  item.icon,
3141
3328
  {
@@ -3143,41 +3330,79 @@ var NavItems = ({
3143
3330
  className: item.iconClassName || iconClassName
3144
3331
  }
3145
3332
  );
3146
- const label = item.label && /* @__PURE__ */ jsxRuntime.jsx("span", { children: item.label });
3333
+ const label = item.label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: item.labelClassName, children: item.label });
3147
3334
  const badge = item.badge != null && /* @__PURE__ */ jsxRuntime.jsx(
3148
3335
  "span",
3149
3336
  {
3150
- className: "pointer-events-none absolute -right-0.5 -top-0.5 inline-flex h-3.5\n min-w-3.5 items-center justify-center rounded-full bg-primary px-1\n text-[9px] font-semibold leading-none text-primary-foreground",
3337
+ className: "pointer-events-none absolute -right-0.5 -top-0.5 inline-flex h-3.5\n min-w-3.5 items-center justify-center rounded-full bg-primary px-1\n text-[9px] font-semibold leading-none text-primary-foreground",
3151
3338
  children: item.badge
3152
3339
  }
3153
3340
  );
3341
+ const tabProps = isTabs ? {
3342
+ role: "tab",
3343
+ "aria-selected": isActive,
3344
+ "aria-controls": panelId?.(key),
3345
+ tabIndex: isActive || activeIndex < 0 && index === 0 ? 0 : -1
3346
+ } : {};
3347
+ const setNode = (el) => {
3348
+ nodes.current[key] = el;
3349
+ itemRef(index)(el);
3350
+ };
3351
+ const caret = asCaret && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3352
+ "span",
3353
+ {
3354
+ role: "button",
3355
+ tabIndex: -1,
3356
+ "aria-label": `${item.name} menu`,
3357
+ onClick: (e) => e.stopPropagation(),
3358
+ className: "ml-0.5 grid size-4 shrink-0 cursor-pointer place-items-center\n rounded-control text-muted-foreground hover:bg-accent hover:text-foreground",
3359
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "size-3" })
3360
+ }
3361
+ ) });
3154
3362
  const inner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3155
3363
  icon,
3156
3364
  label,
3365
+ caret,
3157
3366
  badge
3158
3367
  ] });
3159
- const control = item.href ? /* @__PURE__ */ jsxRuntime.jsx(
3368
+ const control = asCaret ? /* @__PURE__ */ jsxRuntime.jsx(
3369
+ "div",
3370
+ {
3371
+ ref: setNode,
3372
+ ...tabProps,
3373
+ style: item.style,
3374
+ onClick: () => select(item),
3375
+ className: itemClass,
3376
+ children: inner
3377
+ }
3378
+ ) : item.href ? /* @__PURE__ */ jsxRuntime.jsx(
3160
3379
  "a",
3161
3380
  {
3381
+ ref: setNode,
3162
3382
  href: item.href,
3163
- onClick: () => setActiveItem(item.name),
3383
+ style: item.style,
3384
+ onClick: () => select(item),
3164
3385
  className: itemClass,
3165
3386
  children: inner
3166
3387
  }
3167
- ) : item.onClick || hasMenu ? /* @__PURE__ */ jsxRuntime.jsx(
3388
+ ) : item.onClick || hasMenu || isTabs ? /* @__PURE__ */ jsxRuntime.jsx(
3168
3389
  "button",
3169
3390
  {
3391
+ ref: setNode,
3170
3392
  type: "button",
3393
+ disabled: item.disabled,
3394
+ ...tabProps,
3395
+ style: item.style,
3171
3396
  onClick: () => {
3172
- item.onClick?.();
3173
- if (!hasMenu) setActiveItem(isActive ? null : item.name);
3397
+ if (hasMenu) item.onClick?.();
3398
+ else select(item);
3174
3399
  },
3175
3400
  className: itemClass,
3176
3401
  children: inner
3177
3402
  }
3178
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: itemClass, children: inner });
3403
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: setNode, style: item.style, className: itemClass, children: inner });
3179
3404
  const withTooltip = /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
3180
- /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: hasMenu ? /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: control }) : control }),
3405
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: hasMenu && !asCaret ? /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: control }) : control }),
3181
3406
  /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: item.tooltipSide || tooltipSide, children: item.tooltip || item.name })
3182
3407
  ] });
3183
3408
  return /* @__PURE__ */ jsxRuntime.jsxs(React23__namespace.default.Fragment, { children: [
@@ -3188,28 +3413,7 @@ var NavItems = ({
3188
3413
  {
3189
3414
  side: isVertical ? "right" : "bottom",
3190
3415
  align: isVertical ? "start" : "end",
3191
- children: item.menuItems?.map((menuItem) => {
3192
- const MenuIcon = menuItem.icon;
3193
- const gutter = item.menuItems?.some((m) => m.icon);
3194
- return /* @__PURE__ */ jsxRuntime.jsxs(React23__namespace.default.Fragment, { children: [
3195
- menuItem.separatorBefore && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
3196
- /* @__PURE__ */ jsxRuntime.jsxs(
3197
- DropdownMenuItem,
3198
- {
3199
- disabled: menuItem.disabled,
3200
- onSelect: () => menuItem.onSelect?.(),
3201
- className: cn(
3202
- menuItem.destructive && "text-destructive focus:bg-destructive/15 focus:text-destructive hover:bg-destructive/10 hover:text-destructive"
3203
- ),
3204
- children: [
3205
- MenuIcon ? /* @__PURE__ */ jsxRuntime.jsx(MenuIcon, {}) : gutter ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }) : null,
3206
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: menuItem.label }),
3207
- menuItem.shortcut && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuShortcut, { children: menuItem.shortcut })
3208
- ]
3209
- }
3210
- )
3211
- ] }, menuItem.id);
3212
- })
3416
+ children: renderMenuRows(item.menuItems)
3213
3417
  }
3214
3418
  )
3215
3419
  ] }) : withTooltip,
@@ -3221,22 +3425,132 @@ var NavItems = ({
3221
3425
  }
3222
3426
  )
3223
3427
  ] }, item.key || item.name);
3224
- }) });
3428
+ };
3429
+ const rendered = items.map(
3430
+ (item, index) => isHidden(index) ? null : renderItem(item, index)
3431
+ );
3432
+ const overflowTrigger = hiddenIndices.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
3433
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
3434
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
3435
+ "button",
3436
+ {
3437
+ type: "button",
3438
+ "aria-label": overflowLabel,
3439
+ className: cn(
3440
+ `relative inline-flex shrink-0 items-center justify-center border-0
3441
+ transition-colors ring-1 ring-transparent focus-visible:outline-none
3442
+ focus-visible:ring-2 focus-visible:ring-ring cursor-pointer`,
3443
+ V.item,
3444
+ variant === "nav" && "px-2 py-2",
3445
+ V.hover,
3446
+ V.open
3447
+ ),
3448
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: iconClassName })
3449
+ }
3450
+ ) }) }),
3451
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: tooltipSide, children: overflowLabel })
3452
+ ] }),
3453
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuContent, { align: "end", children: hiddenIndices.map((index) => {
3454
+ const item = items[index];
3455
+ if (!item) return null;
3456
+ const Icon = item.icon;
3457
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3458
+ DropdownMenuItem,
3459
+ {
3460
+ disabled: item.disabled,
3461
+ onSelect: () => select(item),
3462
+ children: [
3463
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }),
3464
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: item.label ?? item.name })
3465
+ ]
3466
+ },
3467
+ keyOf(item)
3468
+ );
3469
+ }) })
3470
+ ] });
3471
+ if (!isTabs && !overflow) {
3472
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 0, children: rendered });
3473
+ }
3474
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
3475
+ "div",
3476
+ {
3477
+ ref: containerRef,
3478
+ role: isTabs ? "tablist" : void 0,
3479
+ "aria-orientation": isTabs ? orientation : void 0,
3480
+ onKeyDown: handleKeyDown,
3481
+ className: cn(
3482
+ "flex min-w-0",
3483
+ isVertical ? "flex-col items-center gap-1" : "items-center gap-1",
3484
+ V.strip,
3485
+ className
3486
+ ),
3487
+ children: [
3488
+ rendered,
3489
+ overflowTrigger
3490
+ ]
3491
+ }
3492
+ ) });
3225
3493
  };
3226
- var NavBase = ({ className = "", orientation, sections }) => {
3494
+ function renderMenuRows(menuItems) {
3495
+ const gutter = menuItems?.some((m) => m.icon);
3496
+ return menuItems?.map((menuItem) => {
3497
+ const MenuIcon = menuItem.icon;
3498
+ return /* @__PURE__ */ jsxRuntime.jsxs(React23__namespace.default.Fragment, { children: [
3499
+ menuItem.separatorBefore && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
3500
+ /* @__PURE__ */ jsxRuntime.jsxs(
3501
+ DropdownMenuItem,
3502
+ {
3503
+ disabled: menuItem.disabled,
3504
+ onSelect: () => menuItem.onSelect?.(),
3505
+ className: cn(
3506
+ menuItem.destructive && "text-destructive focus:bg-destructive/15 focus:text-destructive hover:bg-destructive/10 hover:text-destructive"
3507
+ ),
3508
+ children: [
3509
+ MenuIcon ? /* @__PURE__ */ jsxRuntime.jsx(MenuIcon, {}) : gutter ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }) : null,
3510
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: menuItem.label }),
3511
+ menuItem.shortcut && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuShortcut, { children: menuItem.shortcut })
3512
+ ]
3513
+ }
3514
+ )
3515
+ ] }, menuItem.id);
3516
+ });
3517
+ }
3518
+ var NavBase = ({
3519
+ className = "",
3520
+ orientation,
3521
+ sections
3522
+ }) => {
3227
3523
  const isVertical = orientation === "vertical";
3228
3524
  const containerClass = isVertical ? `flex flex-col items-center h-full ${className}` : `flex items-center w-full ${className}`;
3229
3525
  const sectionWrapperClass = isVertical ? "flex flex-col items-center gap-1" : "flex items-center gap-1 ";
3230
3526
  return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: containerClass, children: [
3231
- sections.start?.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${sectionWrapperClass} ${sections.start.className || ""}`, children: sections.start.content }),
3232
- sections.center?.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-1 ${isVertical ? "" : "flex justify-center items-center gap-1"} ${sections.center.className || ""}`, children: sections.center.content }) : (
3527
+ sections.start?.content && /* @__PURE__ */ jsxRuntime.jsx(
3528
+ "div",
3529
+ {
3530
+ className: `${sectionWrapperClass} ${sections.start.className || ""}`,
3531
+ children: sections.start.content
3532
+ }
3533
+ ),
3534
+ sections.center?.content ? /* @__PURE__ */ jsxRuntime.jsx(
3535
+ "div",
3536
+ {
3537
+ className: `flex-1 ${isVertical ? "" : "flex justify-center items-center gap-1"} ${sections.center.className || ""}`,
3538
+ children: sections.center.content
3539
+ }
3540
+ ) : (
3233
3541
  // Empty spacer to push end section to the bottom/right
3234
3542
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" })
3235
3543
  ),
3236
- sections.end?.content && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${sectionWrapperClass} ${isVertical ? "" : "ml-auto"} ${sections.end.className || ""}`, children: sections.end.content })
3544
+ sections.end?.content && /* @__PURE__ */ jsxRuntime.jsx(
3545
+ "div",
3546
+ {
3547
+ className: `${sectionWrapperClass} ${isVertical ? "" : "ml-auto"} ${sections.end.className || ""}`,
3548
+ children: sections.end.content
3549
+ }
3550
+ )
3237
3551
  ] });
3238
3552
  };
3239
- var NavHorizontalItems = ({ items }) => /* @__PURE__ */ jsxRuntime.jsx(NavItems, { items, orientation: "horizontal" });
3553
+ var NavHorizontalItems = (props) => /* @__PURE__ */ jsxRuntime.jsx(NavItems, { ...props, orientation: "horizontal" });
3240
3554
  var NavHorizontal = ({
3241
3555
  left,
3242
3556
  leftNavItems,
@@ -3670,6 +3984,56 @@ var CitationRow = React23__namespace.forwardRef(
3670
3984
  )
3671
3985
  );
3672
3986
  CitationRow.displayName = "CitationRow";
3987
+ var ClampedText = React23__namespace.forwardRef(
3988
+ ({
3989
+ lines = 3,
3990
+ moreLabel = "Show more",
3991
+ lessLabel = "Show less",
3992
+ className,
3993
+ children,
3994
+ ...props
3995
+ }, ref) => {
3996
+ const [expanded, setExpanded] = React23__namespace.useState(false);
3997
+ const [clipped, setClipped] = React23__namespace.useState(false);
3998
+ const bodyRef = React23__namespace.useRef(null);
3999
+ React23__namespace.useLayoutEffect(() => {
4000
+ const el = bodyRef.current;
4001
+ if (!el || expanded) return;
4002
+ const measure = () => setClipped(el.scrollHeight - el.clientHeight > 1);
4003
+ measure();
4004
+ const observer = new ResizeObserver(measure);
4005
+ observer.observe(el);
4006
+ return () => observer.disconnect();
4007
+ }, [expanded, children, lines]);
4008
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("min-w-0", className), ...props, children: [
4009
+ /* @__PURE__ */ jsxRuntime.jsx(
4010
+ "p",
4011
+ {
4012
+ ref: bodyRef,
4013
+ className: cn(
4014
+ "whitespace-pre-line",
4015
+ !expanded && "overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:var(--clamped-lines)] [display:-webkit-box]"
4016
+ ),
4017
+ style: { "--clamped-lines": lines },
4018
+ children
4019
+ }
4020
+ ),
4021
+ clipped ? /* @__PURE__ */ jsxRuntime.jsx(
4022
+ Button,
4023
+ {
4024
+ type: "button",
4025
+ variant: "link",
4026
+ size: "sm",
4027
+ className: "h-auto p-0",
4028
+ "aria-expanded": expanded,
4029
+ onClick: () => setExpanded((open) => !open),
4030
+ children: expanded ? lessLabel : moreLabel
4031
+ }
4032
+ ) : null
4033
+ ] });
4034
+ }
4035
+ );
4036
+ ClampedText.displayName = "ClampedText";
3673
4037
  var ClarifyCard = React23__namespace.forwardRef(
3674
4038
  ({
3675
4039
  step,
@@ -4270,7 +4634,7 @@ var MetricGrid = React23__namespace.forwardRef(
4270
4634
  )
4271
4635
  );
4272
4636
  MetricGrid.displayName = "MetricGrid";
4273
- var NavVerticalItems = ({ items }) => /* @__PURE__ */ jsxRuntime.jsx(NavItems, { items, orientation: "vertical" });
4637
+ var NavVerticalItems = (props) => /* @__PURE__ */ jsxRuntime.jsx(NavItems, { ...props, orientation: "vertical" });
4274
4638
  var NavVertical = ({
4275
4639
  className = "",
4276
4640
  top,
@@ -4878,6 +5242,27 @@ var SearchInput = ({
4878
5242
  }
4879
5243
  );
4880
5244
  };
5245
+ var Eyebrow = React23__namespace.forwardRef(
5246
+ ({ aside, tone = "muted", className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
5247
+ "div",
5248
+ {
5249
+ ref,
5250
+ className: cn(
5251
+ "flex items-baseline gap-2 text-meta font-semibold uppercase tracking-wide",
5252
+ tone === "muted" && "text-muted-foreground",
5253
+ tone === "foreground" && "text-foreground",
5254
+ tone === "accent" && "text-primary",
5255
+ className
5256
+ ),
5257
+ ...props,
5258
+ children: [
5259
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children }),
5260
+ aside != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 font-normal normal-case tracking-normal text-muted-foreground", children: aside }) : null
5261
+ ]
5262
+ }
5263
+ )
5264
+ );
5265
+ Eyebrow.displayName = "Eyebrow";
4881
5266
  var SectionHeader = React23__namespace.forwardRef(({ icon, title, count, actions, bare, className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
4882
5267
  "div",
4883
5268
  {
@@ -4975,6 +5360,9 @@ function TabbedPanel({
4975
5360
  activeTab,
4976
5361
  onTabChange,
4977
5362
  headerActions,
5363
+ overflow = false,
5364
+ overflowLabel = "More panels",
5365
+ keepMounted = false,
4978
5366
  className,
4979
5367
  headerClassName,
4980
5368
  bodyClassName,
@@ -4989,35 +5377,358 @@ function TabbedPanel({
4989
5377
  }
4990
5378
  onTabChange?.(value);
4991
5379
  };
4992
- return /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntime.jsx(Tabs, { value: currentTab, onValueChange: handleTabChange, className: "h-full", children: /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: cn("h-full border rounded-none", className), children: [
4993
- /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: `flex flex-row items-center justify-between py-0 border-b h-[30px] px-0 space-y-0 ${headerClassName || ""}`, children: [
4994
- /* @__PURE__ */ jsxRuntime.jsx(TabsList, { className: "h-full bg-transparent p-0 gap-0 rounded-none", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
4995
- TabsTrigger,
5380
+ const uid = React23__namespace.default.useId();
5381
+ const panelId = (value) => `${uid}-panel-${value}`;
5382
+ const items = React23__namespace.default.useMemo(
5383
+ () => tabs.map((tab) => ({
5384
+ key: tab.value,
5385
+ name: tab.name ?? (typeof tab.label === "string" ? tab.label : tab.value),
5386
+ label: tab.label,
5387
+ icon: tab.icon,
5388
+ disabled: tab.disabled
5389
+ })),
5390
+ [tabs]
5391
+ );
5392
+ return /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxRuntime.jsxs(Card, { className: cn("flex h-full min-h-0 flex-col border rounded-none", className), children: [
5393
+ /* @__PURE__ */ jsxRuntime.jsxs(
5394
+ "div",
5395
+ {
5396
+ className: cn(
5397
+ "flex h-[30px] shrink-0 flex-row items-stretch border-b",
5398
+ headerClassName
5399
+ ),
5400
+ children: [
5401
+ /* @__PURE__ */ jsxRuntime.jsx(
5402
+ NavItems,
5403
+ {
5404
+ items,
5405
+ variant: "underline",
5406
+ selectionMode: "tabs",
5407
+ activeKey: currentTab,
5408
+ onActiveChange: handleTabChange,
5409
+ panelId,
5410
+ overflow,
5411
+ overflowLabel,
5412
+ className: "min-w-0 flex-1 gap-0",
5413
+ iconClassName: "h-3.5 w-3.5 shrink-0"
5414
+ }
5415
+ ),
5416
+ headerActions && headerActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex h-full shrink-0 items-center gap-1 pr-1", children: /* @__PURE__ */ jsxRuntime.jsx(NavHorizontalItems, { items: headerActions }) })
5417
+ ]
5418
+ }
5419
+ ),
5420
+ tabs.map((tab) => {
5421
+ const active = tab.value === currentTab;
5422
+ if (!keepMounted && !active) return null;
5423
+ return /* @__PURE__ */ jsxRuntime.jsx(
5424
+ "div",
4996
5425
  {
4997
- value: tab.value,
4998
- disabled: tab.disabled,
4999
- className: "h-full text-muted-foreground hover:text-foreground data-[state=active]:bg-primary/10 data-[state=active]:text-primary data-[state=active]:border-b-2 data-[state=active]:border-primary data-[state=active]:mb-[-1px] rounded-none px-3 py-2",
5000
- children: [
5001
- tab.icon && /* @__PURE__ */ jsxRuntime.jsx(tab.icon, { className: "h-3.5 w-3.5 mr-1.5" }),
5002
- tab.label
5003
- ]
5426
+ id: panelId(tab.value),
5427
+ role: "tabpanel",
5428
+ "aria-label": tab.name ?? (typeof tab.label === "string" ? tab.label : tab.value),
5429
+ hidden: !active,
5430
+ className: cn("m-0 min-h-0 flex-1 overflow-y-auto", bodyClassName),
5431
+ children: tab.content
5004
5432
  },
5005
5433
  tab.value
5006
- )) }),
5007
- headerActions && headerActions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex h-full shrink-0 items-center gap-1 pr-1", children: /* @__PURE__ */ jsxRuntime.jsx(NavHorizontalItems, { items: headerActions }) })
5434
+ );
5435
+ }),
5436
+ footerContent && /* @__PURE__ */ jsxRuntime.jsx(CardFooter, { className: cn("shrink-0", footerClassName), children: footerContent })
5437
+ ] }) }) });
5438
+ }
5439
+ var BAR = {
5440
+ succeeded: "bg-success",
5441
+ running: "bg-info",
5442
+ failed: "bg-destructive",
5443
+ needs_input: "bg-warning",
5444
+ stopped: "bg-muted-foreground",
5445
+ skipped: "bg-transparent",
5446
+ queued: "bg-transparent"
5447
+ };
5448
+ var DOT = {
5449
+ succeeded: "success",
5450
+ running: "running",
5451
+ failed: "error",
5452
+ needs_input: "warning",
5453
+ stopped: "muted",
5454
+ skipped: "muted",
5455
+ queued: "queued"
5456
+ };
5457
+ var STATUS_LABEL = {
5458
+ succeeded: "succeeded",
5459
+ running: "running",
5460
+ failed: "failed",
5461
+ needs_input: "needs input",
5462
+ stopped: "stopped",
5463
+ skipped: "never ran",
5464
+ queued: "not started"
5465
+ };
5466
+ function renderResult(result) {
5467
+ if (result == null) return null;
5468
+ if (React23__namespace.isValidElement(result)) return result;
5469
+ if (typeof result !== "object") {
5470
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-meta", children: String(result) });
5471
+ }
5472
+ const entries = Object.entries(result);
5473
+ const scalars = entries.filter(([, v]) => v == null || typeof v !== "object");
5474
+ const nested = entries.filter(([, v]) => v != null && typeof v === "object");
5475
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5476
+ scalars.length ? /* @__PURE__ */ jsxRuntime.jsx(PropertyList, { labelWidth: 96, children: scalars.map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsx(PropertyRow, { label: k, mono: true, className: "py-0.5", children: v == null ? "\u2014" : String(v) }, k)) }) : null,
5477
+ nested.map(([k, v]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
5478
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-meta text-muted-foreground", children: k }),
5479
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-40 overflow-auto border border-border bg-muted/40 p-1.5 font-mono text-meta", children: JSON.stringify(v, null, 2) })
5480
+ ] }, k))
5481
+ ] });
5482
+ }
5483
+ var TaskGanttDetailCard = React23__namespace.forwardRef(({ task, elapsedMs, formatDuration = defaultFormatDuration, className, ...props }, ref) => {
5484
+ const status = task.status ?? "succeeded";
5485
+ const ms = task.durationMs ?? elapsedMs;
5486
+ const duration = task.duration ?? (ms != null ? formatDuration(ms) : "\u2014");
5487
+ const attempts = task.attempts ?? [];
5488
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("flex flex-col gap-2", className), ...props, children: [
5489
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
5490
+ /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { tone: DOT[status], size: "md", className: "translate-y-px" }),
5491
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-meta font-medium", children: task.key }),
5492
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 font-mono text-meta text-muted-foreground tabular-nums", children: duration })
5493
+ ] }),
5494
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-meta text-muted-foreground", children: [
5495
+ STATUS_LABEL[status],
5496
+ task.summary != null ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5497
+ " \xB7 ",
5498
+ task.summary
5499
+ ] }) : null
5008
5500
  ] }),
5009
- tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
5010
- TabsContent,
5501
+ attempts.length ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-0.5", children: attempts.map((a, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-meta text-muted-foreground", children: [
5502
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-warning", children: [
5503
+ "attempt ",
5504
+ i + 1
5505
+ ] }),
5506
+ " ",
5507
+ a.title ?? `${a.status ?? "failed"}${a.durationMs != null ? ` \xB7 ${formatDuration(a.durationMs)}` : ""}`
5508
+ ] }, i)) }) : null,
5509
+ task.error != null ? /* @__PURE__ */ jsxRuntime.jsxs(
5510
+ "div",
5011
5511
  {
5012
- value: tab.value,
5013
- className: `h-[calc(100%-30px)] m-0 ${bodyClassName || ""}`,
5014
- children: /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "overflow-y-auto h-full p-0", children: tab.content })
5015
- },
5016
- tab.value
5017
- )),
5018
- footerContent && /* @__PURE__ */ jsxRuntime.jsx(CardFooter, { className: footerClassName, children: footerContent })
5019
- ] }) }) }) });
5512
+ style: { borderColor: "var(--color-destructive)" },
5513
+ className: "border-l-2 bg-destructive/10 px-2 py-1.5",
5514
+ children: [
5515
+ task.error.code != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono text-meta font-medium text-destructive", children: task.error.code }) : null,
5516
+ task.error.message != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-meta", children: task.error.message }) : null,
5517
+ task.error.detail != null ? /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-1 max-h-32 overflow-auto whitespace-pre-wrap font-mono text-meta text-muted-foreground", children: task.error.detail }) : null
5518
+ ]
5519
+ }
5520
+ ) : null,
5521
+ task.result != null ? renderResult(task.result) : null,
5522
+ task.log != null ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border pt-1.5 font-mono text-meta text-muted-foreground", children: task.log }) : null
5523
+ ] });
5524
+ });
5525
+ TaskGanttDetailCard.displayName = "TaskGanttDetailCard";
5526
+ var toMs = (t) => t instanceof Date ? t.getTime() : typeof t === "number" ? t : Date.parse(t);
5527
+ var defaultFormatDuration = (ms) => {
5528
+ if (ms < 100) return `${Math.round(ms)}ms`;
5529
+ const s = ms / 1e3;
5530
+ return s < 10 ? `${(Math.round(s * 10) / 10).toFixed(1)}s` : `${Math.round(s)}s`;
5531
+ };
5532
+ var defaultFormatTick = (ms) => {
5533
+ if (ms === 0) return "0s";
5534
+ const s = ms / 1e3;
5535
+ return Number.isInteger(s) ? `${s}s` : `${(Math.round(s * 10) / 10).toFixed(1)}s`;
5536
+ };
5537
+ function place(seg, origin) {
5538
+ let start = seg.startMs;
5539
+ if (start == null && seg.startedAt != null && origin != null) {
5540
+ start = toMs(seg.startedAt) - origin;
5541
+ }
5542
+ if (start == null || Number.isNaN(start)) return null;
5543
+ let duration = seg.durationMs;
5544
+ if (duration == null && seg.startedAt != null && seg.finishedAt != null) {
5545
+ duration = toMs(seg.finishedAt) - toMs(seg.startedAt);
5546
+ }
5547
+ return { start, duration: duration != null && duration > 0 ? duration : 0 };
5020
5548
  }
5549
+ var TaskGantt = React23__namespace.forwardRef(
5550
+ ({
5551
+ tasks,
5552
+ origin,
5553
+ spanMs,
5554
+ nowMs,
5555
+ openEnded,
5556
+ ticks = 4,
5557
+ formatTick = defaultFormatTick,
5558
+ formatDuration = defaultFormatDuration,
5559
+ labelWidth = 112,
5560
+ density = "comfortable",
5561
+ showDetail = true,
5562
+ renderDetail,
5563
+ detailProps,
5564
+ selectedKey,
5565
+ onSelectTask,
5566
+ className,
5567
+ ...props
5568
+ }, ref) => {
5569
+ const barHeight = density === "compact" ? 11 : 13;
5570
+ const durationWidth = 40;
5571
+ const rows = React23__namespace.useMemo(() => {
5572
+ const stamps = [];
5573
+ for (const t of [...tasks, ...tasks.flatMap((t2) => t2.attempts ?? [])]) {
5574
+ if (t.startedAt != null) stamps.push(toMs(t.startedAt));
5575
+ }
5576
+ const zero = origin != null ? toMs(origin) : stamps.length ? Math.min(...stamps) : void 0;
5577
+ return tasks.map((task) => {
5578
+ const segments = [...task.attempts ?? [], task].map((seg) => {
5579
+ const at = place(seg, zero);
5580
+ return at ? { ...at, status: seg.status ?? task.status ?? "succeeded", title: seg.title } : null;
5581
+ }).filter((s) => s !== null);
5582
+ const last = segments[segments.length - 1];
5583
+ return { task, segments, end: last ? last.start + last.duration : 0 };
5584
+ });
5585
+ }, [tasks, origin]);
5586
+ const span = Math.max(
5587
+ spanMs ?? Math.max(nowMs ?? 0, ...rows.map((r) => r.end)),
5588
+ 1
5589
+ );
5590
+ const pct = (ms) => `${Math.max(0, Math.min(100, ms / span * 100))}%`;
5591
+ const axis = Array.from(
5592
+ { length: ticks + 1 },
5593
+ (_, i) => formatTick(span / ticks * i)
5594
+ );
5595
+ const lastLabel = openEnded ? `${axis[axis.length - 1]}+` : axis[axis.length - 1];
5596
+ const detailFor = (task, elapsedMs) => {
5597
+ if (!showDetail) return null;
5598
+ if (renderDetail) return renderDetail(task);
5599
+ if (task.detail != null) return task.detail;
5600
+ const hasBody = task.result != null || task.error != null || task.log != null || task.summary != null || (task.attempts?.length ?? 0) > 0;
5601
+ if (!hasBody) return null;
5602
+ return /* @__PURE__ */ jsxRuntime.jsx(
5603
+ TaskGanttDetailCard,
5604
+ {
5605
+ task,
5606
+ elapsedMs,
5607
+ formatDuration
5608
+ }
5609
+ );
5610
+ };
5611
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("flex flex-col", className), ...props, children: [
5612
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 pt-0.5 pb-1", "aria-hidden": true, children: [
5613
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0", style: { width: labelWidth } }),
5614
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex flex-1", children: axis.slice(0, -1).map((label, i) => /* @__PURE__ */ jsxRuntime.jsx(
5615
+ "span",
5616
+ {
5617
+ className: "flex-1 border-l border-border pl-1 text-meta text-muted-foreground tabular-nums",
5618
+ children: label
5619
+ },
5620
+ i
5621
+ )) }),
5622
+ /* @__PURE__ */ jsxRuntime.jsx(
5623
+ "span",
5624
+ {
5625
+ className: "shrink-0 text-right text-meta text-muted-foreground tabular-nums",
5626
+ style: { width: durationWidth },
5627
+ children: lastLabel
5628
+ }
5629
+ )
5630
+ ] }),
5631
+ rows.map(({ task, segments }) => {
5632
+ const neverRan = segments.length === 0;
5633
+ const selected = selectedKey != null && selectedKey === task.key;
5634
+ const pickable = onSelectTask != null;
5635
+ const elapsed = task.durationMs ?? segments[segments.length - 1]?.duration;
5636
+ const duration = task.duration ?? (neverRan || elapsed == null ? "\u2014" : formatDuration(elapsed));
5637
+ const detail = detailFor(task, elapsed);
5638
+ const row = /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 py-[3px]", children: [
5639
+ /* @__PURE__ */ jsxRuntime.jsx(
5640
+ "span",
5641
+ {
5642
+ className: cn(
5643
+ "shrink-0 truncate font-mono text-meta",
5644
+ neverRan && "text-muted-foreground"
5645
+ ),
5646
+ style: { width: labelWidth },
5647
+ title: task.key,
5648
+ children: task.label ?? task.key
5649
+ }
5650
+ ),
5651
+ /* @__PURE__ */ jsxRuntime.jsxs(
5652
+ "span",
5653
+ {
5654
+ className: "relative min-w-0 flex-1 bg-muted/55",
5655
+ style: { height: barHeight },
5656
+ children: [
5657
+ neverRan ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 border border-dashed border-border" }) : segments.map((seg, i) => /* @__PURE__ */ jsxRuntime.jsx(
5658
+ "span",
5659
+ {
5660
+ title: seg.title ?? `${task.key} \xB7 ${seg.status} \xB7 ${formatDuration(seg.duration)}`,
5661
+ className: cn("absolute inset-y-0 rounded-[1px]", BAR[seg.status]),
5662
+ style: {
5663
+ left: pct(seg.start),
5664
+ width: pct(seg.duration),
5665
+ minWidth: 2
5666
+ }
5667
+ },
5668
+ i
5669
+ )),
5670
+ nowMs != null ? /* @__PURE__ */ jsxRuntime.jsx(
5671
+ "span",
5672
+ {
5673
+ className: "absolute -top-0.5 -bottom-0.5 w-px bg-info/70",
5674
+ style: { left: pct(nowMs) },
5675
+ "aria-hidden": true
5676
+ }
5677
+ ) : null
5678
+ ]
5679
+ }
5680
+ ),
5681
+ /* @__PURE__ */ jsxRuntime.jsx(
5682
+ "span",
5683
+ {
5684
+ className: "shrink-0 text-right font-mono text-meta text-muted-foreground tabular-nums",
5685
+ style: { width: durationWidth },
5686
+ children: duration
5687
+ }
5688
+ )
5689
+ ] }) });
5690
+ const trigger = pickable ? /* @__PURE__ */ jsxRuntime.jsx(
5691
+ "button",
5692
+ {
5693
+ type: "button",
5694
+ "aria-pressed": selected,
5695
+ onClick: () => onSelectTask(task.key),
5696
+ className: cn(
5697
+ "w-full cursor-pointer text-left focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
5698
+ "hover:bg-accent/60",
5699
+ selected && "bg-accent"
5700
+ ),
5701
+ children: row
5702
+ }
5703
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(detail != null && "hover:bg-accent/60"), children: row });
5704
+ if (detail == null) return /* @__PURE__ */ jsxRuntime.jsx("div", { children: trigger }, task.key);
5705
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5706
+ HoverCard,
5707
+ {
5708
+ openDelay: detailProps?.openDelay ?? 200,
5709
+ closeDelay: detailProps?.closeDelay ?? 80,
5710
+ children: [
5711
+ /* @__PURE__ */ jsxRuntime.jsx(HoverCardTrigger, { asChild: true, children: trigger }),
5712
+ /* @__PURE__ */ jsxRuntime.jsx(
5713
+ HoverCardContent,
5714
+ {
5715
+ side: detailProps?.side ?? "right",
5716
+ align: detailProps?.align ?? "start",
5717
+ sideOffset: detailProps?.sideOffset ?? 8,
5718
+ style: detailProps?.width != null ? { width: detailProps.width } : void 0,
5719
+ className: cn("w-72 p-3", detailProps?.className),
5720
+ children: detail
5721
+ }
5722
+ )
5723
+ ]
5724
+ },
5725
+ task.key
5726
+ );
5727
+ })
5728
+ ] });
5729
+ }
5730
+ );
5731
+ TaskGantt.displayName = "TaskGantt";
5021
5732
  var TemplatePicker = React23__namespace.forwardRef(({ heading, options, value, onSelect, footnote, className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
5022
5733
  "div",
5023
5734
  {
@@ -5128,27 +5839,93 @@ var TerminalLine = React23__namespace.forwardRef(
5128
5839
  )
5129
5840
  );
5130
5841
  TerminalLine.displayName = "TerminalLine";
5131
- var TimelineList = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("ol", { ref, className: cn("flex flex-col", className), ...props, children }));
5842
+ var TimelineVariantContext = React23__namespace.createContext("columns");
5843
+ var RAIL_MARKER_OFFSET = "h-[0.692rem]";
5844
+ var RAIL_FOOTER_TAIL = "h-[1.067rem]";
5845
+ var TimelineList = React23__namespace.forwardRef(({ className, variant = "columns", children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(TimelineVariantContext.Provider, { value: variant, children: /* @__PURE__ */ jsxRuntime.jsx("ol", { ref, className: cn("flex flex-col", className), ...props, children }) }));
5132
5846
  TimelineList.displayName = "TimelineList";
5133
- var TimelineEntry = React23__namespace.forwardRef(({ when, title, marker, className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
5134
- "li",
5135
- {
5136
- ref,
5137
- className: cn("flex gap-2 border-b border-border py-1.5 last:border-b-0", className),
5138
- ...props,
5139
- children: [
5140
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-[72px] shrink-0 items-baseline gap-1.5 text-meta text-muted-foreground", children: [
5141
- marker ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "translate-y-1", children: marker }) : null,
5142
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: when })
5143
- ] }),
5144
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
5145
- title != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: title }) : null,
5146
- children
5147
- ] })
5148
- ]
5847
+ function RailGutter({ marker }) {
5848
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-2.5 shrink-0 flex-col items-center", children: [
5849
+ /* @__PURE__ */ jsxRuntime.jsx(
5850
+ "span",
5851
+ {
5852
+ "aria-hidden": true,
5853
+ className: cn(
5854
+ "w-px shrink-0 bg-border group-first/entry:bg-transparent",
5855
+ RAIL_MARKER_OFFSET
5856
+ )
5857
+ }
5858
+ ),
5859
+ marker,
5860
+ /* @__PURE__ */ jsxRuntime.jsx(
5861
+ "span",
5862
+ {
5863
+ "aria-hidden": true,
5864
+ className: "w-px flex-1 bg-border group-last/entry:bg-transparent"
5865
+ }
5866
+ )
5867
+ ] });
5868
+ }
5869
+ var TimelineEntry = React23__namespace.forwardRef(({ when, title, marker, className, children, ...props }, ref) => {
5870
+ const variant = React23__namespace.useContext(TimelineVariantContext);
5871
+ if (variant === "rail") {
5872
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5873
+ "li",
5874
+ {
5875
+ ref,
5876
+ className: cn("group/entry flex gap-2.5", className),
5877
+ ...props,
5878
+ children: [
5879
+ /* @__PURE__ */ jsxRuntime.jsx(RailGutter, { marker }),
5880
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 pb-4 group-last/entry:pb-0", children: [
5881
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-meta text-muted-foreground", children: when }),
5882
+ title != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: title }) : null,
5883
+ children
5884
+ ] })
5885
+ ]
5886
+ }
5887
+ );
5149
5888
  }
5150
- ));
5889
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5890
+ "li",
5891
+ {
5892
+ ref,
5893
+ className: cn("flex gap-2 border-b border-border py-1.5 last:border-b-0", className),
5894
+ ...props,
5895
+ children: [
5896
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-[72px] shrink-0 items-baseline gap-1.5 text-meta text-muted-foreground", children: [
5897
+ marker ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "translate-y-1", children: marker }) : null,
5898
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: when })
5899
+ ] }),
5900
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
5901
+ title != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: title }) : null,
5902
+ children
5903
+ ] })
5904
+ ]
5905
+ }
5906
+ );
5907
+ });
5151
5908
  TimelineEntry.displayName = "TimelineEntry";
5909
+ var TimelineFooter = React23__namespace.forwardRef(({ className, children, ...props }, ref) => {
5910
+ const variant = React23__namespace.useContext(TimelineVariantContext);
5911
+ if (variant === "rail") {
5912
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5913
+ "li",
5914
+ {
5915
+ ref,
5916
+ role: "presentation",
5917
+ className: cn("flex gap-2.5", className),
5918
+ ...props,
5919
+ children: [
5920
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex w-2.5 shrink-0 justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: cn("w-px bg-border", RAIL_FOOTER_TAIL) }) }),
5921
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1 pt-1.5", children })
5922
+ ]
5923
+ }
5924
+ );
5925
+ }
5926
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { ref, role: "presentation", className: cn("pt-1.5", className), ...props, children });
5927
+ });
5928
+ TimelineFooter.displayName = "TimelineFooter";
5152
5929
  var Toolbar = () => {
5153
5930
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-5 items-center space-x-2", children: [
5154
5931
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -5802,6 +6579,7 @@ exports.ChatSessionTaskRow = ChatSessionTaskRow;
5802
6579
  exports.CitationList = CitationList;
5803
6580
  exports.CitationMarker = CitationMarker;
5804
6581
  exports.CitationRow = CitationRow;
6582
+ exports.ClampedText = ClampedText;
5805
6583
  exports.ClarifyCard = ClarifyCard;
5806
6584
  exports.Command = Command;
5807
6585
  exports.CommandDialog = CommandDialog;
@@ -5848,6 +6626,7 @@ exports.EmissionHeader = EmissionHeader;
5848
6626
  exports.EmptyState = EmptyState;
5849
6627
  exports.EmptyStateLock = EmptyStateLock;
5850
6628
  exports.ErrorBoundary = ErrorBoundary;
6629
+ exports.Eyebrow = Eyebrow;
5851
6630
  exports.FilterBar = FilterBar;
5852
6631
  exports.FilterChip = FilterChip;
5853
6632
  exports.HeatStrip = HeatStrip;
@@ -5980,10 +6759,13 @@ exports.Tabs = Tabs;
5980
6759
  exports.TabsContent = TabsContent;
5981
6760
  exports.TabsList = TabsList;
5982
6761
  exports.TabsTrigger = TabsTrigger;
6762
+ exports.TaskGantt = TaskGantt;
6763
+ exports.TaskGanttDetailCard = TaskGanttDetailCard;
5983
6764
  exports.TemplatePicker = TemplatePicker;
5984
6765
  exports.Terminal = Terminal;
5985
6766
  exports.TerminalLine = TerminalLine;
5986
6767
  exports.TimelineEntry = TimelineEntry;
6768
+ exports.TimelineFooter = TimelineFooter;
5987
6769
  exports.TimelineList = TimelineList;
5988
6770
  exports.Toaster = Toaster;
5989
6771
  exports.Toggle = Toggle;
@@ -6023,6 +6805,7 @@ exports.cn = cn;
6023
6805
  exports.navigationMenuTriggerStyle = navigationMenuTriggerStyle;
6024
6806
  exports.statusDotVariants = statusDotVariants;
6025
6807
  exports.toggleVariants = toggleVariants;
6808
+ exports.useOverflowItems = useOverflowItems;
6026
6809
  exports.useSidebar = useSidebar;
6027
6810
  exports.useTour = useTour;
6028
6811
  //# sourceMappingURL=index.cjs.map