diff --git a/packages/ui/src/components/context-tool-results.tsx b/packages/ui/src/components/context-tool-results.tsx index abed1d84b..a0d9311de 100644 --- a/packages/ui/src/components/context-tool-results.tsx +++ b/packages/ui/src/components/context-tool-results.tsx @@ -58,11 +58,9 @@ export function ContextToolGroupHeader(props: { { - if (!props.pending) props.onOpenChange(v) - }} + open={props.open} + showArrow + onOpenChange={props.onOpenChange} trigger={
boolean, -) { - if (opts.afterTool) return true - if (opts.groupTail === false) return true - if (!pageVisible()) return false - if (opts.working) return false - if (!statuses.length) return false - return !statuses.some((s) => busy(s)) -} - function renderable(part: PartType, showReasoningSummaries = true) { if (part.type === "tool") { if (HIDDEN_TOOLS.has(part.tool)) return false @@ -363,7 +349,6 @@ export function AssistantParts(props: { }) { const data = useData() const emptyParts: PartType[] = [] - const pageVisible = usePageVisibility() const groupState = createGroupOpenState() const grouped = createMemo(() => { const keys: string[] = [] @@ -481,24 +466,9 @@ export function AssistantParts(props: { return COLLAPSIBLE_SPRING }) const contextOpen = createMemo(() => { - const collapse = ( - afterTool?: boolean, - groupTail?: boolean, - group?: { part: ToolPart; message: AssistantMessage }[], - ) => - shouldCollapseGroup( - group?.map((item) => item.part.state.status) ?? [], - { - afterTool, - groupTail, - working: props.working, - }, - pageVisible, - ) const value = ctx() - if (value) return groupState.read(value.groupKey, collapse(value.afterTool, value.tail, value.parts)) - const entry = part() - return groupState.read(entry?.groupKey, collapse(entry?.afterTool, entry?.groupTail, entry?.groupParts)) + if (value) return groupState.read(value.groupKey, true) + return groupState.read(part()?.groupKey, true) }) const visible = createMemo(() => { if (!context()) return true @@ -544,9 +514,7 @@ export function AssistantParts(props: { ctxPartsPrev = result return result }) - const ctxPendingRaw = useContextToolPending(ctxParts, () => !!(props.working && ctx()?.tail)) - const ctxPending = ctxPendingRaw - const ctxHoldOpen = hold(ctxPendingRaw) + const ctxPending = useContextToolPending(ctxParts, () => !!(props.working && ctx()?.tail)) const shell = createMemo(() => { const value = part() if (!value) return @@ -598,12 +566,20 @@ export function AssistantParts(props: { onOpenChange={(value: boolean) => groupState.write(entry().groupKey, value)} /> - - + + )} - {(value) => } + + {(value) => ( + + )} + {(entry) => ( diff --git a/packages/ui/src/components/shell-rolling-results.tsx b/packages/ui/src/components/shell-rolling-results.tsx index 4deef089e..0210e46e0 100644 --- a/packages/ui/src/components/shell-rolling-results.tsx +++ b/packages/ui/src/components/shell-rolling-results.tsx @@ -10,15 +10,7 @@ import { TextShimmer } from "./text-shimmer" import { Tooltip } from "./tooltip" import { GROW_SPRING } from "./motion" import { useSpring } from "./motion-spring" -import { - busy, - createThrottledValue, - hold, - updateScrollMask, - useCollapsible, - useRowWipe, - useToolFade, -} from "./tool-utils" +import { busy, createThrottledValue, updateScrollMask, useCollapsible, useRowWipe, useToolFade } from "./tool-utils" function ShellRollingSubtitle(props: { text: string; animate?: boolean }) { let ref: HTMLSpanElement | undefined @@ -176,24 +168,17 @@ function ShellExpanded(props: { cmd: string; out: string; open: boolean }) { ) } -export function ShellRollingResults(props: { part: ToolPart; animate?: boolean }) { +export function ShellRollingResults(props: { part: ToolPart; animate?: boolean; defaultOpen?: boolean }) { const i18n = useI18n() const reduce = useReducedMotion() const wiped = new Set() const [mounted, setMounted] = createSignal(false) - const [userToggled, setUserToggled] = createSignal(false) - const [userOpen, setUserOpen] = createSignal(false) + const [open, setOpen] = createSignal(props.defaultOpen ?? true) onMount(() => setMounted(true)) const state = createMemo(() => props.part.state as Record) const pending = createMemo(() => busy(props.part.state.status)) - const autoOpen = hold(pending, 2000) - const effectiveOpen = createMemo(() => { - if (pending()) return true - if (userToggled()) return userOpen() - return autoOpen() - }) - const expanded = createMemo(() => !pending() && !autoOpen() && userToggled() && userOpen()) - const previewOpen = createMemo(() => effectiveOpen() && !expanded()) + const expanded = createMemo(() => open() && !pending()) + const previewOpen = createMemo(() => open() && pending()) const command = createMemo(() => { const value = state().input?.command ?? state().metadata?.command if (typeof value === "string") return value @@ -217,12 +202,10 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean } const headerHeight = useSpring(() => (mounted() ? 37 : 0), GROW_SPRING) let headerClipRef: HTMLDivElement | undefined const handleHeaderClick = () => { - if (pending()) return const el = headerClipRef const viewport = el?.closest(".scroll-view__viewport") as HTMLElement | null const beforeY = el?.getBoundingClientRect().top ?? 0 - setUserToggled(true) - setUserOpen((prev) => !prev) + setOpen((prev) => !prev) if (viewport && el) { requestAnimationFrame(() => { const afterY = el.getBoundingClientRect().top @@ -249,7 +232,7 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean } ref={headerClipRef} data-slot="shell-rolling-header-clip" data-scroll-preserve - data-clickable={!pending() ? "true" : "false"} + data-clickable="true" onClick={handleHeaderClick} style={{ height: `${skip() ? (mounted() ? 37 : 0) : headerHeight()}px`, overflow: "clip" }} > @@ -258,13 +241,11 @@ export function ShellRollingResults(props: { part: ToolPart; animate?: boolean } {(text) => } - - - - - + + + - +