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;