@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.js CHANGED
@@ -3075,27 +3075,214 @@ function ChatSessionMessage({
3075
3075
  footer
3076
3076
  ] });
3077
3077
  }
3078
+ var NONE = Object.freeze([]);
3079
+ function same(a, b) {
3080
+ if (a.length !== b.length) return false;
3081
+ for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
3082
+ return true;
3083
+ }
3084
+ function useOverflowItems({
3085
+ count,
3086
+ enabled = true,
3087
+ pinnedIndex,
3088
+ reserve = 32
3089
+ }) {
3090
+ const containerEl = React23.useRef(null);
3091
+ const itemEls = React23.useRef([]);
3092
+ const widths = React23.useRef([]);
3093
+ const itemRefs = React23.useRef([]);
3094
+ const [hidden, setHidden] = React23.useState(NONE);
3095
+ const fit = React23.useCallback(() => {
3096
+ const el = containerEl.current;
3097
+ if (!el || !enabled) {
3098
+ setHidden((prev) => prev === NONE ? prev : NONE);
3099
+ return;
3100
+ }
3101
+ const gap = Number.parseFloat(getComputedStyle(el).columnGap) || 0;
3102
+ for (let i = 0; i < count; i++) {
3103
+ const item = itemEls.current[i];
3104
+ if (item && item.offsetParent !== null) widths.current[i] = item.offsetWidth + gap;
3105
+ }
3106
+ const budget = el.clientWidth;
3107
+ if (budget <= 0) return;
3108
+ let total = 0;
3109
+ for (let i = 0; i < count; i++) total += widths.current[i] ?? 0;
3110
+ if (total <= budget + gap) {
3111
+ setHidden((prev) => prev === NONE ? prev : NONE);
3112
+ return;
3113
+ }
3114
+ const next = [];
3115
+ let used = pinnedIndex != null ? widths.current[pinnedIndex] ?? 0 : 0;
3116
+ for (let i = 0; i < count; i++) {
3117
+ if (i === pinnedIndex) continue;
3118
+ const w = widths.current[i] ?? 0;
3119
+ if (used + w <= budget - reserve) used += w;
3120
+ else next.push(i);
3121
+ }
3122
+ setHidden((prev) => same(prev, next) ? prev : next);
3123
+ }, [count, enabled, pinnedIndex, reserve]);
3124
+ React23.useLayoutEffect(() => {
3125
+ widths.current = [];
3126
+ itemEls.current.length = count;
3127
+ setHidden((prev) => prev === NONE ? prev : NONE);
3128
+ }, [count]);
3129
+ React23.useLayoutEffect(() => {
3130
+ fit();
3131
+ if (!enabled || typeof ResizeObserver === "undefined") return;
3132
+ const observer = new ResizeObserver(() => fit());
3133
+ if (containerEl.current) observer.observe(containerEl.current);
3134
+ for (const item of itemEls.current) if (item) observer.observe(item);
3135
+ return () => observer.disconnect();
3136
+ }, [fit, enabled, hidden]);
3137
+ const containerRef = React23.useCallback((el) => {
3138
+ containerEl.current = el;
3139
+ }, []);
3140
+ const itemRef = React23.useCallback((index) => {
3141
+ let fn = itemRefs.current[index];
3142
+ if (!fn) {
3143
+ fn = (el) => {
3144
+ itemEls.current[index] = el;
3145
+ };
3146
+ itemRefs.current[index] = fn;
3147
+ }
3148
+ return fn;
3149
+ }, []);
3150
+ const isHidden = React23.useCallback(
3151
+ (index) => hidden.includes(index),
3152
+ [hidden]
3153
+ );
3154
+ return { containerRef, itemRef, hiddenIndices: hidden, isHidden };
3155
+ }
3156
+ var VARIANT = {
3157
+ /** The capsule: a filled, ringed chip. Toolbars, rails, header actions. */
3158
+ nav: {
3159
+ item: "rounded-control ring-1 ring-transparent",
3160
+ hover: "hover:bg-primary/10 hover:text-primary hover:ring-primary/25",
3161
+ open: "data-[state=open]:bg-primary/15 data-[state=open]:text-primary data-[state=open]:ring-primary/25",
3162
+ active: "bg-primary/15 text-primary ring-primary/25",
3163
+ strip: ""
3164
+ },
3165
+ /** The panel tab: accented fill with a rule under it, merging into the body. */
3166
+ underline: {
3167
+ item: "h-full rounded-none px-3 py-2",
3168
+ hover: "hover:text-foreground",
3169
+ open: "data-[state=open]:text-foreground",
3170
+ active: "bg-primary/10 text-primary border-b-2 border-primary mb-[-1px]",
3171
+ strip: "items-stretch"
3172
+ },
3173
+ /**
3174
+ * The workbook tab: boxed on top / left / right with an *open bottom*, so it
3175
+ * punches through the strip's own rule — the classic folder-tab notch.
3176
+ */
3177
+ folder: {
3178
+ item: "h-full rounded-none px-3 py-2 gap-1.5",
3179
+ hover: "hover:text-foreground",
3180
+ open: "data-[state=open]:text-foreground",
3181
+ active: "mb-[-1px] rounded-t-md border border-b-0 border-primary text-primary",
3182
+ strip: "items-stretch"
3183
+ }
3184
+ };
3185
+ var keyOf = (item) => item.key ?? item.name;
3078
3186
  var NavItems = ({
3079
3187
  items,
3080
3188
  orientation = "horizontal",
3081
3189
  tooltipSide = orientation === "vertical" ? "right" : "bottom",
3082
- iconClassName = orientation === "vertical" ? "w-5 h-5" : "w-4 h-4 flex-shrink-0"
3190
+ iconClassName = orientation === "vertical" ? "w-5 h-5" : "w-4 h-4 flex-shrink-0",
3191
+ variant = "nav",
3192
+ activeKey,
3193
+ onActiveChange,
3194
+ selectionMode = "none",
3195
+ panelId,
3196
+ overflow = false,
3197
+ overflowLabel = "More",
3198
+ className
3083
3199
  }) => {
3084
3200
  const isVertical = orientation === "vertical";
3085
- const [activeItem, setActiveItem] = React23__default.useState(null);
3086
- return /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => {
3087
- const isActive = activeItem === item.name;
3088
- const padding = item.label ? "px-3 py-1.5" : "px-2 py-2";
3201
+ const isTabs = selectionMode === "tabs";
3202
+ const V = VARIANT[variant];
3203
+ const [internalActive, setInternalActive] = React23__default.useState(
3204
+ null
3205
+ );
3206
+ const isControlled = activeKey !== void 0;
3207
+ const currentKey = isControlled ? activeKey : internalActive;
3208
+ const activeIndex = items.findIndex((item) => keyOf(item) === currentKey);
3209
+ const nodes = React23__default.useRef({});
3210
+ const pendingFocus = React23__default.useRef(null);
3211
+ React23__default.useEffect(() => {
3212
+ const key = pendingFocus.current;
3213
+ if (!key) return;
3214
+ const node = nodes.current[key];
3215
+ if (!node) return;
3216
+ pendingFocus.current = null;
3217
+ node.focus();
3218
+ });
3219
+ const { containerRef, itemRef, hiddenIndices, isHidden } = useOverflowItems({
3220
+ count: items.length,
3221
+ enabled: overflow && !isVertical,
3222
+ pinnedIndex: activeIndex >= 0 ? activeIndex : void 0
3223
+ });
3224
+ const select = React23__default.useCallback(
3225
+ (item) => {
3226
+ if (item.disabled) return;
3227
+ const key = keyOf(item);
3228
+ item.onClick?.();
3229
+ if (!isControlled) {
3230
+ setInternalActive((prev) => isTabs ? key : prev === key ? null : key);
3231
+ }
3232
+ onActiveChange?.(key);
3233
+ },
3234
+ [isControlled, isTabs, onActiveChange]
3235
+ );
3236
+ const step = (from, delta) => {
3237
+ if (items.length === 0) return -1;
3238
+ let i = from < 0 ? delta > 0 ? -1 : 0 : from;
3239
+ for (let n = 0; n < items.length; n++) {
3240
+ i = (i + delta + items.length) % items.length;
3241
+ if (!items[i]?.disabled) return i;
3242
+ }
3243
+ return -1;
3244
+ };
3245
+ const edge = (delta) => {
3246
+ const start = delta > 0 ? -1 : items.length;
3247
+ return step(start, delta);
3248
+ };
3249
+ const handleKeyDown = (event) => {
3250
+ if (!isTabs) return;
3251
+ const next = isVertical ? "ArrowDown" : "ArrowRight";
3252
+ const prev = isVertical ? "ArrowUp" : "ArrowLeft";
3253
+ let target = -1;
3254
+ if (event.key === next) target = step(activeIndex, 1);
3255
+ else if (event.key === prev) target = step(activeIndex, -1);
3256
+ else if (event.key === "Home") target = edge(1);
3257
+ else if (event.key === "End") target = edge(-1);
3258
+ else return;
3259
+ const item = items[target];
3260
+ if (!item) return;
3261
+ event.preventDefault();
3262
+ pendingFocus.current = keyOf(item);
3263
+ select(item);
3264
+ };
3265
+ const renderItem = (item, index) => {
3266
+ const key = keyOf(item);
3267
+ const isActive = key === currentKey;
3089
3268
  const hasMenu = !item.href && Boolean(item.menuItems?.length);
3090
- const isInteractive = Boolean(item.href || item.onClick || hasMenu);
3091
- const itemClass = `relative inline-flex border-0 items-center justify-center gap-2
3092
- whitespace-nowrap rounded-control transition-colors
3093
- ring-1 ring-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring
3094
- ${padding}
3095
- ${isInteractive ? "cursor-pointer hover:bg-primary/10 hover:text-primary hover:ring-primary/25" : ""}
3096
- ${hasMenu ? "data-[state=open]:bg-primary/15 data-[state=open]:text-primary data-[state=open]:ring-primary/25" : ""}
3097
- ${isActive ? `bg-primary/15 text-primary ring-primary/25 ${item.activeClass || ""}` : ""}
3098
- ${item.className || ""}`;
3269
+ const asCaret = hasMenu && item.menuTrigger === "caret";
3270
+ const isInteractive = Boolean(
3271
+ item.href || item.onClick || hasMenu || isTabs
3272
+ );
3273
+ const padding = variant === "nav" ? item.label ? "px-3 py-1.5" : "px-2 py-2" : "";
3274
+ const itemClass = cn(
3275
+ `relative inline-flex border-0 items-center justify-center gap-2
3276
+ whitespace-nowrap transition-colors
3277
+ ring-1 ring-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`,
3278
+ V.item,
3279
+ padding,
3280
+ isInteractive && !item.disabled && `cursor-pointer ${V.hover}`,
3281
+ hasMenu && !asCaret && V.open,
3282
+ item.disabled && "pointer-events-none opacity-50",
3283
+ isActive && cn(V.active, item.activeClass),
3284
+ item.className
3285
+ );
3099
3286
  const icon = item.icon && /* @__PURE__ */ jsx(
3100
3287
  item.icon,
3101
3288
  {
@@ -3103,41 +3290,79 @@ var NavItems = ({
3103
3290
  className: item.iconClassName || iconClassName
3104
3291
  }
3105
3292
  );
3106
- const label = item.label && /* @__PURE__ */ jsx("span", { children: item.label });
3293
+ const label = item.label && /* @__PURE__ */ jsx("span", { className: item.labelClassName, children: item.label });
3107
3294
  const badge = item.badge != null && /* @__PURE__ */ jsx(
3108
3295
  "span",
3109
3296
  {
3110
- 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",
3297
+ 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",
3111
3298
  children: item.badge
3112
3299
  }
3113
3300
  );
3301
+ const tabProps = isTabs ? {
3302
+ role: "tab",
3303
+ "aria-selected": isActive,
3304
+ "aria-controls": panelId?.(key),
3305
+ tabIndex: isActive || activeIndex < 0 && index === 0 ? 0 : -1
3306
+ } : {};
3307
+ const setNode = (el) => {
3308
+ nodes.current[key] = el;
3309
+ itemRef(index)(el);
3310
+ };
3311
+ const caret = asCaret && /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3312
+ "span",
3313
+ {
3314
+ role: "button",
3315
+ tabIndex: -1,
3316
+ "aria-label": `${item.name} menu`,
3317
+ onClick: (e) => e.stopPropagation(),
3318
+ 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",
3319
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-3" })
3320
+ }
3321
+ ) });
3114
3322
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
3115
3323
  icon,
3116
3324
  label,
3325
+ caret,
3117
3326
  badge
3118
3327
  ] });
