diff options
| author | Adam <[email protected]> | 2025-11-12 07:03:35 -0600 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-11-12 07:03:39 -0600 |
| commit | 0ccb26df94ed83c5a6492068a7bbe73445e54f0c (patch) | |
| tree | a8889c4d9764776eaaa3deae4317c8e10b194064 | |
| parent | 71fd5966add098c92949da26a07346412ad9b804 (diff) | |
| download | opencode-0ccb26df94ed83c5a6492068a7bbe73445e54f0c.tar.gz opencode-0ccb26df94ed83c5a6492068a7bbe73445e54f0c.zip | |
feat(desktop): sticky diff headers
| -rw-r--r-- | packages/desktop/src/pages/session.tsx | 41 | ||||
| -rw-r--r-- | packages/ui/src/components/accordion.css | 38 | ||||
| -rw-r--r-- | packages/ui/src/styles/theme.css | 4 |
3 files changed, 53 insertions, 30 deletions
diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 9c639919f..f16729cc2 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -44,7 +44,7 @@ import { useDragDropContext, } from "@thisbeyond/solid-dnd" import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" -import type { JSX } from "solid-js" +import type { JSX, ParentProps } from "solid-js" import { useSync } from "@/context/sync" import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" import { Markdown } from "@opencode-ai/ui" @@ -477,7 +477,7 @@ export default function Page() { class="flex flex-col items-start self-stretch gap-8 pb-20" > {/* Title */} - <div class="flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10 pb-1"> + <div class="flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-20 pb-1"> <div class="w-full text-14-medium text-text-strong"> <Show when={titled()} @@ -524,7 +524,7 @@ export default function Page() { <For each={message.summary?.diffs ?? []}> {(diff) => ( <Accordion.Item value={diff.file}> - <Accordion.Header> + <StickyAccordionHeader class="top-10 data-expanded:before:-top-10 "> <Accordion.Trigger> <div class="flex items-center justify-between w-full gap-5"> <div class="grow flex items-center gap-5 min-w-0"> @@ -549,8 +549,8 @@ export default function Page() { </div> </div> </Accordion.Trigger> - </Accordion.Header> - <Accordion.Content class="max-h-[360px] overflow-y-auto no-scrollbar"> + </StickyAccordionHeader> + <Accordion.Content class="max-h-60 overflow-y-auto no-scrollbar"> <Diff before={{ name: diff.file!, @@ -682,7 +682,7 @@ export default function Page() { <For each={session.diffs()}> {(diff) => ( <Accordion.Item value={diff.file} defaultOpen> - <Accordion.Header> + <StickyAccordionHeader> <Accordion.Trigger> <div class="flex items-center justify-between w-full gap-5"> <div class="grow flex items-center gap-5 min-w-0"> @@ -702,7 +702,7 @@ export default function Page() { </div> </div> </Accordion.Trigger> - </Accordion.Header> + </StickyAccordionHeader> <Accordion.Content> <Diff before={{ @@ -725,19 +725,19 @@ export default function Page() { </div> </Tabs.Content> <Show when={local.layout.review.state() === "tab" && session.diffs().length}> - <Tabs.Content value="review" class="select-text mt-8"> + <Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden mt-8"> <div classList={{ - "relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true, + "relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0 overflow-hidden": true, }} > - <div class="text-14-medium text-text-strong">All changes</div> - <div class="h-full pb-40 overflow-y-auto no-scrollbar"> + <div class="text-14-medium text-text-strong shrink-0">All changes</div> + <div class="flex-1 min-h-0 pb-40 overflow-y-auto no-scrollbar"> <Accordion class="w-full" multiple> <For each={session.diffs()}> {(diff) => ( <Accordion.Item value={diff.file} defaultOpen> - <Accordion.Header> + <StickyAccordionHeader> <Accordion.Trigger> <div class="flex items-center justify-between w-full gap-5"> <div class="grow flex items-center gap-5 min-w-0"> @@ -755,7 +755,7 @@ export default function Page() { </div> </div> </Accordion.Trigger> - </Accordion.Header> + </StickyAccordionHeader> <Accordion.Content> <Diff diffStyle="split" @@ -895,3 +895,18 @@ export default function Page() { </div> ) } + +function StickyAccordionHeader(props: ParentProps<{ class?: string }>) { + return ( + <Accordion.Header + classList={{ + "sticky top-0 data-expanded:z-10": true, + "data-expanded:before:content-[''] data-expanded:before:z-[-10]": true, + "data-expanded:before:absolute data-expanded:before:inset-0 data-expanded:before:bg-background-stronger": true, + [props.class ?? ""]: !!props.class, + }} + > + {props.children} + </Accordion.Header> + ) +} diff --git a/packages/ui/src/components/accordion.css b/packages/ui/src/components/accordion.css index 5524838bc..f949dc948 100644 --- a/packages/ui/src/components/accordion.css +++ b/packages/ui/src/components/accordion.css @@ -12,9 +12,6 @@ align-items: flex-start; gap: 0px; align-self: stretch; - border: 1px solid var(--border-weak-base); - border-bottom: none; - border-top: none; overflow: clip; [data-slot="accordion-header"] { @@ -36,7 +33,7 @@ user-select: none; background-color: var(--surface-base); - border-bottom: 1px solid var(--border-weak-base); + border: 1px solid var(--border-weak-base); overflow: clip; color: var(--text-strong); transition: background-color 0.15s ease; @@ -62,11 +59,19 @@ } &[data-expanded] { - border: 1px solid var(--border-weak-base); - border-bottom: 1px solid var(--border-weak-base); margin-top: 8px; margin-bottom: 8px; - border-radius: 8px; + + [data-slot="accordion-trigger"] { + border-radius: 8px 8px 0 0; + } + + [data-slot="accordion-content"] { + border: 1px solid var(--border-weak-base); + border-top: none; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + } [data-slot="accordion-item"]:has(+ &) { &[data-closed] { @@ -81,18 +86,23 @@ } & + [data-slot="accordion-item"] { - border-top: 1px solid var(--border-weak-base); - border-top-left-radius: 8px; - border-top-right-radius: 8px; margin-top: 8px; + + [data-slot="accordion-trigger"] { + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + } + } + + &[data-closed] + &[data-closed] { + [data-slot="accordion-trigger"] { + border-top: none; } } &:first-child { margin-top: 0px; - border-top: 1px solid var(--border-weak-base); - border-top-left-radius: 8px; - border-top-right-radius: 8px; &[data-closed] { [data-slot="accordion-trigger"] { @@ -104,8 +114,6 @@ &:last-child { margin-bottom: 0px; - border-bottom-left-radius: 8px; - border-bottom-right-radius: 8px; &[data-closed] { [data-slot="accordion-trigger"] { diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index f6511e312..05164092e 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -77,7 +77,7 @@ --background-weak: var(--smoke-light-3); --background-strong: var(--smoke-light-1); --background-stronger: #fcfcfc; - --surface-base: var(--smoke-light-alpha-2); + --surface-base: var(--smoke-light-3); --base: var(--smoke-light-alpha-2); --surface-base-hover: #0500000f; --surface-base-active: var(--smoke-light-alpha-3); @@ -317,7 +317,7 @@ --background-weak: #1b1818; --background-strong: #151313; --background-stronger: #191515; - --surface-base: var(--smoke-dark-alpha-2); + --surface-base: var(--smoke-dark-3); --base: var(--smoke-dark-alpha-2); --surface-base-hover: #e0b7b716; --surface-base-active: var(--smoke-dark-alpha-3); |
