feat(app): review panel reactivity improvements (#37089)

This commit is contained in:
Aarav Sareen
2026-07-17 17:40:42 +08:00
committed by GitHub
parent b527f605d9
commit ed926be253
6 changed files with 112 additions and 9 deletions
@@ -551,7 +551,7 @@ export function SessionSidePanel(props: {
> >
<Show when={props.reviewSidebarToggle}> <Show when={props.reviewSidebarToggle}>
{(toggle) => ( {(toggle) => (
<div class="h-full shrink-0 flex items-center justify-center"> <div class="session-review-v2-sidebar-toggle-slot h-full shrink-0 sticky left-0 z-10 flex items-center justify-center bg-v2-background-bg-base">
{toggle()(activeTab() === SESSION_OPEN_FILE_TAB)} {toggle()(activeTab() === SESSION_OPEN_FILE_TAB)}
</div> </div>
)} )}
@@ -263,7 +263,9 @@ export function SessionReviewFilePreviewV2(props: SessionReviewFilePreviewV2Prop
<span data-slot="session-review-v2-file-path">{getDirectory(props.file)}</span> <span data-slot="session-review-v2-file-path">{getDirectory(props.file)}</span>
</Show> </Show>
</div> </div>
<DiffChanges changes={view()} /> <div data-slot="session-review-v2-file-diff">
<DiffChanges changes={view()} />
</div>
</div> </div>
<div <div
ref={(el) => { ref={(el) => {
@@ -149,7 +149,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 8px; gap: 8px;
padding: 8px 12px; padding: 10px 12px;
padding-left: 8px; padding-left: 8px;
flex-shrink: 0; flex-shrink: 0;
border-bottom: 1px solid var(--border-weaker-base, var(--v2-border-border-weak)); border-bottom: 1px solid var(--border-weaker-base, var(--v2-border-border-weak));
@@ -273,9 +273,37 @@
flex: 1; flex: 1;
} }
[data-component="session-review-v2"] [data-slot="session-review-v2-file-header"] [data-component="diff-changes"] { [data-component="session-review-v2"] [data-slot="session-review-v2-file-diff"] {
position: relative;
flex-shrink: 0; flex-shrink: 0;
margin-left: auto; margin-left: auto;
background-color: var(--v2-background-bg-base);
&::before {
content: "";
position: absolute;
top: -8px;
bottom: -8px;
right: 100%;
width: 16px;
z-index: 1;
pointer-events: none;
background: linear-gradient(90deg, transparent, var(--v2-background-bg-base));
}
&::after {
content: "";
position: absolute;
inset: -8px -16px -8px 0;
z-index: 0;
pointer-events: none;
background-color: var(--v2-background-bg-base);
}
[data-component="diff-changes"] {
position: relative;
z-index: 1;
}
} }
[data-component="session-review-v2"] [data-slot="session-review-v2-file-status"] { [data-component="session-review-v2"] [data-slot="session-review-v2-file-status"] {
@@ -139,6 +139,7 @@ export function SessionReviewV2Sidebar(props: SessionReviewV2SidebarProps) {
min={minWidth()} min={minWidth()}
max={maxWidth()} max={maxWidth()}
onResize={(next) => props.onWidthChange?.(next)} onResize={(next) => props.onWidthChange?.(next)}
onDblClick={() => props.onWidthChange?.(SESSION_REVIEW_V2_SIDEBAR_WIDTH_DEFAULT)}
/> />
</div> </div>
</Show> </Show>
+21 -5
View File
@@ -8,6 +8,8 @@ export interface ResizeHandleProps extends Omit<JSX.HTMLAttributes<HTMLDivElemen
max: number max: number
onResize: (size: number) => void onResize: (size: number) => void
onCollapse?: () => void onCollapse?: () => void
/** Called while dragging when size crosses `collapseThreshold`. */
onCollapseChange?: (collapsed: boolean) => void
collapseThreshold?: number collapseThreshold?: number
} }
@@ -20,17 +22,26 @@ export function ResizeHandle(props: ResizeHandleProps) {
"max", "max",
"onResize", "onResize",
"onCollapse", "onCollapse",
"onCollapseChange",
"collapseThreshold", "collapseThreshold",
"class", "class",
"classList", "classList",
]) ])
const handleMouseDown = (e: MouseEvent) => { const handleMouseDown = (e: MouseEvent) => {
if (e.detail > 1) return
e.preventDefault() e.preventDefault()
const edge = local.edge ?? (local.direction === "vertical" ? "start" : "end") const edge = local.edge ?? (local.direction === "vertical" ? "start" : "end")
const start = local.direction === "horizontal" ? e.clientX : e.clientY const start = local.direction === "horizontal" ? e.clientX : e.clientY
const startSize = local.size const startSize = local.size
const min = local.min
const max = local.max
const threshold = local.collapseThreshold ?? 0
const onResize = local.onResize
const onCollapse = local.onCollapse
const onCollapseChange = local.onCollapseChange
let current = startSize let current = startSize
let collapsed = false
document.body.style.userSelect = "none" document.body.style.userSelect = "none"
document.body.style.overflow = "hidden" document.body.style.overflow = "hidden"
@@ -46,8 +57,12 @@ export function ResizeHandle(props: ResizeHandleProps) {
? start - pos ? start - pos
: pos - start : pos - start
current = startSize + delta current = startSize + delta
const clamped = Math.min(local.max, Math.max(local.min, current)) const nextCollapsed = threshold > 0 && current < threshold
local.onResize(clamped) if (nextCollapsed !== collapsed) {
collapsed = nextCollapsed
onCollapseChange?.(collapsed)
}
onResize(Math.min(max, Math.max(min, current)))
} }
const onMouseUp = () => { const onMouseUp = () => {
@@ -56,10 +71,11 @@ export function ResizeHandle(props: ResizeHandleProps) {
document.removeEventListener("mousemove", onMouseMove) document.removeEventListener("mousemove", onMouseMove)
document.removeEventListener("mouseup", onMouseUp) document.removeEventListener("mouseup", onMouseUp)
const threshold = local.collapseThreshold ?? 0 if (collapsed) {
if (local.onCollapse && threshold > 0 && current < threshold) { onCollapse?.()
local.onCollapse() return
} }
onCollapseChange?.(false)
} }
document.addEventListener("mousemove", onMouseMove) document.addEventListener("mousemove", onMouseMove)
+56
View File
@@ -793,6 +793,62 @@ body[data-new-layout]
[data-slot="tabs-list"] [data-slot="tabs-list"]
> .sticky { > .sticky {
padding-right: 0; padding-right: 0;
[data-component="icon-button-v2"] {
position: relative;
&::after {
content: "";
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 28px;
background-color: var(--v2-background-bg-base);
pointer-events: none;
}
}
}
body[data-new-layout]
#review-panel
[data-component="tabs"]
.session-review-v2-tabs-bar
[data-slot="tabs-list"]
> .session-review-v2-sidebar-toggle-slot.sticky {
padding-right: 0;
&::before {
content: "";
position: absolute;
top: 50%;
bottom: auto;
left: auto;
right: 100%;
transform: translateY(-50%);
width: 12px;
height: 28px;
background-color: var(--v2-background-bg-base);
background-image: none;
pointer-events: none;
}
&::after {
content: "";
position: absolute;
top: 50%;
left: 100%;
transform: translateY(-50%);
width: 8px;
height: 28px;
pointer-events: none;
background: linear-gradient(90deg, var(--v2-background-bg-base), transparent);
}
[data-component="icon-button-v2"]::after {
display: none;
}
} }
body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app-slot { body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app-slot {