summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/src/pages
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-10-28 15:29:11 -0500
committerAdam <[email protected]>2025-10-28 15:29:16 -0500
commit545f34584847e3216ecf95ab493dacac282ec465 (patch)
tree9bf9c1e8cd2d49ee63a155e4b4131b4e1960fc0f /packages/desktop/src/pages
parent77ae0b527e4b1a8277b0391d0acbc7d82b08e4ea (diff)
downloadopencode-545f34584847e3216ecf95ab493dacac282ec465.tar.gz
opencode-545f34584847e3216ecf95ab493dacac282ec465.zip
wip: desktop work
Diffstat (limited to 'packages/desktop/src/pages')
-rw-r--r--packages/desktop/src/pages/index.tsx136
1 files changed, 68 insertions, 68 deletions
diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx
index b4bd36162..c1884b2c0 100644
--- a/packages/desktop/src/pages/index.tsx
+++ b/packages/desktop/src/pages/index.tsx
@@ -1,8 +1,19 @@
-import { Button, List, SelectDialog, Tooltip, IconButton, Tabs, Icon, Accordion, Diff } from "@opencode-ai/ui"
+import {
+ Button,
+ List,
+ SelectDialog,
+ Tooltip,
+ IconButton,
+ Tabs,
+ Icon,
+ Accordion,
+ Diff,
+ Collapsible,
+} from "@opencode-ai/ui"
import { FileIcon } from "@/ui"
import FileTree from "@/components/file-tree"
import { For, onCleanup, onMount, Show, Match, Switch, createSignal, createEffect, createMemo } from "solid-js"
-import { useLocal, type LocalFile, type TextSelection } from "@/context/local"
+import { useLocal, type LocalFile } from "@/context/local"
import { createStore } from "solid-js/store"
import { getDirectory, getFilename } from "@/utils"
import { ContentPart, PromptInput } from "@/components/prompt-input"
@@ -185,42 +196,10 @@ export default function Page() {
}
if (!session) return
- interface SubmissionAttachment {
- path: string
- selection?: TextSelection
- label: string
- }
-
- const createAttachmentKey = (path: string, selection?: TextSelection) => {
- if (!selection) return path
- return `${path}:${selection.startLine}:${selection.startChar}:${selection.endLine}:${selection.endChar}`
- }
-
- const formatAttachmentLabel = (path: string, selection?: TextSelection) => {
- if (!selection) return getFilename(path)
- return `${getFilename(path)} (${selection.startLine}-${selection.endLine})`
- }
-
const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path))
const text = parts.map((part) => part.content).join("")
- const attachments = new Map<string, SubmissionAttachment>()
-
- const registerAttachment = (path: string, selection: TextSelection | undefined, label?: string) => {
- if (!path) return
- const key = createAttachmentKey(path, selection)
- if (attachments.has(key)) return
- attachments.set(key, {
- path,
- selection,
- label: label ?? formatAttachmentLabel(path, selection),
- })
- }
-
- const promptAttachments = parts.filter((part) => part.type === "file")
- for (const part of promptAttachments) {
- registerAttachment(part.path, part.selection, part.content)
- }
+ const attachments = parts.filter((part) => part.type === "file")
// const activeFile = local.context.active()
// if (activeFile) {
@@ -239,7 +218,7 @@ export default function Page() {
// )
// }
- const attachmentParts = Array.from(attachments.values()).map((attachment) => {
+ const attachmentParts = attachments.map((attachment) => {
const absolute = toAbsolutePath(attachment.path)
const query = attachment.selection
? `?start=${attachment.selection.startLine}&end=${attachment.selection.endLine}`
@@ -252,9 +231,9 @@ export default function Page() {
source: {
type: "file" as const,
text: {
- value: `@${attachment.label}`,
- start: 0,
- end: 0,
+ value: attachment.content,
+ start: attachment.start,
+ end: attachment.end,
},
path: absolute,
},
@@ -510,8 +489,8 @@ export default function Page() {
</Show>
</div>
</div>
- <Tabs.Content value="chat" class="select-text flex flex-col flex-1 min-h-0">
- <div class="p-6 pt-12 max-w-[904px] w-full mx-auto flex flex-col flex-1 min-h-0">
+ <Tabs.Content value="chat" class="select-text flex flex-col flex-1 min-h-0 overflow-y-hidden">
+ <div class="px-6 pt-12 max-w-[904px] w-full mx-auto flex flex-col flex-1 min-h-0">
<Show
when={local.session.active()}
fallback={
@@ -537,7 +516,7 @@ export default function Page() {
}
>
{(activeSession) => (
- <div class="py-3 flex flex-col flex-1 min-h-0">
+ <div class="pt-3 flex flex-col flex-1 min-h-0">
<div class="flex items-start gap-8 flex-1 min-h-0">
<Show when={local.session.userMessages().length > 1}>
<ul role="list" class="w-60 shrink-0 flex flex-col items-start gap-1">
@@ -665,30 +644,65 @@ export default function Page() {
(m) => m.role === "assistant" && m.parentID == message.id,
) as AssistantMessageType[]
})
+ const working = createMemo(() => {
+ const last = assistantMessages()[assistantMessages().length - 1]
+ 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 (
- <div
- data-message={message.id}
- class="flex flex-col items-start self-stretch gap-14 pt-1.5"
- >
+ <div data-message={message.id} class="flex flex-col items-start self-stretch gap-8">
{/* Title */}
- <div class="flex flex-col items-start gap-2 self-stretch">
+ <div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger">
<h1 class="text-14-medium text-text-strong overflow-hidden text-ellipsis min-w-0">
{title() ?? prompt()}
</h1>
- <Show when={title}>
- <div class="text-12-regular text-text-base">{prompt()}</div>
+ </div>
+ <Show when={title}>
+ <div class="-mt-5 text-12-regular text-text-base line-clamp-3">{prompt()}</div>
+ </Show>
+ {/* Response */}
+ <div class="w-full flex flex-col gap-2">
+ <Collapsible variant="ghost">
+ <Collapsible.Trigger class="text-text-weak hover:text-text-strong">
+ <div class="flex items-center gap-1 self-stretch">
+ <h2 class="text-12-medium">Show steps</h2>
+ <Collapsible.Arrow />
+ </div>
+ </Collapsible.Trigger>
+ <Collapsible.Content>
+ <div class="w-full flex flex-col items-start self-stretch gap-8">
+ <For each={assistantMessages()}>
+ {(assistantMessage) => {
+ const parts = createMemo(() => sync.data.part[assistantMessage.id])
+ return <AssistantMessage message={assistantMessage} parts={parts()} />
+ }}
+ </For>
+ </div>
+ </Collapsible.Content>
+ </Collapsible>
+ <Show when={working() && lastWithContent()}>
+ {(last) => {
+ const lastParts = createMemo(() => sync.data.part[last().id])
+ return (
+ <AssistantMessage lastToolOnly message={last()} parts={lastParts()} />
+ )
+ }}
</Show>
</div>
{/* Summary */}
- <div class="w-full flex flex-col gap-6 items-start self-stretch">
- <Show when={summary}>
+ <Show when={!working()}>
+ <div class="w-full flex flex-col gap-6 items-start self-stretch">
<div class="flex flex-col items-start gap-1 self-stretch">
<h2 class="text-12-medium text-text-weak">Summary</h2>
<div class="text-14-regular text-text-base self-stretch">{summary()}</div>
</div>
- </Show>
- <Show when={message.summary?.diffs.length}>
<Accordion class="w-full" multiple>
<For each={message.summary?.diffs || []}>
{(diff) => (
@@ -735,22 +749,8 @@ export default function Page() {
)}
</For>
</Accordion>
- </Show>
- </div>
- {/* Response */}
- <div data-todo="Response" class="w-full">
- <div class="flex flex-col items-start gap-1 self-stretch">
- <h2 class="text-12-medium text-text-weak">Response</h2>
</div>
- <div class="w-full flex flex-col items-start self-stretch gap-8">
- <For each={assistantMessages()}>
- {(assistantMessage) => {
- const parts = createMemo(() => sync.data.part[assistantMessage.id])
- return <AssistantMessage message={assistantMessage} parts={parts()} />
- }}
- </For>
- </div>
- </div>
+ </Show>
</div>
)
}}