Skip to content

Commit 558ea27

Browse files
committed
fix(workflow): robust drag finalize — last-applied value, sidebar unmount, clamp skip
Addresses three review findings on the scoped-resize change: - useDragResize: track the last applied value and commit that on release; only recompute from the pointer event while the target is still connected. On an unmount the target's rect can be detached, so a layout-reading compute (e.g. the output panel's) would return a degenerate MIN — committing the last shown value avoids persisting the wrong width, and keeps flick-safety on a normal release. - use-sidebar-resize: finalize on unmount (run endDrag, not bare cleanup) so a drag interrupted by unmount persists the width and drops the scoped override. This matters more than for the panel/terminal because .sidebar-shell-outer lives in the workspace chrome and outlives the sidebar, so a stranded override would win over :root. - terminal output-panel clamp: skip while an output-panel drag is active (its scoped inline override is present). That drag's own compute clamps every frame against the live terminal rect, and a store-driven :root write here would be masked by the inline override anyway.
1 parent d7579ed commit 558ea27

3 files changed

Lines changed: 30 additions & 19 deletions

File tree

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/terminal.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,12 @@ export const Terminal = memo(function Terminal() {
12671267
const handleResize = () => {
12681268
if (!selectedEntry) return
12691269

1270+
// An active output-panel drag owns clamping: its own compute clamps every
1271+
// frame against the live terminal rect, and its scoped inline override on
1272+
// `.terminal-container` would mask a store-driven `:root` write here
1273+
// anyway. The inline override is present only while that drag is active.
1274+
if (el.style.getPropertyValue('--output-panel-width')) return
1275+
12701276
const maxWidth = el.getBoundingClientRect().width - TERMINAL_CONFIG.BLOCK_COLUMN_WIDTH_PX
12711277

12721278
if (maxWidth < MIN_OUTPUT_PANEL_WIDTH_PX) {
@@ -1275,12 +1281,7 @@ export const Terminal = memo(function Terminal() {
12751281
return
12761282
}
12771283

1278-
const liveWidth = Number.parseFloat(
1279-
el.style.getPropertyValue('--output-panel-width') ||
1280-
document.documentElement.style.getPropertyValue('--output-panel-width')
1281-
)
1282-
const currentWidth = Number.isNaN(liveWidth) ? outputPanelWidth : liveWidth
1283-
if (currentWidth > maxWidth) {
1284+
if (outputPanelWidth > maxWidth) {
12841285
setOutputPanelWidth(Math.max(maxWidth, MIN_OUTPUT_PANEL_WIDTH_PX))
12851286
}
12861287
}

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/hooks/use-sidebar-resize.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,19 @@ import { useSidebarStore } from '@/stores/sidebar/store'
2626
* stealing focus) can never leave the `is-resizing` / `sidebar-resizing` classes
2727
* stuck — which would otherwise freeze the sidebar at a tiny width with the
2828
* collapse transition permanently disabled. A single-flight guard prevents
29-
* stacking listeners across rapid presses, and an unmount cleanup tears down a
30-
* drag still in flight when the sidebar unmounts (e.g. route change).
29+
* stacking listeners across rapid presses, and unmounting mid-drag finalizes it
30+
* the same way a release does — persisting the last width and dropping the
31+
* scoped override — which matters because `.sidebar-shell-outer` lives in the
32+
* workspace chrome and outlives the sidebar, so a stranded override would
33+
* otherwise win over the committed `:root` value.
3134
*/
3235
export function useSidebarResize() {
3336
const setSidebarWidth = useSidebarStore((s) => s.setSidebarWidth)
34-
const cleanupRef = useRef<(() => void) | null>(null)
37+
const teardownRef = useRef<(() => void) | null>(null)
3538

3639
const handlePointerDown = useCallback(
3740
(e: React.PointerEvent<HTMLElement>) => {
38-
if (cleanupRef.current) return
41+
if (teardownRef.current) return
3942

4043
const handle = e.currentTarget
4144
const pointerId = e.pointerId
@@ -76,7 +79,7 @@ export function useSidebarResize() {
7679
document.removeEventListener('pointerup', endDrag)
7780
document.removeEventListener('pointercancel', endDrag)
7881
window.removeEventListener('blur', endDrag)
79-
cleanupRef.current = null
82+
teardownRef.current = null
8083
}
8184

8285
function endDrag() {
@@ -87,7 +90,7 @@ export function useSidebarResize() {
8790
}
8891
}
8992

90-
cleanupRef.current = cleanup
93+
teardownRef.current = endDrag
9194
document.addEventListener('pointermove', onPointerMove)
9295
document.addEventListener('pointerup', endDrag)
9396
document.addEventListener('pointercancel', endDrag)
@@ -96,7 +99,7 @@ export function useSidebarResize() {
9699
[setSidebarWidth]
97100
)
98101

99-
useEffect(() => () => cleanupRef.current?.(), [])
102+
useEffect(() => () => teardownRef.current?.(), [])
100103

101104
return { handlePointerDown }
102105
}

apps/sim/hooks/use-drag-resize.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,11 @@ export function useDragResize(options: UseDragResizeOptions) {
9797

9898
let rafId: number | null = null
9999
let lastEvent: PointerEvent | null = null
100+
let lastApplied: number | null = null
100101

101102
const applyValue = (value: number) => {
102103
target.style.setProperty(cssVar, `${value}px`)
104+
lastApplied = value
103105
optionsRef.current.onApply?.(value)
104106
}
105107

@@ -131,13 +133,18 @@ export function useDragResize(options: UseDragResizeOptions) {
131133

132134
function endDrag() {
133135
cleanup()
134-
if (lastEvent !== null) {
136+
// Recompute the final value from the last pointer position for an exact
137+
// finish (and flick-safety when no frame ran) — but only while the target
138+
// is still attached. On an unmount its rect can be detached, so a
139+
// layout-reading compute would return a degenerate value; there, commit
140+
// the last value actually shown to the user instead.
141+
if (lastEvent !== null && target.isConnected) {
135142
const value = optionsRef.current.compute(lastEvent)
136-
if (value !== null) {
137-
applyValue(value)
138-
optionsRef.current.commit(value)
139-
if (target !== document.documentElement) target.style.removeProperty(cssVar)
140-
}
143+
if (value !== null) applyValue(value)
144+
}
145+
if (lastApplied !== null) {
146+
optionsRef.current.commit(lastApplied)
147+
if (target !== document.documentElement) target.style.removeProperty(cssVar)
141148
}
142149
optionsRef.current.onEnd?.()
143150
}

0 commit comments

Comments
 (0)