3119
- const control = item.href ? /* @__PURE__ */ jsx(
3328
+ const control = asCaret ? /* @__PURE__ */ jsx(
3329
+ "div",
3330
+ {
3331
+ ref: setNode,
3332
+ ...tabProps,
3333
+ style: item.style,
3334
+ onClick: () => select(item),
3335
+ className: itemClass,
3336
+ children: inner
3337
+ }
3338
+ ) : item.href ? /* @__PURE__ */ jsx(
3120
3339
  "a",
3121
3340
  {
3341
+ ref: setNode,
3122
3342
  href: item.href,
3123
- onClick: () => setActiveItem(item.name),
3343
+ style: item.style,
3344
+ onClick: () => select(item),
3124
3345
  className: itemClass,
3125
3346
  children: inner
3126
3347
  }
3127
- ) : item.onClick || hasMenu ? /* @__PURE__ */ jsx(
3348
+ ) : item.onClick || hasMenu || isTabs ? /* @__PURE__ */ jsx(
3128
3349
  "button",
3129
3350
  {
3351
+ ref: setNode,
3130
3352
  type: "button",
3353
+ disabled: item.disabled,
3354
+ ...tabProps,
3355
+ style: item.style,
3131
3356
  onClick: () => {
3132
- item.onClick?.();
3133
- if (!hasMenu) setActiveItem(isActive ? null : item.name);
3357
+ if (hasMenu) item.onClick?.();
3358
+ else select(item);
3134
3359
  },
3135
3360
  className: itemClass,
3136
3361
  children: inner
3137
3362
  }
3138
- ) : /* @__PURE__ */ jsx("div", { className: itemClass, children: inner });
3363
+ ) : /* @__PURE__ */ jsx("div", { ref: setNode, style: item.style, className: itemClass, children: inner });
3139
3364
  const withTooltip = /* @__PURE__ */ jsxs(Tooltip, { children: [
3140
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: hasMenu ? /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: control }) : control }),
3365
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: hasMenu && !asCaret ? /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: control }) : control }),
3141
3366
  /* @__PURE__ */ jsx(TooltipContent, { side: item.tooltipSide || tooltipSide, children: item.tooltip || item.name })
