diff --git a/packages/app/src/pages/session.tsx b/packages/app/src/pages/session.tsx index 2327cbf735b1..d74efd9b5ad5 100644 --- a/packages/app/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -97,6 +97,7 @@ import { Identifier } from "@/utils/id" import { diffs as list } from "@/utils/diffs" import { Persist, persisted } from "@/utils/persist" import { extractPromptFromParts } from "@/utils/prompt" +import { same } from "@/utils/same" import { formatServerError, isLocalSessionNotFoundError, isSessionNotFoundError } from "@/utils/server-errors" import { legacySessionHref, requireServerKey, sessionHref } from "@/utils/session-route" import { useUsageExceededDialogs } from "./session/usage-exceeded-dialogs" @@ -532,7 +533,7 @@ export default function Page() { const info = createMemo(() => (params.id ? sync().session.get(params.id) : undefined)) const isChildSession = createMemo(() => !!info()?.parentID) - const diffs = createMemo(() => (params.id ? list(sync().data.session_diff[params.id]) : [])) + const diffs = createMemo(() => (params.id ? list(sync().data.session_diff[params.id]) : []), [], { equals: same }) const canReview = createMemo(() => !!sync().project) const reviewTab = createMemo(() => isDesktop()) const tabState = createSessionTabs({ @@ -587,11 +588,15 @@ export default function Page() { createEffect( on( - () => ({ dir: sdk().directory, id: params.id }), - (next, prev) => { + // Use a primitive string key instead of creating a new object on every + // evaluation. SolidJS on() uses === comparison, so a string avoids + // the always-different-reference problem that objects have. + () => `${params.dir}\0${params.id ?? ""}`, + (_next, prev) => { if (!prev) return - if (next.dir === prev.dir && next.id === prev.id) return - if (prev.id && !next.id) local.session.reset() + // prev had a session ID (non-empty after the separator) and now there's none + const prevHadId = prev.indexOf("\0") < prev.length - 1 + if (prevHadId && !params.id) local.session.reset() }, { defer: true }, ), diff --git a/packages/app/src/pages/session/use-session-hash-scroll.ts b/packages/app/src/pages/session/use-session-hash-scroll.ts index 65577abae229..30bd6f78a61a 100644 --- a/packages/app/src/pages/session/use-session-hash-scroll.ts +++ b/packages/app/src/pages/session/use-session-hash-scroll.ts @@ -1,6 +1,6 @@ import type { UserMessage } from "@opencode-ai/sdk/v2" import { useLocation, useNavigate } from "@solidjs/router" -import { createEffect, createMemo, onCleanup, onMount } from "solid-js" +import { createEffect, onCleanup, onMount } from "solid-js" import { messageIdFromHash } from "./message-id-from-hash" export const useSessionHashScroll = (input: { @@ -22,8 +22,10 @@ export const useSessionHashScroll = (input: { scheduleScrollState: (el: HTMLDivElement) => void consumePendingMessage: (key: string) => string | undefined }) => { - const visibleUserMessages = createMemo(() => input.visibleUserMessages()) - const messageById = createMemo(() => new Map(visibleUserMessages().map((m) => [m.id, m]))) + // Use input directly instead of wrapping in a memo without an equality comparator. + // Replace the Map (rebuilt on every change) with a lazy lookup — user message + // counts are small enough that linear scan is fine for the point lookups here. + const findMessage = (id: string) => input.visibleUserMessages().find((m) => m.id === id) let pendingKey = "" let clearing = false @@ -110,7 +112,7 @@ export const useSessionHashScroll = (input: { const messageId = messageIdFromHash(hash) if (messageId) { input.autoScroll.pause() - const msg = messageById().get(messageId) + const msg = findMessage(messageId) if (msg) { scrollToMessage(msg, behavior) return @@ -141,7 +143,8 @@ export const useSessionHashScroll = (input: { createEffect(() => { if (!input.sessionID() || !input.messagesReady()) return - visibleUserMessages() + // Subscribe to message list changes + input.visibleUserMessages() let targetId = input.pendingMessage() if (!targetId) { @@ -160,7 +163,7 @@ export const useSessionHashScroll = (input: { if (!targetId) return const pending = input.pendingMessage() === targetId - const msg = messageById().get(targetId) + const msg = findMessage(targetId) if (!msg) return if (pending) input.setPendingMessage(undefined) @@ -175,12 +178,13 @@ export const useSessionHashScroll = (input: { const sessionID = input.sessionID() if (!sessionID || !input.messagesReady()) return - visibleUserMessages() + // Subscribe to message list changes + input.visibleUserMessages() let targetId = input.pendingMessage() if (!targetId && !clearing) targetId = messageIdFromHash(location.hash) if (!targetId) return - if (messageById().has(targetId)) return + if (findMessage(targetId)) return if (!input.historyMore() || input.historyLoading()) return void input.loadMore(sessionID)