From 8e1b53b32c2f74c4983e0762cd90f4c2ecc7fda8 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Mon, 23 Mar 2026 18:34:32 +0800 Subject: [PATCH] fix(app): handle session busy state better (#18758) --- bun.lock | 8 ++- packages/app/package.json | 1 + .../src/pages/session/message-timeline.tsx | 55 ++++++------------- 3 files changed, 26 insertions(+), 38 deletions(-) diff --git a/bun.lock b/bun.lock index 6116d26fa..d706f926d 100644 --- a/bun.lock +++ b/bun.lock @@ -41,6 +41,7 @@ "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", "@solid-primitives/storage": "catalog:", + "@solid-primitives/timer": "1.4.4", "@solid-primitives/websocket": "1.3.1", "@solidjs/meta": "catalog:", "@solidjs/router": "catalog:", @@ -57,6 +58,7 @@ "shiki": "catalog:", "solid-js": "catalog:", "solid-list": "catalog:", + "solid-presence": "0.2.0", "tailwindcss": "catalog:", "virtua": "catalog:", "zod": "catalog:", @@ -1889,6 +1891,8 @@ "@solid-primitives/storage": ["@solid-primitives/storage@4.3.3", "", { "dependencies": { "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "@tauri-apps/plugin-store": "*", "solid-js": "^1.6.12" }, "optionalPeers": ["@tauri-apps/plugin-store"] }, "sha512-ACbNwMZ1s8VAvld6EUXkDkX/US3IhtlPLxg6+B2s9MwNUugwdd51I98LPEaHrdLpqPmyzqgoJe0TxEFlf3Dqrw=="], + "@solid-primitives/timer": ["@solid-primitives/timer@1.4.4", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-Ayjyb3+v1hyU92vuLUN0tVHq2mmTCPGxSDLGJMsDydRqx9ZfJIc9xj6cxK4XvdY3pif3ps2mIv52pjgToybEpQ=="], + "@solid-primitives/trigger": ["@solid-primitives/trigger@1.2.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-Za2JebEiDyfamjmDwRaESYqBBYOlgYGzB8kHYH0QrkXyLf2qNADlKdGN+z3vWSLCTDcKxChS43Kssjuc0OZhng=="], "@solid-primitives/utils": ["@solid-primitives/utils@6.4.0", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-AeGTBg8Wtkh/0s+evyLtP8piQoS4wyqqQaAFs2HJcFMMjYAtUgo+ZPduRXLjPlqKVc2ejeR544oeqpbn8Egn8A=="], @@ -4261,7 +4265,7 @@ "solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="], - "solid-presence": ["solid-presence@0.1.8", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-pWGtXUFWYYUZNbg5YpG5vkQJyOtzn2KXhxYaMx/4I+lylTLYkITOLevaCwMRN+liCVk0pqB6EayLWojNqBFECA=="], + "solid-presence": ["solid-presence@0.2.0", "", { "dependencies": { "@corvu/utils": "~0.4.2" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-YM92o+jvpzX3XGaD4rLYmq/Kc2ZVh47GSCLEufHBFQQIurvZTs8SoGJxO8BJGNDxBKdcS8F3dYhW1SDXp4BNjA=="], "solid-prevent-scroll": ["solid-prevent-scroll@0.1.10", "", { "dependencies": { "@corvu/utils": "~0.4.1" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-KplGPX2GHiWJLZ6AXYRql4M127PdYzfwvLJJXMkO+CMb8Np4VxqDAg5S8jLdwlEuBis/ia9DKw2M8dFx5u8Mhw=="], @@ -5115,6 +5119,8 @@ "@jsx-email/doiuse-email/htmlparser2": ["htmlparser2@9.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.1.0", "entities": "^4.5.0" } }, "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ=="], + "@kobalte/core/solid-presence": ["solid-presence@0.1.8", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-pWGtXUFWYYUZNbg5YpG5vkQJyOtzn2KXhxYaMx/4I+lylTLYkITOLevaCwMRN+liCVk0pqB6EayLWojNqBFECA=="], + "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], "@mdx-js/mdx/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], diff --git a/packages/app/package.json b/packages/app/package.json index 8181825c0..61265c28a 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -51,6 +51,7 @@ "@solid-primitives/resize-observer": "2.1.3", "@solid-primitives/scroll": "2.1.3", "@solid-primitives/storage": "catalog:", + "@solid-primitives/timer": "1.4.4", "@solid-primitives/websocket": "1.3.1", "@solidjs/meta": "catalog:", "@solidjs/router": "catalog:", diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index 7ced21353..fe61f1685 100644 --- a/packages/app/src/pages/session/message-timeline.tsx +++ b/packages/app/src/pages/session/message-timeline.tsx @@ -1,4 +1,4 @@ -import { For, createEffect, createMemo, on, onCleanup, Show, Index, type JSX } from "solid-js" +import { For, createEffect, createMemo, on, onCleanup, Show, Index, type JSX, createSignal } from "solid-js" import { createStore, produce } from "solid-js/store" import { useNavigate } from "@solidjs/router" import { useMutation } from "@tanstack/solid-query" @@ -30,6 +30,7 @@ import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" import { messageAgentColor } from "@/utils/agent" import { parseCommentNote, readCommentMetadata } from "@/utils/comment-note" +import { makeTimer } from "@solid-primitives/timer" type MessageComment = { path: string @@ -250,38 +251,21 @@ export function MessageTimeline(props: { const working = createMemo(() => !!pending() || sessionStatus().type !== "idle") const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent)) - const [slot, setSlot] = createStore({ - open: false, - show: false, - fade: false, + const [timeoutDone, setTimeoutDone] = createSignal(true) + + const workingStatus = createMemo<"hidden" | "showing" | "hiding">((prev) => { + if (working()) return "showing" + if (prev === "showing" || !timeoutDone()) return "hiding" + return "hidden" }) - let f: number | undefined - const clear = () => { - if (f !== undefined) window.clearTimeout(f) - f = undefined - } + createEffect(() => { + if (workingStatus() !== "hiding") return + + setTimeoutDone(false) + makeTimer(() => setTimeoutDone(true), 260, setTimeout) + }) - onCleanup(clear) - createEffect( - on( - working, - (on, prev) => { - clear() - if (on) { - setSlot({ open: true, show: true, fade: false }) - return - } - if (prev) { - setSlot({ open: false, show: true, fade: true }) - f = window.setTimeout(() => setSlot({ show: false, fade: false }), 260) - return - } - setSlot({ open: false, show: false, fade: false }) - }, - { defer: true }, - ), - ) const activeMessageID = createMemo(() => { const parentID = pending()?.parentID if (parentID) { @@ -676,17 +660,15 @@ export function MessageTimeline(props: { -