mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-21 09:44:21 +01:00
wip: desktop work
This commit is contained in:
@@ -1,23 +1,47 @@
|
|||||||
import { For, Match, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
import { For, JSXElement, Match, Switch, createEffect, createMemo, createSignal, onCleanup } from "solid-js"
|
||||||
import { Part } from "@opencode-ai/ui"
|
import { Part } from "@opencode-ai/ui"
|
||||||
import { useSync } from "@/context/sync"
|
import { useSync } from "@/context/sync"
|
||||||
import type { AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
|
import type { AssistantMessage as AssistantMessageType, Part as PartType, ToolPart } from "@opencode-ai/sdk"
|
||||||
|
|
||||||
export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[] }) {
|
export function MessageProgress(props: { assistantMessages: () => AssistantMessageType[]; done?: boolean }) {
|
||||||
const sync = useSync()
|
const sync = useSync()
|
||||||
const items = createMemo(() => props.assistantMessages().flatMap((m) => sync.data.part[m.id]))
|
const parts = createMemo(() => props.assistantMessages().flatMap((m) => sync.data.part[m.id]))
|
||||||
|
const done = createMemo(() => props.done ?? false)
|
||||||
|
const currentTask = createMemo(
|
||||||
|
() =>
|
||||||
|
parts().findLast(
|
||||||
|
(p) =>
|
||||||
|
p &&
|
||||||
|
p.type === "tool" &&
|
||||||
|
p.tool === "task" &&
|
||||||
|
p.state &&
|
||||||
|
"metadata" in p.state &&
|
||||||
|
p.state.metadata &&
|
||||||
|
p.state.metadata.sessionId &&
|
||||||
|
p.state.status === "running",
|
||||||
|
) as ToolPart,
|
||||||
|
)
|
||||||
|
|
||||||
const finishedItems = createMemo(() => [
|
const eligibleItems = createMemo(() => {
|
||||||
"",
|
let allParts = parts()
|
||||||
"",
|
const task = currentTask()
|
||||||
"Loading...",
|
if (task && task.state && "metadata" in task.state && task.state.metadata?.sessionId) {
|
||||||
...items().filter(
|
const messages = sync.data.message[task.state.metadata.sessionId as string]?.filter((m) => m.role === "assistant")
|
||||||
|
allParts = messages?.flatMap((m) => sync.data.part[m.id]) ?? parts()
|
||||||
|
}
|
||||||
|
return allParts.filter(
|
||||||
(p) =>
|
(p) =>
|
||||||
p?.type === "text" ||
|
p?.type === "text" ||
|
||||||
(p?.type === "reasoning" && p.time?.end) ||
|
(p?.type === "reasoning" && p.time?.end) ||
|
||||||
(p?.type === "tool" && p.state.status === "completed"),
|
(p?.type === "tool" && p.state.status === "completed"),
|
||||||
),
|
)
|
||||||
|
})
|
||||||
|
const finishedItems = createMemo<(JSXElement | PartType)[]>(() => [
|
||||||
"",
|
"",
|
||||||
|
"",
|
||||||
|
<div class="text-text-diff-add-base">Loading...</div>,
|
||||||
|
...eligibleItems(),
|
||||||
|
...(done() ? ["", "", ""] : []),
|
||||||
])
|
])
|
||||||
|
|
||||||
const MINIMUM_DELAY = 400
|
const MINIMUM_DELAY = 400
|
||||||
@@ -54,26 +78,28 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa
|
|||||||
>
|
>
|
||||||
<For each={finishedItems()}>
|
<For each={finishedItems()}>
|
||||||
{(part) => {
|
{(part) => {
|
||||||
if (typeof part === "string") return <div class="h-8 flex items-center w-full">{part}</div>
|
if (part && typeof part === "object" && "type" in part) {
|
||||||
const message = createMemo(() => sync.data.message[part.sessionID].find((m) => m.id === part.messageID))
|
const message = createMemo(() => sync.data.message[part.sessionID].find((m) => m.id === part.messageID))
|
||||||
return (
|
return (
|
||||||
<div class="h-8 flex items-center w-full">
|
<div class="h-8 flex items-center w-full">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={part.type === "text" && part}>
|
<Match when={part.type === "text" && part}>
|
||||||
{(p) => (
|
{(p) => (
|
||||||
<div
|
<div
|
||||||
textContent={p().text}
|
textContent={p().text}
|
||||||
class="text-12-regular text-text-base whitespace-nowrap truncate w-full"
|
class="text-12-regular text-text-base whitespace-nowrap truncate w-full"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={part.type === "reasoning" && part}>
|
<Match when={part.type === "reasoning" && part}>
|
||||||
{(p) => <Part message={message()!} part={p()} />}
|
{(p) => <Part message={message()!} part={p()} />}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={part.type === "tool" && part}>{(p) => <Part message={message()!} part={p()} />}</Match>
|
<Match when={part.type === "tool" && part}>{(p) => <Part message={message()!} part={p()} />}</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
return <div class="h-8 flex items-center w-full">{part}</div>
|
||||||
}}
|
}}
|
||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -583,7 +583,8 @@ export default function Page() {
|
|||||||
<For each={local.session.userMessages()}>
|
<For each={local.session.userMessages()}>
|
||||||
{(message) => {
|
{(message) => {
|
||||||
const isActive = createMemo(() => local.session.activeMessage()?.id === message.id)
|
const isActive = createMemo(() => local.session.activeMessage()?.id === message.id)
|
||||||
const [initialized, setInitialized] = createSignal(!!message.summary?.title)
|
const [titled, setTitled] = createSignal(!!message.summary?.title)
|
||||||
|
const [completed, setCompleted] = createSignal(!!message.summary?.body)
|
||||||
const [expanded, setExpanded] = createSignal(false)
|
const [expanded, setExpanded] = createSignal(false)
|
||||||
const parts = createMemo(() => sync.data.part[message.id])
|
const parts = createMemo(() => sync.data.part[message.id])
|
||||||
const title = createMemo(() => message.summary?.title)
|
const title = createMemo(() => message.summary?.title)
|
||||||
@@ -597,11 +598,16 @@ export default function Page() {
|
|||||||
const hasToolPart = createMemo(() =>
|
const hasToolPart = createMemo(() =>
|
||||||
assistantMessages()
|
assistantMessages()
|
||||||
?.flatMap((m) => sync.data.part[m.id])
|
?.flatMap((m) => sync.data.part[m.id])
|
||||||
.some((p) => p.type === "tool"),
|
.some((p) => p?.type === "tool"),
|
||||||
)
|
)
|
||||||
const working = createMemo(() => !summary())
|
const working = createMemo(() => !summary())
|
||||||
|
|
||||||
|
// allowing time for the animations to finish
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
setTimeout(() => setInitialized(!!title()), 10_000)
|
setTimeout(() => setTitled(!!title()), 10_000)
|
||||||
|
})
|
||||||
|
createEffect(() => {
|
||||||
|
setTimeout(() => setCompleted(!!summary()), 3_000)
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -613,7 +619,7 @@ export default function Page() {
|
|||||||
{/* Title */}
|
{/* Title */}
|
||||||
<div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10">
|
<div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10">
|
||||||
<div class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
|
<div class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
|
||||||
<Show when={initialized()} fallback={<Typewriter as="h1" text={title()} />}>
|
<Show when={titled()} fallback={<Typewriter as="h1" text={title()} />}>
|
||||||
<h1>{title()}</h1>
|
<h1>{title()}</h1>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
@@ -684,10 +690,10 @@ export default function Page() {
|
|||||||
{/* Response */}
|
{/* Response */}
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={working()}>
|
<Match when={!completed()}>
|
||||||
<MessageProgress assistantMessages={assistantMessages} />
|
<MessageProgress assistantMessages={assistantMessages} done={!working()} />
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={!working() && hasToolPart()}>
|
<Match when={completed() && hasToolPart()}>
|
||||||
<Collapsible variant="ghost" open={expanded()} onOpenChange={setExpanded}>
|
<Collapsible variant="ghost" open={expanded()} onOpenChange={setExpanded}>
|
||||||
<Collapsible.Trigger class="text-text-weak hover:text-text-strong">
|
<Collapsible.Trigger class="text-text-weak hover:text-text-strong">
|
||||||
<div class="flex items-center gap-1 self-stretch">
|
<div class="flex items-center gap-1 self-stretch">
|
||||||
|
|||||||
Reference in New Issue
Block a user