diff options
| author | Adam <[email protected]> | 2025-10-29 16:04:30 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-29 16:04:34 -0500 |
| commit | 3210df74287f8c2afa4bb382d1da420c2826c2b7 (patch) | |
| tree | d762679b36178da5b59dca13616646f44866f976 /packages | |
| parent | cdeb82e9ca0213960202447896493c8d18cb867b (diff) | |
| download | opencode-3210df74287f8c2afa4bb382d1da420c2826c2b7.tar.gz opencode-3210df74287f8c2afa4bb382d1da420c2826c2b7.zip | |
wip: desktop work
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/desktop/src/pages/index.tsx | 122 | ||||
| -rw-r--r-- | packages/ui/src/styles/tailwind/index.css | 20 |
2 files changed, 114 insertions, 28 deletions
diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 341da2d95..5216c4272 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -36,6 +36,7 @@ import { ProgressCircle } from "@/components/progress-circle" import { Message, Part } from "@/components/message" import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" import { DiffChanges } from "@/components/diff-changes" +import { Markdown } from "@/components/markdown" export default function Page() { const local = useLocal() @@ -491,8 +492,8 @@ export default function Page() { </Show> </div> </div> - <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"> + <Tabs.Content value="chat" class="@container select-text flex flex-col flex-1 min-h-0 overflow-y-hidden"> + <div class="relative px-6 pt-12 max-w-2xl w-full mx-auto flex flex-col flex-1 min-h-0"> <Show when={local.session.active()} fallback={ @@ -519,9 +520,12 @@ export default function Page() { > {(activeSession) => ( <div class="pt-3 flex flex-col flex-1 min-h-0"> - <div class="flex items-start gap-8 flex-1 min-h-0"> + <div class="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"> + <ul + role="list" + class="absolute right-full mr-8 hidden w-60 shrink-0 @7xl:flex flex-col items-start gap-1" + > <For each={local.session.userMessages()}> {(message) => { const countLines = (text: string) => { @@ -648,16 +652,12 @@ 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 working = createMemo(() => !summary()) return ( <div data-message={message.id} - class="flex flex-col items-start self-stretch gap-8 min-h-[calc(100vh-15rem)]" + class="flex flex-col items-start self-stretch gap-8 min-h-screen" > {/* Title */} <div class="py-2 flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger"> @@ -675,7 +675,9 @@ export default function Page() { <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> + <Show when={summary()}> + <Markdown text={summary()!} /> + </Show> </div> <Accordion class="w-full" multiple> <For each={message.summary?.diffs || []}> @@ -729,14 +731,85 @@ export default function Page() { <div class="w-full"> <Switch> <Match when={working()}> - <div class="w-full flex flex-col-reverse items-start self-stretch gap-6 max-h-30 overflow-y-auto no-scrollbar pointer-events-none mask-alpha mask-y-from-66% mask-y-from-background-base mask-y-to-transparent"> - <For each={assistantMessages()?.toReversed()}> - {(assistantMessage) => { - const parts = createMemo(() => sync.data.part[assistantMessage.id]) - return <Message message={assistantMessage} parts={parts()} /> - }} - </For> - </div> + {(_) => { + const items = createMemo(() => + assistantMessages().flatMap((m) => sync.data.part[m.id]), + ) + const finishedItems = createMemo(() => + items().filter( + (p) => + (p?.type === "text" && p.time?.end) || + (p?.type === "reasoning" && p.time?.end) || + (p?.type === "tool" && p.state.status === "completed"), + ), + ) + + const MINIMUM_DELAY = 800 + const [visibleCount, setVisibleCount] = createSignal(1) + + createEffect(() => { + const total = finishedItems().length + if (total > visibleCount()) { + const timer = setTimeout(() => { + setVisibleCount((prev) => prev + 1) + }, MINIMUM_DELAY) + onCleanup(() => clearTimeout(timer)) + } else if (total < visibleCount()) { + setVisibleCount(total) + } + }) + + const translateY = createMemo(() => { + const total = visibleCount() + if (total < 2) return "0px" + return `-${(total - 2) * 48 - 8}px` + }) + + return ( + <div class="flex flex-col gap-3"> + <div + class="h-36 overflow-hidden pointer-events-none + mask-alpha mask-y-from-66% mask-y-from-background-base mask-y-to-transparent" + > + <div + class="w-full flex flex-col items-start self-stretch gap-2 py-10 + transform transition-transform duration-500 ease-[cubic-bezier(0.22,1,0.36,1)]" + style={{ transform: `translateY(${translateY()})` }} + > + <For each={finishedItems()}> + {(part) => { + const message = createMemo(() => + sync.data.message[part.sessionID].find( + (m) => m.id === part.messageID, + ), + ) + return ( + <div class="h-10 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> + ) + }} + </For> + </div> + </div> + </div> + ) + }} </Match> <Match when={!working()}> <Collapsible variant="ghost" open={expanded()} onOpenChange={setExpanded}> @@ -752,7 +825,7 @@ export default function Page() { </div> </Collapsible.Trigger> <Collapsible.Content> - <div class="w-full flex flex-col-reverse items-start self-stretch gap-8"> + <div class="w-full flex flex-col items-start self-stretch gap-8"> <For each={assistantMessages()}> {(assistantMessage) => { const parts = createMemo( @@ -807,14 +880,7 @@ export default function Page() { })()} </DragOverlay> </DragDropProvider> - <div - classList={{ - "absolute inset-x-0 px-6 max-w-[904px] flex flex-col justify-center items-center z-50 mx-auto": true, - "bottom-8": true, - // "bottom-8": !!local.session.active(), - // "bottom-1/2 translate-y-1/2": !local.session.active(), - }} - > + <div class="absolute inset-x-0 px-6 max-w-2xl flex flex-col justify-center items-center z-50 mx-auto bottom-8"> <PromptInput ref={(el) => { inputRef = el diff --git a/packages/ui/src/styles/tailwind/index.css b/packages/ui/src/styles/tailwind/index.css index e8e9641b4..76d8c7d3e 100644 --- a/packages/ui/src/styles/tailwind/index.css +++ b/packages/ui/src/styles/tailwind/index.css @@ -11,6 +11,26 @@ --spacing: 0.25rem; /* --spacing: var(--spacing); */ + --breakpoint-sm: 40rem; + --breakpoint-md: 48rem; + --breakpoint-lg: 64rem; + --breakpoint-xl: 80rem; + --breakpoint-2xl: 96rem; + + --container-3xs: 16rem; + --container-2xs: 18rem; + --container-xs: 20rem; + --container-sm: 24rem; + --container-md: 28rem; + --container-lg: 32rem; + --container-xl: 36rem; + --container-2xl: 42rem; + --container-3xl: 48rem; + --container-4xl: 56rem; + --container-5xl: 64rem; + --container-6xl: 72rem; + --container-7xl: 80rem; + --font-sans: var(--font-family-sans); --font-sans--font-feature-settings: var(--font-family-sans--font-feature-settings); --font-mono: var(--font-family-mono); |
