diff options
| author | Adam <[email protected]> | 2026-03-08 07:09:41 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2026-03-08 07:11:15 -0500 |
| commit | c53d1d3ad8280881fbb4364e0061a682608c420f (patch) | |
| tree | 3d64c47908d835fd157b50b01a7ac4edbf0a4b34 /packages/ui/src/components | |
| parent | f386137fbaf2e2f56fb32f8656e802f592a41341 (diff) | |
| download | opencode-c53d1d3ad8280881fbb4364e0061a682608c420f.tar.gz opencode-c53d1d3ad8280881fbb4364e0061a682608c420f.zip | |
fix(app): less auto-expand/collapse
Diffstat (limited to 'packages/ui/src/components')
| -rw-r--r-- | packages/ui/src/components/context-tool-results.tsx | 8 | ||||
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 54 | ||||
| -rw-r--r-- | packages/ui/src/components/shell-rolling-results.tsx | 41 |
3 files changed, 29 insertions, 74 deletions
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: { <ToolCall variant="row" icon="magnifying-glass-menu" - open={!props.pending && props.open} - showArrow={!props.pending} - onOpenChange={(v) => { - if (!props.pending) props.onOpenChange(v) - }} + open={props.open} + showArrow + onOpenChange={props.onOpenChange} trigger={ <div data-component="context-tool-group-trigger" data-pending={props.pending || undefined}> <span diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 1885c19f5..d82121159 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1,4 +1,3 @@ -import { usePageVisibility } from "@solid-primitives/page-visibility" import { Component, createEffect, createMemo, createSignal, For, Match, on, Show, Switch, type JSX } from "solid-js" import stripAnsi from "strip-ansi" import { createStore } from "solid-js/store" @@ -39,7 +38,7 @@ import { TextShimmer } from "./text-shimmer" import { list } from "./text-utils" import { GrowBox } from "./grow-box" import { COLLAPSIBLE_SPRING } from "./motion" -import { busy, hold, createThrottledValue, useToolFade, useContextToolPending } from "./tool-utils" +import { busy, createThrottledValue, useToolFade, useContextToolPending } from "./tool-utils" import { ContextToolGroupHeader, ContextToolExpandedList, ContextToolRollingResults } from "./context-tool-results" import { ShellRollingResults } from "./shell-rolling-results" @@ -273,19 +272,6 @@ function createGroupOpenState() { return { read, controlled, write } } -function shouldCollapseGroup( - statuses: (string | undefined)[], - opts: { afterTool?: boolean; groupTail?: boolean; working?: boolean }, - pageVisible: () => 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)} /> </PartGrow> - <ContextToolExpandedList parts={ctxParts()} expanded={!ctxPending() && contextOpen()} /> - <ContextToolRollingResults parts={ctxParts()} pending={ctxHoldOpen()} /> + <ContextToolExpandedList parts={ctxParts()} expanded={contextOpen() && !ctxPending()} /> + <ContextToolRollingResults parts={ctxParts()} pending={contextOpen() && ctxPending()} /> </> )} </Show> - <Show when={shell()}>{(value) => <ShellRollingResults part={value()} animate={props.animate} />}</Show> + <Show when={shell()}> + {(value) => ( + <ShellRollingResults + part={value()} + animate={props.animate} + defaultOpen={props.shellToolDefaultOpen} + /> + )} + </Show> <Show when={!shell() ? part() : undefined}> {(entry) => ( <Show when={!entry().context}> 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<string>() 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<string, any>) 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 } <TextShimmer text={i18n.t("ui.tool.shell")} active={pending()} /> </span> <Show when={subtitle()}>{(text) => <ShellRollingSubtitle text={text()} animate={props.animate} />}</Show> - <Show when={!pending()}> - <span data-slot="shell-rolling-actions"> - <span data-slot="shell-rolling-arrow" data-open={effectiveOpen() ? "true" : "false"}> - <Icon name="chevron-down" size="small" /> - </span> + <span data-slot="shell-rolling-actions"> + <span data-slot="shell-rolling-arrow" data-open={open() ? "true" : "false"}> + <Icon name="chevron-down" size="small" /> </span> - </Show> + </span> </div> </div> <div |
