summaryrefslogtreecommitdiffhomepage
path: root/packages
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-11-03 06:50:38 -0600
committerAdam <[email protected]>2025-11-03 08:29:13 -0600
commit7a32fec008fdfb5bc034ba3ce1bca365c1d342f5 (patch)
treea9d377796a187df98118d18e7c71fd78eab47a85 /packages
parent37a6b5177e793d8ea446f75cc691d4dffc3b39fd (diff)
downloadopencode-7a32fec008fdfb5bc034ba3ce1bca365c1d342f5.tar.gz
opencode-7a32fec008fdfb5bc034ba3ce1bca365c1d342f5.zip
wip: desktop work
Diffstat (limited to 'packages')
-rw-r--r--packages/desktop/src/components/message-progress.tsx172
-rw-r--r--packages/desktop/src/pages/index.tsx78
2 files changed, 119 insertions, 131 deletions
diff --git a/packages/desktop/src/components/message-progress.tsx b/packages/desktop/src/components/message-progress.tsx
index 5533ae413..4633a953a 100644
--- a/packages/desktop/src/components/message-progress.tsx
+++ b/packages/desktop/src/components/message-progress.tsx
@@ -1,7 +1,7 @@
import { For, JSXElement, Match, Show, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
-import { Markdown, Part } from "@opencode-ai/ui"
+import { Part } from "@opencode-ai/ui"
import { useSync } from "@/context/sync"
-import type { AssistantMessage as AssistantMessageType, Part as PartType, ToolPart } from "@opencode-ai/sdk"
+import type { AssistantMessage as AssistantMessageType, ToolPart } from "@opencode-ai/sdk"
import { Spinner } from "./spinner"
export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) {
@@ -22,7 +22,6 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
p.state.status === "running",
) as ToolPart,
)
-
const resolvedParts = createMemo(() => {
let resolved = parts()
const task = currentTask()
@@ -32,20 +31,18 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
}
return resolved
})
- const currentText = createMemo(
- () =>
- resolvedParts().findLast((p) => p?.type === "text")?.text ||
- resolvedParts().findLast((p) => p?.type === "reasoning")?.text,
- )
+ // const currentText = createMemo(
+ // () =>
+ // resolvedParts().findLast((p) => p?.type === "text")?.text ||
+ // resolvedParts().findLast((p) => p?.type === "reasoning")?.text,
+ // )
const eligibleItems = createMemo(() => {
- return resolvedParts().filter((p) => p?.type === "tool" && p.state.status === "completed")
+ return resolvedParts().filter((p) => p?.type === "tool" && p.state.status === "completed") as ToolPart[]
})
- const finishedItems = createMemo<(JSXElement | PartType)[]>(() => [
+ const finishedItems = createMemo<(JSXElement | ToolPart)[]>(() => [
+ <div class="h-8 w-full" />,
<div class="h-8 w-full" />,
<div class="h-8 w-full" />,
- <div class="flex items-center gap-x-5 pl-3 text-text-base">
- <Spinner /> <span class="text-12-medium">Thinking...</span>
- </div>,
...eligibleItems(),
...(done() ? [<div class="h-8 w-full" />, <div class="h-8 w-full" />, <div class="h-8 w-full" />] : []),
])
@@ -71,57 +68,120 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
return `-${(total - 2) * 40 - 8}px`
})
+ const lastPart = createMemo(() => resolvedParts().slice(-1)?.at(0))
+ const rawStatus = createMemo(() => {
+ const defaultStatus = "Working..."
+ const last = lastPart()
+ if (!last) return defaultStatus
+
+ if (last.type === "tool") {
+ switch (last.tool) {
+ case "task":
+ return "Delegating work..."
+ case "todowrite":
+ case "todoread":
+ return "Planning next steps..."
+ case "read":
+ return "Gathering context..."
+ case "list":
+ case "grep":
+ case "glob":
+ return "Searching the codebase..."
+ case "webfetch":
+ return "Searching the web..."
+ case "edit":
+ case "write":
+ return "Making edits..."
+ case "bash":
+ return "Running commands..."
+ default:
+ break
+ }
+ } else if (last.type === "reasoning") {
+ return "Thinking..."
+ } else if (last.type === "text") {
+ return "Gathering thoughts..."
+ }
+ return defaultStatus
+ })
+
+ const [status, setStatus] = createSignal(rawStatus())
+ let lastStatusChange = Date.now()
+ let statusTimeout: number | undefined
+
+ createEffect(() => {
+ const newStatus = rawStatus()
+ if (newStatus === status()) return
+
+ const timeSinceLastChange = Date.now() - lastStatusChange
+
+ if (timeSinceLastChange >= 1000) {
+ setStatus(newStatus)
+ lastStatusChange = Date.now()
+ if (statusTimeout) {
+ clearTimeout(statusTimeout)
+ statusTimeout = undefined
+ }
+ } else {
+ if (statusTimeout) clearTimeout(statusTimeout)
+ statusTimeout = setTimeout(() => {
+ setStatus(rawStatus())
+ lastStatusChange = Date.now()
+ statusTimeout = undefined
+ }, 1000 - timeSinceLastChange) as unknown as number
+ }
+ })
+
return (
<div class="flex flex-col gap-3">
- <div
- class="h-30 overflow-hidden pointer-events-none pb-1
+ {/* <Show when={currentText()}> */}
+ {/* {(text) => ( */}
+ {/* <div */}
+ {/* class="h-20 flex flex-col justify-end overflow-hidden py-3 */}
+ {/* mask-alpha mask-t-from-80% mask-t-from-background-base mask-t-to-transparent" */}
+ {/* > */}
+ {/* <Markdown text={text()} class="w-full shrink-0 overflow-visible" /> */}
+ {/* </div> */}
+ {/* )} */}
+ {/* </Show> */}
+ <div class="flex items-center gap-x-5 pl-3 border border-transparent text-text-base">
+ <Spinner /> <span class="text-12-medium">{status()}</span>
+ </div>
+ <Show when={eligibleItems().length > 0}>
+ <div
+ class="h-30 overflow-hidden pointer-events-none pb-1
mask-alpha mask-t-from-33% mask-t-from-background-base mask-t-to-transparent
mask-b-from-95% mask-b-from-background-base mask-b-to-transparent"
- >
- <div
- class="w-full flex flex-col items-start self-stretch gap-2 py-8
- transform transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]"
- style={{ transform: `translateY(${translateY()})` }}
>
- <For each={finishedItems()}>
- {(part) => {
- if (part && typeof part === "object" && "type" in part) {
- const message = createMemo(() => sync.data.message[part.sessionID].find((m) => m.id === part.messageID))
- return (
- <div class="h-8 flex items-center w-full">
- <Switch>
- <Match when={part.type === "text" && part}>
- {(p) => (
- <div
- textContent={p().text}
- class="text-12-regular text-text-base whitespace-nowrap truncate w-full"
- />
- )}
- </Match>
- <Match when={part.type === "reasoning" && part}>
- {(p) => <Part message={message()!} part={p()} />}
- </Match>
- <Match when={part.type === "tool" && part}>
- {(p) => <Part message={message()!} part={p()} />}
- </Match>
- </Switch>
- </div>
- )
- }
- return <div class="h-8 flex items-center w-full">{part}</div>
- }}
- </For>
- </div>
- </div>
- <Show when={currentText()}>
- {(text) => (
<div
- class="max-h-36 flex flex-col justify-end overflow-hidden py-3
- mask-alpha mask-t-from-80% mask-t-from-background-base mask-t-to-transparent"
+ class="w-full flex flex-col items-start self-stretch gap-2 py-8
+ transform transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]"
+ style={{ transform: `translateY(${translateY()})` }}
>
- <Markdown text={text()} class="w-full shrink-0 overflow-visible" />
+ <For each={finishedItems()}>
+ {(part) => (
+ <Switch>
+ <Match when={part && typeof part === "object" && "type" in part && part}>
+ {(p) => {
+ const part = p() as ToolPart
+ const message = createMemo(() =>
+ sync.data.message[part.sessionID].find((m) => m.id === part.messageID),
+ )
+ return (
+ <div class="h-8 flex items-center w-full">
+ <Part message={message()!} part={part} />
+ </div>
+ )
+ }}
+ </Match>
+ <Match when={true}>
+ <div class="h-8 flex items-center w-full">{part as JSXElement}</div>
+ </Match>
+ </Switch>
+ )}
+ </For>
</div>
- )}
+ </div>
</Show>
</div>
)
diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx
index badd0009d..eebfc4362 100644
--- a/packages/desktop/src/pages/index.tsx
+++ b/packages/desktop/src/pages/index.tsx
@@ -548,77 +548,6 @@ export default function Page() {
{(message) => {
const diffs = createMemo(() => message.summary?.diffs ?? [])
const working = createMemo(() => !message.summary?.body)
- const assistantMessages = createMemo(() => {
- return sync.data.message[activeSession().id]?.filter(
- (m) => m.role === "assistant" && m.parentID == message.id,
- ) as AssistantMessageType[]
- })
- const parts = createMemo(() =>
- assistantMessages().flatMap((m) => sync.data.part[m.id]),
- )
- const lastPart = createMemo(() => parts().slice(-1)?.at(0))
- const rawStatus = createMemo(() => {
- const defaultStatus = "Working..."
- const last = lastPart()
- if (!last) return defaultStatus
-
- if (last.type === "tool") {
- switch (last.tool) {
- case "task":
- return "Delegating work..."
- case "todowrite":
- case "todoread":
- return "Planning next steps..."
- case "read":
- return "Gathering context..."
- case "list":
- case "grep":
- case "glob":
- return "Searching the codebase..."
- case "webfetch":
- return "Searching the web..."
- case "edit":
- case "write":
- return "Making edits..."
- case "bash":
- return "Running commands..."
- default:
- break
- }
- } else if (last.type === "reasoning") {
- return "Thinking..."
- } else if (last.type === "text") {
- return "Gathering thoughts..."
- }
- return defaultStatus
- })
-
- const [status, setStatus] = createSignal(rawStatus())
- let lastStatusChange = Date.now()
- let statusTimeout: number | undefined
-
- createEffect(() => {
- const newStatus = rawStatus()
- if (newStatus === status()) return
-
- const timeSinceLastChange = Date.now() - lastStatusChange
-
- if (timeSinceLastChange >= 1000) {
- setStatus(newStatus)
- lastStatusChange = Date.now()
- if (statusTimeout) {
- clearTimeout(statusTimeout)
- statusTimeout = undefined
- }
- } else {
- if (statusTimeout) clearTimeout(statusTimeout)
- statusTimeout = setTimeout(() => {
- setStatus(rawStatus())
- lastStatusChange = Date.now()
- statusTimeout = undefined
- }, 1000 - timeSinceLastChange) as unknown as number
- }
- })
return (
<li class="group/li flex items-center self-stretch">
@@ -641,10 +570,9 @@ export default function Page() {
"text-text-weak data-[active=true]:text-text-strong group-hover/li:text-text-base": true,
}}
>
- <Switch>
- <Match when={working()}>{status()}</Match>
- <Match when={true}>{message.summary?.title}</Match>
- </Switch>
+ <Show when={message.summary?.title} fallback="New message">
+ {message.summary?.title}
+ </Show>
</div>
</button>
</li>