Skip to content

Commit ddac419

Browse files
committed
fix: add shared ChatPane wrapper for composer overlay layout
1 parent 75c2a4d commit ddac419

9 files changed

Lines changed: 109 additions & 17 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { useEffect, useMemo, useRef, useState, type CSSProperties, type ReactNode } from "react";
2+
3+
type ChatPaneProps = {
4+
messagesNode: ReactNode;
5+
composerNode: ReactNode;
6+
className?: string;
7+
};
8+
9+
export function ChatPane({ messagesNode, composerNode, className }: ChatPaneProps) {
10+
const composerRef = useRef<HTMLDivElement | null>(null);
11+
const [composerHeight, setComposerHeight] = useState(0);
12+
13+
useEffect(() => {
14+
if (!composerNode) {
15+
setComposerHeight(0);
16+
return;
17+
}
18+
19+
const node = composerRef.current;
20+
if (!node) {
21+
return;
22+
}
23+
24+
const updateComposerHeight = () => {
25+
setComposerHeight(Math.ceil(node.getBoundingClientRect().height));
26+
};
27+
28+
updateComposerHeight();
29+
30+
const observer = new ResizeObserver(() => {
31+
updateComposerHeight();
32+
});
33+
observer.observe(node);
34+
35+
return () => {
36+
observer.disconnect();
37+
};
38+
}, [composerNode]);
39+
40+
const paneStyle = useMemo(
41+
() =>
42+
({
43+
["--composer-overlay-height" as string]: `${composerHeight}px`,
44+
}) satisfies CSSProperties,
45+
[composerHeight],
46+
);
47+
48+
return (
49+
<div className={`chat-pane${className ? ` ${className}` : ""}`} style={paneStyle}>
50+
<div className="chat-pane-messages">{messagesNode}</div>
51+
{composerNode ? (
52+
<div className="chat-pane-composer" ref={composerRef}>
53+
{composerNode}
54+
</div>
55+
) : null}
56+
</div>
57+
);
58+
}

src/features/layout/components/DesktopLayout.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect, useRef, type MouseEvent, type ReactNode } from "react";
22
import { MainTopbar } from "../../app/components/MainTopbar";
3+
import { ChatPane } from "./ChatPane";
34

45
type CenterMode = "chat" | "diff";
56

