diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 1cc92f759..6e993ff8f 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -1,4 +1,17 @@ -import { For, onCleanup, onMount, Show, Match, Switch, createResource, createMemo, createEffect, on } from "solid-js" +import { + For, + onCleanup, + onMount, + Show, + Match, + Switch, + createResource, + createMemo, + createEffect, + on, + createRenderEffect, + batch, +} from "solid-js" import { Dynamic } from "solid-js/web" import { useLocal, type LocalFile } from "@/context/local" import { createStore } from "solid-js/store" @@ -130,7 +143,8 @@ export default function Page() { clickTimer: undefined as number | undefined, activeDraggable: undefined as string | undefined, activeTerminalDraggable: undefined as string | undefined, - stepsExpanded: false, + userInteracted: false, + stepsExpanded: true, }) let inputRef!: HTMLDivElement @@ -159,7 +173,28 @@ export default function Page() { ), ) + createEffect(() => { + params.id + const status = sync.data.session_status[params.id ?? ""] ?? { type: "idle" } + batch(() => { + setStore("userInteracted", false) + setStore("stepsExpanded", status.type !== "idle") + }) + }) + const status = createMemo(() => sync.data.session_status[params.id ?? ""] ?? { type: "idle" }) + const working = createMemo(() => status().type !== "idle" && activeMessage()?.id === lastUserMessage()?.id) + + createRenderEffect((prev) => { + const isWorking = working() + if (!prev && isWorking) { + setStore("stepsExpanded", true) + } + if (prev && !isWorking && !store.userInteracted) { + setStore("stepsExpanded", false) + } + return isWorking + }, working()) command.register(() => [ { @@ -619,7 +654,8 @@ export default function Page() { sessionID={params.id!} messageID={activeMessage()!.id} stepsExpanded={store.stepsExpanded} - onStepsExpandedChange={(expanded) => setStore("stepsExpanded", expanded)} + onStepsExpandedToggle={() => setStore("stepsExpanded", (x) => !x)} + onUserInteracted={() => setStore("userInteracted", true)} classes={{ root: "pb-20 flex-1 min-w-0", content: "pb-20", diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index bae7a2a40..6a0e11422 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -25,7 +25,8 @@ export function SessionTurn( sessionID: string messageID: string stepsExpanded?: boolean - onStepsExpandedChange?: (expanded: boolean) => void + onStepsExpandedToggle?: () => void + onUserInteracted?: () => void classes?: { root?: string content?: string @@ -171,7 +172,6 @@ export function SessionTurn( stickyHeaderHeight: 0, retrySeconds: 0, status: rawStatus(), - stepsExpanded: props.stepsExpanded ?? working(), duration: duration(), }) @@ -192,18 +192,26 @@ export function SessionTurn( function handleScroll() { if (!scrollRef || store.autoScrolled) return - const { scrollTop } = scrollRef - // only mark as user scrolled if they actively scrolled upward - // content growth increases scrollHeight but never decreases scrollTop + const scrollTop = scrollRef.scrollTop + const reset = scrollTop <= 0 && store.lastScrollTop > 100 && working() && !store.userScrolled + if (reset) { + setStore("lastScrollTop", scrollTop) + requestAnimationFrame(scrollToBottom) + return + } const scrolledUp = scrollTop < store.lastScrollTop - 10 if (scrolledUp && working()) { setStore("userScrolled", true) + props.onUserInteracted?.() } setStore("lastScrollTop", scrollTop) } function handleInteraction() { - if (working()) setStore("userScrolled", true) + if (working()) { + setStore("userScrolled", true) + props.onUserInteracted?.() + } } function scrollToBottom() { @@ -242,12 +250,6 @@ export function SessionTurn( }, ) - createEffect(() => { - if (props.stepsExpanded !== undefined) { - setStore("stepsExpanded", props.stepsExpanded) - } - }) - createEffect(() => { const timer = setInterval(() => { setStore("duration", duration()) @@ -262,7 +264,6 @@ export function SessionTurn( if (newStatus === store.status || !newStatus) return const timeSinceLastChange = Date.now() - lastStatusChange - if (timeSinceLastChange >= 2500) { setStore("status", newStatus) lastStatusChange = Date.now() @@ -280,19 +281,6 @@ export function SessionTurn( } }) - createEffect((prev) => { - const isWorking = working() - if (!prev && isWorking) { - setStore("stepsExpanded", true) - props.onStepsExpandedChange?.(true) - } - if (prev && !isWorking && !store.userScrolled) { - setStore("stepsExpanded", false) - props.onStepsExpandedChange?.(false) - } - return isWorking - }, working()) - return (