mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-29 05:34:19 +01:00
wip: desktop work
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart, Message } from "@opencode-ai/sdk"
|
||||
import type { Part, ReasoningPart, TextPart, ToolPart, Message, AssistantMessage, UserMessage } from "@opencode-ai/sdk"
|
||||
import { children, Component, createMemo, For, Match, Show, Switch, type JSX } from "solid-js"
|
||||
import { Dynamic } from "solid-js/web"
|
||||
import { Markdown } from "./markdown"
|
||||
@@ -17,7 +17,20 @@ import type { WriteTool } from "opencode/tool/write"
|
||||
import type { TodoWriteTool } from "opencode/tool/todo"
|
||||
import { DiffChanges } from "./diff-changes"
|
||||
|
||||
export function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
|
||||
export function Message(props: { message: Message; parts: Part[] }) {
|
||||
return (
|
||||
<Switch>
|
||||
<Match when={props.message.role === "user" && props.message}>
|
||||
{(userMessage) => <UserMessage message={userMessage()} parts={props.parts} />}
|
||||
</Match>
|
||||
<Match when={props.message.role === "assistant" && props.message}>
|
||||
{(assistantMessage) => <AssistantMessage message={assistantMessage()} parts={props.parts} />}
|
||||
</Match>
|
||||
</Switch>
|
||||
)
|
||||
}
|
||||
|
||||
function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) {
|
||||
const filteredParts = createMemo(() => {
|
||||
return props.parts?.filter((x) => {
|
||||
if (x.type === "reasoning") return false
|
||||
@@ -31,11 +44,26 @@ export function AssistantMessage(props: { message: AssistantMessage; parts: Part
|
||||
)
|
||||
}
|
||||
|
||||
export function Part(props: { part: Part; message: Message; readonly?: boolean }) {
|
||||
function UserMessage(props: { message: UserMessage; parts: Part[] }) {
|
||||
const text = createMemo(() =>
|
||||
props.parts
|
||||
?.filter((p) => p.type === "text" && !p.synthetic)
|
||||
?.map((p) => (p as TextPart).text)
|
||||
?.join(""),
|
||||
)
|
||||
return <div class="text-12-regular text-text-base line-clamp-3">{text()}</div>
|
||||
}
|
||||
|
||||
export function Part(props: { part: Part; message: Message; hideDetails?: boolean }) {
|
||||
const component = createMemo(() => PART_MAPPING[props.part.type as keyof typeof PART_MAPPING])
|
||||
return (
|
||||
<Show when={component()}>
|
||||
<Dynamic component={component()} part={props.part as any} message={props.message} readonly={props.readonly} />
|
||||
<Dynamic
|
||||
component={component()}
|
||||
part={props.part as any}
|
||||
message={props.message}
|
||||
hideDetails={props.hideDetails}
|
||||
/>
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
@@ -62,7 +90,7 @@ function TextPart(props: { part: TextPart; message: Message }) {
|
||||
)
|
||||
}
|
||||
|
||||
function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean }) {
|
||||
function ToolPart(props: { part: ToolPart; message: Message; hideDetails?: boolean }) {
|
||||
const component = createMemo(() => {
|
||||
const render = ToolRegistry.render(props.part.tool) ?? GenericTool
|
||||
const metadata = props.part.state.status === "pending" ? {} : (props.part.state.metadata ?? {})
|
||||
@@ -75,7 +103,7 @@ function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean
|
||||
tool={props.part.tool}
|
||||
metadata={metadata}
|
||||
output={props.part.state.status === "completed" ? props.part.state.output : undefined}
|
||||
readonly={props.readonly}
|
||||
hideDetails={props.hideDetails}
|
||||
/>
|
||||
)
|
||||
})
|
||||
@@ -101,7 +129,7 @@ function BasicTool(props: {
|
||||
icon: IconProps["name"]
|
||||
trigger: TriggerTitle | JSX.Element
|
||||
children?: JSX.Element
|
||||
readonly?: boolean
|
||||
hideDetails?: boolean
|
||||
}) {
|
||||
const resolved = children(() => props.children)
|
||||
return (
|
||||
@@ -157,12 +185,12 @@ function BasicTool(props: {
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={resolved() && !props.readonly}>
|
||||
<Show when={resolved() && !props.hideDetails}>
|
||||
<Collapsible.Arrow />
|
||||
</Show>
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Show when={resolved() && !props.readonly}>
|
||||
<Show when={resolved() && !props.hideDetails}>
|
||||
<Collapsible.Content>{resolved()}</Collapsible.Content>
|
||||
</Show>
|
||||
</Collapsible>
|
||||
@@ -173,7 +201,7 @@ function BasicTool(props: {
|
||||
}
|
||||
|
||||
function GenericTool(props: ToolProps<any>) {
|
||||
return <BasicTool icon="mcp" trigger={{ title: props.tool }} readonly={props.readonly} />
|
||||
return <BasicTool icon="mcp" trigger={{ title: props.tool }} hideDetails={props.hideDetails} />
|
||||
}
|
||||
|
||||
type ToolProps<T extends Tool.Info> = {
|
||||
@@ -181,7 +209,7 @@ type ToolProps<T extends Tool.Info> = {
|
||||
metadata: Partial<Tool.InferMetadata<T>>
|
||||
tool: string
|
||||
output?: string
|
||||
readonly?: boolean
|
||||
hideDetails?: boolean
|
||||
}
|
||||
|
||||
const ToolRegistry = (() => {
|
||||
@@ -33,7 +33,7 @@ import { Code } from "@/components/code"
|
||||
import { useSync } from "@/context/sync"
|
||||
import { useSDK } from "@/context/sdk"
|
||||
import { ProgressCircle } from "@/components/progress-circle"
|
||||
import { AssistantMessage, Part } from "@/components/assistant-message"
|
||||
import { Message, Part } from "@/components/message"
|
||||
import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
|
||||
import { DiffChanges } from "@/components/diff-changes"
|
||||
|
||||
@@ -198,6 +198,7 @@ export default function Page() {
|
||||
}
|
||||
if (!session) return
|
||||
|
||||
local.session.setActive(session.id)
|
||||
const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path))
|
||||
|
||||
const text = parts.map((part) => part.content).join("")
|
||||
@@ -259,7 +260,6 @@ export default function Page() {
|
||||
],
|
||||
},
|
||||
})
|
||||
local.session.setActive(session.id)
|
||||
}
|
||||
|
||||
const handleNewSession = () => {
|
||||
@@ -639,8 +639,9 @@ export default function Page() {
|
||||
<For each={local.session.userMessages()}>
|
||||
{(message) => {
|
||||
const [expanded, setExpanded] = createSignal(false)
|
||||
const title = createMemo(() => message.summary?.title)
|
||||
const parts = createMemo(() => sync.data.part[message.id])
|
||||
const prompt = createMemo(() => local.session.getMessageText(message))
|
||||
const title = createMemo(() => message.summary?.title)
|
||||
const summary = createMemo(() => message.summary?.body)
|
||||
const assistantMessages = createMemo(() => {
|
||||
return sync.data.message[activeSession().id]?.filter(
|
||||
@@ -665,7 +666,9 @@ export default function Page() {
|
||||
</h1>
|
||||
</div>
|
||||
<Show when={title}>
|
||||
<div class="-mt-8 text-12-regular text-text-base line-clamp-3">{prompt()}</div>
|
||||
<div class="-mt-8">
|
||||
<Message message={message} parts={parts()} />
|
||||
</div>
|
||||
</Show>
|
||||
{/* Response */}
|
||||
<div class="w-full flex flex-col gap-2">
|
||||
@@ -686,7 +689,7 @@ export default function Page() {
|
||||
<For each={assistantMessages()}>
|
||||
{(assistantMessage) => {
|
||||
const parts = createMemo(() => sync.data.part[assistantMessage.id])
|
||||
return <AssistantMessage message={assistantMessage} parts={parts()} />
|
||||
return <Message message={assistantMessage} parts={parts()} />
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
@@ -722,7 +725,9 @@ export default function Page() {
|
||||
const lastTextPart = createMemo(() =>
|
||||
sync.data.part[last().id].findLast((p) => p.type === "text"),
|
||||
)
|
||||
return <Part message={last()} part={lastTextPart()!} readonly />
|
||||
return (
|
||||
<Part message={last()} part={lastTextPart()!} hideDetails />
|
||||
)
|
||||
}}
|
||||
</Match>
|
||||
<Match when={lastMessageWithReasoning()}>
|
||||
@@ -733,7 +738,11 @@ export default function Page() {
|
||||
),
|
||||
)
|
||||
return (
|
||||
<Part message={last()} part={lastReasoningPart()!} readonly />
|
||||
<Part
|
||||
message={last()}
|
||||
part={lastReasoningPart()!}
|
||||
hideDetails
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</Match>
|
||||
@@ -745,7 +754,7 @@ export default function Page() {
|
||||
(p) => p.type === "tool" && p.state.status === "completed",
|
||||
),
|
||||
)
|
||||
return <Part message={last()} part={lastToolPart()!} readonly />
|
||||
return <Part message={last()} part={lastToolPart()!} hideDetails />
|
||||
}}
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user