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