3142
3367
  ] });
3143
3368
  return /* @__PURE__ */ jsxs(React23__default.Fragment, { children: [
@@ -3148,28 +3373,7 @@ var NavItems = ({
3148
3373
  {
3149
3374
  side: isVertical ? "right" : "bottom",
3150
3375
  align: isVertical ? "start" : "end",
3151
- children: item.menuItems?.map((menuItem) => {
3152
- const MenuIcon = menuItem.icon;
3153
- const gutter = item.menuItems?.some((m) => m.icon);
3154
- return /* @__PURE__ */ jsxs(React23__default.Fragment, { children: [
3155
- menuItem.separatorBefore && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
3156
- /* @__PURE__ */ jsxs(
3157
- DropdownMenuItem,
3158
- {
3159
- disabled: menuItem.disabled,
3160
- onSelect: () => menuItem.onSelect?.(),
3161
- className: cn(
3162
- menuItem.destructive && "text-destructive focus:bg-destructive/15 focus:text-destructive hover:bg-destructive/10 hover:text-destructive"
3163
- ),
3164
- children: [
3165
- MenuIcon ? /* @__PURE__ */ jsx(MenuIcon, {}) : gutter ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }) : null,
3166
- /* @__PURE__ */ jsx("span", { className: "truncate", children: menuItem.label }),
3167
- menuItem.shortcut && /* @__PURE__ */ jsx(DropdownMenuShortcut, { children: menuItem.shortcut })
3168
- ]
3169
- }
3170
- )
3171
- ] }, menuItem.id);
3172
- })
3376
+ children: renderMenuRows(item.menuItems)
3173
3377
  }
3174
3378
  )
3175
3379
  ] }) : withTooltip,
@@ -3181,22 +3385,132 @@ var NavItems = ({
3181
3385
  }
3182
3386
  )
3183
3387
  ] }, item.key || item.name);
3184
- }) });
3388
+ };
3389
+ const rendered = items.map(
3390
+ (item, index) => isHidden(index) ? null : renderItem(item, index)
3391
+ );
3392
+ const overflowTrigger = hiddenIndices.length > 0 && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
3393
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
3394
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3395
+ "button",
3396
+ {
3397
+ type: "button",
3398
+ "aria-label": overflowLabel,
3399
+ className: cn(
3400
+ `relative inline-flex shrink-0 items-center justify-center border-0
3401
+ transition-colors ring-1 ring-transparent focus-visible:outline-none
3402
+ focus-visible:ring-2 focus-visible:ring-ring cursor-pointer`,
3403
+ V.item,
3404
+ variant === "nav" && "px-2 py-2",
3405
+ V.hover,
3406
+ V.open
3407
+ ),
3408
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: iconClassName })
3409
+ }
3410
+ ) }) }),
3411
+ /* @__PURE__ */ jsx(TooltipContent, { side: tooltipSide, children: overflowLabel })
3412
+ ] }),
3413
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: hiddenIndices.map((index) => {
3414
+ const item = items[index];
3415
+ if (!item) return null;
3416
+ const Icon = item.icon;
3417
+ return /* @__PURE__ */ jsxs(
3418
+ DropdownMenuItem,
3419
+ {
3420
+ disabled: item.disabled,
3421
+ onSelect: () => select(item),
3422
+ children: [
3423
+ Icon ? /* @__PURE__ */ jsx(Icon, {}) : /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }),
3424
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label ?? item.name })
3425
+ ]
3426
+ },
3427
+ keyOf(item)
3428
+ );
3429
+ }) })
3430
+ ] });
3431
+ if (!isTabs && !overflow) {
3432
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 0, children: rendered });
3433
+ }
3434
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs(
3435
+ "div",
3436
+ {
3437
+ ref: containerRef,
3438
+ role: isTabs ? "tablist" : void 0,
3439
+ "aria-orientation": isTabs ? orientation : void 0,
3440
+ onKeyDown: handleKeyDown,
3441
+ className: cn(
3442
+ "flex min-w-0",
3443
+ isVertical ? "flex-col items-center gap-1" : "items-center gap-1",
3444
+ V.strip,
3445
+ className
3446
+ ),
3447
+ children: [
3448
+ rendered,
3449
+ overflowTrigger
3450
+ ]
3451
+ }
3452
+ ) });
3185
3453
  };
