fix(app): suppress review sidebar hydration motion (#36847)

This commit is contained in:
Luke Parker 2026-07-14 22:50:16 +10:00 committed by GitHub
commit 21f8184c58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 74 additions and 2 deletions

View file

@ -9,7 +9,7 @@ import { createStore } from "solid-js/store"
import { Persist, persisted } from "@/utils/persist"
export function createReviewPanelV2State() {
const [store, setStore] = persisted(
const [store, setStore, , ready] = persisted(
Persist.global("review-panel-v2"),
createStore({
sidebarOpened: true,
@ -24,6 +24,7 @@ export function createReviewPanelV2State() {
return {
sidebarOpened: () => store.sidebarOpened,
sidebarWidth: () => store.sidebarWidth,
sidebarTransition: ready,
filter,
setFilter,
expandMode: () => store.expandMode,

View file

@ -200,6 +200,7 @@ function ReviewPanelV2Sidebar(props: {
return (
<SessionReviewV2Sidebar
open={props.state.sidebarOpened()}
transition={props.state.sidebarTransition()}
title={props.title}
stats={<DiffChanges changes={props.diffs()} />}
filter={props.state.filter()}

View file

@ -19,6 +19,7 @@ const emptyFiles: string[] = []
export type SessionFileBrowserState = {
sidebarOpened: () => boolean
sidebarWidth: () => number
sidebarTransition: () => boolean
resizeSidebar: (width: number) => void
toggleSidebar: () => void
}
@ -96,6 +97,7 @@ export function SessionFileBrowserTab(props: {
sidebar={
<SessionReviewV2Sidebar
open={sidebarOpened()}
transition={props.state.sidebarTransition()}
title={<span class="truncate">{title()}</span>}
filter={filter()}
onFilterChange={setFilter}

View file

@ -0,0 +1,65 @@
import { beforeAll, expect, mock, test } from "bun:test"
import type { AsyncStorage } from "@solid-primitives/storage"
import { createEffect, createRoot } from "solid-js"
let createReviewPanelV2State: typeof import("@/pages/session/v2/review-panel-v2-state").createReviewPanelV2State
let read: ((value: string | null) => void) | undefined
const storage: AsyncStorage = {
getItem: () => new Promise((resolve) => (read = resolve)),
setItem: async () => undefined,
removeItem: async () => undefined,
clear: async () => undefined,
key: async () => null,
getLength: async () => 0,
length: Promise.resolve(0),
}
beforeAll(async () => {
mock.module("@opencode-ai/session-ui/v2/session-review-v2", () => ({
SESSION_REVIEW_V2_SIDEBAR_WIDTH_DEFAULT: 240,
SESSION_REVIEW_V2_SIDEBAR_WIDTH_MIN: 200,
SESSION_REVIEW_V2_SIDEBAR_WIDTH_MAX: 480,
}))
mock.module("@/context/platform", () => ({
usePlatform: () => ({ platform: "desktop", storage: () => storage }),
}))
createReviewPanelV2State = (await import("@/pages/session/v2/review-panel-v2-state")).createReviewPanelV2State
})
test("enables sidebar motion only after custom width hydration", async () => {
await new Promise<void>((resolve, reject) => {
createRoot((dispose) => {
const state = createReviewPanelV2State()
const transition =
"sidebarTransition" in state && typeof state.sidebarTransition === "function"
? (state.sidebarTransition as () => boolean)
: undefined
try {
expect(transition).toBeFunction()
expect(transition?.()).toBeFalse()
expect(state.sidebarWidth()).toBe(240)
} catch (error) {
dispose()
reject(error)
return
}
createEffect(() => {
if (!transition?.()) return
try {
expect(state.sidebarWidth()).toBe(360)
dispose()
resolve()
} catch (error) {
dispose()
reject(error)
}
})
read?.(JSON.stringify({ sidebarOpened: true, sidebarWidth: 360, expandMode: "collapse" }))
})
})
})

View file

@ -38,7 +38,8 @@
border-right-width: 0;
}
[data-component="session-review-v2-sidebar-root"] [data-slot="session-review-v2-sidebar"]:not([data-resizing]) {
[data-component="session-review-v2-sidebar-root"]
[data-slot="session-review-v2-sidebar"][data-transition]:not([data-resizing]) {
transition:
width 200ms cubic-bezier(0.22, 1, 0.36, 1),
border-right-width 200ms cubic-bezier(0.22, 1, 0.36, 1);

View file

@ -39,6 +39,7 @@ export type SessionReviewV2Props = {
export type SessionReviewV2SidebarProps = {
open: boolean
transition: boolean
title?: JSX.Element
stats?: JSX.Element
filter: string
@ -76,6 +77,7 @@ export function SessionReviewV2Sidebar(props: SessionReviewV2SidebarProps) {
<Show when={props.open}>
<aside
data-slot="session-review-v2-sidebar"
data-transition={props.transition ? "" : undefined}
data-resizing={resizing() ? "" : undefined}
style={{ width: `${width()}px` }}
>