|
|
@@ -6,7 +6,7 @@ import { Binary } from "@opencode-ai/util/binary"
|
|
|
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
|
|
import { createEffect, createMemo, createSignal, For, on, ParentProps, Show } from "solid-js"
|
|
|
import { Dynamic } from "solid-js/web"
|
|
|
-import { AssistantParts, Message, PART_MAPPING } from "./message-part"
|
|
|
+import { AssistantParts, Message, Part, PART_MAPPING } from "./message-part"
|
|
|
import { Card } from "./card"
|
|
|
import { Accordion } from "./accordion"
|
|
|
import { StickyAccordionHeader } from "./sticky-accordion-header"
|
|
|
@@ -139,7 +139,6 @@ export function SessionTurn(
|
|
|
props: ParentProps<{
|
|
|
sessionID: string
|
|
|
messageID: string
|
|
|
- lastUserMessageID?: string
|
|
|
showReasoningSummaries?: boolean
|
|
|
shellToolDefaultOpen?: boolean
|
|
|
editToolDefaultOpen?: boolean
|
|
|
@@ -187,18 +186,18 @@ export function SessionTurn(
|
|
|
return msg
|
|
|
})
|
|
|
|
|
|
- const lastUserMessageID = createMemo(() => {
|
|
|
- if (props.lastUserMessageID) return props.lastUserMessageID
|
|
|
-
|
|
|
+ const pending = createMemo(() => {
|
|
|
const messages = allMessages() ?? emptyMessages
|
|
|
- for (let i = messages.length - 1; i >= 0; i--) {
|
|
|
- const msg = messages[i]
|
|
|
- if (msg?.role === "user") return msg.id
|
|
|
- }
|
|
|
- return undefined
|
|
|
+ return messages.findLast(
|
|
|
+ (item): item is AssistantMessage => item.role === "assistant" && typeof item.time.completed !== "number",
|
|
|
+ )
|
|
|
+ })
|
|
|
+ const active = createMemo(() => {
|
|
|
+ const msg = message()
|
|
|
+ const item = pending()
|
|
|
+ if (!msg || !item) return false
|
|
|
+ return item.parentID === msg.id
|
|
|
})
|
|
|
-
|
|
|
- const isLastUserMessage = createMemo(() => props.messageID === lastUserMessageID())
|
|
|
|
|
|
const parts = createMemo(() => {
|
|
|
const msg = message()
|
|
|
@@ -206,6 +205,8 @@ export function SessionTurn(
|
|
|
return list(data.store.part?.[msg.id], emptyParts)
|
|
|
})
|
|
|
|
|
|
+ const compaction = createMemo(() => parts().find((part) => part.type === "compaction"))
|
|
|
+
|
|
|
const diffs = createMemo(() => {
|
|
|
const files = message()?.summary?.diffs
|
|
|
if (!files?.length) return emptyDiffs
|
|
|
@@ -285,7 +286,7 @@ export function SessionTurn(
|
|
|
})
|
|
|
|
|
|
const status = createMemo(() => data.store.session_status[props.sessionID] ?? idle)
|
|
|
- const working = createMemo(() => status().type !== "idle" && isLastUserMessage())
|
|
|
+ const working = createMemo(() => status().type !== "idle" && active())
|
|
|
const showReasoningSummaries = createMemo(() => props.showReasoningSummaries ?? true)
|
|
|
|
|
|
const assistantCopyPartID = createMemo(() => {
|
|
|
@@ -365,6 +366,13 @@ export function SessionTurn(
|
|
|
<div data-slot="session-turn-message-content" aria-live="off">
|
|
|
<Message message={msg()} parts={parts()} interrupted={interrupted()} />
|
|
|
</div>
|
|
|
+ <Show when={compaction()}>
|
|
|
+ {(part) => (
|
|
|
+ <div data-slot="session-turn-compaction">
|
|
|
+ <Part part={part()} message={msg()} hideDetails />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </Show>
|
|
|
<Show when={assistantMessages().length > 0}>
|
|
|
<div data-slot="session-turn-assistant-content" aria-hidden={working()}>
|
|
|
<AssistantParts
|
|
|
@@ -386,7 +394,7 @@ export function SessionTurn(
|
|
|
</Show>
|
|
|
</div>
|
|
|
</Show>
|
|
|
- <SessionRetry status={status()} show={isLastUserMessage()} />
|
|
|
+ <SessionRetry status={status()} show={active()} />
|
|
|
<Show when={edited() > 0 && !working()}>
|
|
|
<div data-slot="session-turn-diffs">
|
|
|
<Collapsible open={open()} onOpenChange={setOpen} variant="ghost">
|