perf(ui): split right panel and secondary viewer chunks (#239)

## Summary
- split the right panel, picker, and tool call secondary viewers into
smaller deferred chunks
- release hidden right-panel file buffers and stop tracking static
tool-call scrollers when they are not needed
- keep this branch focused on the remaining secondary viewer chunking
work now that the Monaco-specific chunking moved into PR 215

## Testing
- npm run build --workspace @codenomad/ui
This commit is contained in:
Pascal André
2026-03-23 09:47:03 +01:00
committed by GitHub
parent 09284ee2ce
commit 8567d49178
9 changed files with 201 additions and 142 deletions

View File

@@ -1,8 +1,10 @@
import { import {
Show, Show,
Suspense,
createEffect, createEffect,
createMemo, createMemo,
createSignal, createSignal,
lazy,
onCleanup, onCleanup,
type Accessor, type Accessor,
type Component, type Component,
@@ -20,11 +22,6 @@ import type { Session } from "../../../../types/session"
import type { DrawerViewState } from "../types" import type { DrawerViewState } from "../types"
import type { DiffContextMode, DiffViewMode, DiffWordWrapMode, RightPanelTab } from "./types" import type { DiffContextMode, DiffViewMode, DiffWordWrapMode, RightPanelTab } from "./types"
import ChangesTab from "./tabs/ChangesTab"
import FilesTab from "./tabs/FilesTab"
import GitChangesTab from "./tabs/GitChangesTab"
import StatusTab from "./tabs/StatusTab"
import { getDefaultWorktreeSlug, getOrCreateWorktreeClient, getWorktreeSlugForSession } from "../../../../stores/worktrees" import { getDefaultWorktreeSlug, getOrCreateWorktreeClient, getWorktreeSlugForSession } from "../../../../stores/worktrees"
import { requestData } from "../../../../lib/opencode-api" import { requestData } from "../../../../lib/opencode-api"
import { buildUnifiedDiffFromSdkPatch, tryReverseApplyUnifiedDiff } from "../../../../lib/unified-diff-reverse" import { buildUnifiedDiffFromSdkPatch, tryReverseApplyUnifiedDiff } from "../../../../lib/unified-diff-reverse"
@@ -49,6 +46,15 @@ import {
readStoredRightPanelTab, readStoredRightPanelTab,
} from "../storage" } from "../storage"
const LazyChangesTab = lazy(() => import("./tabs/ChangesTab"))
const LazyGitChangesTab = lazy(() => import("./tabs/GitChangesTab"))
const LazyFilesTab = lazy(() => import("./tabs/FilesTab"))
const LazyStatusTab = lazy(() => import("./tabs/StatusTab"))
function RightPanelTabFallback() {
return <div class="flex-1 min-h-0" />
}
interface RightPanelProps { interface RightPanelProps {
t: (key: string, vars?: Record<string, any>) => string t: (key: string, vars?: Record<string, any>) => string
@@ -565,6 +571,13 @@ const RightPanel: Component<RightPanelProps> = (props) => {
void loadBrowserEntries(browserPath()) void loadBrowserEntries(browserPath())
}) })
createEffect(() => {
if (rightPanelTab() === "files") return
setBrowserSelectedContent(null)
setBrowserSelectedLoading(false)
setBrowserSelectedError(null)
})
createEffect(() => { createEffect(() => {
if (rightPanelTab() !== "git-changes") return if (rightPanelTab() !== "git-changes") return
if (gitStatusLoading()) return if (gitStatusLoading()) return
@@ -572,6 +585,14 @@ const RightPanel: Component<RightPanelProps> = (props) => {
void loadGitStatus() void loadGitStatus()
}) })
createEffect(() => {
if (rightPanelTab() === "git-changes") return
setGitSelectedBefore(null)
setGitSelectedAfter(null)
setGitSelectedLoading(false)
setGitSelectedError(null)
})
const handleSelectChangesFile = (file: string, closeList: boolean) => { const handleSelectChangesFile = (file: string, closeList: boolean) => {
setSelectedFile(file) setSelectedFile(file)
if (closeList) { if (closeList) {
@@ -738,101 +759,109 @@ const RightPanel: Component<RightPanelProps> = (props) => {
<div class="flex-1 overflow-y-auto"> <div class="flex-1 overflow-y-auto">
<Show when={rightPanelTab() === "changes"}> <Show when={rightPanelTab() === "changes"}>
<ChangesTab <Suspense fallback={<RightPanelTabFallback />}>
t={props.t} <LazyChangesTab
instanceId={props.instanceId} t={props.t}
activeSessionId={props.activeSessionId} instanceId={props.instanceId}
activeSessionDiffs={props.activeSessionDiffs} activeSessionId={props.activeSessionId}
selectedFile={selectedFile} activeSessionDiffs={props.activeSessionDiffs}
onSelectFile={handleSelectChangesFile} selectedFile={selectedFile}
diffViewMode={diffViewMode} onSelectFile={handleSelectChangesFile}
diffContextMode={diffContextMode} diffViewMode={diffViewMode}
diffWordWrapMode={diffWordWrapMode} diffContextMode={diffContextMode}
onViewModeChange={setDiffViewMode} diffWordWrapMode={diffWordWrapMode}
onContextModeChange={setDiffContextMode} onViewModeChange={setDiffViewMode}
onWordWrapModeChange={setDiffWordWrapMode} onContextModeChange={setDiffContextMode}
listOpen={changesListOpen} onWordWrapModeChange={setDiffWordWrapMode}
onToggleList={toggleChangesList} listOpen={changesListOpen}
splitWidth={changesSplitWidth} onToggleList={toggleChangesList}
onResizeMouseDown={handleSplitResizeMouseDown("changes")} splitWidth={changesSplitWidth}
onResizeTouchStart={handleSplitResizeTouchStart("changes")} onResizeMouseDown={handleSplitResizeMouseDown("changes")}
isPhoneLayout={props.isPhoneLayout} onResizeTouchStart={handleSplitResizeTouchStart("changes")}
/> isPhoneLayout={props.isPhoneLayout}
/>
</Suspense>
</Show> </Show>
<Show when={rightPanelTab() === "git-changes"}> <Show when={rightPanelTab() === "git-changes"}>
<GitChangesTab <Suspense fallback={<RightPanelTabFallback />}>
t={props.t} <LazyGitChangesTab
activeSessionId={props.activeSessionId} t={props.t}
entries={gitStatusEntries} activeSessionId={props.activeSessionId}
statusLoading={gitStatusLoading} entries={gitStatusEntries}
statusError={gitStatusError} statusLoading={gitStatusLoading}
selectedPath={gitSelectedPath} statusError={gitStatusError}
selectedLoading={gitSelectedLoading} selectedPath={gitSelectedPath}
selectedError={gitSelectedError} selectedLoading={gitSelectedLoading}
selectedBefore={gitSelectedBefore} selectedError={gitSelectedError}
selectedAfter={gitSelectedAfter} selectedBefore={gitSelectedBefore}
mostChangedPath={gitMostChangedPath} selectedAfter={gitSelectedAfter}
scopeKey={gitScopeKey} mostChangedPath={gitMostChangedPath}
diffViewMode={diffViewMode} scopeKey={gitScopeKey}
diffContextMode={diffContextMode} diffViewMode={diffViewMode}
diffWordWrapMode={diffWordWrapMode} diffContextMode={diffContextMode}
onViewModeChange={setDiffViewMode} diffWordWrapMode={diffWordWrapMode}
onContextModeChange={setDiffContextMode} onViewModeChange={setDiffViewMode}
onWordWrapModeChange={setDiffWordWrapMode} onContextModeChange={setDiffContextMode}
onOpenFile={(path) => void openGitFile(path)} onWordWrapModeChange={setDiffWordWrapMode}
onRefresh={() => void refreshGitStatus()} onOpenFile={(path: string) => void openGitFile(path)}
listOpen={gitChangesListOpen} onRefresh={() => void refreshGitStatus()}
onToggleList={toggleGitList} listOpen={gitChangesListOpen}
splitWidth={gitChangesSplitWidth} onToggleList={toggleGitList}
onResizeMouseDown={handleSplitResizeMouseDown("git-changes")} splitWidth={gitChangesSplitWidth}
onResizeTouchStart={handleSplitResizeTouchStart("git-changes")} onResizeMouseDown={handleSplitResizeMouseDown("git-changes")}
isPhoneLayout={props.isPhoneLayout} onResizeTouchStart={handleSplitResizeTouchStart("git-changes")}
/> isPhoneLayout={props.isPhoneLayout}
/>
</Suspense>
</Show> </Show>
<Show when={rightPanelTab() === "files"}> <Show when={rightPanelTab() === "files"}>
<FilesTab <Suspense fallback={<RightPanelTabFallback />}>
t={props.t} <LazyFilesTab
browserPath={browserPath} t={props.t}
browserEntries={browserEntries} browserPath={browserPath}
browserLoading={browserLoading} browserEntries={browserEntries}
browserError={browserError} browserLoading={browserLoading}
browserSelectedPath={browserSelectedPath} browserError={browserError}
browserSelectedContent={browserSelectedContent} browserSelectedPath={browserSelectedPath}
browserSelectedLoading={browserSelectedLoading} browserSelectedContent={browserSelectedContent}
browserSelectedError={browserSelectedError} browserSelectedLoading={browserSelectedLoading}
parentPath={browserParentPath} browserSelectedError={browserSelectedError}
scopeKey={browserScopeKey} parentPath={browserParentPath}
onLoadEntries={(path) => void loadBrowserEntries(path)} scopeKey={browserScopeKey}
onOpenFile={(path) => void openBrowserFile(path)} onLoadEntries={(path: string) => void loadBrowserEntries(path)}
onRefresh={() => void refreshFilesTab()} onOpenFile={(path: string) => void openBrowserFile(path)}
listOpen={filesListOpen} onRefresh={() => void refreshFilesTab()}
onToggleList={toggleFilesList} listOpen={filesListOpen}
splitWidth={filesSplitWidth} onToggleList={toggleFilesList}
onResizeMouseDown={handleSplitResizeMouseDown("files")} splitWidth={filesSplitWidth}
onResizeTouchStart={handleSplitResizeTouchStart("files")} onResizeMouseDown={handleSplitResizeMouseDown("files")}
isPhoneLayout={props.isPhoneLayout} onResizeTouchStart={handleSplitResizeTouchStart("files")}
/> isPhoneLayout={props.isPhoneLayout}
/>
</Suspense>
</Show> </Show>
<Show when={rightPanelTab() === "status"}> <Show when={rightPanelTab() === "status"}>
<StatusTab <Suspense fallback={<RightPanelTabFallback />}>
t={props.t} <LazyStatusTab
instanceId={props.instanceId} t={props.t}
instance={props.instance} instanceId={props.instanceId}
activeSessionId={props.activeSessionId} instance={props.instance}
activeSession={props.activeSession} activeSessionId={props.activeSessionId}
activeSessionDiffs={props.activeSessionDiffs} activeSession={props.activeSession}
latestTodoState={props.latestTodoState} activeSessionDiffs={props.activeSessionDiffs}
backgroundProcessList={props.backgroundProcessList} latestTodoState={props.latestTodoState}
onOpenBackgroundOutput={props.onOpenBackgroundOutput} backgroundProcessList={props.backgroundProcessList}
onStopBackgroundProcess={props.onStopBackgroundProcess} onOpenBackgroundOutput={props.onOpenBackgroundOutput}
onTerminateBackgroundProcess={props.onTerminateBackgroundProcess} onStopBackgroundProcess={props.onStopBackgroundProcess}
expandedItems={rightPanelExpandedItems} onTerminateBackgroundProcess={props.onTerminateBackgroundProcess}
onExpandedItemsChange={handleAccordionChange} expandedItems={rightPanelExpandedItems}
onOpenChangesTab={openChangesTabFromStatus} onExpandedItemsChange={handleAccordionChange}
/> onOpenChangesTab={openChangesTabFromStatus}
/>
</Suspense>
</Show> </Show>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,6 @@
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, onCleanup, untrack } from "solid-js" import { For, Match, Show, Suspense, Switch, createEffect, createMemo, createSignal, lazy, onCleanup, untrack } from "solid-js"
import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, ListStart, Trash } from "lucide-solid" import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, ListStart, Trash } from "lucide-solid"
import MessageItem from "./message-item" import MessageItem from "./message-item"
import ToolCall from "./tool-call"
import type { InstanceMessageStore } from "../stores/message-v2/instance-store" import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
import type { ClientPart, MessageInfo } from "../types/message" import type { ClientPart, MessageInfo } from "../types/message"
import { partHasRenderableText } from "../types/message" import { partHasRenderableText } from "../types/message"
@@ -29,6 +28,12 @@ const USER_BORDER_COLOR = "var(--message-user-border)"
const ASSISTANT_BORDER_COLOR = "var(--message-assistant-border)" const ASSISTANT_BORDER_COLOR = "var(--message-assistant-border)"
const TOOL_BORDER_COLOR = "var(--message-tool-border)" const TOOL_BORDER_COLOR = "var(--message-tool-border)"
const LazyToolCall = lazy(() => import("./tool-call"))
function ToolCallFallback() {
return <div class="tool-call tool-call-loading" />
}
type ToolCallPart = Extract<ClientPart, { type: "tool" }> type ToolCallPart = Extract<ClientPart, { type: "tool" }>
@@ -500,16 +505,18 @@ function ToolCallItem(props: ToolCallItemProps) {
</div> </div>
</div> </div>
<ToolCall <Suspense fallback={<ToolCallFallback />}>
toolCall={resolvedToolPart()} <LazyToolCall
toolCallId={props.partId} toolCall={resolvedToolPart()}
messageId={props.messageId} toolCallId={props.partId}
messageVersion={messageVersion()} messageId={props.messageId}
partVersion={partVersion()} messageVersion={messageVersion()}
instanceId={props.instanceId} partVersion={partVersion()}
sessionId={props.sessionId} instanceId={props.instanceId}
onContentRendered={props.onContentRendered} sessionId={props.sessionId}
/> onContentRendered={props.onContentRendered}
/>
</Suspense>
</div> </div>
)} )}
</Show> </Show>

View File

@@ -1,5 +1,4 @@
import { Show, Match, Switch } from "solid-js" import { Match, Show, Suspense, Switch, lazy } from "solid-js"
import ToolCall from "./tool-call"
import { isItemExpanded, toggleItemExpanded } from "../stores/tool-call-state" import { isItemExpanded, toggleItemExpanded } from "../stores/tool-call-state"
import { Markdown } from "./markdown" import { Markdown } from "./markdown"
import { useTheme } from "../lib/theme" import { useTheme } from "../lib/theme"
@@ -7,6 +6,8 @@ import { partHasRenderableText, SDKPart, TextPart, ClientPart } from "../types/m
type ToolCallPart = Extract<ClientPart, { type: "tool" }> type ToolCallPart = Extract<ClientPart, { type: "tool" }>
const LazyToolCall = lazy(() => import("./tool-call"))
interface MessagePartProps { interface MessagePartProps {
part: ClientPart part: ClientPart
messageType?: "user" | "assistant" messageType?: "user" | "assistant"
@@ -153,12 +154,14 @@ export default function MessagePart(props: MessagePartProps) {
</Match> </Match>
<Match when={partType() === "tool"}> <Match when={partType() === "tool"}>
<ToolCall <Suspense fallback={<div class="tool-call tool-call-loading" />}>
toolCall={props.part as ToolCallPart} <LazyToolCall
toolCallId={props.part?.id} toolCall={props.part as ToolCallPart}
instanceId={props.instanceId} toolCallId={props.part?.id}
sessionId={props.sessionId} instanceId={props.instanceId}
/> sessionId={props.sessionId}
/>
</Suspense>
</Match> </Match>

View File

@@ -1,4 +1,4 @@
import { For, Show, createMemo, createSignal, createEffect, onCleanup, type Component } from "solid-js" import { For, Show, Suspense, createMemo, createSignal, createEffect, lazy, onCleanup, type Component } from "solid-js"
import type { PermissionRequestLike } from "../types/permission" import type { PermissionRequestLike } from "../types/permission"
import { getPermissionCallId, getPermissionDisplayTitle, getPermissionKind, getPermissionMessageId, getPermissionSessionId } from "../types/permission" import { getPermissionCallId, getPermissionDisplayTitle, getPermissionKind, getPermissionMessageId, getPermissionSessionId } from "../types/permission"
import { getQuestionCallId, getQuestionMessageId, getQuestionSessionId, type QuestionRequest } from "../types/question" import { getQuestionCallId, getQuestionMessageId, getQuestionSessionId, type QuestionRequest } from "../types/question"
@@ -12,7 +12,8 @@ import {
} from "../stores/instances" } from "../stores/instances"
import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions" import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions"
import { messageStoreBus } from "../stores/message-v2/bus" import { messageStoreBus } from "../stores/message-v2/bus"
import ToolCall from "./tool-call"
const LazyToolCall = lazy(() => import("./tool-call"))
interface PermissionApprovalModalProps { interface PermissionApprovalModalProps {
instanceId: string instanceId: string
@@ -408,15 +409,17 @@ const PermissionApprovalModal: Component<PermissionApprovalModalProps> = (props)
} }
> >
{(data) => ( {(data) => (
<ToolCall <Suspense fallback={<div class="tool-call tool-call-loading" />}>
toolCall={data().toolPart} <LazyToolCall
toolCallId={data().toolPart.id} toolCall={data().toolPart}
messageId={data().messageId} toolCallId={data().toolPart.id}
messageVersion={data().messageVersion} messageId={data().messageId}
partVersion={data().partVersion} messageVersion={data().messageVersion}
instanceId={props.instanceId} partVersion={data().partVersion}
sessionId={data().sessionId} instanceId={props.instanceId}
/> sessionId={data().sessionId}
/>
</Suspense>
)} )}
</Show> </Show>
</div> </div>

View File

@@ -1,6 +1,5 @@
import { createSignal, Show, onMount, onCleanup, createEffect, on } from "solid-js" import { Suspense, createEffect, createSignal, lazy, on, onCleanup, onMount, Show } from "solid-js"
import { ArrowBigUp, ArrowBigDown } from "lucide-solid" import { ArrowBigUp, ArrowBigDown } from "lucide-solid"
import UnifiedPicker from "./unified-picker"
import ExpandButton from "./expand-button" import ExpandButton from "./expand-button"
import { clearAttachments, removeAttachment } from "../stores/attachments" import { clearAttachments, removeAttachment } from "../stores/attachments"
import { resolvePastedPlaceholders } from "../lib/prompt-placeholders" import { resolvePastedPlaceholders } from "../lib/prompt-placeholders"
@@ -20,6 +19,7 @@ import { usePromptAttachments } from "./prompt-input/usePromptAttachments"
import { usePromptPicker } from "./prompt-input/usePromptPicker" import { usePromptPicker } from "./prompt-input/usePromptPicker"
import { usePromptKeyDown } from "./prompt-input/usePromptKeyDown" import { usePromptKeyDown } from "./prompt-input/usePromptKeyDown"
const log = getLogger("actions") const log = getLogger("actions")
const LazyUnifiedPicker = lazy(() => import("./unified-picker"))
function getConsumedPastedTextAttachmentIds(text: string, attachments: Attachment[]): string[] { function getConsumedPastedTextAttachmentIds(text: string, attachments: Attachment[]): string[] {
if (!text || attachments.length === 0) return [] if (!text || attachments.length === 0) return []
@@ -467,18 +467,20 @@ export default function PromptInput(props: PromptInputProps) {
onDrop={handleDrop} onDrop={handleDrop}
> >
<Show when={showPicker() && instance()}> <Show when={showPicker() && instance()}>
<UnifiedPicker <Suspense fallback={null}>
open={showPicker()} <LazyUnifiedPicker
mode={pickerMode()} open={showPicker()}
onClose={handlePickerClose} mode={pickerMode()}
onSelect={handlePickerSelect} onClose={handlePickerClose}
agents={instanceAgents()} onSelect={handlePickerSelect}
commands={getCommands(props.instanceId)} agents={instanceAgents()}
instanceClient={instance()!.client} commands={getCommands(props.instanceId)}
searchQuery={searchQuery()} instanceClient={instance()!.client}
textareaRef={textareaRef} searchQuery={searchQuery()}
workspaceId={props.instanceId} textareaRef={textareaRef}
/> workspaceId={props.instanceId}
/>
</Suspense>
</Show> </Show>
<div class="flex flex-1 flex-col"> <div class="flex flex-1 flex-col">

View File

@@ -514,6 +514,7 @@ function ToolCallDetails(props: {
}) })
const { renderDiffContent } = createDiffContentRenderer({ const { renderDiffContent } = createDiffContentRenderer({
toolState: props.toolState,
preferences: props.preferences, preferences: props.preferences,
setDiffViewMode: props.setDiffViewMode, setDiffViewMode: props.setDiffViewMode,
isDark: props.isDark, isDark: props.isDark,

View File

@@ -20,6 +20,14 @@ export function createAnsiContentRenderer(params: {
const runningAnsiRenderer = createAnsiStreamRenderer() const runningAnsiRenderer = createAnsiStreamRenderer()
let runningAnsiSource = "" let runningAnsiSource = ""
const registerTracked = (element: HTMLDivElement | null) => {
params.scrollHelpers.registerContainer(element)
}
const registerUntracked = (element: HTMLDivElement | null) => {
params.scrollHelpers.registerContainer(element, { disableTracking: true })
}
const getMode = () => { const getMode = () => {
const version = params.partVersion?.() const version = params.partVersion?.()
return typeof version === "number" ? String(version) : undefined return typeof version === "number" ? String(version) : undefined
@@ -36,6 +44,8 @@ export function createAnsiContentRenderer(params: {
const cached = cacheHandle.get<AnsiRenderCache>() const cached = cacheHandle.get<AnsiRenderCache>()
const mode = getMode() const mode = getMode()
const isRunningVariant = options.variant === "running" const isRunningVariant = options.variant === "running"
const disableScrollTracking = !isRunningVariant
const registerRef = disableScrollTracking ? registerUntracked : registerTracked
let nextCache: AnsiRenderCache let nextCache: AnsiRenderCache
@@ -87,9 +97,9 @@ export function createAnsiContentRenderer(params: {
} }
return ( return (
<div class={messageClass} ref={params.scrollHelpers.registerContainer} onScroll={params.scrollHelpers.handleScroll}> <div class={messageClass} ref={registerRef} onScroll={disableScrollTracking ? undefined : params.scrollHelpers.handleScroll}>
<pre class="tool-call-content tool-call-ansi" dir="auto" innerHTML={nextCache.html} /> <pre class="tool-call-content tool-call-ansi" dir="auto" innerHTML={nextCache.html} />
{params.scrollHelpers.renderSentinel()} {params.scrollHelpers.renderSentinel({ disableTracking: disableScrollTracking })}
</div> </div>
) )
} }

View File

@@ -1,4 +1,5 @@
import { Suspense, lazy, onMount, type Accessor, type JSXElement } from "solid-js" import { Suspense, lazy, onMount, type Accessor, type JSXElement } from "solid-js"
import type { ToolState } from "@opencode-ai/sdk/v2"
import type { RenderCache } from "../../types/message" import type { RenderCache } from "../../types/message"
import type { DiffViewMode } from "../../stores/preferences" import type { DiffViewMode } from "../../stores/preferences"
import type { DiffPayload, DiffRenderOptions, ToolScrollHelpers } from "./types" import type { DiffPayload, DiffRenderOptions, ToolScrollHelpers } from "./types"
@@ -31,6 +32,7 @@ type DiffPrefs = {
} }
export function createDiffContentRenderer(params: { export function createDiffContentRenderer(params: {
toolState: Accessor<ToolState | undefined>
preferences: Accessor<DiffPrefs> preferences: Accessor<DiffPrefs>
setDiffViewMode: (mode: DiffViewMode) => void setDiffViewMode: (mode: DiffViewMode) => void
isDark: Accessor<boolean> isDark: Accessor<boolean>
@@ -58,7 +60,10 @@ export function createDiffContentRenderer(params: {
const cacheHandle = selectedVariant === "permission-diff" ? params.permissionDiffCache : params.diffCache const cacheHandle = selectedVariant === "permission-diff" ? params.permissionDiffCache : params.diffCache
const diffMode = () => (params.preferences().diffViewMode || "split") as DiffViewMode const diffMode = () => (params.preferences().diffViewMode || "split") as DiffViewMode
const themeKey = params.isDark() ? "dark" : "light" const themeKey = params.isDark() ? "dark" : "light"
const disableScrollTracking = Boolean(options?.disableScrollTracking) const state = params.toolState()
const disableScrollTracking = Boolean(
options?.disableScrollTracking || (state?.status !== "running" && state?.status !== "pending"),
)
const registerRef = disableScrollTracking ? registerUntracked : registerTracked const registerRef = disableScrollTracking ? registerUntracked : registerTracked
const baseEntryParams = cacheHandle.params() as any const baseEntryParams = cacheHandle.params() as any

View File

@@ -31,10 +31,9 @@ export function createMarkdownContentRenderer(params: {
const size = options.size || "default" const size = options.size || "default"
const disableHighlight = options.disableHighlight || false const disableHighlight = options.disableHighlight || false
const messageClass = `message-text tool-call-markdown${size === "large" ? " tool-call-markdown-large" : ""}` const messageClass = `message-text tool-call-markdown${size === "large" ? " tool-call-markdown-large" : ""}`
const disableScrollTracking = options.disableScrollTracking || false
const registerRef = disableScrollTracking ? registerUntracked : registerTracked
const state = params.toolState() const state = params.toolState()
const disableScrollTracking = options.disableScrollTracking || (state?.status !== "running" && state?.status !== "pending")
const registerRef = disableScrollTracking ? registerUntracked : registerTracked
const shouldDeferMarkdown = Boolean(state && (state.status === "running" || state.status === "pending") && disableHighlight) const shouldDeferMarkdown = Boolean(state && (state.status === "running" || state.status === "pending") && disableHighlight)
if (shouldDeferMarkdown) { if (shouldDeferMarkdown) {
return ( return (