3186
- var NavBase = ({ className = "", orientation, sections }) => {
3454
+ function renderMenuRows(menuItems) {
3455
+ const gutter = menuItems?.some((m) => m.icon);
3456
+ return menuItems?.map((menuItem) => {
3457
+ const MenuIcon = menuItem.icon;
3458
+ return /* @__PURE__ */ jsxs(React23__default.Fragment, { children: [
3459
+ menuItem.separatorBefore && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
3460
+ /* @__PURE__ */ jsxs(
3461
+ DropdownMenuItem,
3462
+ {
3463
+ disabled: menuItem.disabled,
3464
+ onSelect: () => menuItem.onSelect?.(),
3465
+ className: cn(
3466
+ menuItem.destructive && "text-destructive focus:bg-destructive/15 focus:text-destructive hover:bg-destructive/10 hover:text-destructive"
3467
+ ),
3468
+ children: [
3469
+ MenuIcon ? /* @__PURE__ */ jsx(MenuIcon, {}) : gutter ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }) : null,
3470
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: menuItem.label }),
3471
+ menuItem.shortcut && /* @__PURE__ */ jsx(DropdownMenuShortcut, { children: menuItem.shortcut })
3472
+ ]
3473
+ }
3474
+ )
3475
+ ] }, menuItem.id);
3476
+ });
3477
+ }
3478
+ var NavBase = ({
3479
+ className = "",
3480
+ orientation,
3481
+ sections
3482
+ }) => {
3187
3483
  const isVertical = orientation === "vertical";
3188
3484
  const containerClass = isVertical ? `flex flex-col items-center h-full ${className}` : `flex items-center w-full ${className}`;
3189
3485
  const sectionWrapperClass = isVertical ? "flex flex-col items-center gap-1" : "flex items-center gap-1 ";
3190
3486
  return /* @__PURE__ */ jsxs("nav", { className: containerClass, children: [
3191
- sections.start?.content && /* @__PURE__ */ jsx("div", { className: `${sectionWrapperClass} ${sections.start.className || ""}`, children: sections.start.content }),
3192
- sections.center?.content ? /* @__PURE__ */ jsx("div", { className: `flex-1 ${isVertical ? "" : "flex justify-center items-center gap-1"} ${sections.center.className || ""}`, children: sections.center.content }) : (
3487
+ sections.start?.content && /* @__PURE__ */ jsx(
3488
+ "div",
3489
+ {
3490
+ className: `${sectionWrapperClass} ${sections.start.className || ""}`,
3491
+ children: sections.start.content
3492
+ }
3493
+ ),
3494
+ sections.center?.content ? /* @__PURE__ */ jsx(
3495
+ "div",
3496
+ {
3497
+ className: `flex-1 ${isVertical ? "" : "flex justify-center items-center gap-1"} ${sections.center.className || ""}`,
3498
+ children: sections.center.content
3499
+ }
3500
+ ) : (
3193
3501
  // Empty spacer to push end section to the bottom/right
3194
3502
  /* @__PURE__ */ jsx("div", { className: "flex-1" })
3195
3503
  ),
3196
- sections.end?.content && /* @__PURE__ */ jsx("div", { className: `${sectionWrapperClass} ${isVertical ? "" : "ml-auto"} ${sections.end.className || ""}`, children: sections.end.content })
3504
+ sections.end?.content && /* @__PURE__ */ jsx(
3505
+ "div",
3506
+ {
3507
+ className: `${sectionWrapperClass} ${isVertical ? "" : "ml-auto"} ${sections.end.className || ""}`,
3508
+ children: sections.end.content
3509
+ }
3510
+ )
3197
3511
  ] });
3198
3512
  };
3199
- var NavHorizontalItems = ({ items }) => /* @__PURE__ */ jsx(NavItems, { items, orientation: "horizontal" });
3513
+ var NavHorizontalItems = (props) => /* @__PURE__ */ jsx(NavItems, { ...props, orientation: "horizontal" });
3200
3514
  var NavHorizontal = ({
3201
3515
  left,
3202
3516
  leftNavItems,
@@ -3630,6 +3944,56 @@ var CitationRow = React23.forwardRef(
3630
3944
  )
3631
3945
  );
3632
3946
  CitationRow.displayName = "CitationRow";
3947
+ var ClampedText = React23.forwardRef(
3948
+ ({
3949
+ lines = 3,
3950
+ moreLabel = "Show more",
3951
+ lessLabel = "Show less",
3952
+ className,
3953
+ children,
3954
+ ...props
3955
+ }, ref) => {
3956
+ const [expanded, setExpanded] = React23.useState(false);
3957
+ const [clipped, setClipped] = React23.useState(false);
3958
+ const bodyRef = React23.useRef(null);
3959
+ React23.useLayoutEffect(() => {
3960
+ const el = bodyRef.current;
3961
+ if (!el || expanded) return;
3962
+ const measure = () => setClipped(el.scrollHeight - el.clientHeight > 1);
3963
+ measure();
3964
+ const observer = new ResizeObserver(measure);
3965
+ observer.observe(el);
3966
+ return () => observer.disconnect();
3967
+ }, [expanded, children, lines]);
3968
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("min-w-0", className), ...props, children: [
3969
+ /* @__PURE__ */ jsx(
3970
+ "p",
3971
+ {
3972
+ ref: bodyRef,
3973
+ className: cn(
3974
+ "whitespace-pre-line",
3975
+ !expanded && "overflow-hidden [-webkit-box-orient:vertical] [-webkit-line-clamp:var(--clamped-lines)] [display:-webkit-box]"
3976
+ ),
3977
+ style: { "--clamped-lines": lines },
3978
+ children
3979
+ }
3980
+ ),
3981
+ clipped ? /* @__PURE__ */ jsx(
3982
+ Button,
3983
+ {
3984
+ type: "button",
3985
+ variant: "link",
3986
+ size: "sm",
3987
+ className: "h-auto p-0",
3988
+ "aria-expanded": expanded,
3989
+ onClick: () => setExpanded((open) => !open),
3990
+ children: expanded ? lessLabel : moreLabel
3991
+ }
3992
+ ) : null
3993
+ ] });
3994
+ }
3995
+ );
3996
+ ClampedText.displayName = "ClampedText";
3633
3997
  var ClarifyCard = React23.forwardRef(
3634
3998
  ({
3635
3999
  step,
@@ -4230,7 +4594,7 @@ var MetricGrid = React23.forwardRef(
4230
4594
  )
4231
4595
  );
4232
4596
  MetricGrid.displayName = "MetricGrid";
4233
- var NavVerticalItems = ({ items }) => /* @__PURE__ */ jsx(NavItems, { items, orientation: "vertical" });
4597
+ var NavVerticalItems = (props) => /* @__PURE__ */ jsx(NavItems, { ...props, orientation: "vertical" });
4234
4598
  var NavVertical = ({
4235
4599
  className = "",
4236
4600
  top,
@@ -4838,6 +5202,27 @@ var SearchInput = ({
4838
5202
  }
4839
5203
  );
4840
5204
  };
5205
+ var Eyebrow = React23.forwardRef(
5206
+ ({ aside, tone = "muted", className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
5207
+ "div",
5208
+ {
5209
+ ref,
5210
+ className: cn(
5211
+ "flex items-baseline gap-2 text-meta font-semibold uppercase tracking-wide",
5212
+ tone === "muted" && "text-muted-foreground",
5213
+ tone === "foreground" && "text-foreground",
5214
+ tone === "accent" && "text-primary",
5215
+ className
5216
+ ),
5217
+ ...props,
5218
+ children: [
5219
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children }),
5220
+ aside != null ? /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 font-normal normal-case tracking-normal text-muted-foreground", children: aside }) : null
5221
+ ]
5222
+ }
5223
+ )
5224
+ );
5225
+ Eyebrow.displayName = "Eyebrow";
4841
5226
  var SectionHeader = React23.forwardRef(({ icon, title, count, actions, bare, className, ...props }, ref) => /* @__PURE__ */ jsxs(
4842
5227
  "div",
4843
5228
  {
@@ -4935,6 +5320,9 @@ function TabbedPanel({
4935
5320
  activeTab,
4936
5321
  onTabChange,
4937
5322
  headerActions,
5323
+ overflow = false,
5324
+ overflowLabel = "More panels",
5325
+ keepMounted = false,
4938
5326
  className,
4939
5327
  headerClassName,
4940
5328
  bodyClassName,
@@ -4949,35 +5337,358 @@ function TabbedPanel({
4949
5337
  }
4950
5338
  onTabChange?.(value);
4951
5339
  };
4952
- return /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsx(Tabs, { value: currentTab, onValueChange: handleTabChange, className: "h-full", children: /* @__PURE__ */ jsxs(Card, { className: cn("h-full border rounded-none", className), children: [
4953
- /* @__PURE__ */ jsxs(CardHeader, { className: `flex flex-row items-center justify-between py-0 border-b h-[30px] px-0 space-y-0 ${headerClassName || ""}`, children: [
4954
- /* @__PURE__ */ jsx(TabsList, { className: "h-full bg-transparent p-0 gap-0 rounded-none", children: tabs.map((tab) => /* @__PURE__ */ jsxs(
4955
- TabsTrigger,
5340
+ const uid = React23__default.useId();
5341
+ const panelId = (value) => `${uid}-panel-${value}`;
5342
+ const items = React23__default.useMemo(
5343
+ () => tabs.map((tab) => ({
5344
+ key: tab.value,
5345
+ name: tab.name ?? (typeof tab.label === "string" ? tab.label : tab.value),
5346
+ label: tab.label,
5347
+ icon: tab.icon,
5348
+ disabled: tab.disabled
5349
+ })),
5350
+ [tabs]
5351
+ );
5352
+ return /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 0, children: /* @__PURE__ */ jsxs(Card, { className: cn("flex h-full min-h-0 flex-col border rounded-none", className), children: [
5353
+ /* @__PURE__ */ jsxs(
5354
+ "div",
5355
+ {
5356
+ className: cn(
5357
+ "flex h-[30px] shrink-0 flex-row items-stretch border-b",
5358
+ headerClassName
5359
+ ),
5360
+ children: [
5361
+ /* @__PURE__ */ jsx(
5362
+ NavItems,
5363
+ {
5364
+ items,
5365
+ variant: "underline",
5366
+ selectionMode: "tabs",
5367
+ activeKey: currentTab,
5368
+ onActiveChange: handleTabChange,
5369
+ panelId,
5370
+ overflow,
5371
+ overflowLabel,
5372
+ className: "min-w-0 flex-1 gap-0",
5373
+ iconClassName: "h-3.5 w-3.5 shrink-0"
5374
+ }
5375
+ ),
5376
+ headerActions && headerActions.length > 0 && /* @__PURE__ */ jsx("div", { className: "ml-auto flex h-full shrink-0 items-center gap-1 pr-1", children: /* @__PURE__ */ jsx(NavHorizontalItems, { items: headerActions }) })
5377
+ ]
5378
+ }
5379
+ ),
5380
+ tabs.map((tab) => {
5381
+ const active = tab.value === currentTab;
5382
+ if (!keepMounted && !active) return null;
5383
+ return /* @__PURE__ */ jsx(
5384
+ "div",
4956
5385
  {
4957
- value: tab.value,
4958
- disabled: tab.disabled,
4959
- 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",
4960
- children: [
4961
- tab.icon && /* @__PURE__ */ jsx(tab.icon, { className: "h-3.5 w-3.5 mr-1.5" }),
4962
- tab.label
4963
- ]
5386
+ id: panelId(tab.value),
5387
+ role: "tabpanel",
5388
+ "aria-label": tab.name ?? (typeof tab.label === "string" ? tab.label : tab.value),
5389
+ hidden: !active,
5390
+ className: cn("m-0 min-h-0 flex-1 overflow-y-auto", bodyClassName),
5391
+ children: tab.content
4964
5392
  },
4965
5393
  tab.value
4966
- )) }),
4967
- headerActions && headerActions.length > 0 && /* @__PURE__ */ jsx("div", { className: "ml-auto flex h-full shrink-0 items-center gap-1 pr-1", children: /* @__PURE__ */ jsx(NavHorizontalItems, { items: headerActions }) })
5394
+ );
5395
+ }),
5396
+ footerContent && /* @__PURE__ */ jsx(CardFooter, { className: cn("shrink-0", footerClassName), children: footerContent })
5397
+ ] }) }) });
5398
+ }
5399
+ var BAR = {
5400
+ succeeded: "bg-success",
5401
+ running: "bg-info",
5402
+ failed: "bg-destructive",
5403
+ needs_input: "bg-warning",
5404
+ stopped: "bg-muted-foreground",
5405
+ skipped: "bg-transparent",
5406
+ queued: "bg-transparent"
5407
+ };
5408
+ var DOT = {
5409
+ succeeded: "success",
5410
+ running: "running",
5411
+ failed: "error",
5412
+ needs_input: "warning",
5413
+ stopped: "muted",
5414
+ skipped: "muted",
5415
+ queued: "queued"
5416
+ };
5417
+ var STATUS_LABEL = {
5418
+ succeeded: "succeeded",
5419
+ running: "running",
5420
+ failed: "failed",
5421
+ needs_input: "needs input",
5422
+ stopped: "stopped",
5423
+ skipped: "never ran",
5424
+ queued: "not started"
5425
+ };
5426
+ function renderResult(result) {
5427
+ if (result == null) return null;
5428
+ if (React23.isValidElement(result)) return result;
5429
+ if (typeof result !== "object") {
5430
+ return /* @__PURE__ */ jsx("span", { className: "font-mono text-meta", children: String(result) });
5431
+ }
5432
+ const entries = Object.entries(result);
5433
+ const scalars = entries.filter(([, v]) => v == null || typeof v !== "object");
5434
+ const nested = entries.filter(([, v]) => v != null && typeof v === "object");
5435
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5436
+ scalars.length ? /* @__PURE__ */ jsx(PropertyList, { labelWidth: 96, children: scalars.map(([k, v]) => /* @__PURE__ */ jsx(PropertyRow, { label: k, mono: true, className: "py-0.5", children: v == null ? "\u2014" : String(v) }, k)) }) : null,
5437
+ nested.map(([k, v]) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
5438
+ /* @__PURE__ */ jsx("span", { className: "text-meta text-muted-foreground", children: k }),
5439
+ /* @__PURE__ */ 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) })
5440
+ ] }, k))
5441
+ ] });
5442
+ }
5443
+ var TaskGanttDetailCard = React23.forwardRef(({ task, elapsedMs, formatDuration = defaultFormatDuration, className, ...props }, ref) => {
5444
+ const status = task.status ?? "succeeded";
5445
+ const ms = task.durationMs ?? elapsedMs;
5446
+ const duration = task.duration ?? (ms != null ? formatDuration(ms) : "\u2014");
5447
+ const attempts = task.attempts ?? [];
5448
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col gap-2", className), ...props, children: [
5449
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
5450
+ /* @__PURE__ */ jsx(StatusDot, { tone: DOT[status], size: "md", className: "translate-y-px" }),
5451
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-meta font-medium", children: task.key }),
5452
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-meta text-muted-foreground tabular-nums", children: duration })
5453
+ ] }),
5454
+ /* @__PURE__ */ jsxs("div", { className: "text-meta text-muted-foreground", children: [
5455
+ STATUS_LABEL[status],
5456
+ task.summary != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
5457
+ " \xB7 ",
5458
+ task.summary
5459
+ ] }) : null
4968
5460
  ] }),
