@iloveagents/foundry-web-ui 0.14.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/components/assistant-chat.js +1 -1
- package/dist/components/chat-message-parts.d.ts +12 -7
- package/dist/components/chat-message-parts.js +52 -17
- package/dist/components/data-table/data-table-column-header.d.ts +8 -0
- package/dist/components/data-table/data-table-column-header.js +12 -0
- package/dist/components/data-table/data-table-faceted-filter.d.ts +16 -0
- package/dist/components/data-table/data-table-faceted-filter.js +32 -0
- package/dist/components/data-table/data-table-pagination.d.ts +8 -0
- package/dist/components/data-table/data-table-pagination.js +15 -0
- package/dist/components/data-table/data-table-row-actions.d.ts +16 -0
- package/dist/components/data-table/data-table-row-actions.js +11 -0
- package/dist/components/data-table/data-table-toolbar.d.ts +27 -0
- package/dist/components/data-table/data-table-toolbar.js +18 -0
- package/dist/components/data-table/data-table-view-options.d.ts +7 -0
- package/dist/components/data-table/data-table-view-options.js +11 -0
- package/dist/components/data-table/data-table.d.ts +25 -0
- package/dist/components/data-table/data-table.js +65 -0
- package/dist/components/data-table/facets.d.ts +82 -0
- package/dist/components/data-table/facets.js +156 -0
- package/dist/components/data-table/selection-column.d.ts +4 -0
- package/dist/components/data-table/selection-column.js +19 -0
- package/dist/components/data-table/state.d.ts +41 -0
- package/dist/components/data-table/state.js +148 -0
- package/dist/components/data-table/use-data-table.d.ts +22 -0
- package/dist/components/data-table/use-data-table.js +102 -0
- package/dist/components/message-disclosure.d.ts +6 -18
- package/dist/components/message-disclosure.js +24 -9
- package/dist/components/reasoning-effort-picker.js +38 -2
- package/dist/components/reasoning-part.d.ts +8 -5
- package/dist/components/reasoning-part.js +15 -11
- package/dist/components/sidebar.js +22 -20
- package/dist/components/turn-activity.d.ts +33 -0
- package/dist/components/turn-activity.js +110 -0
- package/dist/index.d.ts +14 -2
- package/dist/index.js +14 -2
- package/dist/lib/nav-config.d.ts +16 -0
- package/dist/ui/dropdown-menu.d.ts +1 -0
- package/dist/ui/dropdown-menu.js +4 -1
- package/package.json +5 -4
- package/dist/components/tool-run-group.d.ts +0 -29
- package/dist/components/tool-run-group.js +0 -56
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
* a curious reader, not the response. It stays visible after the run so a
|
|
7
7
|
* reader can check *why* an answer came out the way it did.
|
|
8
8
|
*
|
|
9
|
-
* The row shape comes from `MessageDisclosure`, shared with the
|
|
10
|
-
*
|
|
11
|
-
* separately, which is how they ended up with their carets on opposite
|
|
9
|
+
* The row shape comes from `MessageDisclosure`, shared with the turn's
|
|
10
|
+
* activity row — these sit next to each other in a message and used to be
|
|
11
|
+
* built separately, which is how they ended up with their carets on opposite
|
|
12
|
+
* sides.
|
|
12
13
|
*/
|
|
13
|
-
export declare function ReasoningPart({ text }: {
|
|
14
|
+
export declare function ReasoningPart({ text, nested }: {
|
|
14
15
|
text: string;
|
|
16
|
+
nested?: boolean;
|
|
15
17
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
16
18
|
/** Adapter for assistant-ui's `Reasoning` part slot. */
|
|
17
|
-
export declare const ReasoningMessagePartComponent: ({ text }: {
|
|
19
|
+
export declare const ReasoningMessagePartComponent: ({ text, nested, }: {
|
|
18
20
|
text: string;
|
|
21
|
+
nested?: boolean;
|
|
19
22
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Brain } from "lucide-react";
|
|
4
4
|
import { MessageDisclosure } from "./message-disclosure.js";
|
|
@@ -10,19 +10,23 @@ import { MessageDisclosure } from "./message-disclosure.js";
|
|
|
10
10
|
* a curious reader, not the response. It stays visible after the run so a
|
|
11
11
|
* reader can check *why* an answer came out the way it did.
|
|
12
12
|
*
|
|
13
|
-
* The row shape comes from `MessageDisclosure`, shared with the
|
|
14
|
-
*
|
|
15
|
-
* separately, which is how they ended up with their carets on opposite
|
|
13
|
+
* The row shape comes from `MessageDisclosure`, shared with the turn's
|
|
14
|
+
* activity row — these sit next to each other in a message and used to be
|
|
15
|
+
* built separately, which is how they ended up with their carets on opposite
|
|
16
|
+
* sides.
|
|
16
17
|
*/
|
|
17
|
-
export function ReasoningPart({ text }) {
|
|
18
|
+
export function ReasoningPart({ text, nested = false }) {
|
|
18
19
|
const [isOpen, setIsOpen] = useState(false);
|
|
19
20
|
if (!text.trim())
|
|
20
21
|
return null;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
// Inside the turn's activity fold there is no second fold: the reader
|
|
23
|
+
// already opened one to get here, and a disclosure containing disclosures
|
|
24
|
+
// is a maze. Just the thinking, captioned, in the slot the model produced
|
|
25
|
+
// it in.
|
|
26
|
+
if (nested) {
|
|
27
|
+
return (_jsxs("div", { className: "flex flex-col gap-1", "data-reasoning-part": true, children: [_jsxs("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [_jsx(Brain, { className: "size-3.5" }), "Reasoning"] }), _jsx("div", { className: "whitespace-pre-wrap text-xs text-muted-foreground", children: text })] }));
|
|
28
|
+
}
|
|
29
|
+
return (_jsx(MessageDisclosure, { icon: _jsx(Brain, { className: "size-3.5" }), label: "Reasoning", open: isOpen, onOpenChange: setIsOpen, ariaLabel: isOpen ? "Hide reasoning" : "Show reasoning", children: _jsx("div", { className: "whitespace-pre-wrap text-xs text-muted-foreground", children: text }) }));
|
|
26
30
|
}
|
|
27
31
|
/** Adapter for assistant-ui's `Reasoning` part slot. */
|
|
28
|
-
export const ReasoningMessagePartComponent = ({ text }) =>
|
|
32
|
+
export const ReasoningMessagePartComponent = ({ text, nested = false, }) => _jsx(ReasoningPart, { text: text, nested: nested });
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { NavLink, useLocation, useNavigate } from "react-router";
|
|
4
|
-
import { Ellipsis, PanelLeft, PanelLeftOpen, SquarePen, ChevronDown, ChevronRight, GripVertical, Plus, Upload } from "lucide-react";
|
|
4
|
+
import { Ellipsis, PanelLeft, PanelLeftOpen, SquarePen, ChevronDown, ChevronRight, GripVertical, Plus, Upload, } from "lucide-react";
|
|
5
5
|
import { cn } from "@iloveagents/foundry-web-primitives";
|
|
6
6
|
import { useSidebarStore, RAIL_WIDTH, SIDEBAR_MIN_WIDTH, SIDEBAR_MAX_WIDTH, } from "../lib/sidebar-store.js";
|
|
7
7
|
import { useToolPanelStore } from "../lib/tool-panel-store.js";
|
|
8
8
|
import { useNewConversation } from "../lib/use-new-conversation.js";
|
|
9
9
|
import { useNavStore } from "../lib/nav-store.js";
|
|
10
10
|
import { useAppStore } from "../lib/app-store.js";
|
|
11
|
-
import { getContainerDropZoneLabel, hasAnyDropTarget, hasExternalFilesData } from "../lib/nav-dnd.js";
|
|
11
|
+
import { getContainerDropZoneLabel, hasAnyDropTarget, hasExternalFilesData, } from "../lib/nav-dnd.js";
|
|
12
12
|
import { TooltipIconButton } from "./tooltip-icon-button.js";
|
|
13
13
|
import { Button } from "@iloveagents/foundry-web-primitives";
|
|
14
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
|
|
14
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "../ui/dropdown-menu.js";
|
|
15
15
|
import { AppBrand } from "./app-brand.js";
|
|
16
16
|
const ACTIVE_NAV_ROW_CLASS = "border border-transparent bg-sidebar-selected shadow-none";
|
|
17
17
|
const ACTIVE_NAV_TEXT_CLASS = "font-semibold text-sidebar-foreground";
|
|
@@ -127,10 +127,7 @@ function useActiveNavRowScroll(isActive) {
|
|
|
127
127
|
return;
|
|
128
128
|
const rect = element.getBoundingClientRect();
|
|
129
129
|
const viewportWidth = window.innerWidth || document.documentElement.clientWidth;
|
|
130
|
-
const isOnScreen = rect.width > 0 &&
|
|
131
|
-
rect.height > 0 &&
|
|
132
|
-
rect.right > 0 &&
|
|
133
|
-
rect.left < viewportWidth;
|
|
130
|
+
const isOnScreen = rect.width > 0 && rect.height > 0 && rect.right > 0 && rect.left < viewportWidth;
|
|
134
131
|
if (!isOnScreen)
|
|
135
132
|
return;
|
|
136
133
|
const nav = element.closest("nav");
|
|
@@ -427,10 +424,10 @@ function ContainerDropZone({ dnd }) {
|
|
|
427
424
|
* row underneath; the branch chevron keeps its own, since positioned
|
|
428
425
|
* elements paint above the static button and win the hit test.
|
|
429
426
|
*/
|
|
430
|
-
function NavTwisty({ hasChildren, isExpanded, onToggle, label, isActive, depth, inTree = true, }) {
|
|
427
|
+
function NavTwisty({ hasChildren, childrenUnloaded = false, isExpanded, onToggle, label, isActive, depth, inTree = true, }) {
|
|
431
428
|
const cell = cn(NAV_TWISTY_CLASS, getNavDepthOffsets(depth).column);
|
|
432
429
|
if (!hasChildren) {
|
|
433
|
-
return (_jsx("span", { "data-nav-twisty-cell": true, "aria-hidden": "true", className: cn(cell, "pointer-events-none"), children: inTree && (_jsx("span", { className: cn("size-1 rounded-full", isActive ? "bg-primary/70" : "bg-sidebar-foreground/30") })) }));
|
|
430
|
+
return (_jsx("span", { "data-nav-twisty-cell": true, "aria-hidden": "true", className: cn(cell, "pointer-events-none"), children: inTree && !childrenUnloaded && (_jsx("span", { "data-nav-leaf-dot": true, className: cn("size-1 rounded-full", isActive ? "bg-primary/70" : "bg-sidebar-foreground/30") })) }));
|
|
434
431
|
}
|
|
435
432
|
return (_jsx("span", { "data-nav-twisty-cell": true, className: cell, children: _jsx("button", { type: "button", "data-nav-twisty": true, "aria-expanded": isExpanded, "aria-label": isExpanded ? `Collapse ${label}` : `Expand ${label}`, onClick: (e) => {
|
|
436
433
|
e.preventDefault();
|
|
@@ -480,11 +477,13 @@ function NavLeafItem({ item, isActive, onClick, depth = 0, }) {
|
|
|
480
477
|
!isDisabled &&
|
|
481
478
|
"ring-2 ring-inset ring-sidebar-ring/50 bg-sidebar-selected", isFileDragOver &&
|
|
482
479
|
!isDisabled &&
|
|
483
|
-
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && !isDisabled && "opacity-50"), children: [_jsx(NavTwisty, { hasChildren: false, label: item.label, isActive: isActive && !isDisabled, depth: depth }), _jsxs("button", { type: "button", disabled: isDisabled, onClick: isDisabled ? undefined : onClick, className: cn("flex items-center gap-2 flex-1 min-w-0 py-1.5 text-sm text-left", bodyPl, "pr-3 transition-[padding] duration-150", NAV_ROW_ACTIONS_CLEARANCE, isDisabled
|
|
480
|
+
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && !isDisabled && "opacity-50"), children: [_jsx(NavTwisty, { hasChildren: false, childrenUnloaded: item.childrenUnloaded, label: item.label, isActive: isActive && !isDisabled, depth: depth }), _jsxs("button", { type: "button", disabled: isDisabled, onClick: isDisabled ? undefined : onClick, className: cn("flex items-center gap-2 flex-1 min-w-0 py-1.5 text-sm text-left", bodyPl, "pr-3 transition-[padding] duration-150", NAV_ROW_ACTIONS_CLEARANCE, isDisabled
|
|
484
481
|
? "text-sidebar-foreground/45 cursor-default"
|
|
485
482
|
: isActive
|
|
486
483
|
? ACTIVE_NAV_TEXT_CLASS
|
|
487
|
-
: "text-sidebar-foreground/80"), children: [_jsx(item.icon, { className: cn("size-3.5 shrink-0", isActive && !isDisabled ? ACTIVE_NAV_ICON_CLASS : "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive
|
|
484
|
+
: "text-sidebar-foreground/80"), children: [_jsx(item.icon, { className: cn("size-3.5 shrink-0", isActive && !isDisabled ? ACTIVE_NAV_ICON_CLASS : "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive
|
|
485
|
+
? ACTIVE_NAV_BADGE_CLASS
|
|
486
|
+
: "bg-sidebar-accent text-sidebar-accent-foreground"), children: item.badge })), _jsx(NavStatusIcon, { statusIcon: item.statusIcon }), _jsx(NavStatusDot, { statusDot: item.statusDot })] }), isFileDragOver && !isDisabled && (_jsx(Upload, { "aria-hidden": "true", className: "size-3.5 mr-2 shrink-0 text-primary pointer-events-none" })), _jsx(NavRowControls, { item: item })] }));
|
|
488
487
|
}
|
|
489
488
|
return (_jsxs("button", { ref: activeButtonRef, "data-active-nav-row": isActive ? "true" : undefined, "data-nav-depth": depth, type: "button", disabled: isDisabled, onClick: isDisabled ? undefined : onClick, ...(isDisabled ? {} : draggableProps), ...(isDisabled ? {} : dropTargetProps), className: cn("relative flex items-center gap-2 w-full", "rounded-lg border border-transparent pr-3 py-1.5 text-sm text-left", bodyPl, isDisabled
|
|
490
489
|
? "opacity-40 cursor-default"
|
|
@@ -493,7 +492,7 @@ function NavLeafItem({ item, isActive, onClick, depth = 0, }) {
|
|
|
493
492
|
!isDisabled &&
|
|
494
493
|
"ring-2 ring-inset ring-sidebar-ring/50 bg-sidebar-selected", isFileDragOver &&
|
|
495
494
|
!isDisabled &&
|
|
496
|
-
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && !isDisabled && "opacity-50"), children: [_jsx(NavTwisty, { hasChildren: false, label: item.label, isActive: isActive && !isDisabled, depth: depth }), _jsx(item.icon, { className: cn("size-3.5 shrink-0", isActive && !isDisabled ? ACTIVE_NAV_ICON_CLASS : "text-sidebar-foreground/60") }), _jsx("span", { className: cn("truncate", isActive && !isDisabled && ACTIVE_NAV_TEXT_CLASS), children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive ? ACTIVE_NAV_BADGE_CLASS : "bg-sidebar-accent text-sidebar-accent-foreground"), children: item.badge })), _jsx(NavStatusIcon, { statusIcon: item.statusIcon }), _jsx(NavStatusDot, { statusDot: item.statusDot }), isFileDragOver && !isDisabled && (_jsx(Upload, { "aria-hidden": "true", className: "ml-auto size-3.5 shrink-0 text-primary pointer-events-none" }))] }));
|
|
495
|
+
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && !isDisabled && "opacity-50"), children: [_jsx(NavTwisty, { hasChildren: false, childrenUnloaded: item.childrenUnloaded, label: item.label, isActive: isActive && !isDisabled, depth: depth }), _jsx(item.icon, { className: cn("size-3.5 shrink-0", isActive && !isDisabled ? ACTIVE_NAV_ICON_CLASS : "text-sidebar-foreground/60") }), _jsx("span", { className: cn("truncate", isActive && !isDisabled && ACTIVE_NAV_TEXT_CLASS), children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive ? ACTIVE_NAV_BADGE_CLASS : "bg-sidebar-accent text-sidebar-accent-foreground"), children: item.badge })), _jsx(NavStatusIcon, { statusIcon: item.statusIcon }), _jsx(NavStatusDot, { statusDot: item.statusDot }), isFileDragOver && !isDisabled && (_jsx(Upload, { "aria-hidden": "true", className: "ml-auto size-3.5 shrink-0 text-primary pointer-events-none" }))] }));
|
|
497
496
|
}
|
|
498
497
|
function SubNavItems({ items, parentPath, depth = 1, }) {
|
|
499
498
|
const navigate = useNavigate();
|
|
@@ -587,7 +586,9 @@ function NestedFolderItem({ item, depth }) {
|
|
|
587
586
|
? ACTIVE_NAV_ICON_CLASS
|
|
588
587
|
: isAncestorActive
|
|
589
588
|
? "text-sidebar-foreground/80"
|
|
590
|
-
: "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive
|
|
589
|
+
: "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive
|
|
590
|
+
? ACTIVE_NAV_BADGE_CLASS
|
|
591
|
+
: "bg-sidebar-accent text-sidebar-accent-foreground"), children: item.badge })), _jsx(NavStatusIcon, { statusIcon: item.statusIcon }), _jsx(NavStatusDot, { statusDot: item.statusDot })] }), isFileDragOver && !isDisabled && (_jsx(Upload, { "aria-hidden": "true", className: "size-3.5 mr-2 shrink-0 text-primary pointer-events-none" })), _jsx(NavRowControls, { item: item })] }), isExpanded && item.children && (_jsxs("div", { children: [item.children.map((child) => {
|
|
591
592
|
const hasNestedChildren = child.children && child.children.length > 0;
|
|
592
593
|
if (hasNestedChildren) {
|
|
593
594
|
return (_jsx(NestedFolderItem, { item: child, depth: depth + 1 }, child.to + child.label));
|
|
@@ -634,7 +635,7 @@ function CollapsibleNavItem({ item, inTree = true }) {
|
|
|
634
635
|
!isDisabled &&
|
|
635
636
|
"ring-2 ring-inset ring-sidebar-ring/50 bg-sidebar-selected", isFileDragOver &&
|
|
636
637
|
!isDisabled &&
|
|
637
|
-
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && !isDisabled && "opacity-50"), children: [_jsx(NavTwisty, { hasChildren: hasChildren, inTree: inTree, isExpanded: isExpanded, onToggle: hasChildren ? () => setManualOverride(!isExpanded) : undefined, label: item.label, isActive: (selected || ancestor) && !isDisabled, depth: 0 }), _jsxs("button", { type: "button", onClick: () => {
|
|
638
|
+
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && !isDisabled && "opacity-50"), children: [_jsx(NavTwisty, { hasChildren: hasChildren, childrenUnloaded: item.childrenUnloaded, inTree: inTree, isExpanded: isExpanded, onToggle: hasChildren ? () => setManualOverride(!isExpanded) : undefined, label: item.label, isActive: (selected || ancestor) && !isDisabled, depth: 0 }), _jsxs("button", { type: "button", onClick: () => {
|
|
638
639
|
if (isDisabled)
|
|
639
640
|
return;
|
|
640
641
|
setNavContext({
|
|
@@ -659,13 +660,15 @@ function CollapsibleNavItem({ item, inTree = true }) {
|
|
|
659
660
|
? ACTIVE_NAV_ICON_CLASS
|
|
660
661
|
: ancestor
|
|
661
662
|
? "text-sidebar-foreground/80"
|
|
662
|
-
: "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", selected
|
|
663
|
+
: "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", selected
|
|
664
|
+
? ACTIVE_NAV_BADGE_CLASS
|
|
665
|
+
: "bg-sidebar-accent text-sidebar-accent-foreground"), children: item.badge })), _jsx(NavStatusIcon, { statusIcon: item.statusIcon }), _jsx(NavStatusDot, { statusDot: item.statusDot })] }), isFileDragOver && !isDisabled && (_jsx(Upload, { "aria-hidden": "true", className: "size-3.5 mr-2 shrink-0 text-primary pointer-events-none" })), _jsx(NavRowControls, { item: item })] }));
|
|
663
666
|
return (_jsxs("div", { ...(isDisabled ? {} : dropTargetProps), ...(isDisabled ? {} : draggableProps), children: [inner(isActive, isAncestorActive), hasChildren && isExpanded && (_jsxs(_Fragment, { children: [_jsx(SubNavItems, { items: item.children, parentPath: item.to }), item.dnd?.canDrop() && _jsx(ContainerDropZone, { dnd: item.dnd })] }))] }));
|
|
664
667
|
}
|
|
665
668
|
if (isDisabled) {
|
|
666
669
|
return (_jsxs("div", { "data-nav-depth": 0, className: cn("relative flex items-center gap-2 w-full opacity-40", inTree ? getNavDepthOffsets(0).body : NAV_FLAT_BODY_CLASS,
|
|
667
670
|
// Transparent border for box-model parity — see the NavLink variant.
|
|
668
|
-
"rounded-lg border border-transparent pr-3 py-2 text-left text-sm text-sidebar-foreground/45"), children: [_jsx(NavTwisty, { hasChildren: false, inTree: inTree, label: item.label, depth: 0 }), _jsx(item.icon, { className: "size-4 shrink-0 text-sidebar-foreground/45" }), _jsx("span", { className: "truncate", children: item.label })] }));
|
|
671
|
+
"rounded-lg border border-transparent pr-3 py-2 text-left text-sm text-sidebar-foreground/45"), children: [_jsx(NavTwisty, { hasChildren: false, childrenUnloaded: item.childrenUnloaded, inTree: inTree, label: item.label, depth: 0 }), _jsx(item.icon, { className: "size-4 shrink-0 text-sidebar-foreground/45" }), _jsx("span", { className: "truncate", children: item.label })] }));
|
|
669
672
|
}
|
|
670
673
|
return (_jsx(NavLink, { ref: activeLinkRef, "data-active-nav-row": currentPath === item.to ? "true" : undefined, "data-nav-depth": 0, to: item.to, ...draggableProps, ...dropTargetProps, className: ({ isActive }) => cn("relative flex items-center gap-2 w-full", inTree ? getNavDepthOffsets(0).body : NAV_FLAT_BODY_CLASS,
|
|
671
674
|
// The transparent border is NOT decoration: it is what makes this
|
|
@@ -675,7 +678,8 @@ function CollapsibleNavItem({ item, inTree = true }) {
|
|
|
675
678
|
// siblings' and the column goes ragged.
|
|
676
679
|
"rounded-xl border border-transparent pr-3 py-2 text-sm text-left", "text-sidebar-foreground/85 transition-colors hover:bg-sidebar-accent/70", isActive && cn(ACTIVE_NAV_ROW_CLASS, ACTIVE_NAV_TEXT_CLASS), isDragOver &&
|
|
677
680
|
!isFileDragOver &&
|
|
678
|
-
"bg-sidebar-selected ring-2 ring-inset ring-sidebar-ring/50", isFileDragOver &&
|
|
681
|
+
"bg-sidebar-selected ring-2 ring-inset ring-sidebar-ring/50", isFileDragOver &&
|
|
682
|
+
"outline-dashed outline-2 -outline-offset-2 outline-primary bg-primary/10", item.dimmed && "opacity-50"), children: ({ isActive }) => (_jsxs(_Fragment, { children: [_jsx(NavTwisty, { hasChildren: false, childrenUnloaded: item.childrenUnloaded, inTree: inTree, label: item.label, isActive: isActive, depth: 0 }), _jsx(item.icon, { className: cn("size-4 shrink-0", isActive ? ACTIVE_NAV_ICON_CLASS : "text-sidebar-foreground/60") }), _jsx("span", { className: "truncate", children: item.label }), item.badge && (_jsx("span", { className: cn("shrink-0 rounded px-1 py-0.5 text-[10px] font-medium", isActive
|
|
679
683
|
? ACTIVE_NAV_BADGE_CLASS
|
|
680
684
|
: "bg-sidebar-accent text-sidebar-accent-foreground"), children: item.badge })), _jsx(NavStatusIcon, { statusIcon: item.statusIcon }), _jsx(NavStatusDot, { statusDot: item.statusDot }), isFileDragOver && (_jsx(Upload, { "aria-hidden": "true", className: "ml-auto size-3.5 shrink-0 text-primary pointer-events-none" }))] })) }));
|
|
681
685
|
}
|
|
@@ -802,9 +806,7 @@ function useGroupCollapsed(label, defaultCollapsed) {
|
|
|
802
806
|
// that happens to collide with a prototype key (e.g. ``toString``,
|
|
803
807
|
// ``__proto__``, ``constructor``) is not treated as a persisted
|
|
804
808
|
// entry. ``map`` is parsed JSON so its prototype is ``Object``'s.
|
|
805
|
-
return Object.prototype.hasOwnProperty.call(map, label)
|
|
806
|
-
? map[label]
|
|
807
|
-
: Boolean(defaultCollapsed);
|
|
809
|
+
return Object.prototype.hasOwnProperty.call(map, label) ? map[label] : Boolean(defaultCollapsed);
|
|
808
810
|
}
|
|
809
811
|
function toggleGroupCollapsed(label, current) {
|
|
810
812
|
writeCollapseMap({ ...readCollapseMap(), [label]: !current });
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* ONE row for everything a turn did before it answered.
|
|
4
|
+
*
|
|
5
|
+
* A turn that searched a corpus eleven times used to render eleven summary
|
|
6
|
+
* rows — one per adjacent run of tool calls — plus a reasoning row of its
|
|
7
|
+
* own, and the answer somewhere underneath. Each row was individually
|
|
8
|
+
* reasonable; stacked, they were a ladder the reader had to climb past to
|
|
9
|
+
* reach the point.
|
|
10
|
+
*
|
|
11
|
+
* So the machinery folds: "Worked · 20 tools · reasoning". The model's PROSE
|
|
12
|
+
* never goes inside — it is what the reader came for and it stays in the
|
|
13
|
+
* open, which also means each stretch of it ends one of these rows and
|
|
14
|
+
* starts the next. A turn that narrates its steps gets one row per step
|
|
15
|
+
* rather than one per tool run; a turn that just works and answers gets
|
|
16
|
+
* exactly one.
|
|
17
|
+
*
|
|
18
|
+
* Order is CHRONOLOGICAL, inside the fold and outside it. Reasoning explains
|
|
19
|
+
* why the next call happened ("I need the termination clause from both
|
|
20
|
+
* agreements" → three searches); moving it away from the calls it explains
|
|
21
|
+
* turns a narrative into a pile. Outside, a multi-step turn reads as work,
|
|
22
|
+
* what it learned, work, what it learned, answer — the model's prose ends
|
|
23
|
+
* one of these rows and starts the next.
|
|
24
|
+
*
|
|
25
|
+
* Collapsed by default, for the reason the per-run fold this replaces had
|
|
26
|
+
* already found: in a real transcript the machinery dominates the answer it
|
|
27
|
+
* was meant to support, and the reader scrolls past it to reach the point.
|
|
28
|
+
*/
|
|
29
|
+
export declare function TurnActivity({ indices, isRunning, children, }: {
|
|
30
|
+
indices: readonly number[];
|
|
31
|
+
isRunning: boolean;
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { useAuiState } from "@assistant-ui/react";
|
|
4
|
+
import { Loader2, CheckCircle2, XCircle, AlertCircle } from "lucide-react";
|
|
5
|
+
import { MessageDisclosure } from "./message-disclosure.js";
|
|
6
|
+
import { _detectErrorResult } from "./tool-fallback.js";
|
|
7
|
+
/**
|
|
8
|
+
* ONE row for everything a turn did before it answered.
|
|
9
|
+
*
|
|
10
|
+
* A turn that searched a corpus eleven times used to render eleven summary
|
|
11
|
+
* rows — one per adjacent run of tool calls — plus a reasoning row of its
|
|
12
|
+
* own, and the answer somewhere underneath. Each row was individually
|
|
13
|
+
* reasonable; stacked, they were a ladder the reader had to climb past to
|
|
14
|
+
* reach the point.
|
|
15
|
+
*
|
|
16
|
+
* So the machinery folds: "Worked · 20 tools · reasoning". The model's PROSE
|
|
17
|
+
* never goes inside — it is what the reader came for and it stays in the
|
|
18
|
+
* open, which also means each stretch of it ends one of these rows and
|
|
19
|
+
* starts the next. A turn that narrates its steps gets one row per step
|
|
20
|
+
* rather than one per tool run; a turn that just works and answers gets
|
|
21
|
+
* exactly one.
|
|
22
|
+
*
|
|
23
|
+
* Order is CHRONOLOGICAL, inside the fold and outside it. Reasoning explains
|
|
24
|
+
* why the next call happened ("I need the termination clause from both
|
|
25
|
+
* agreements" → three searches); moving it away from the calls it explains
|
|
26
|
+
* turns a narrative into a pile. Outside, a multi-step turn reads as work,
|
|
27
|
+
* what it learned, work, what it learned, answer — the model's prose ends
|
|
28
|
+
* one of these rows and starts the next.
|
|
29
|
+
*
|
|
30
|
+
* Collapsed by default, for the reason the per-run fold this replaces had
|
|
31
|
+
* already found: in a real transcript the machinery dominates the answer it
|
|
32
|
+
* was meant to support, and the reader scrolls past it to reach the point.
|
|
33
|
+
*/
|
|
34
|
+
export function TurnActivity({ indices, isRunning, children, }) {
|
|
35
|
+
const { tools, failed, hasReasoning } = useTurnOutcome(indices);
|
|
36
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
37
|
+
const summary = summarise({ tools, failed, hasReasoning, isRunning });
|
|
38
|
+
// Red only when EVERY call failed AND the turn has SETTLED. One failure
|
|
39
|
+
// among twenty is a turn that recovered, and painting it red would report a
|
|
40
|
+
// broken answer where there is a working one — but so would painting it red
|
|
41
|
+
// mid-run, when the only calls so far happen to have failed and the model is
|
|
42
|
+
// still reasoning its way to one that works. A partial failure still says
|
|
43
|
+
// so, in the count, without the alarm.
|
|
44
|
+
const allFailed = !isRunning && tools > 0 && failed === tools;
|
|
45
|
+
return (_jsx(MessageDisclosure, { open: isOpen, onOpenChange: setIsOpen, tone: allFailed ? "destructive" : "muted", icon: isRunning ? (_jsx(Loader2, { className: "size-3.5 animate-spin text-primary" })) : allFailed ? (_jsx(XCircle, { className: "size-3.5 text-destructive" })) : failed > 0 ? (_jsx(AlertCircle, { className: "size-3.5 text-muted-foreground" })) : (_jsx(CheckCircle2, { className: "size-3.5 text-primary" })), label: summary,
|
|
46
|
+
// The summary goes in the accessible name, not just the visible one.
|
|
47
|
+
// An `aria-label` REPLACES the name a button would otherwise derive
|
|
48
|
+
// from its contents, so a bare "Show what this turn did" would have
|
|
49
|
+
// announced the row while hiding the very thing it summarises — the
|
|
50
|
+
// call count, whether anything failed, whether there is thinking to
|
|
51
|
+
// read. A screen-reader user got strictly less than a sighted one.
|
|
52
|
+
ariaLabel: `${summary} — ${isOpen ? "hide" : "show"} details`, "data-turn-activity": true, children: _jsx("div", { className: "flex flex-col gap-2", children: children }) }));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The row's one line of text.
|
|
56
|
+
*
|
|
57
|
+
* Says what happened, in the order a reader asks it: is it still going, how
|
|
58
|
+
* much work was there, did any of it fail, and was there thinking to read.
|
|
59
|
+
* "Worked" alone when a turn only thought — claiming tools it never called
|
|
60
|
+
* would be a lie in the summary line, which is the one part nobody expands
|
|
61
|
+
* to check.
|
|
62
|
+
*
|
|
63
|
+
* This row describes its CONTENTS; it does not narrate the run. The phase a
|
|
64
|
+
* run is in belongs to the status indicator above the composer, which knows
|
|
65
|
+
* things this row cannot — which tool is in flight, whether the stream has
|
|
66
|
+
* stalled. A turn with reasoning and no calls yet therefore says "Reasoning"
|
|
67
|
+
* rather than "Thinking…", which is what the indicator is already saying two
|
|
68
|
+
* lines below; the same word twice on one screen reads as a stutter, not as
|
|
69
|
+
* two facts.
|
|
70
|
+
*
|
|
71
|
+
* No ellipses either: the spinner in the row's own icon slot carries "still
|
|
72
|
+
* going", and it does it without competing for the label.
|
|
73
|
+
*/
|
|
74
|
+
function summarise({ tools, failed, hasReasoning, isRunning, }) {
|
|
75
|
+
const toolText = tools === 1 ? "1 tool" : `${tools} tools`;
|
|
76
|
+
// 0 tools while running means the group holds nothing but reasoning —
|
|
77
|
+
// `useTurnOutcome` counts every non-reasoning part as a call — so naming it
|
|
78
|
+
// is accurate, not a guess.
|
|
79
|
+
if (isRunning)
|
|
80
|
+
return tools > 0 ? `Working · ${toolText}` : "Reasoning";
|
|
81
|
+
const parts = ["Worked"];
|
|
82
|
+
if (tools > 0)
|
|
83
|
+
parts.push(failed > 0 ? `${toolText} · ${failed} failed` : toolText);
|
|
84
|
+
if (hasReasoning)
|
|
85
|
+
parts.push("reasoning");
|
|
86
|
+
return parts.join(" · ");
|
|
87
|
+
}
|
|
88
|
+
/** What the turn actually contains, read from the message's own parts. */
|
|
89
|
+
function useTurnOutcome(indices) {
|
|
90
|
+
const parts = useAuiState((s) => s.message.parts);
|
|
91
|
+
let tools = 0;
|
|
92
|
+
let failed = 0;
|
|
93
|
+
let hasReasoning = false;
|
|
94
|
+
for (const index of indices) {
|
|
95
|
+
const part = parts?.[index];
|
|
96
|
+
if (!part)
|
|
97
|
+
continue;
|
|
98
|
+
if (part.type === "reasoning") {
|
|
99
|
+
hasReasoning = true;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
// Anything else under this node is a call the turn made. Counted by
|
|
103
|
+
// exclusion rather than by an allow-list of type names, so a new tool
|
|
104
|
+
// part type shows up in the count instead of vanishing from it.
|
|
105
|
+
tools++;
|
|
106
|
+
if (part.status?.type === "incomplete" || _detectErrorResult(part.result))
|
|
107
|
+
failed++;
|
|
108
|
+
}
|
|
109
|
+
return { tools, failed, hasReasoning };
|
|
110
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,18 @@ export { CollectionEmptyState } from "./components/collection-empty-state.js";
|
|
|
33
33
|
export { CollectionSkeleton } from "./components/collection-skeleton.js";
|
|
34
34
|
export { InfiniteScrollSentinel } from "./components/infinite-scroll-sentinel.js";
|
|
35
35
|
export { CollectionSurface } from "./components/collection-surface.js";
|
|
36
|
+
export { DataTable, type DataTableProps } from "./components/data-table/data-table.js";
|
|
37
|
+
export { DataTableToolbar, type DataTableFacet, type DataTableToolbarProps, } from "./components/data-table/data-table-toolbar.js";
|
|
38
|
+
export { DataTableFacetedFilter, type DataTableFacetedFilterProps, } from "./components/data-table/data-table-faceted-filter.js";
|
|
39
|
+
export { DataTableViewOptions, type DataTableViewOptionsProps, } from "./components/data-table/data-table-view-options.js";
|
|
40
|
+
export { DataTableColumnHeader, type DataTableColumnHeaderProps, } from "./components/data-table/data-table-column-header.js";
|
|
41
|
+
export { DataTablePagination, type DataTablePaginationProps, } from "./components/data-table/data-table-pagination.js";
|
|
42
|
+
export { DataTableRowActions, type DataTableRowAction, type DataTableRowActionsProps, } from "./components/data-table/data-table-row-actions.js";
|
|
43
|
+
export { selectionColumn, SELECTION_COLUMN_ID } from "./components/data-table/selection-column.js";
|
|
44
|
+
export { useDataTable, type UseDataTableOptions } from "./components/data-table/use-data-table.js";
|
|
45
|
+
export { DEFAULT_PAGE_SIZE, EMPTY_DATA_TABLE_STATE, isDataTableUrlKey, mergeDataTableUrlState, parseDataTableState, serializeDataTableState, type DataTableState, type DataTableUrlOptions, } from "./components/data-table/state.js";
|
|
46
|
+
export { FACET_EMPTY, cellText, columnLabel, facetCounts, facetFilterFn, facetKeys, facetOptions, normalizeFacetValue, rowSearchText, tokenSearchFilterFn, type DataTableFacetCount, type DataTableFacetOption, } from "./components/data-table/facets.js";
|
|
47
|
+
export { createColumnHelper, flexRender, type Column, type ColumnDef, type ColumnFiltersState, type Row, type RowSelectionState, type SortingState, type Table, type VisibilityState, } from "@tanstack/react-table";
|
|
36
48
|
export { ThemeRuntimeProvider, ThemeScope, ThemeDocumentMetadata, useThemeRuntime, } from "./components/theme-runtime-provider.js";
|
|
37
49
|
export { JsonViewer } from "./components/json-viewer.js";
|
|
38
50
|
export { UserMenu } from "./components/user-menu.js";
|
|
@@ -42,7 +54,7 @@ export { NameDialog } from "./components/name-dialog.js";
|
|
|
42
54
|
export { SurfaceCard, surfaceCardVariants } from "./components/surface-card.js";
|
|
43
55
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./ui/tooltip.js";
|
|
44
56
|
export { Collapsible, CollapsibleTrigger, CollapsibleContent } from "./ui/collapsible.js";
|
|
45
|
-
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, } from "./ui/dropdown-menu.js";
|
|
57
|
+
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, } from "./ui/dropdown-menu.js";
|
|
46
58
|
export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, } from "./ui/dialog.js";
|
|
47
59
|
export { Popover, PopoverTrigger, PopoverAnchor, PopoverContent } from "./ui/popover.js";
|
|
48
60
|
export { Select, selectClassName } from "./ui/select.js";
|
|
@@ -66,7 +78,7 @@ export { usePageTools } from "./lib/use-page-tools.js";
|
|
|
66
78
|
export { useNewConversation } from "./lib/use-new-conversation.js";
|
|
67
79
|
export { AGUIAdapterSDK } from "./lib/ag-ui-adapter.js";
|
|
68
80
|
export { FileAttachmentAdapter, resolveMimeType } from "./lib/attachment-adapter.js";
|
|
69
|
-
export { registerAttachmentAdapter, useAttachmentAdapterStore
|
|
81
|
+
export { registerAttachmentAdapter, useAttachmentAdapterStore } from "./lib/attachment-registry.js";
|
|
70
82
|
export { ReasoningPart, ReasoningMessagePartComponent } from "./components/reasoning-part.js";
|
|
71
83
|
export { ReasoningEffortPicker } from "./components/reasoning-effort-picker.js";
|
|
72
84
|
export { AuthProvider } from "./lib/auth-provider.js";
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,18 @@ export { CollectionEmptyState } from "./components/collection-empty-state.js";
|
|
|
31
31
|
export { CollectionSkeleton } from "./components/collection-skeleton.js";
|
|
32
32
|
export { InfiniteScrollSentinel } from "./components/infinite-scroll-sentinel.js";
|
|
33
33
|
export { CollectionSurface } from "./components/collection-surface.js";
|
|
34
|
+
export { DataTable } from "./components/data-table/data-table.js";
|
|
35
|
+
export { DataTableToolbar, } from "./components/data-table/data-table-toolbar.js";
|
|
36
|
+
export { DataTableFacetedFilter, } from "./components/data-table/data-table-faceted-filter.js";
|
|
37
|
+
export { DataTableViewOptions, } from "./components/data-table/data-table-view-options.js";
|
|
38
|
+
export { DataTableColumnHeader, } from "./components/data-table/data-table-column-header.js";
|
|
39
|
+
export { DataTablePagination, } from "./components/data-table/data-table-pagination.js";
|
|
40
|
+
export { DataTableRowActions, } from "./components/data-table/data-table-row-actions.js";
|
|
41
|
+
export { selectionColumn, SELECTION_COLUMN_ID } from "./components/data-table/selection-column.js";
|
|
42
|
+
export { useDataTable } from "./components/data-table/use-data-table.js";
|
|
43
|
+
export { DEFAULT_PAGE_SIZE, EMPTY_DATA_TABLE_STATE, isDataTableUrlKey, mergeDataTableUrlState, parseDataTableState, serializeDataTableState, } from "./components/data-table/state.js";
|
|
44
|
+
export { FACET_EMPTY, cellText, columnLabel, facetCounts, facetFilterFn, facetKeys, facetOptions, normalizeFacetValue, rowSearchText, tokenSearchFilterFn, } from "./components/data-table/facets.js";
|
|
45
|
+
export { createColumnHelper, flexRender, } from "@tanstack/react-table";
|
|
34
46
|
export { ThemeRuntimeProvider, ThemeScope, ThemeDocumentMetadata, useThemeRuntime, } from "./components/theme-runtime-provider.js";
|
|
35
47
|
export { JsonViewer } from "./components/json-viewer.js";
|
|
36
48
|
export { UserMenu } from "./components/user-menu.js";
|
|
@@ -46,7 +58,7 @@ export { SurfaceCard, surfaceCardVariants } from "./components/surface-card.js";
|
|
|
46
58
|
// package. Tracked for a follow-up under #95/#96.
|
|
47
59
|
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./ui/tooltip.js";
|
|
48
60
|
export { Collapsible, CollapsibleTrigger, CollapsibleContent } from "./ui/collapsible.js";
|
|
49
|
-
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, } from "./ui/dropdown-menu.js";
|
|
61
|
+
export { DropdownMenu, DropdownMenuTrigger, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel, } from "./ui/dropdown-menu.js";
|
|
50
62
|
export { Dialog, DialogPortal, DialogOverlay, DialogTrigger, DialogClose, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, } from "./ui/dialog.js";
|
|
51
63
|
export { Popover, PopoverTrigger, PopoverAnchor, PopoverContent } from "./ui/popover.js";
|
|
52
64
|
export { Select, selectClassName } from "./ui/select.js";
|
|
@@ -74,7 +86,7 @@ export { useNewConversation } from "./lib/use-new-conversation.js";
|
|
|
74
86
|
// --- Adapters ---
|
|
75
87
|
export { AGUIAdapterSDK } from "./lib/ag-ui-adapter.js";
|
|
76
88
|
export { FileAttachmentAdapter, resolveMimeType } from "./lib/attachment-adapter.js";
|
|
77
|
-
export { registerAttachmentAdapter, useAttachmentAdapterStore
|
|
89
|
+
export { registerAttachmentAdapter, useAttachmentAdapterStore } from "./lib/attachment-registry.js";
|
|
78
90
|
export { ReasoningPart, ReasoningMessagePartComponent } from "./components/reasoning-part.js";
|
|
79
91
|
export { ReasoningEffortPicker } from "./components/reasoning-effort-picker.js";
|
|
80
92
|
// --- Auth ---
|
package/dist/lib/nav-config.d.ts
CHANGED
|
@@ -67,6 +67,22 @@ export interface NavItem {
|
|
|
67
67
|
contextInstructions?: string;
|
|
68
68
|
/** Sub-navigation items (rendered indented under parent) */
|
|
69
69
|
children?: NavItem[];
|
|
70
|
+
/**
|
|
71
|
+
* The row's children have not been fetched yet, so `children` being empty
|
|
72
|
+
* says nothing about whether it has any.
|
|
73
|
+
*
|
|
74
|
+
* A tree row normally shows one of two markers: a chevron if it branches, a
|
|
75
|
+
* dot if it is a leaf. That is two states for a question with THREE answers
|
|
76
|
+
* — has children, has none, and nobody has looked. A caller that loads
|
|
77
|
+
* subtrees lazily (only the active one, say) leaves every other row with an
|
|
78
|
+
* empty `children`, and the row then claims to be a leaf on the strength of
|
|
79
|
+
* a fetch that never happened.
|
|
80
|
+
*
|
|
81
|
+
* Set this while the answer is unknown and the row shows neither marker: no
|
|
82
|
+
* chevron to click into nothing, no dot asserting an emptiness no one
|
|
83
|
+
* checked. Clear it once the children are known, empty or not.
|
|
84
|
+
*/
|
|
85
|
+
childrenUnloaded?: boolean;
|
|
70
86
|
/** Inline action buttons rendered next to the nav item (e.g., + document, + folder) */
|
|
71
87
|
actions?: NavItemAction[];
|
|
72
88
|
/** Optional drag-and-drop behavior (provided by feature modules) */
|
|
@@ -5,3 +5,4 @@ export declare const DropdownMenuContent: import("react").ForwardRefExoticCompon
|
|
|
5
5
|
export declare const DropdownMenuItem: import("react").ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
6
6
|
export declare const DropdownMenuSeparator: import("react").ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSeparatorProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
7
7
|
export declare const DropdownMenuLabel: import("react").ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuLabelProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
8
|
+
export declare const DropdownMenuCheckboxItem: import("react").ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuCheckboxItemProps & import("react").RefAttributes<HTMLDivElement>, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/ui/dropdown-menu.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
3
3
|
import { forwardRef } from "react";
|
|
4
|
+
import { Check } from "lucide-react";
|
|
4
5
|
import { cn } from "@iloveagents/foundry-web-primitives";
|
|
5
6
|
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
6
7
|
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
@@ -17,3 +18,5 @@ export const DropdownMenuSeparator = forwardRef(({ className, ...props }, ref) =
|
|
|
17
18
|
DropdownMenuSeparator.displayName = "DropdownMenuSeparator";
|
|
18
19
|
export const DropdownMenuLabel = forwardRef(({ className, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Label, { ref: ref, className: cn("px-2 py-1.5 text-sm font-semibold", className), ...props })));
|
|
19
20
|
DropdownMenuLabel.displayName = "DropdownMenuLabel";
|
|
21
|
+
export const DropdownMenuCheckboxItem = forwardRef(({ className, children, checked, ...props }, ref) => (_jsxs(DropdownMenuPrimitive.CheckboxItem, { ref: ref, checked: checked, className: cn("relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none", "transition-colors focus:bg-accent focus:text-accent-foreground", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className), ...props, children: [_jsx("span", { className: "absolute left-2 flex size-4 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Check, { className: "size-4" }) }) }), children] })));
|
|
22
|
+
DropdownMenuCheckboxItem.displayName = "DropdownMenuCheckboxItem";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iloveagents/foundry-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React agent UI core for Foundry UI — chat, composer, AG-UI adapter for assistant-ui, tool cards, panels, sidebar, theme runtime, and UI stores.",
|
|
6
6
|
"keywords": [
|
|
@@ -65,13 +65,14 @@
|
|
|
65
65
|
"@radix-ui/react-popover": "^1.1.15",
|
|
66
66
|
"@radix-ui/react-slot": "^1.2.0",
|
|
67
67
|
"@radix-ui/react-tooltip": "^1.2.0",
|
|
68
|
+
"@tanstack/react-table": "^8.21.3",
|
|
68
69
|
"class-variance-authority": "^0.7.0",
|
|
69
70
|
"clsx": "^2.1.0",
|
|
70
|
-
"tailwind-merge": "^3.5.0",
|
|
71
71
|
"react-markdown": "^10.0.0",
|
|
72
72
|
"remark-gfm": "^4.0.0",
|
|
73
|
-
"
|
|
74
|
-
"@iloveagents/foundry-
|
|
73
|
+
"tailwind-merge": "^3.5.0",
|
|
74
|
+
"@iloveagents/foundry-agent": "^0.17.0",
|
|
75
|
+
"@iloveagents/foundry-web-primitives": "^0.17.0"
|
|
75
76
|
},
|
|
76
77
|
"devDependencies": {
|
|
77
78
|
"@ag-ui/client": "^0.0.52",
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
2
|
-
/**
|
|
3
|
-
* One row standing in for a run of adjacent tool calls, with the cards under
|
|
4
|
-
* it behind a disclosure.
|
|
5
|
-
*
|
|
6
|
-
* A turn that calls eight tools used to render eight bordered cards with no
|
|
7
|
-
* way to fold them away. The run is now one summary line — "8 tools · done" —
|
|
8
|
-
* over a group the reader can collapse when the machinery is in the way.
|
|
9
|
-
*
|
|
10
|
-
* COLLAPSED by default. This was open at first, on the reasoning that a
|
|
11
|
-
* developer watching an agent work wants the detail — but in a real
|
|
12
|
-
* transcript the cards dominate the answer they were supposed to support,
|
|
13
|
-
* and the reader scrolls past machinery to reach the point. The summary line
|
|
14
|
-
* already carries what matters at a glance (how many calls, and whether they
|
|
15
|
-
* worked); opening it is the deliberate act.
|
|
16
|
-
*
|
|
17
|
-
* The row shape is shared with the reasoning part via `MessageDisclosure`, so
|
|
18
|
-
* the two foldaways in a message read as one family instead of two widgets.
|
|
19
|
-
*
|
|
20
|
-
* RED only when the whole run failed. One failure among eight successful
|
|
21
|
-
* calls is not a red turn — the agent recovered, and painting the summary red
|
|
22
|
-
* would report a broken run where there was a working one. A partial failure
|
|
23
|
-
* still says so, in the count, without the alarm.
|
|
24
|
-
*/
|
|
25
|
-
export declare function ToolRunGroup({ indices, isRunning, children, }: {
|
|
26
|
-
indices: readonly number[];
|
|
27
|
-
isRunning: boolean;
|
|
28
|
-
children: ReactNode;
|
|
29
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { useAuiState } from "@assistant-ui/react";
|
|
4
|
-
import { Loader2, CheckCircle2, XCircle, AlertCircle } from "lucide-react";
|
|
5
|
-
import { MessageDisclosure } from "./message-disclosure.js";
|
|
6
|
-
import { _detectErrorResult } from "./tool-fallback.js";
|
|
7
|
-
/**
|
|
8
|
-
* One row standing in for a run of adjacent tool calls, with the cards under
|
|
9
|
-
* it behind a disclosure.
|
|
10
|
-
*
|
|
11
|
-
* A turn that calls eight tools used to render eight bordered cards with no
|
|
12
|
-
* way to fold them away. The run is now one summary line — "8 tools · done" —
|
|
13
|
-
* over a group the reader can collapse when the machinery is in the way.
|
|
14
|
-
*
|
|
15
|
-
* COLLAPSED by default. This was open at first, on the reasoning that a
|
|
16
|
-
* developer watching an agent work wants the detail — but in a real
|
|
17
|
-
* transcript the cards dominate the answer they were supposed to support,
|
|
18
|
-
* and the reader scrolls past machinery to reach the point. The summary line
|
|
19
|
-
* already carries what matters at a glance (how many calls, and whether they
|
|
20
|
-
* worked); opening it is the deliberate act.
|
|
21
|
-
*
|
|
22
|
-
* The row shape is shared with the reasoning part via `MessageDisclosure`, so
|
|
23
|
-
* the two foldaways in a message read as one family instead of two widgets.
|
|
24
|
-
*
|
|
25
|
-
* RED only when the whole run failed. One failure among eight successful
|
|
26
|
-
* calls is not a red turn — the agent recovered, and painting the summary red
|
|
27
|
-
* would report a broken run where there was a working one. A partial failure
|
|
28
|
-
* still says so, in the count, without the alarm.
|
|
29
|
-
*/
|
|
30
|
-
export function ToolRunGroup({ indices, isRunning, children, }) {
|
|
31
|
-
const { total, failed } = useToolRunOutcome(indices);
|
|
32
|
-
// Every call in the run failed — the only state that earns the alarm colour.
|
|
33
|
-
const allFailed = total > 0 && failed === total;
|
|
34
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
35
|
-
const label = isRunning
|
|
36
|
-
? `Using ${total === 1 ? "1 tool" : `${total} tools`}…`
|
|
37
|
-
: failed > 0
|
|
38
|
-
? `${total === 1 ? "1 tool" : `${total} tools`} · ${failed} failed`
|
|
39
|
-
: `${total === 1 ? "1 tool" : `${total} tools`} · done`;
|
|
40
|
-
return (_jsx(MessageDisclosure, { open: isOpen, onOpenChange: setIsOpen, tone: allFailed ? "destructive" : "muted", icon: isRunning ? (_jsx(Loader2, { className: "size-3.5 animate-spin text-primary" })) : allFailed ? (_jsx(XCircle, { className: "size-3.5 text-destructive" })) : failed > 0 ? (
|
|
41
|
-
// Partial: the run produced a result, some of it the hard way.
|
|
42
|
-
_jsx(AlertCircle, { className: "size-3.5 text-muted-foreground" })) : (_jsx(CheckCircle2, { className: "size-3.5 text-primary" })), label: label, "data-tool-run-group": true, children: _jsx("div", { className: "flex flex-col gap-2", children: children }) }));
|
|
43
|
-
}
|
|
44
|
-
function useToolRunOutcome(indices) {
|
|
45
|
-
const parts = useAuiState((s) => s.message.parts);
|
|
46
|
-
let failed = 0;
|
|
47
|
-
for (const index of indices) {
|
|
48
|
-
const part = parts?.[index];
|
|
49
|
-
if (!part)
|
|
50
|
-
continue;
|
|
51
|
-
const status = part.status;
|
|
52
|
-
if (status?.type === "incomplete" || _detectErrorResult(part.result))
|
|
53
|
-
failed++;
|
|
54
|
-
}
|
|
55
|
-
return { total: indices.length, failed };
|
|
56
|
-
}
|