diff --git a/packages/desktop/src/components/assistant-message.tsx b/packages/desktop/src/components/assistant-message.tsx index 90f6e70f..8c654660 100644 --- a/packages/desktop/src/components/assistant-message.tsx +++ b/packages/desktop/src/components/assistant-message.tsx @@ -1,4 +1,4 @@ -import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart } from "@opencode-ai/sdk" +import type { Part, AssistantMessage, ReasoningPart, TextPart, ToolPart, Message } 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,47 +17,36 @@ 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[]; lastToolOnly?: boolean }) { +export function AssistantMessage(props: { message: AssistantMessage; parts: Part[] }) { const filteredParts = createMemo(() => { - let tool = false return props.parts?.filter((x) => { - if (x.type === "tool" && props.lastToolOnly && tool) return false - if (x.type === "tool") tool = true + if (x.type === "reasoning") return false return x.type !== "tool" || x.tool !== "todoread" }) }) return (
- - {(part) => { - const component = createMemo(() => PART_MAPPING[part.type as keyof typeof PART_MAPPING]) - return ( - - - - ) - }} - + {(part) => }
) } +export function Part(props: { part: Part; message: Message; readonly?: boolean }) { + const component = createMemo(() => PART_MAPPING[props.part.type as keyof typeof PART_MAPPING]) + return ( + + + + ) +} + const PART_MAPPING = { text: TextPart, tool: ToolPart, reasoning: ReasoningPart, } -function ReasoningPart(props: { part: ReasoningPart; message: AssistantMessage }) { - return null - // return ( - // - //
{props.part.text}
- //
- // ) -} - -function TextPart(props: { part: TextPart; message: AssistantMessage }) { +function ReasoningPart(props: { part: ReasoningPart; message: Message }) { return ( @@ -65,17 +54,19 @@ function TextPart(props: { part: TextPart; message: AssistantMessage }) { ) } -function ToolPart(props: { part: ToolPart; message: AssistantMessage }) { - // const sync = useSync() +function TextPart(props: { part: TextPart; message: Message }) { + return ( + + + + ) +} +function ToolPart(props: { part: ToolPart; message: Message; readonly?: boolean }) { const component = createMemo(() => { const render = ToolRegistry.render(props.part.tool) ?? GenericTool - const metadata = props.part.state.status === "pending" ? {} : (props.part.state.metadata ?? {}) const input = props.part.state.status === "completed" ? props.part.state.input : {} - // const permissions = sync.data.permission[props.message.sessionID] ?? [] - // const permissionIndex = permissions.findIndex((x) => x.callID === props.part.callID) - // const permission = permissions[permissionIndex] return ( ) }) @@ -106,7 +97,12 @@ const isTriggerTitle = (val: any): val is TriggerTitle => { return typeof val === "object" && val !== null && "title" in val && !(val instanceof Node) } -function BasicTool(props: { icon: IconProps["name"]; trigger: TriggerTitle | JSX.Element; children?: JSX.Element }) { +function BasicTool(props: { + icon: IconProps["name"] + trigger: TriggerTitle | JSX.Element + children?: JSX.Element + readonly?: boolean +}) { const resolved = children(() => props.children) return ( @@ -161,13 +157,13 @@ function BasicTool(props: { icon: IconProps["name"]; trigger: TriggerTitle | JSX - + - - {props.children} + + {resolved()} // <> @@ -177,15 +173,15 @@ function BasicTool(props: { icon: IconProps["name"]; trigger: TriggerTitle | JSX } function GenericTool(props: ToolProps) { - return + return } type ToolProps = { input: Partial> metadata: Partial> - // permission: Record tool: string output?: string + readonly?: boolean } const ToolRegistry = (() => { diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index c1884b2c..800f3651 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -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 } from "@/components/assistant-message" +import { AssistantMessage, Part } from "@/components/assistant-message" import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" import { DiffChanges } from "@/components/diff-changes" @@ -178,6 +178,8 @@ export default function Page() { } const handleDiffTriggerClick = (event: MouseEvent) => { + // disabling scroll to diff for now + return const target = event.currentTarget as HTMLElement queueMicrotask(() => { if (target.getAttribute("aria-expanded") !== "true") return @@ -636,6 +638,7 @@ export default function Page() {
{(message) => { + const [expanded, setExpanded] = createSignal(false) const title = createMemo(() => message.summary?.title) const prompt = createMemo(() => local.session.getMessageText(message)) const summary = createMemo(() => message.summary?.body) @@ -649,15 +652,12 @@ export default function Page() { if (!last) return false return !last.time.completed }) - const lastWithContent = createMemo(() => - assistantMessages().findLast((m) => { - const parts = sync.data.part[m.id] - return parts?.find((p) => p.type === "text" || p.type === "tool") - }), - ) return ( -
+
{/* Title */}

@@ -665,14 +665,19 @@ export default function Page() {

-
{prompt()}
+
{prompt()}
{/* Response */}
- +
-

Show steps

+

+ + Hide steps + Show steps + +

@@ -687,11 +692,63 @@ export default function Page() {
- - {(last) => { - const lastParts = createMemo(() => sync.data.part[last().id]) + + {(_) => { + const lastMessageWithText = createMemo(() => + assistantMessages().findLast((m) => { + const parts = sync.data.part[m.id] + return parts?.find((p) => p.type === "text") + }), + ) + const lastMessageWithReasoning = createMemo(() => + assistantMessages().findLast((m) => { + const parts = sync.data.part[m.id] + return parts?.find((p) => p.type === "reasoning") + }), + ) + const lastMessageWithTool = createMemo(() => + assistantMessages().findLast((m) => { + const parts = sync.data.part[m.id] + return parts?.find( + (p) => p.type === "tool" && p.state.status === "completed", + ) + }), + ) return ( - +
+ + + {(last) => { + const lastTextPart = createMemo(() => + sync.data.part[last().id].findLast((p) => p.type === "text"), + ) + return + }} + + + {(last) => { + const lastReasoningPart = createMemo(() => + sync.data.part[last().id].findLast( + (p) => p.type === "reasoning", + ), + ) + return ( + + ) + }} + + + + {(last) => { + const lastToolPart = createMemo(() => + sync.data.part[last().id].findLast( + (p) => p.type === "tool" && p.state.status === "completed", + ), + ) + return + }} + +
) }}
diff --git a/packages/ui/src/components/diff.css b/packages/ui/src/components/diff.css index c3484e20..c4e83187 100644 --- a/packages/ui/src/components/diff.css +++ b/packages/ui/src/components/diff.css @@ -18,6 +18,7 @@ [data-slot="diff-hunk-separator-content"] { position: sticky; background-color: var(--surface-diff-hidden-base); + color: var(--text-base); width: var(--pjs-column-content-width); left: var(--pjs-column-number-width); padding-left: 8px; diff --git a/packages/ui/src/styles/utilities.css b/packages/ui/src/styles/utilities.css index 85b90156..99b7760a 100644 --- a/packages/ui/src/styles/utilities.css +++ b/packages/ui/src/styles/utilities.css @@ -48,35 +48,6 @@ border-width: 0; } -.scroller { - --fade-height: 1.5rem; - - --mask-top: linear-gradient(to bottom, transparent, black var(--fade-height)); - --mask-bottom: linear-gradient(to top, transparent, black var(--fade-height)); - - mask-image: var(--mask-top), var(--mask-bottom); - mask-repeat: no-repeat; - - mask-size: 100% var(--fade-height); - - animation: adjust-masks linear; - animation-timeline: scroll(self); -} - -@keyframes adjust-masks { - from { - mask-position: - 0 calc(0% - var(--fade-height)), - 0 100%; - } - - to { - mask-position: - 0 0, - 0 calc(100% + var(--fade-height)); - } -} - .truncate-start { text-overflow: ellipsis; overflow: hidden;