Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 0ccb26d

Browse files
committed
feat(desktop): sticky diff headers
1 parent 71fd596 commit 0ccb26d

3 files changed

Lines changed: 53 additions & 30 deletions

File tree

packages/desktop/src/pages/session.tsx

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import {
4444
useDragDropContext,
4545
} from "@thisbeyond/solid-dnd"
4646
import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd"
47-
import type { JSX } from "solid-js"
47+
import type { JSX, ParentProps } from "solid-js"
4848
import { useSync } from "@/context/sync"
4949
import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk"
5050
import { Markdown } from "@opencode-ai/ui"
@@ -477,7 +477,7 @@ export default function Page() {
477477
class="flex flex-col items-start self-stretch gap-8 pb-20"
478478
>
479479
{/* Title */}
480-
<div class="flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-10 pb-1">
480+
<div class="flex flex-col items-start gap-2 self-stretch sticky top-0 bg-background-stronger z-20 pb-1">
481481
<div class="w-full text-14-medium text-text-strong">
482482
<Show
483483
when={titled()}
@@ -524,7 +524,7 @@ export default function Page() {
524524
<For each={message.summary?.diffs ?? []}>
525525
{(diff) => (
526526
<Accordion.Item value={diff.file}>
527-
<Accordion.Header>
527+
<StickyAccordionHeader class="top-10 data-expanded:before:-top-10 ">
528528
<Accordion.Trigger>
529529
<div class="flex items-center justify-between w-full gap-5">
530530
<div class="grow flex items-center gap-5 min-w-0">
@@ -549,8 +549,8 @@ export default function Page() {
549549
</div>
550550
</div>
551551
</Accordion.Trigger>
552-
</Accordion.Header>
553-
<Accordion.Content class="max-h-[360px] overflow-y-auto no-scrollbar">
552+
</StickyAccordionHeader>
553+
<Accordion.Content class="max-h-60 overflow-y-auto no-scrollbar">
554554
<Diff
555555
before={{
556556
name: diff.file!,
@@ -682,7 +682,7 @@ export default function Page() {
682682
<For each={session.diffs()}>
683683
{(diff) => (
684684
<Accordion.Item value={diff.file} defaultOpen>
685-
<Accordion.Header>
685+
<StickyAccordionHeader>
686686
<Accordion.Trigger>
687687
<div class="flex items-center justify-between w-full gap-5">
688688
<div class="grow flex items-center gap-5 min-w-0">
@@ -702,7 +702,7 @@ export default function Page() {
702702
</div>
703703
</div>
704704
</Accordion.Trigger>
705-
</Accordion.Header>
705+
</StickyAccordionHeader>
706706
<Accordion.Content>
707707
<Diff
708708
before={{
@@ -725,19 +725,19 @@ export default function Page() {
725725
</div>
726726
</Tabs.Content>
727727
<Show when={local.layout.review.state() === "tab" && session.diffs().length}>
728-
<Tabs.Content value="review" class="select-text mt-8">
728+
<Tabs.Content value="review" class="select-text flex flex-col h-full overflow-hidden mt-8">
729729
<div
730730
classList={{
731-
"relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true,
731+
"relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0 overflow-hidden": true,
732732
}}
733733
>
734-
<div class="text-14-medium text-text-strong">All changes</div>
735-
<div class="h-full pb-40 overflow-y-auto no-scrollbar">
734+
<div class="text-14-medium text-text-strong shrink-0">All changes</div>
735+
<div class="flex-1 min-h-0 pb-40 overflow-y-auto no-scrollbar">
736736
<Accordion class="w-full" multiple>
737737
<For each={session.diffs()}>
738738
{(diff) => (
739739
<Accordion.Item value={diff.file} defaultOpen>
740-
<Accordion.Header>
740+
<StickyAccordionHeader>
741741
<Accordion.Trigger>
742742
<div class="flex items-center justify-between w-full gap-5">
743743
<div class="grow flex items-center gap-5 min-w-0">
@@ -755,7 +755,7 @@ export default function Page() {
755755
</div>
756756
</div>
757757
</Accordion.Trigger>
758-
</Accordion.Header>
758+
</StickyAccordionHeader>
759759
<Accordion.Content>
760760
<Diff
761761
diffStyle="split"
@@ -895,3 +895,18 @@ export default function Page() {
895895
</div>
896896
)
897897
}
898+
899+
function StickyAccordionHeader(props: ParentProps<{ class?: string }>) {
900+
return (
901+
<Accordion.Header
902+
classList={{
903+
"sticky top-0 data-expanded:z-10": true,
904+
"data-expanded:before:content-[''] data-expanded:before:z-[-10]": true,
905+
"data-expanded:before:absolute data-expanded:before:inset-0 data-expanded:before:bg-background-stronger": true,
906+
[props.class ?? ""]: !!props.class,
907+
}}
908+
>
909+
{props.children}
910+
</Accordion.Header>
911+
)
912+
}

packages/ui/src/components/accordion.css

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
align-items: flex-start;
1313
gap: 0px;
1414
align-self: stretch;
15-
border: 1px solid var(--border-weak-base);
16-
border-bottom: none;
17-
border-top: none;
1815
overflow: clip;
1916

2017
[data-slot="accordion-header"] {
@@ -36,7 +33,7 @@
3633
user-select: none;
3734

3835
background-color: var(--surface-base);
39-
border-bottom: 1px solid var(--border-weak-base);
36+
border: 1px solid var(--border-weak-base);
4037
overflow: clip;
4138
color: var(--text-strong);
4239
transition: background-color 0.15s ease;
@@ -62,11 +59,19 @@
6259
}
6360

6461
&[data-expanded] {
65-
border: 1px solid var(--border-weak-base);
66-
border-bottom: 1px solid var(--border-weak-base);
6762
margin-top: 8px;
6863
margin-bottom: 8px;
69-
border-radius: 8px;
64+
65+
[data-slot="accordion-trigger"] {
66+
border-radius: 8px 8px 0 0;
67+
}
68+
69+
[data-slot="accordion-content"] {
70+
border: 1px solid var(--border-weak-base);
71+
border-top: none;
72+
border-bottom-left-radius: 8px;
73+
border-bottom-right-radius: 8px;
74+
}
7075

7176
[data-slot="accordion-item"]:has(+ &) {
7277
&[data-closed] {
@@ -81,18 +86,23 @@
8186
}
8287

8388
& + [data-slot="accordion-item"] {
84-
border-top: 1px solid var(--border-weak-base);
85-
border-top-left-radius: 8px;
86-
border-top-right-radius: 8px;
8789
margin-top: 8px;
90+
91+
[data-slot="accordion-trigger"] {
92+
border-top-left-radius: 8px;
93+
border-top-right-radius: 8px;
94+
}
95+
}
96+
}
97+
98+
&[data-closed] + &[data-closed] {
99+
[data-slot="accordion-trigger"] {
100+
border-top: none;
88101
}
89102
}
90103

91104
&:first-child {
92105
margin-top: 0px;
93-
border-top: 1px solid var(--border-weak-base);
94-
border-top-left-radius: 8px;
95-
border-top-right-radius: 8px;
96106

97107
&[data-closed] {
98108
[data-slot="accordion-trigger"] {
@@ -104,8 +114,6 @@
104114

105115
&:last-child {
106116
margin-bottom: 0px;
107-
border-bottom-left-radius: 8px;
108-
border-bottom-right-radius: 8px;
109117

110118
&[data-closed] {
111119
[data-slot="accordion-trigger"] {

packages/ui/src/styles/theme.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
--background-weak: var(--smoke-light-3);
7878
--background-strong: var(--smoke-light-1);
7979
--background-stronger: #fcfcfc;
80-
--surface-base: var(--smoke-light-alpha-2);
80+
--surface-base: var(--smoke-light-3);
8181
--base: var(--smoke-light-alpha-2);
8282
--surface-base-hover: #0500000f;
8383
--surface-base-active: var(--smoke-light-alpha-3);
@@ -317,7 +317,7 @@
317317
--background-weak: #1b1818;
318318
--background-strong: #151313;
319319
--background-stronger: #191515;
320-
--surface-base: var(--smoke-dark-alpha-2);
320+
--surface-base: var(--smoke-dark-3);
321321
--base: var(--smoke-dark-alpha-2);
322322
--surface-base-hover: #e0b7b716;
323323
--surface-base-active: var(--smoke-dark-alpha-3);

0 commit comments

Comments
 (0)