diff options
Diffstat (limited to 'packages/ui/src/components')
| -rw-r--r-- | packages/ui/src/components/message-part.css | 27 | ||||
| -rw-r--r-- | packages/ui/src/components/message-part.tsx | 15 | ||||
| -rw-r--r-- | packages/ui/src/components/session-turn.css | 6 | ||||
| -rw-r--r-- | packages/ui/src/components/session-turn.tsx | 36 |
4 files changed, 70 insertions, 14 deletions
diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index 58227f625..6727bb22f 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -225,6 +225,33 @@ } } +[data-component="compaction-part"] { + width: 100%; + display: flex; + flex-direction: column; + align-items: stretch; + + [data-slot="compaction-part-divider"] { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 0; + width: 100%; + } + + [data-slot="compaction-part-line"] { + flex: 1 1 auto; + height: 1px; + background: var(--border-weak-base); + } + + [data-slot="compaction-part-label"] { + flex: 0 0 auto; + white-space: nowrap; + text-align: center; + } +} + [data-component="reasoning-part"] { width: 100%; color: var(--text-base); diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 6b6dfe2e5..02a99f9dd 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -1037,6 +1037,21 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { ) } +PART_MAPPING["compaction"] = function CompactionPartDisplay() { + const i18n = useI18n() + return ( + <div data-component="compaction-part"> + <div data-slot="compaction-part-divider"> + <span data-slot="compaction-part-line" /> + <span data-slot="compaction-part-label" class="text-12-regular text-text-weak"> + {i18n.t("ui.messagePart.compaction")} + </span> + <span data-slot="compaction-part-line" /> + </div> + </div> + ) +} + PART_MAPPING["text"] = function TextPartDisplay(props) { const data = useData() const i18n = useI18n() diff --git a/packages/ui/src/components/session-turn.css b/packages/ui/src/components/session-turn.css index 9639e6635..4af87b361 100644 --- a/packages/ui/src/components/session-turn.css +++ b/packages/ui/src/components/session-turn.css @@ -37,6 +37,12 @@ max-width: 100%; } + [data-slot="session-turn-compaction"] { + width: 100%; + min-width: 0; + align-self: stretch; + } + [data-slot="session-turn-thinking"] { display: flex; align-items: center; diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index bd4f2843a..e329b1170 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -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"> |