@@ -108,6 +109,7 @@ export function DesktopLayout({
108109
}: DesktopLayoutProps) {
109110
const diffLayerRef = useRef<HTMLDivElement | null>(null);
110111
const chatLayerRef = useRef<HTMLDivElement | null>(null);
112+
const chatPaneNode = <ChatPane messagesNode={messagesNode} composerNode={composerNode} />;
111113
const diffLayerActive = isActiveLayer(centerMode, "diff");
112114
const chatLayerActive = isActiveLayer(centerMode, "chat");
113115
const showDiffViewer = shouldRenderDiffViewer({
@@ -168,7 +170,7 @@ export function DesktopLayout({
168170
})}
169171
ref={chatLayerRef}
170172
>
171-
{messagesNode}
173+
{chatPaneNode}
172174
</div>
173175
<div
174176
className="content-split-resizer"
@@ -210,7 +212,7 @@ export function DesktopLayout({
210212
aria-hidden={!splitChatDiffView ? !chatLayerActive : undefined}
211213
ref={chatLayerRef}
212214
>
213-
{messagesNode}
215+
{chatPaneNode}
214216
</div>
215217
</>
216218
)}
@@ -235,8 +237,6 @@ export function DesktopLayout({
235237
/>
236238
<div className="right-panel-bottom">{planPanelNode}</div>
237239
</div>
238-
239-
{composerNode}
240240
{terminalDockNode}
241241
{debugPanelNode}
242242
</>

src/features/layout/components/PhoneLayout.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ReactNode } from "react";
22
import { MainTopbar } from "../../app/components/MainTopbar";
3+
import { ChatPane } from "./ChatPane";
34

45
type PhoneLayoutProps = {
56
approvalToastsNode: ReactNode;
@@ -60,8 +61,9 @@ export function PhoneLayout({
6061
actionsNode={topbarActionsNode}
6162
className="compact-topbar"
6263
/>
63-
<div className="content compact-content">{messagesNode}</div>
64-
{composerNode}
64+
<div className="content compact-content">
65+
<ChatPane messagesNode={messagesNode} composerNode={composerNode} />
66+
</div>
6567
</>
6668
) : (
6769
compactEmptyCodexNode

src/features/layout/components/TabletLayout.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { MouseEvent, ReactNode } from "react";
22
import { MainTopbar } from "../../app/components/MainTopbar";
3+
import { ChatPane } from "./ChatPane";
34

45
type TabletLayoutProps = {
56
tabletNavNode: ReactNode;
@@ -64,10 +65,9 @@ export function TabletLayout({
6465
className="tablet-topbar"
6566
/>
6667
{tabletTab === "codex" && (
67-
<>
68-
<div className="content tablet-content">{messagesNode}</div>
69-
{composerNode}
70-
</>
68+
<div className="content tablet-content">
69+
<ChatPane messagesNode={messagesNode} composerNode={composerNode} />
70+
</div>
7171
)}
7272
{tabletTab === "git" && (
7373
<div className="tablet-git">

src/styles/compact-phone.css

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,12 @@
3737
}
3838

3939
.app.layout-phone .messages-full {
40-
padding: 12px 16px 12px;
40+
padding: 12px 16px calc(12px + var(--composer-overlay-height, 0px));
4141
}
4242

4343
.app.layout-phone .composer {
44-
position: sticky;
45-
bottom: 0;
46-
z-index: 2;
4744
padding: 10px 12px calc(10px + var(--phone-composer-safe-bottom, 0px));
4845
border-top: 1px solid var(--border-subtle);
49-
backdrop-filter: blur(18px) saturate(1.1);
5046
}
5147

5248
html[data-mobile-composer-focus="true"] .app.layout-phone {

src/styles/compact-tablet.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
}
102102

103103
.app.layout-tablet .messages-full {
104-
padding: 12px 20px 16px;
104+
padding: 12px 20px calc(16px + var(--composer-overlay-height, 0px));
105105
}
106106

107107
.app.layout-tablet .composer {

src/styles/composer.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
padding: 10px var(--main-panel-padding) 20px;
77
border-top: 1px solid var(--cm-border-default);
88
background: var(--cm-surface-composer);
9+
backdrop-filter: blur(18px) saturate(1.1);
10+
-webkit-backdrop-filter: blur(18px) saturate(1.1);
911
grid-column: 1;
1012
grid-row: 3;
1113
}
@@ -15,6 +17,11 @@
1517
margin-inline: auto;
1618
}
1719

20+
.app.reduced-transparency .composer {
21+
backdrop-filter: none;
22+
-webkit-backdrop-filter: none;
23+
}
24+
1825
.composer-queue {
1926
display: flex;
2027
flex-direction: column;

src/styles/main.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,35 @@
921921
);
922922
}
923923

924+
.chat-pane {
925+
--composer-overlay-height: 0px;
926+
position: relative;
927+
display: flex;
928+
flex: 1;
929+
min-height: 0;
930+
min-width: 0;
931+
}
932+
933+
.chat-pane-messages {
934+
display: flex;
935+
flex: 1;
936+
min-height: 0;
937+
min-width: 0;
938+
}
939+
940+
.chat-pane-composer {
941+
position: absolute;
942+
left: 0;
943+
right: 0;
944+
bottom: 0;
945+
z-index: 2;
946+
pointer-events: none;
947+
}
948+
949+
.chat-pane-composer > .composer {
950+
pointer-events: auto;
951+
}
952+
924953
.content-split {
925954
position: relative;
926955
}

src/styles/messages.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
.messages-full {
1010
background: transparent;
1111
border-radius: 0;
12-
padding: 14px var(--main-panel-padding) 18px;
12+
padding: 14px var(--main-panel-padding) calc(18px + var(--composer-overlay-height, 0px));
1313
border: none;
1414
}
1515

0 commit comments

Comments
 (0)