diff options
| author | Adam <[email protected]> | 2025-12-29 11:19:34 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-12-29 11:19:40 -0600 |
| commit | 77c837eb1ae34332607ac17e5a1a0d403dd3e8f7 (patch) | |
| tree | 733c5dbebee5ed375098946774473dc5b7d97789 | |
| parent | db77cc9845f8f47d220c67018edb67d7ee7ef151 (diff) | |
| download | opencode-77c837eb1ae34332607ac17e5a1a0d403dd3e8f7.tar.gz opencode-77c837eb1ae34332607ac17e5a1a0d403dd3e8f7.zip | |
fix(desktop): throttle markdown renders
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 6fee7cd26..31103b35c 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1,4 +1,15 @@ -import { Component, createEffect, createMemo, createSignal, For, Match, Show, Switch, type JSX } from "solid-js" +import { + Component, + createEffect, + createMemo, + createSignal, + For, + Match, + Show, + Switch, + onCleanup, + type JSX, +} from "solid-js" import { Dynamic } from "solid-js/web" import { AssistantMessage, @@ -80,6 +91,41 @@ export type PartComponent = Component<MessagePartProps> export const PART_MAPPING: Record<string, PartComponent | undefined> = {} +const TEXT_RENDER_THROTTLE_MS = 250 + +function createThrottledValue(getValue: () => string) { + const [value, setValue] = createSignal(getValue()) + let timeout: ReturnType<typeof setTimeout> | undefined + let last = 0 + + createEffect(() => { + const next = getValue() + const now = Date.now() + const remaining = TEXT_RENDER_THROTTLE_MS - (now - last) + if (remaining <= 0) { + if (timeout) { + clearTimeout(timeout) + timeout = undefined + } + last = now + setValue(next) + return + } + if (timeout) clearTimeout(timeout) + timeout = setTimeout(() => { + last = Date.now() + setValue(next) + timeout = undefined + }, remaining) + }) + + onCleanup(() => { + if (timeout) clearTimeout(timeout) + }) + + return value +} + function relativizeProjectPaths(text: string, directory?: string) { if (!text) return "" if (!directory) return text @@ -464,11 +510,12 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { const data = useData() const part = props.part as TextPart const displayText = () => relativizeProjectPaths((part.text ?? "").trim(), data.directory) + const throttledText = createThrottledValue(displayText) return ( - <Show when={displayText()}> + <Show when={throttledText()}> <div data-component="text-part"> - <Markdown text={displayText()} /> + <Markdown text={throttledText()} /> </div> </Show> ) @@ -476,10 +523,13 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props) { const part = props.part as ReasoningPart + const text = () => part.text.trim() + const throttledText = createThrottledValue(text) + return ( - <Show when={part.text.trim()}> + <Show when={throttledText()}> <div data-component="reasoning-part"> - <Markdown text={part.text.trim()} /> + <Markdown text={throttledText()} /> </div> </Show> ) |
