|
const measureInlineEditorMaxHeight = useCallback(() => { |
|
if (!inlineEditorActive) { |
|
setInlineEditorMaxHeight(null); |
|
setInlineEditorDesiredHeight(null); |
|
return; |
|
} |
|
const viewport = getScrollElement?.(); |
|
const surface = surfaceRef.current; |
|
const composerShell = surface?.closest<HTMLElement>("[data-app-composer]"); |
|
const container = composerShell?.parentElement; |
|
if (!viewport || !surface || !container) { |
|
setInlineEditorMaxHeight(null); |
|
return; |
|
} |
|
|
|
const nextHeight = getInlineEditorSurfaceMaxHeight({ |
|
containerHeight: container.getBoundingClientRect().height, |
|
surfaceHeight: surface.getBoundingClientRect().height, |
|
viewportHeight: viewport.clientHeight, |
|
}); |
|
setInlineEditorMaxHeight((currentHeight) => |
|
currentHeight === nextHeight ? currentHeight : nextHeight, |
|
); |
|
|
|
const list = listRef.current; |
|
const scroll = scrollRef.current; |
|
const editorElement = list?.querySelector<HTMLElement>( |
|
"[data-queued-message-inline-editor]", |
|
); |
|
if (!list || !scroll || !editorElement) { |
|
setInlineEditorDesiredHeight(null); |
|
return; |
|
} |
|
const items = Array.from(list.children); |
|
const editorIndex = items.indexOf(editorElement); |
|
const previousRow = items |
|
.slice(0, editorIndex) |
|
.reverse() |
|
.find((item) => item.hasAttribute("data-queued-message-row")); |
|
const followingRow = items |
|
.slice(editorIndex + 1) |
|
.find((item) => item.hasAttribute("data-queued-message-row")); |
|
const firstElement = (previousRow ?? editorElement) as HTMLElement; |
|
const lastElement = (followingRow ?? editorElement) as HTMLElement; |
|
const surfaceRect = surface.getBoundingClientRect(); |
|
const scrollRect = scroll.getBoundingClientRect(); |
|
const contentHeight = |
|
lastElement.getBoundingClientRect().bottom - |
|
firstElement.getBoundingClientRect().top; |
|
const chromeHeight = Math.max(0, surfaceRect.height - scrollRect.height); |
|
const desiredHeight = Math.max( |
|
WORKSPACE_MIN_HEIGHT, |
|
Math.ceil(contentHeight + chromeHeight), |
|
); |
|
setInlineEditorDesiredHeight((currentHeight) => |
|
currentHeight === desiredHeight ? currentHeight : desiredHeight, |
|
); |
|
// The surface height is animated. ResizeObserver calls this throughout the |
|
// transition, so re-align the neighborhood as usable space appears instead |
|
// of leaving the editor pinned to the top based on the first, short frame. |
|
scrollInlineEditorNeighborhoodIntoView(); |
|
}, [ |
|
getScrollElement, |
|
inlineEditorActive, |
|
scrollInlineEditorNeighborhoodIntoView, |
|
scrollRef, |
|
]); |
|
|
|
useLayoutEffect(() => { |
|
measureInlineEditorMaxHeight(); |
|
if (!inlineEditorActive) return; |
|
|
|
const viewport = getScrollElement?.(); |
|
const surface = surfaceRef.current; |
|
const composerShell = surface?.closest<HTMLElement>("[data-app-composer]"); |
|
const container = composerShell?.parentElement; |
|
const animationFrame = window.requestAnimationFrame( |
|
measureInlineEditorMaxHeight, |
|
); |
|
const resizeObserver = |
|
typeof ResizeObserver === "undefined" |
|
? null |
|
: new ResizeObserver(measureInlineEditorMaxHeight); |
|
if (viewport) resizeObserver?.observe(viewport); |
|
if (surface) resizeObserver?.observe(surface); |
|
if (listRef.current) resizeObserver?.observe(listRef.current); |
|
const editorElement = listRef.current?.querySelector<HTMLElement>( |
|
"[data-queued-message-inline-editor]", |
|
); |
|
if (editorElement) resizeObserver?.observe(editorElement); |
|
if (composerShell) resizeObserver?.observe(composerShell); |
|
if (container) resizeObserver?.observe(container); |
|
window.addEventListener("resize", measureInlineEditorMaxHeight); |
|
return () => { |
|
window.cancelAnimationFrame(animationFrame); |
|
resizeObserver?.disconnect(); |
|
window.removeEventListener("resize", measureInlineEditorMaxHeight); |
|
}; |
|
}, [getScrollElement, inlineEditorActive, measureInlineEditorMaxHeight]); |
Summary
This same failure has now occurred four times. During the latest directly observed occurrence, while an agent turn was active in bb Desktop, I clicked Edit on a queued message. The desktop UI immediately stopped responding while one Electron renderer consumed about 99% CPU and grew beyond 10 GiB RSS; the bb server and an Air/Connect client remained responsive. I expected the queued-message inline editor to open without affecting the rest of the app.
Versions and environment
0.39.0desktop app;bb settings version --jsonreportedsource: npm,latestVersion: 0.39.0, and no update available.b33abbff098ac4c857578e7350d492dcaa65d489.26.6.2(25G83) on the Pro that runs the sole bb server and desktop renderer.0.149.1; a Codex turn was active, but the provider process did not appear to be the failing boundary.proj_ivbg7bbvgw; environmentenv_4brjp4yccv; threadthr_n9sh8rhz6t.Steps to reproduce
The reporter has now experienced this failure four times; the latest occurrence was directly observed on the latest release. I did not deliberately repeat it after recovery because the affected renderer had already exceeded 10 GiB RSS and was still growing.
Observed recovery attempts:
Cmd+R) did not recover the UI.The exact queued-message text was not isolated as a contributing factor. A controlled second reproduction has not been attempted.
Expected vs actual
Evidence
psagainst renderer PID55035while the UI was frozen./tmp/bb-renderer-hang.txt— 631,364 bytes, captured 2026-08-25 04:02 PDT./tmp/bb-renderer-55035.sample— 318,664 bytes, captured 2026-08-25 04:23 PDT.bb Helper (Renderer)version0.39.0. Their dominant main-thread stack is inside Electron/V8 microtask execution. That localizes the hot work to the renderer but does not name the application callback responsible.Suspected cause, not yet proven
The strongest source-level lead is a resize/animation/scroll feedback loop in the queued-message inline editor:
bb/apps/app/src/components/promptbox/banner/QueuedMessagesList.tsx
Lines 1057 to 1155 in b33abbf
bb/apps/app/src/components/promptbox/banner/QueuedMessagesList.tsx
Lines 1513 to 1523 in b33abbf
mainat42d2c1bec2e9830bd8e2b8f5f355ebffda37bded:bb/apps/app/src/components/promptbox/banner/QueuedMessagesList.tsx
Lines 1041 to 1139 in 42d2c1b
This mechanism is a strong inference from the trigger, process sample, and source. The process sample does not directly attribute the loop to
measureInlineEditorMaxHeight, so the report should not present it as a confirmed root cause.What you ruled out
QueuedMessagesList.tsxresize path without a frame budget, but explicitly says its findings are judgment rather than measurement. It does not report a macOS Electron renderer lockup, runaway RSS, or this Edit trigger: iOS Safari performance audit: 20 findings across 384 frontend files #1616QueuedMessagesList.tsx: Calm the iOS shell-geometry handler and the timeline resize cascade #2385 and [stacked on #2385 + #2386] Consolidate per-row ResizeObservers into shared read/write-phased observers #2392mainstill contains the suspected measurement/animation structure. This was verified by source comparison, not by running a current-main desktop build.QueuedMessagesList.Suggested priority and effort
High — this is now a recurring failure with four reported occurrences. One common queued-message action can make the local desktop unusable and drive unbounded renderer memory growth. Force Reload is a workaround and no persisted-data loss was observed. Likely effort: Medium, beginning with a bounded regression test or instrumentation around queued-editor resize convergence before choosing a fix.
Checks
BB-Thread-ID: thr_n9sh8rhz6t