diff --git a/packages/app/src/pages/session/message-timeline.tsx b/packages/app/src/pages/session/message-timeline.tsx index 19d6e09d9..fbf5ba291 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, startTransition, type JSX } from "solid-js" +import { For, createEffect, createMemo, on, onCleanup, Show, startTransition, Index, type JSX } from "solid-js" import { createStore, produce } from "solid-js/store" import { useNavigate, useParams } from "@solidjs/router" import { Button } from "@opencode-ai/ui/button" @@ -711,28 +711,34 @@ export function MessageTimeline(props: {
- - {(comment) => ( -
-
- - {getFilename(comment.path)} - - {(selection) => ( - - {selection().startLine === selection().endLine - ? `:${selection().startLine}` - : `:${selection().startLine}-${selection().endLine}`} - - )} - + + {(commentAccessor: () => MessageComment) => { + const comment = createMemo(() => commentAccessor()) + return ( +
+
+ + {getFilename(comment().path)} + + {(selection) => ( + + {selection().startLine === selection().endLine + ? `:${selection().startLine}` + : `:${selection().startLine}-${selection().endLine}`} + + )} + +
+
+ {comment().comment} +
-
- {comment.comment} -
-
- )} - + ) + }} +
diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 2490f5c17..a97b38671 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -762,10 +762,12 @@ function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean }) {
- - {(part) => { - const trigger = contextToolTrigger(part, i18n) - const running = part.state.status === "pending" || part.state.status === "running" + + {(partAccessor) => { + const trigger = createMemo(() => contextToolTrigger(partAccessor(), i18n)) + const running = createMemo( + () => partAccessor().state.status === "pending" || partAccessor().state.status === "running", + ) return (
@@ -774,13 +776,13 @@ function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean }) {
- + - - {trigger.subtitle} + + {trigger().subtitle} - - + + {(arg) => {arg}} @@ -792,7 +794,7 @@ function ContextToolGroup(props: { parts: ToolPart[]; busy?: boolean }) {
) }} - +
@@ -1096,30 +1098,30 @@ function ToolFileAccordion(props: { path: string; actions?: JSX.Element; childre PART_MAPPING["tool"] = function ToolPartDisplay(props) { const i18n = useI18n() - const part = props.part as ToolPart - if (part.tool === "todowrite" || part.tool === "todoread") return null + const part = () => props.part as ToolPart + if (part().tool === "todowrite" || part().tool === "todoread") return null const hideQuestion = createMemo( - () => part.tool === "question" && (part.state.status === "pending" || part.state.status === "running"), + () => part().tool === "question" && (part().state.status === "pending" || part().state.status === "running"), ) const emptyInput: Record = {} const emptyMetadata: Record = {} - const input = () => part.state?.input ?? emptyInput + const input = () => part().state?.input ?? emptyInput // @ts-expect-error - const partMetadata = () => part.state?.metadata ?? emptyMetadata + const partMetadata = () => part().state?.metadata ?? emptyMetadata - const render = ToolRegistry.render(part.tool) ?? GenericTool + const render = createMemo(() => ToolRegistry.render(part().tool) ?? GenericTool) return (
- + {(error) => { const cleaned = error().replace("Error: ", "") - if (part.tool === "question" && cleaned.includes("dismissed this question")) { + if (part().tool === "question" && cleaned.includes("dismissed this question")) { return (
@@ -1151,13 +1153,13 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { @@ -1186,7 +1188,7 @@ PART_MAPPING["compaction"] = function CompactionPartDisplay() { PART_MAPPING["text"] = function TextPartDisplay(props) { const data = useData() const i18n = useI18n() - const part = props.part as TextPart + const part = () => props.part as TextPart const interrupted = createMemo( () => props.message.role === "assistant" && (props.message as AssistantMessage).error?.name === "MessageAbortedError", @@ -1229,18 +1231,18 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { return items.filter((x) => !!x).join(" \u00B7 ") }) - const displayText = () => (part.text ?? "").trim() + const displayText = () => (part().text ?? "").trim() const throttledText = createThrottledValue(displayText) const isLastTextPart = createMemo(() => { const last = (data.store.part?.[props.message.id] ?? []) .filter((item): item is TextPart => item?.type === "text" && !!item.text?.trim()) .at(-1) - return last?.id === part.id + return last?.id === part().id }) const showCopy = createMemo(() => { if (props.message.role !== "assistant") return isLastTextPart() if (props.showAssistantCopyPartID === null) return false - if (typeof props.showAssistantCopyPartID === "string") return props.showAssistantCopyPartID === part.id + if (typeof props.showAssistantCopyPartID === "string") return props.showAssistantCopyPartID === part().id return isLastTextPart() }) const [copied, setCopied] = createSignal(false) @@ -1257,7 +1259,7 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
- +
@@ -1288,14 +1290,14 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { } PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props) { - const part = props.part as ReasoningPart - const text = () => part.text.trim() + const part = () => props.part as ReasoningPart + const text = () => part().text.trim() const throttledText = createThrottledValue(text) return (
- +
) diff --git a/packages/ui/src/hooks/create-auto-scroll.tsx b/packages/ui/src/hooks/create-auto-scroll.tsx index 8483915a8..d67b1f31f 100644 --- a/packages/ui/src/hooks/create-auto-scroll.tsx +++ b/packages/ui/src/hooks/create-auto-scroll.tsx @@ -48,14 +48,14 @@ export function createAutoScroll(options: AutoScrollOptions) { autoTimer = setTimeout(() => { auto = undefined autoTimer = undefined - }, 250) + }, 1500) } const isAuto = (el: HTMLElement) => { const a = auto if (!a) return false - if (Date.now() - a.time > 250) { + if (Date.now() - a.time > 1500) { auto = undefined return false }