Explorar o código

fix(desktop): throttle markdown renders

Adam hai 1 mes
pai
achega
77c837eb1a
Modificáronse 1 ficheiros con 55 adicións e 5 borrados
  1. 55 5
      packages/ui/src/components/message-part.tsx

+ 55 - 5
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 { Dynamic } from "solid-js/web"
 import {
 import {
   AssistantMessage,
   AssistantMessage,
@@ -80,6 +91,41 @@ export type PartComponent = Component<MessagePartProps>
 
 
 export const PART_MAPPING: Record<string, PartComponent | undefined> = {}
 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) {
 function relativizeProjectPaths(text: string, directory?: string) {
   if (!text) return ""
   if (!text) return ""
   if (!directory) return text
   if (!directory) return text
@@ -464,11 +510,12 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
   const data = useData()
   const data = useData()
   const part = props.part as TextPart
   const part = props.part as TextPart
   const displayText = () => relativizeProjectPaths((part.text ?? "").trim(), data.directory)
   const displayText = () => relativizeProjectPaths((part.text ?? "").trim(), data.directory)
+  const throttledText = createThrottledValue(displayText)
 
 
   return (
   return (
-    <Show when={displayText()}>
+    <Show when={throttledText()}>
       <div data-component="text-part">
       <div data-component="text-part">
-        <Markdown text={displayText()} />
+        <Markdown text={throttledText()} />
       </div>
       </div>
     </Show>
     </Show>
   )
   )
@@ -476,10 +523,13 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
 
 
 PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props) {
 PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props) {
   const part = props.part as ReasoningPart
   const part = props.part as ReasoningPart
+  const text = () => part.text.trim()
+  const throttledText = createThrottledValue(text)
+
   return (
   return (
-    <Show when={part.text.trim()}>
+    <Show when={throttledText()}>
       <div data-component="reasoning-part">
       <div data-component="reasoning-part">
-        <Markdown text={part.text.trim()} />
+        <Markdown text={throttledText()} />
       </div>
       </div>
     </Show>
     </Show>
   )
   )