4969
- tabs.map((tab) => /* @__PURE__ */ jsx(
4970
- TabsContent,
5461
+ attempts.length ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children: attempts.map((a, i) => /* @__PURE__ */ jsxs("span", { className: "text-meta text-muted-foreground", children: [
5462
+ /* @__PURE__ */ jsxs("span", { className: "font-medium text-warning", children: [
5463
+ "attempt ",
5464
+ i + 1
5465
+ ] }),
5466
+ " ",
5467
+ a.title ?? `${a.status ?? "failed"}${a.durationMs != null ? ` \xB7 ${formatDuration(a.durationMs)}` : ""}`
5468
+ ] }, i)) }) : null,
5469
+ task.error != null ? /* @__PURE__ */ jsxs(
5470
+ "div",
4971
5471
  {
4972
- value: tab.value,
4973
- className: `h-[calc(100%-30px)] m-0 ${bodyClassName || ""}`,
4974
- children: /* @__PURE__ */ jsx(CardContent, { className: "overflow-y-auto h-full p-0", children: tab.content })
4975
- },
4976
- tab.value
4977
- )),
4978
- footerContent && /* @__PURE__ */ jsx(CardFooter, { className: footerClassName, children: footerContent })
4979
- ] }) }) }) });
5472
+ style: { borderColor: "var(--color-destructive)" },
5473
+ className: "border-l-2 bg-destructive/10 px-2 py-1.5",
5474
+ children: [
5475
+ task.error.code != null ? /* @__PURE__ */ jsx("div", { className: "font-mono text-meta font-medium text-destructive", children: task.error.code }) : null,
5476
+ task.error.message != null ? /* @__PURE__ */ jsx("div", { className: "text-meta", children: task.error.message }) : null,
5477
+ task.error.detail != null ? /* @__PURE__ */ 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
5478
+ ]
5479
+ }
5480
+ ) : null,
5481
+ task.result != null ? renderResult(task.result) : null,
5482
+ task.log != null ? /* @__PURE__ */ jsx("div", { className: "border-t border-border pt-1.5 font-mono text-meta text-muted-foreground", children: task.log }) : null
5483
+ ] });
5484
+ });
5485
+ TaskGanttDetailCard.displayName = "TaskGanttDetailCard";
5486
+ var toMs = (t) => t instanceof Date ? t.getTime() : typeof t === "number" ? t : Date.parse(t);
5487
+ var defaultFormatDuration = (ms) => {
5488
+ if (ms < 100) return `${Math.round(ms)}ms`;
5489
+ const s = ms / 1e3;
5490
+ return s < 10 ? `${(Math.round(s * 10) / 10).toFixed(1)}s` : `${Math.round(s)}s`;
5491
+ };
5492
+ var defaultFormatTick = (ms) => {
5493
+ if (ms === 0) return "0s";
5494
+ const s = ms / 1e3;
5495
+ return Number.isInteger(s) ? `${s}s` : `${(Math.round(s * 10) / 10).toFixed(1)}s`;
5496
+ };
5497
+ function place(seg, origin) {
5498
+ let start = seg.startMs;
5499
+ if (start == null && seg.startedAt != null && origin != null) {
5500
+ start = toMs(seg.startedAt) - origin;
5501
+ }
5502
+ if (start == null || Number.isNaN(start)) return null;
5503
+ let duration = seg.durationMs;
5504
+ if (duration == null && seg.startedAt != null && seg.finishedAt != null) {
5505
+ duration = toMs(seg.finishedAt) - toMs(seg.startedAt);
5506
+ }
5507
+ return { start, duration: duration != null && duration > 0 ? duration : 0 };
4980
5508
  }
