import { AssistantMessage } from "@opencode-ai/sdk" import { useData } from "../context" import { Binary } from "@opencode-ai/util/binary" import { getDirectory, getFilename } from "@opencode-ai/util/path" import { createEffect, createMemo, createSignal, For, Match, ParentProps, Show, Switch } from "solid-js" import { DiffChanges } from "./diff-changes" import { Typewriter } from "./typewriter" import { Message } from "./message-part" import { Markdown } from "./markdown" import { Accordion } from "./accordion" import { StickyAccordionHeader } from "./sticky-accordion-header" import { FileIcon } from "./file-icon" import { Icon } from "./icon" import { Diff } from "./diff" import { Card } from "./card" import { MessageProgress } from "./message-progress" import { Collapsible } from "./collapsible" export function SessionTurn( props: ParentProps<{ sessionID: string messageID: string classes?: { root?: string content?: string container?: string } }>, ) { const data = useData() const match = Binary.search(data.session, props.sessionID, (s) => s.id) if (!match.found) throw new Error(`Session ${props.sessionID} not found`) const messages = createMemo(() => (props.sessionID ? (data.message[props.sessionID] ?? []) : [])) const userMessages = createMemo(() => messages() .filter((m) => m.role === "user") .sort((a, b) => b.id.localeCompare(a.id)), ) const lastUserMessage = createMemo(() => { return userMessages()?.at(0) }) const message = createMemo(() => userMessages()?.find((m) => m.id === props.messageID)) const status = createMemo( () => data.session_status[props.sessionID] ?? { type: "idle", }, ) const working = createMemo(() => status()?.type !== "idle") return (
{(msg) => { const titleSeen = createMemo(() => true) const contentSeen = createMemo(() => true) const [titled, setTitled] = createSignal(titleSeen()) const assistantMessages = createMemo(() => { return messages()?.filter((m) => m.role === "assistant" && m.parentID == msg().id) as AssistantMessage[] }) const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) const [detailsExpanded, setDetailsExpanded] = createSignal(false) const parts = createMemo(() => data.part[msg().id]) const hasToolPart = createMemo(() => assistantMessages() ?.flatMap((m) => data.part[m.id]) .some((p) => p?.type === "tool"), ) const messageWorking = createMemo(() => msg().id === lastUserMessage()?.id && working()) const initialCompleted = !(msg().id === lastUserMessage()?.id && working()) const [completed, setCompleted] = createSignal(initialCompleted) // allowing time for the animations to finish createEffect(() => { if (titleSeen()) return const title = msg().summary?.title if (title) setTimeout(() => setTitled(true), 10_000) }) createEffect(() => { const completed = !messageWorking() setTimeout(() => setCompleted(completed), 1200) }) return (
{/* Title */}
} >

{msg().summary?.title}

{/* Summary */}

Summary Response

{(summary) => ( )}
{(diff) => (
{getDirectory(diff.file)}‎ {getFilename(diff.file)}
)}
{error()?.data?.message as string} {/* Response */}
Hide details Show details
{(assistantMessage) => { const parts = createMemo(() => data.part[assistantMessage.id]) return }} {error()?.data?.message as string}
) }}
{props.children}
) }