Skip to content

Commit 48cfb2e

Browse files
committed
wip: desktop work
1 parent 4a77e94 commit 48cfb2e

File tree

2 files changed

+56
-19
lines changed

2 files changed

+56
-19
lines changed

packages/desktop/src/components/assistant-message.tsx renamed to packages/desktop/src/components/message.tsx

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart, Message } from "@opencode-ai/sdk"
1+
import type { Part, ReasoningPart, TextPart, ToolPart, Message, AssistantMessage, UserMessage } from "@opencode-ai/sdk"
22
import { children, Component, createMemo, For, Match, Show, Switch, type JSX } from "solid-js"
33
import { Dynamic } from "solid-js/web"
44
import { Markdown } from "./markdown"
@@ -17,7 +17,20 @@ import type { WriteTool } from "opencode/tool/write"
1717
import type { TodoWriteTool } from "opencode/tool/todo"
1818
import { DiffChanges } from "./diff-changes"
1919

20-
export function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
20+
export function Message(props: { message: Message; parts: Part[] }) {
21+
return (
22+
<Switch>
23+
<Match when={props.message.role === "user" && props.message}>
24+
{(userMessage) => <UserMessage message={userMessage()} parts={props.parts} />}
25+
</Match>
26+
<Match when={props.message.role === "assistant" && props.message}>
27+
{(assistantMessage) => <AssistantMessage message={assistantMessage()} parts={props.parts} />}
28+
</Match>
29+
</Switch>
30+
)
31+
}
32+
33+
function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
2134
const filteredParts = createMemo(() => {
2235
return props.parts?.filter((x) => {
2336
if (x.type === "reasoning") return false
@@ -31,11 +44,26 @@ export function AssistantMessage(props: { message: AssistantMessage; parts: Part
3144
)
3245
}
3346

34-
export function Part(props: { part: Part; message: Message; readonly?: boolean }) {
47+
function UserMessage(props: { message: UserMessage; parts: Part[] }) {
48+
const text = createMemo(() =>
49+
props.parts
50+
?.filter((p) => p.type === "text" && !p.synthetic)
51+
?.map((p) => (p as TextPart).text)
52+
?.join(""),
53+
)
54+
return <div class="text-12-regular text-text-base line-clamp-3">{text()}</div>
55+
}
56+
57+
export function Part(props: { part: Part; message: Message; hideDetails?: boolean }) {
3558
const component = createMemo(() => PART_MAPPING[props.part.type as keyof typeof PART_MAPPING])
3659
return (
3760
<Show when={component()}>
38-
<Dynamic component={component()} part={props.part as any} message={props.message} readonly={props.readonly} />
61+
<Dynamic
62+
component={component()}
63+
part={props.part as any}
64+
message={props.message}
65+
hideDetails={props.hideDetails}
66+
/>
3967
</Show>
4068
)
4169
}
@@ -62,7 +90,7 @@ function TextPart(props: { part: TextPart; message: Message }) {
6290
)
6391
}
6492

65-
function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean }) {
93+
function ToolPart(props: { part: ToolPart; message: Message; hideDetails?: boolean }) {
6694
const component = createMemo(() => {
6795
const render = ToolRegistry.render(props.part.tool) ?? GenericTool
6896
const metadata = props.part.state.status === "pending" ? {} : (props.part.state.metadata ?? {})
@@ -75,7 +103,7 @@ function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean
75103
tool={props.part.tool}
76104
metadata={metadata}
77105
output={props.part.state.status === "completed" ? props.part.state.output : undefined}
78-
readonly={props.readonly}
106+
hideDetails={props.hideDetails}
79107
/>
80108
)
81109
})
@@ -101,7 +129,7 @@ function BasicTool(props: {
101129
icon: IconProps["name"]
102130
trigger: TriggerTitle | JSX.Element
103131
children?: JSX.Element
104-
readonly?: boolean
132+
hideDetails?: boolean
105133
}) {
106134
const resolved = children(() => props.children)
107135
return (
@@ -157,12 +185,12 @@ function BasicTool(props: {
157185
</Switch>
158186
</div>
159187
</div>
160-
<Show when={resolved() && !props.readonly}>
188+
<Show when={resolved() && !props.hideDetails}>
161189
<Collapsible.Arrow />
162190
</Show>
163191
</div>
164192
</Collapsible.Trigger>
165-
<Show when={resolved() && !props.readonly}>
193+
<Show when={resolved() && !props.hideDetails}>
166194
<Collapsible.Content>{resolved()}</Collapsible.Content>
167195
</Show>
168196
</Collapsible>
@@ -173,15 +201,15 @@ function BasicTool(props: {
173201
}
174202

175203
function GenericTool(props: ToolProps<any>) {
176-
return <BasicTool icon="mcp" trigger={{ title: props.tool }} readonly={props.readonly} />
204+
return <BasicTool icon="mcp" trigger={{ title: props.tool }} hideDetails={props.hideDetails} />
177205
}
178206

179207
type ToolProps<T extends Tool.Info> = {
180208
input: Partial<Tool.InferParameters<T>>
181209
metadata: Partial<Tool.InferMetadata<T>>
182210
tool: string
183211
output?: string
184-
readonly?: boolean
212+
hideDetails?: boolean
185213
}
186214

187215
const ToolRegistry = (() => {

packages/desktop/src/pages/index.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { Code } from "@/components/code"
3333
import { useSync } from "@/context/sync"
3434
import { useSDK } from "@/context/sdk"
3535
import { ProgressCircle } from "@/components/progress-circle"
36-
import { AssistantMessage, Part } from "@/components/assistant-message"
36+
import { Message, Part } from "@/components/message"
3737
import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
3838
import { DiffChanges } from "@/components/diff-changes"
3939

@@ -198,6 +198,7 @@ export default function Page() {
198198
}
199199
if (!session) return
200200

201+
local.session.setActive(session.id)
201202
const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path))
202203

203204
const text = parts.map((part) => part.content).join("")
@@ -259,7 +260,6 @@ export default function Page() {
259260
],
260261
},
261262
})
262-
local.session.setActive(session.id)
263263
}
264264

265265
const handleNewSession = () => {
@@ -639,8 +639,9 @@ export default function Page() {
639639
<For each={local.session.userMessages()}>
640640
{(message) => {
641641
const [expanded, setExpanded] = createSignal(false)
642-
const title = createMemo(() => message.summary?.title)
642+
const parts = createMemo(() => sync.data.part[message.id])
643643
const prompt = createMemo(() => local.session.getMessageText(message))
644+
const title = createMemo(() => message.summary?.title)
644645
const summary = createMemo(() => message.summary?.body)
645646
const assistantMessages = createMemo(() => {
646647
return sync.data.message[activeSession().id]?.filter(
@@ -665,7 +666,9 @@ export default function Page() {
665666
</h1>
666667
</div>
667668
<Show when={title}>
668-
<div class="-mt-8 text-12-regular text-text-base line-clamp-3">{prompt()}</div>
669+
<div class="-mt-8">
670+
<Message message={message} parts={parts()} />
671+
</div>
669672
</Show>
670673
{/* Response */}
671674
<div class="w-full flex flex-col gap-2">
@@ -686,7 +689,7 @@ export default function Page() {
686689
<For each={assistantMessages()}>
687690
{(assistantMessage) => {
688691
const parts = createMemo(() => sync.data.part[assistantMessage.id])
689-
return <AssistantMessage message={assistantMessage} parts={parts()} />
692+
return <Message message={assistantMessage} parts={parts()} />
690693
}}
691694
</For>
692695
</div>
@@ -722,7 +725,9 @@ export default function Page() {
722725
const lastTextPart = createMemo(() =>
723726
sync.data.part[last().id].findLast((p) => p.type === "text"),
724727
)
725-
return <Part message={last()} part={lastTextPart()!} readonly />
728+
return (
729+
<Part message={last()} part={lastTextPart()!} hideDetails />
730+
)
726731
}}
727732
</Match>
728733
<Match when={lastMessageWithReasoning()}>
@@ -733,7 +738,11 @@ export default function Page() {
733738
),
734739
)
735740
return (
736-
<Part message={last()} part={lastReasoningPart()!} readonly />
741+
<Part
742+
message={last()}
743+
part={lastReasoningPart()!}
744+
hideDetails
745+
/>
737746
)
738747
}}
739748
</Match>
@@ -745,7 +754,7 @@ export default function Page() {
745754
(p) => p.type === "tool" && p.state.status === "completed",
746755
),
747756
)
748-
return <Part message={last()} part={lastToolPart()!} readonly />
757+
return <Part message={last()} part={lastToolPart()!} hideDetails />
749758
}}
750759
</Show>
751760
</div>

0 commit comments

Comments
 (0)