feat(app): add compact ui (#15578)

This commit is contained in:
Filip
2026-03-01 15:41:47 +01:00
committed by GitHub
parent c8866e60ba
commit b15fb21191
23 changed files with 87 additions and 17 deletions

View File

@@ -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);

View File

@@ -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()

View File

@@ -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;

View File

@@ -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">