5509
+ var TaskGantt = React23.forwardRef(
5510
+ ({
5511
+ tasks,
5512
+ origin,
5513
+ spanMs,
5514
+ nowMs,
5515
+ openEnded,
5516
+ ticks = 4,
5517
+ formatTick = defaultFormatTick,
5518
+ formatDuration = defaultFormatDuration,
5519
+ labelWidth = 112,
5520
+ density = "comfortable",
5521
+ showDetail = true,
5522
+ renderDetail,
5523
+ detailProps,
5524
+ selectedKey,
5525
+ onSelectTask,
5526
+ className,
5527
+ ...props
5528
+ }, ref) => {
5529
+ const barHeight = density === "compact" ? 11 : 13;
5530
+ const durationWidth = 40;
5531
+ const rows = React23.useMemo(() => {
5532
+ const stamps = [];
5533
+ for (const t of [...tasks, ...tasks.flatMap((t2) => t2.attempts ?? [])]) {
5534
+ if (t.startedAt != null) stamps.push(toMs(t.startedAt));
5535
+ }
5536
+ const zero = origin != null ? toMs(origin) : stamps.length ? Math.min(...stamps) : void 0;
5537
+ return tasks.map((task) => {
5538
+ const segments = [...task.attempts ?? [], task].map((seg) => {
5539
+ const at = place(seg, zero);
5540
+ return at ? { ...at, status: seg.status ?? task.status ?? "succeeded", title: seg.title } : null;
5541
+ }).filter((s) => s !== null);
5542
+ const last = segments[segments.length - 1];
5543
+ return { task, segments, end: last ? last.start + last.duration : 0 };
5544
+ });
5545
+ }, [tasks, origin]);
5546
+ const span = Math.max(
5547
+ spanMs ?? Math.max(nowMs ?? 0, ...rows.map((r) => r.end)),
5548
+ 1
5549
+ );
5550
+ const pct = (ms) => `${Math.max(0, Math.min(100, ms / span * 100))}%`;
5551
+ const axis = Array.from(
5552
+ { length: ticks + 1 },
5553
+ (_, i) => formatTick(span / ticks * i)
5554
+ );
5555
+ const lastLabel = openEnded ? `${axis[axis.length - 1]}+` : axis[axis.length - 1];
5556
+ const detailFor = (task, elapsedMs) => {
5557
+ if (!showDetail) return null;
5558
+ if (renderDetail) return renderDetail(task);
5559
+ if (task.detail != null) return task.detail;
5560
+ const hasBody = task.result != null || task.error != null || task.log != null || task.summary != null || (task.attempts?.length ?? 0) > 0;
5561
+ if (!hasBody) return null;
5562
+ return /* @__PURE__ */ jsx(
5563
+ TaskGanttDetailCard,
5564
+ {
5565
+ task,
5566
+ elapsedMs,
5567
+ formatDuration
5568
+ }
5569
+ );
5570
+ };
5571
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col", className), ...props, children: [
5572
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pt-0.5 pb-1", "aria-hidden": true, children: [
5573
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", style: { width: labelWidth } }),
5574
+ /* @__PURE__ */ jsx("span", { className: "flex flex-1", children: axis.slice(0, -1).map((label, i) => /* @__PURE__ */ jsx(
5575
+ "span",
5576
+ {
5577
+ className: "flex-1 border-l border-border pl-1 text-meta text-muted-foreground tabular-nums",
5578
+ children: label
5579
+ },
5580
+ i
5581
+ )) }),
5582
+ /* @__PURE__ */ jsx(
5583
+ "span",
5584
+ {
5585
+ className: "shrink-0 text-right text-meta text-muted-foreground tabular-nums",
5586
+ style: { width: durationWidth },
5587
+ children: lastLabel
5588
+ }
5589
+ )
5590
+ ] }),
5591
+ rows.map(({ task, segments }) => {
5592
+ const neverRan = segments.length === 0;
5593
+ const selected = selectedKey != null && selectedKey === task.key;
5594
+ const pickable = onSelectTask != null;
5595
+ const elapsed = task.durationMs ?? segments[segments.length - 1]?.duration;
5596
+ const duration = task.duration ?? (neverRan || elapsed == null ? "\u2014" : formatDuration(elapsed));
5597
+ const detail = detailFor(task, elapsed);
5598
+ const row = /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-[3px]", children: [
5599
+ /* @__PURE__ */ jsx(
5600
+ "span",
5601
+ {
5602
+ className: cn(
5603
+ "shrink-0 truncate font-mono text-meta",
5604
+ neverRan && "text-muted-foreground"
5605
+ ),
5606
+ style: { width: labelWidth },
5607
+ title: task.key,
5608
+ children: task.label ?? task.key
5609
+ }
5610
+ ),
5611
+ /* @__PURE__ */ jsxs(
5612
+ "span",
5613
+ {
5614
+ className: "relative min-w-0 flex-1 bg-muted/55",
5615
+ style: { height: barHeight },
5616
+ children: [
5617
+ neverRan ? /* @__PURE__ */ jsx("span", { className: "absolute inset-0 border border-dashed border-border" }) : segments.map((seg, i) => /* @__PURE__ */ jsx(
5618
+ "span",
5619
+ {
5620
+ title: seg.title ?? `${task.key} \xB7 ${seg.status} \xB7 ${formatDuration(seg.duration)}`,
5621
+ className: cn("absolute inset-y-0 rounded-[1px]", BAR[seg.status]),
5622
+ style: {
5623
+ left: pct(seg.start),
5624
+ width: pct(seg.duration),
5625
+ minWidth: 2
5626
+ }
5627
+ },
5628
+ i
5629
+ )),
5630
+ nowMs != null ? /* @__PURE__ */ jsx(
5631
+ "span",
5632
+ {
5633
+ className: "absolute -top-0.5 -bottom-0.5 w-px bg-info/70",
5634
+ style: { left: pct(nowMs) },
5635
+ "aria-hidden": true
5636
+ }
5637
+ ) : null
5638
+ ]
5639
+ }
5640
+ ),
5641
+ /* @__PURE__ */ jsx(
5642
+ "span",
5643
+ {
5644
+ className: "shrink-0 text-right font-mono text-meta text-muted-foreground tabular-nums",
5645
+ style: { width: durationWidth },
5646
+ children: duration
5647
+ }
5648
+ )
5649
+ ] }) });
5650
+ const trigger = pickable ? /* @__PURE__ */ jsx(
5651
+ "button",
5652
+ {
5653
+ type: "button",
5654
+ "aria-pressed": selected,
5655
+ onClick: () => onSelectTask(task.key),
5656
+ className: cn(
5657
+ "w-full cursor-pointer text-left focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
5658
+ "hover:bg-accent/60",
5659
+ selected && "bg-accent"
5660
+ ),
5661
+ children: row
5662
+ }
5663
+ ) : /* @__PURE__ */ jsx("div", { className: cn(detail != null && "hover:bg-accent/60"), children: row });
5664
+ if (detail == null) return /* @__PURE__ */ jsx("div", { children: trigger }, task.key);
5665
+ return /* @__PURE__ */ jsxs(
5666
+ HoverCard,
5667
+ {
5668
+ openDelay: detailProps?.openDelay ?? 200,
5669
+ closeDelay: detailProps?.closeDelay ?? 80,
5670
+ children: [
5671
+ /* @__PURE__ */ jsx(HoverCardTrigger, { asChild: true, children: trigger }),
5672
+ /* @__PURE__ */ jsx(
5673
+ HoverCardContent,
5674
+ {
5675
+ side: detailProps?.side ?? "right",
5676
+ align: detailProps?.align ?? "start",
5677
+ sideOffset: detailProps?.sideOffset ?? 8,
5678
+ style: detailProps?.width != null ? { width: detailProps.width } : void 0,
5679
+ className: cn("w-72 p-3", detailProps?.className),
5680
+ children: detail
5681
+ }
5682
+ )
5683
+ ]
5684
+ },
5685
+ task.key
5686
+ );
5687
+ })
5688
+ ] });
5689
+ }
5690
+ );
5691
+ TaskGantt.displayName = "TaskGantt";
4981
5692
  var TemplatePicker = React23.forwardRef(({ heading, options, value, onSelect, footnote, className, ...props }, ref) => /* @__PURE__ */ jsxs(
4982
5693
  "div",
4983
5694
  {
@@ -5088,27 +5799,93 @@ var TerminalLine = React23.forwardRef(
5088
5799
  )
5089
5800
  );
5090
5801
  TerminalLine.displayName = "TerminalLine";
5091
- var TimelineList = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx("ol", { ref, className: cn("flex flex-col", className), ...props, children }));
5802
+ var TimelineVariantContext = React23.createContext("columns");
5803
+ var RAIL_MARKER_OFFSET = "h-[0.692rem]";
5804
+ var RAIL_FOOTER_TAIL = "h-[1.067rem]";
5805
+ var TimelineList = React23.forwardRef(({ className, variant = "columns", children, ...props }, ref) => /* @__PURE__ */ jsx(TimelineVariantContext.Provider, { value: variant, children: /* @__PURE__ */ jsx("ol", { ref, className: cn("flex flex-col", className), ...props, children }) }));
5092
5806
  TimelineList.displayName = "TimelineList";
5093
- var TimelineEntry = React23.forwardRef(({ when, title, marker, className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
5094
- "li",
5095
- {
5096
- ref,
5097
- className: cn("flex gap-2 border-b border-border py-1.5 last:border-b-0", className),
5098
- ...props,
5099
- children: [
5100
- /* @__PURE__ */ jsxs("span", { className: "flex w-[72px] shrink-0 items-baseline gap-1.5 text-meta text-muted-foreground", children: [
5101
- marker ? /* @__PURE__ */ jsx("span", { className: "translate-y-1", children: marker }) : null,
5102
- /* @__PURE__ */ jsx("span", { className: "truncate", children: when })
5103
- ] }),
5104
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
5105
- title != null ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: title }) : null,
5106
- children
5107
- ] })
5108
- ]
5807
+ function RailGutter({ marker }) {
5808
+ return /* @__PURE__ */ jsxs("span", { className: "flex w-2.5 shrink-0 flex-col items-center", children: [
5809
+ /* @__PURE__ */ jsx(
5810
+ "span",
5811
+ {
5812
+ "aria-hidden": true,
5813
+ className: cn(
5814
+ "w-px shrink-0 bg-border group-first/entry:bg-transparent",
5815
+ RAIL_MARKER_OFFSET
5816
+ )
5817
+ }
5818
+ ),
5819
+ marker,
5820
+ /* @__PURE__ */ jsx(
5821
+ "span",
5822
+ {
5823
+ "aria-hidden": true,
5824
+ className: "w-px flex-1 bg-border group-last/entry:bg-transparent"
5825
+ }
5826
+ )
5827
+ ] });
5828
+ }
5829
+ var TimelineEntry = React23.forwardRef(({ when, title, marker, className, children, ...props }, ref) => {
5830
+ const variant = React23.useContext(TimelineVariantContext);
5831
+ if (variant === "rail") {
5832
+ return /* @__PURE__ */ jsxs(
5833
+ "li",
5834
+ {
5835
+ ref,
5836
+ className: cn("group/entry flex gap-2.5", className),
5837
+ ...props,
5838
+ children: [
5839
+ /* @__PURE__ */ jsx(RailGutter, { marker }),
5840
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 pb-4 group-last/entry:pb-0", children: [
5841
+ /* @__PURE__ */ jsx("span", { className: "text-meta text-muted-foreground", children: when }),
5842
+ title != null ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: title }) : null,
5843
+ children
5844
+ ] })
5845
+ ]
5846
+ }
5847
+ );
5109
5848
  }
