diff --git a/apps/desktop/src/app/chat/composer/index.tsx b/apps/desktop/src/app/chat/composer/index.tsx
index f6a5c5ff48d..44ad0fa2a39 100644
--- a/apps/desktop/src/app/chat/composer/index.tsx
+++ b/apps/desktop/src/app/chat/composer/index.tsx
@@ -12,7 +12,6 @@ import {
useRef,
useState
} from 'react'
-import { createPortal } from 'react-dom'
import { hermesDirectiveFormatter, type SlashChipKind } from '@/components/assistant-ui/directive-text'
import { composerFill, composerSurfaceGlass } from '@/components/chat/composer-dock'
@@ -1924,7 +1923,7 @@ export function ChatBar({
)
- const composerOverlay = (
+ return (
<>
{dragging && poppedOut && (
- >
- )
-
- return (
- <>
- {/* Floating: portal to so position:fixed resolves against the
- viewport. The chat content wrapper sets `contain: layout paint`, which
- makes it a containing block for (and clips) fixed descendants — left
- inline, the popped-out composer is positioned/clipped relative to the
- chat column (which shifts with the sidebars), not the viewport, so the
- viewport-based clamp can't keep it on-screen. Docked stays inline: it's
- `absolute` within that column by design. */}
- {poppedOut ? createPortal(composerOverlay, document.body) : composerOverlay}
-
+ {/* Composer renders OUTSIDE the contain:[layout paint] wrapper above:
+ that wrapper is a containing block for — and clips — position:fixed
+ descendants, so the popped-out (fixed) composer would anchor to the
+ chat column (which shifts/resizes with the sidebars) and get clipped
+ off-screen instead of floating against the viewport. As a sibling it
+ anchors to the outer relative container instead: docked is absolute
+ (identical placement), floating resolves against the viewport. Both
+ states stay mounted here, so dock⇄float never remounts the editor. */}
+ {showChatBar && (
+ }>
+
+
)}
- {showChatBar && }
-
-
-