Skip to content

Commit 73c9b68

Browse files
committed
fix(app): all panels transition
1 parent 99d8aab commit 73c9b68

5 files changed

Lines changed: 450 additions & 255 deletions

File tree

packages/app/src/pages/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2252,7 +2252,7 @@ export default function Layout(props: ParentProps) {
22522252
>
22532253
<main
22542254
classList={{
2255-
"size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base xl:border-l xl:rounded-tl-[12px]": true,
2255+
"size-full overflow-x-hidden flex flex-col items-start contain-strict border-t border-border-weak-base bg-background-base xl:border-l xl:rounded-tl-[12px]": true,
22562256
}}
22572257
>
22582258
<Show when={!autoselecting()} fallback={<div class="size-full" />}>

packages/app/src/pages/session.tsx

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import { usePrompt } from "@/context/prompt"
3333
import { useSDK } from "@/context/sdk"
3434
import { useSync } from "@/context/sync"
3535
import { createSessionComposerState, SessionComposerRegion } from "@/pages/session/composer"
36-
import { createOpenReviewFile } from "@/pages/session/helpers"
36+
import { createOpenReviewFile, createSizing } from "@/pages/session/helpers"
3737
import { MessageTimeline } from "@/pages/session/message-timeline"
3838
import { type DiffStyle, SessionReviewTab, type SessionReviewTabProps } from "@/pages/session/review-tab"
3939
import { createScrollSpy } from "@/pages/session/scroll-spy"
@@ -332,6 +332,7 @@ export default function Page() {
332332
)
333333

334334
const isDesktop = createMediaQuery("(min-width: 768px)")
335+
const size = createSizing()
335336
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
336337
const desktopFileTreeOpen = createMemo(() => isDesktop() && layout.fileTree.opened())
337338
const desktopSidePanelOpen = createMemo(() => desktopReviewOpen() || desktopFileTreeOpen())
@@ -1252,9 +1253,9 @@ export default function Page() {
12521253
{/* Session panel */}
12531254
<div
12541255
classList={{
1255-
"@container relative shrink-0 flex flex-col min-h-0 h-full bg-background-stronger": true,
1256-
"flex-1": true,
1257-
"md:flex-none": desktopSidePanelOpen(),
1256+
"@container relative shrink-0 flex flex-col min-h-0 h-full bg-background-stronger flex-1 md:flex-none": true,
1257+
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
1258+
!size.active(),
12581259
}}
12591260
style={{
12601261
width: sessionPanelWidth(),
@@ -1351,17 +1352,27 @@ export default function Page() {
13511352
/>
13521353

13531354
<Show when={desktopReviewOpen()}>
1354-
<ResizeHandle
1355-
direction="horizontal"
1356-
size={layout.session.width()}
1357-
min={450}
1358-
max={typeof window === "undefined" ? 1000 : window.innerWidth * 0.45}
1359-
onResize={layout.session.resize}
1360-
/>
1355+
<div onPointerDown={() => size.start()}>
1356+
<ResizeHandle
1357+
direction="horizontal"
1358+
size={layout.session.width()}
1359+
min={450}
1360+
max={typeof window === "undefined" ? 1000 : window.innerWidth * 0.45}
1361+
onResize={(width) => {
1362+
size.touch()
1363+
layout.session.resize(width)
1364+
}}
1365+
/>
1366+
</div>
13611367
</Show>
13621368
</div>
13631369

1364-
<SessionSidePanel reviewPanel={reviewPanel} activeDiff={tree.activeDiff} focusReviewDiff={focusReviewDiff} />
1370+
<SessionSidePanel
1371+
reviewPanel={reviewPanel}
1372+
activeDiff={tree.activeDiff}
1373+
focusReviewDiff={focusReviewDiff}
1374+
size={size}
1375+
/>
13651376
</div>
13661377

13671378
<TerminalPanel />

packages/app/src/pages/session/helpers.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { batch } from "solid-js"
1+
import { batch, createEffect, on, onCleanup, onMount, type Accessor } from "solid-js"
2+
import { createStore } from "solid-js/store"
23

34
export const focusTerminalById = (id: string) => {
45
const wrapper = document.getElementById(`terminal-wrapper-${id}`)
@@ -69,3 +70,104 @@ export const getTabReorderIndex = (tabs: readonly string[], from: string, to: st
6970
if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return undefined
7071
return toIndex
7172
}
73+
74+
export const createSizing = () => {
75+
const [state, setState] = createStore({ active: false })
76+
let t: number | undefined
77+
78+
const stop = () => {
79+
if (t !== undefined) {
80+
clearTimeout(t)
81+
t = undefined
82+
}
83+
setState("active", false)
84+
}
85+
86+
const start = () => {
87+
if (t !== undefined) {
88+
clearTimeout(t)
89+
t = undefined
90+
}
91+
setState("active", true)
92+
}
93+
94+
onMount(() => {
95+
window.addEventListener("pointerup", stop)
96+
window.addEventListener("pointercancel", stop)
97+
window.addEventListener("blur", stop)
98+
onCleanup(() => {
99+
window.removeEventListener("pointerup", stop)
100+
window.removeEventListener("pointercancel", stop)
101+
window.removeEventListener("blur", stop)
102+
})
103+
})
104+
105+
onCleanup(() => {
106+
if (t !== undefined) clearTimeout(t)
107+
})
108+
109+
return {
110+
active: () => state.active,
111+
start,
112+
touch() {
113+
start()
114+
t = window.setTimeout(stop, 120)
115+
},
116+
}
117+
}
118+
119+
export type Sizing = ReturnType<typeof createSizing>
120+
121+
export const createPresence = (open: Accessor<boolean>, wait = 200) => {
122+
const [state, setState] = createStore({
123+
show: open(),
124+
open: open(),
125+
})
126+
let frame: number | undefined
127+
let t: number | undefined
128+
129+
const clear = () => {
130+
if (frame !== undefined) {
131+
cancelAnimationFrame(frame)
132+
frame = undefined
133+
}
134+
if (t !== undefined) {
135+
clearTimeout(t)
136+
t = undefined
137+
}
138+
}
139+
140+
createEffect(
141+
on(open, (next) => {
142+
clear()
143+
144+
if (next) {
145+
if (state.show) {
146+
setState("open", true)
147+
return
148+
}
149+
150+
setState({ show: true, open: false })
151+
frame = requestAnimationFrame(() => {
152+
frame = undefined
153+
setState("open", true)
154+
})
155+
return
156+
}
157+
158+
if (!state.show) return
159+
setState("open", false)
160+
t = window.setTimeout(() => {
161+
t = undefined
162+
setState("show", false)
163+
}, wait)
164+
}),
165+
)
166+
167+
onCleanup(clear)
168+
169+
return {
170+
show: () => state.show,
171+
open: () => state.open,
172+
}
173+
}

0 commit comments

Comments
 (0)