1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
import { Component, createMemo, For, Match, Show, Switch } from "solid-js"
import { Dynamic } from "solid-js/web"
import {
AssistantMessage,
Message as MessageType,
Part as PartType,
TextPart,
ToolPart,
UserMessage,
} from "@opencode-ai/sdk"
export interface MessageProps {
message: MessageType
parts: PartType[]
}
export interface MessagePartProps {
part: PartType
message: MessageType
hideDetails?: boolean
}
export type PartComponent = Component<MessagePartProps>
const PART_MAPPING: Record<string, PartComponent | undefined> = {}
export function registerPartComponent(type: string, component: PartComponent) {
PART_MAPPING[type] = component
}
export function Message(props: MessageProps) {
return (
<Switch>
<Match when={props.message.role === "user" && props.message}>
{(userMessage) => (
<UserMessageDisplay message={userMessage() as UserMessage} parts={props.parts} />
)}
</Match>
<Match when={props.message.role === "assistant" && props.message}>
{(assistantMessage) => (
<AssistantMessageDisplay
message={assistantMessage() as AssistantMessage}
parts={props.parts}
/>
)}
</Match>
</Switch>
)
}
export function AssistantMessageDisplay(props: { message: AssistantMessage; parts: PartType[] }) {
const filteredParts = createMemo(() => {
return props.parts?.filter((x) => {
if (x.type === "reasoning") return false
return x.type !== "tool" || (x as ToolPart).tool !== "todoread"
})
})
return <For each={filteredParts()}>{(part) => <Part part={part} message={props.message} />}</For>
}
export function UserMessageDisplay(props: { message: UserMessage; parts: PartType[] }) {
const text = createMemo(() =>
props.parts
?.filter((p) => p.type === "text" && !(p as TextPart).synthetic)
?.map((p) => (p as TextPart).text)
?.join(""),
)
return <div data-component="user-message">{text()}</div>
}
export function Part(props: MessagePartProps) {
const component = createMemo(() => PART_MAPPING[props.part.type])
return (
<Show when={component()}>
<Dynamic
component={component()}
part={props.part}
message={props.message}
hideDetails={props.hideDetails}
/>
</Show>
)
}
|