Browse Source

refactor(app): remove Accessor wrapping from timeline row renders (#28334)

Brendan Allan 3 months ago
parent
commit
922b2e10eb
1 changed files with 41 additions and 67 deletions
  1. 41 67
      packages/app/src/pages/session/message-timeline.tsx

+ 41 - 67
packages/app/src/pages/session/message-timeline.tsx

@@ -1,19 +1,4 @@
-import {
-  createEffect,
-  createMemo,
-  createSignal,
-  For,
-  Index,
-  Match,
-  Switch,
-  on,
-  onCleanup,
-  Show,
-  mapArray,
-  untrack,
-  type Accessor,
-  type JSX,
-} from "solid-js"
+import { createEffect, createMemo, createSignal, For, Index, on, onCleanup, Show, mapArray, type JSX } from "solid-js"
 import { createStore, produce } from "solid-js/store"
 import { Dynamic } from "solid-js/web"
 import { useNavigate } from "@solidjs/router"
@@ -995,26 +980,26 @@ export function MessageTimeline(props: {
 
   const getMsgPart = (messageID: string, partID: string) => getMsgParts(messageID).find((part) => part.id === partID)
 
-  const renderAssistantPartGroup = (row: Accessor<TimelineRowMap["AssistantPart"]>) => {
-    if (untrack(row).group.type === "context") {
+  const renderAssistantPartGroup = (row: TimelineRowMap["AssistantPart"]) => {
+    if (row.group.type === "context") {
       const parts = createMemo(() => {
-        const group = row().group
+        const group = row.group
         if (group.type !== "context") return emptyTools
         return group.refs
           .map((ref) => getMsgPart(ref.messageID, ref.partID))
           .filter((part): part is ToolPart => part?.type === "tool")
       })
 
-      return <ContextToolGroup parts={parts()} busy={workingTurn(row().userMessageID) && row().lastAssistantPart} />
+      return <ContextToolGroup parts={parts()} busy={workingTurn(row.userMessageID) && row.lastAssistantPart} />
     }
 
     const message = createMemo(() => {
-      const group = row().group
+      const group = row.group
       if (group.type !== "part") return
       return messageByID().get(group.ref.messageID)
     })
     const part = createMemo(() => {
-      const group = row().group
+      const group = row.group
       if (group.type !== "part") return
       return getMsgPart(group.ref.messageID, group.ref.partID)
     })
@@ -1027,8 +1012,8 @@ export function MessageTimeline(props: {
               <MessagePart
                 part={part()}
                 message={message()}
-                showAssistantCopyPartID={assistantCopyPartID(row().userMessageID)}
-                turnDurationMs={turnDurationMs(row().userMessageID)}
+                showAssistantCopyPartID={assistantCopyPartID(row.userMessageID)}
+                turnDurationMs={turnDurationMs(row.userMessageID)}
                 defaultOpen={partDefaultOpen(
                   part(),
                   settings.general.shellToolPartsExpanded(),
@@ -1043,25 +1028,25 @@ export function MessageTimeline(props: {
     )
   }
 
-  function TimelineRowFrame(input: { row: Accessor<FramedTimelineRow>; children: JSX.Element }) {
+  function TimelineRowFrame(input: { row: FramedTimelineRow; children: JSX.Element }) {
     const anchor = () => {
-      const row = input.row()
+      const row = input.row
       return row._tag === "CommentStrip" || (row._tag === "UserMessage" && row.anchor)
     }
     const previousUserMessage = () => {
-      const row = input.row()
+      const row = input.row
       return (row._tag === "CommentStrip" || row._tag === "UserMessage") && row.previousUserMessage
     }
     const previousAssistantPart = () => {
-      const row = input.row()
+      const row = input.row
       return row._tag === "AssistantPart" && row.previousAssistantPart
     }
 
     return (
       <div
-        id={anchor() ? props.anchor(input.row().userMessageID) : undefined}
-        data-message-id={input.row().userMessageID}
-        data-timeline-row={input.row()._tag}
+        id={anchor() ? props.anchor(input.row.userMessageID) : undefined}
+        data-message-id={input.row.userMessageID}
+        data-timeline-row={input.row._tag}
         classList={{
           "min-w-0 w-full max-w-full": true,
           "md:max-w-200 2xl:max-w-[1000px]": props.centered,
@@ -1077,15 +1062,14 @@ export function MessageTimeline(props: {
     )
   }
 
-  const renderTimelineRow = (row: Accessor<TimelineRow.TimelineRow>) => {
-    switch (row()._tag) {
+  const renderTimelineRow = (row: TimelineRow.TimelineRow) => {
+    switch (row._tag) {
       case "CommentStrip": {
-        const commentStripRow = row as Accessor<TimelineRowByTag<"CommentStrip">>
         const comments = createMemo(() =>
-          getMsgParts(commentStripRow().userMessageID).flatMap((part) => MessageComment.fromPart(part) ?? []),
+          getMsgParts(row.userMessageID).flatMap((part) => MessageComment.fromPart(part) ?? []),
         )
         return (
-          <TimelineRowFrame row={commentStripRow}>
+          <TimelineRowFrame row={row}>
             <div class="w-full px-4 md:px-5 pb-2">
               <div class="ml-auto max-w-[82%] overflow-x-auto no-scrollbar">
                 <div class="flex w-max min-w-full justify-end gap-2">
@@ -1118,22 +1102,17 @@ export function MessageTimeline(props: {
         )
       }
       case "UserMessage": {
-        const userMessageRow = row as Accessor<TimelineRowByTag<"UserMessage">>
         const message = createMemo(() => {
-          const m = messageByID().get(userMessageRow().userMessageID)
+          const m = messageByID().get(row.userMessageID)
           if (m?.role === "user") return m
         })
         return (
-          <TimelineRowFrame row={userMessageRow}>
+          <TimelineRowFrame row={row}>
             <Show when={message()}>
               {(message) => (
                 <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
                   <div data-slot="session-turn-message-content" aria-live="off">
-                    <Message
-                      message={message()}
-                      parts={getMsgParts(userMessageRow().userMessageID)}
-                      actions={props.actions}
-                    />
+                    <Message message={message()} parts={getMsgParts(row.userMessageID)} actions={props.actions} />
                   </div>
                 </div>
               )}
@@ -1142,14 +1121,13 @@ export function MessageTimeline(props: {
         )
       }
       case "TurnDivider": {
-        const turnDividerRow = row as Accessor<TimelineRowByTag<"TurnDivider">>
         return (
-          <TimelineRowFrame row={turnDividerRow}>
+          <TimelineRowFrame row={row}>
             <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
               <div data-slot="session-turn-compaction">
                 <MessageDivider
                   label={language.t(
-                    turnDividerRow().label === "compaction" ? "ui.messagePart.compaction" : "ui.message.interrupted",
+                    row.label === "compaction" ? "ui.messagePart.compaction" : "ui.message.interrupted",
                   )}
                 />
               </div>
@@ -1158,27 +1136,22 @@ export function MessageTimeline(props: {
         )
       }
       case "AssistantPart": {
-        const assistantPartRow = row as Accessor<TimelineRowByTag<"AssistantPart">>
         return (
-          <TimelineRowFrame row={assistantPartRow}>
+          <TimelineRowFrame row={row}>
             <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
-              <div
-                data-slot="session-turn-assistant-content"
-                aria-hidden={workingTurn(assistantPartRow().userMessageID)}
-              >
-                {renderAssistantPartGroup(assistantPartRow)}
+              <div data-slot="session-turn-assistant-content" aria-hidden={workingTurn(row.userMessageID)}>
+                {renderAssistantPartGroup(row)}
               </div>
             </div>
           </TimelineRowFrame>
         )
       }
       case "Thinking": {
-        const thinkingRow = row as Accessor<TimelineRowByTag<"Thinking">>
         return (
-          <TimelineRowFrame row={thinkingRow}>
+          <TimelineRowFrame row={row}>
             <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
               <TimelineThinkingRow
-                reasoningHeading={thinkingRow().reasoningHeading}
+                reasoningHeading={row.reasoningHeading}
                 showReasoningSummaries={settings.general.showReasoningSummaries()}
               />
             </div>
@@ -1186,32 +1159,29 @@ export function MessageTimeline(props: {
         )
       }
       case "Retry": {
-        const retryRow = row as Accessor<TimelineRowByTag<"Retry">>
         return (
-          <TimelineRowFrame row={retryRow}>
+          <TimelineRowFrame row={row}>
             <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
-              <SessionRetry status={sessionStatus()} show={activeMessageID() === retryRow().userMessageID} />
+              <SessionRetry status={sessionStatus()} show={activeMessageID() === row.userMessageID} />
             </div>
           </TimelineRowFrame>
         )
       }
       case "DiffSummary": {
-        const diffSummaryRow = row as Accessor<TimelineRowByTag<"DiffSummary">>
         return (
-          <TimelineRowFrame row={diffSummaryRow}>
+          <TimelineRowFrame row={row}>
             <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
-              <TimelineDiffSummaryRow diffs={diffSummaryRow().diffs} />
+              <TimelineDiffSummaryRow diffs={row.diffs} />
             </div>
           </TimelineRowFrame>
         )
       }
       case "Error": {
-        const errorRow = row as Accessor<TimelineRowByTag<"Error">>
         return (
-          <TimelineRowFrame row={errorRow}>
+          <TimelineRowFrame row={row}>
             <div data-slot="session-turn-message-container" class="w-full px-4 md:px-5">
               <Card variant="error" class="error-card">
-                {errorRow().text}
+                {row.text}
               </Card>
             </div>
           </TimelineRowFrame>
@@ -1223,7 +1193,11 @@ export function MessageTimeline(props: {
   }
 
   function TimelineRowView(props: { rowKey: string }) {
-    return <Show when={timelineRowByKey().get(props.rowKey)}>{(item) => renderTimelineRow(item)}</Show>
+    return (
+      <Show when={timelineRowByKey().get(props.rowKey)} keyed>
+        {(item) => renderTimelineRow(item)}
+      </Show>
+    )
   }
 
   return (