5110
- ));
5849
+ return /* @__PURE__ */ jsxs(
5850
+ "li",
5851
+ {
5852
+ ref,
5853
+ className: cn("flex gap-2 border-b border-border py-1.5 last:border-b-0", className),
5854
+ ...props,
5855
+ children: [
5856
+ /* @__PURE__ */ jsxs("span", { className: "flex w-[72px] shrink-0 items-baseline gap-1.5 text-meta text-muted-foreground", children: [
5857
+ marker ? /* @__PURE__ */ jsx("span", { className: "translate-y-1", children: marker }) : null,
5858
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: when })
5859
+ ] }),
5860
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
5861
+ title != null ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: title }) : null,
5862
+ children
5863
+ ] })
5864
+ ]
5865
+ }
5866
+ );
5867
+ });
5111
5868
  TimelineEntry.displayName = "TimelineEntry";
5869
+ var TimelineFooter = React23.forwardRef(({ className, children, ...props }, ref) => {
5870
+ const variant = React23.useContext(TimelineVariantContext);
5871
+ if (variant === "rail") {
5872
+ return /* @__PURE__ */ jsxs(
5873
+ "li",
5874
+ {
5875
+ ref,
5876
+ role: "presentation",
5877
+ className: cn("flex gap-2.5", className),
5878
+ ...props,
5879
+ children: [
5880
+ /* @__PURE__ */ jsx("span", { className: "flex w-2.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("w-px bg-border", RAIL_FOOTER_TAIL) }) }),
5881
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 pt-1.5", children })
5882
+ ]
5883
+ }
5884
+ );
5885
+ }
5886
+ return /* @__PURE__ */ jsx("li", { ref, role: "presentation", className: cn("pt-1.5", className), ...props, children });
5887
+ });
5888
+ TimelineFooter.displayName = "TimelineFooter";
5112
5889
  var Toolbar = () => {
5113
5890
  return /* @__PURE__ */ jsxs("div", { className: "flex h-5 items-center space-x-2", children: [
5114
5891
  /* @__PURE__ */ jsx(
@@ -5695,6 +6472,6 @@ var Typography = {
5695
6472
  Pre: TypographyPre
5696
6473
  };
5697
6474
 
5698
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AgentChip, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppStatusBar, Avatar, AvatarFallback, AvatarImage, Badge, BarChartH, BarChartV, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, ButtonWithTooltip, CannotAnswerCard, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardWithHeader, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChatSession, ChatSessionActivityRow, ChatSessionActivitySubLine, ChatSessionComposer, ChatSessionContextChip, ChatSessionDisclosure, ChatSessionMessage, ChatSessionMessageOptions, ChatSessionProgressLine, ChatSessionPromptRow, ChatSessionStatusBar, ChatSessionTaskGroup, ChatSessionTaskRow, CitationList, CitationMarker, CitationRow, ClarifyCard, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextBar, DiagnosisCard, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffList, DiffRow, DivergingBar, DotRating, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmissionCard, EmissionHeader, EmptyState, EmptyStateLock, ErrorBoundary, FilterBar, FilterChip, HeatStrip, HoverCard, HoverCardContent, HoverCardTrigger, Item3 as Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Legend, LegendItem, MenuItem, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MetricGrid, MetricTile, NavBase, NavHorizontal, NavHorizontalItems, NavItems, NavVertical, NavVerticalItems, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NestedMenu, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PanelContent, PanelStack, Popover, PopoverContent, PopoverTrigger, Progress, PropertyList, PropertyRow, ProposalCard, RatingControl, RepairNote, ResizableHandle, ResizablePanel, ResizablePanelGroup, RetryNote, RichSelect, ScrollArea, ScrollBar, SearchInput, SectionHeader, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Sparkline, Spinner, StatusDot, TabbedPanel, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TemplatePicker, Terminal, TerminalLine, TimelineEntry, TimelineList, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Tour, TreeItem, TreeView, Typography, TypographyBlockquote, TypographyH1, TypographyH2, TypographyH3, TypographyH4, TypographyH5, TypographyH6, TypographyInlineCode, TypographyLarge, TypographyLead, TypographyList, TypographyMuted, TypographyP, TypographyPre, TypographySmall, UnderDevelopment, agentChipVariants, badgeVariants, buttonGroupVariants, buttonVariants, chatSessionGutterClass, cn, navigationMenuTriggerStyle, statusDotVariants, toggleVariants, useSidebar, useTour };
6475
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AgentChip, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppStatusBar, Avatar, AvatarFallback, AvatarImage, Badge, BarChartH, BarChartV, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, ButtonWithTooltip, CannotAnswerCard, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CardWithHeader, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, ChatSession, ChatSessionActivityRow, ChatSessionActivitySubLine, ChatSessionComposer, ChatSessionContextChip, ChatSessionDisclosure, ChatSessionMessage, ChatSessionMessageOptions, ChatSessionProgressLine, ChatSessionPromptRow, ChatSessionStatusBar, ChatSessionTaskGroup, ChatSessionTaskRow, CitationList, CitationMarker, CitationRow, ClampedText, ClarifyCard, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ContextBar, DiagnosisCard, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffList, DiffRow, DivergingBar, DotRating, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmissionCard, EmissionHeader, EmptyState, EmptyStateLock, ErrorBoundary, Eyebrow, FilterBar, FilterChip, HeatStrip, HoverCard, HoverCardContent, HoverCardTrigger, Item3 as Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Kbd, KbdGroup, Legend, LegendItem, MenuItem, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MetricGrid, MetricTile, NavBase, NavHorizontal, NavHorizontalItems, NavItems, NavVertical, NavVerticalItems, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, NestedMenu, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PanelContent, PanelStack, Popover, PopoverContent, PopoverTrigger, Progress, PropertyList, PropertyRow, ProposalCard, RatingControl, RepairNote, ResizableHandle, ResizablePanel, ResizablePanelGroup, RetryNote, RichSelect, ScrollArea, ScrollBar, SearchInput, SectionHeader, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Sparkline, Spinner, StatusDot, TabbedPanel, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TaskGantt, TaskGanttDetailCard, TemplatePicker, Terminal, TerminalLine, TimelineEntry, TimelineFooter, TimelineList, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, Tour, TreeItem, TreeView, Typography, TypographyBlockquote, TypographyH1, TypographyH2, TypographyH3, TypographyH4, TypographyH5, TypographyH6, TypographyInlineCode, TypographyLarge, TypographyLead, TypographyList, TypographyMuted, TypographyP, TypographyPre, TypographySmall, UnderDevelopment, agentChipVariants, badgeVariants, buttonGroupVariants, buttonVariants, chatSessionGutterClass, cn, navigationMenuTriggerStyle, statusDotVariants, toggleVariants, useOverflowItems, useSidebar, useTour };
5699
6476
  //# sourceMappingURL=index.js.map
5700
6477
  //# sourceMappingURL=index.js.map