Skip to content

Commit 1d63c10

Browse files
msukkariclaudebrendan-kellam
authored
fix(web): prevent top banner from hiding the chat prompt (#1259)
* fix(web): prevent top banner from pushing the chat prompt off-screen Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feedback --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Brendan Kellam <brendan@sourcebot.dev>
1 parent a683d15 commit 1d63c10

4 files changed

Lines changed: 57 additions & 11 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use client';
2+
3+
import { useEffect, useRef } from "react";
4+
5+
const BANNER_HEIGHT_VAR = '--banner-height';
6+
7+
/**
8+
* Measures the rendered height of the top banner and exposes it as the
9+
* `--banner-height` CSS variable on the document root (0px when no banner is
10+
* shown). Viewport-based layouts (e.g. the chat thread's `calc(100vh - ...)`
11+
* sizing) subtract this so they don't overflow when a banner is present.
12+
*/
13+
export function BannerHeightObserver({ children }: { children: React.ReactNode }) {
14+
const ref = useRef<HTMLDivElement>(null);
15+
16+
useEffect(() => {
17+
const element = ref.current;
18+
if (!element) {
19+
return;
20+
}
21+
22+
const root = document.documentElement;
23+
const setHeight = (height: number) => {
24+
root.style.setProperty(BANNER_HEIGHT_VAR, `${height}px`);
25+
};
26+
27+
const resizeObserver = new ResizeObserver((entries) => {
28+
for (const entry of entries) {
29+
setHeight(entry.contentRect.height);
30+
}
31+
});
32+
resizeObserver.observe(element);
33+
34+
return () => {
35+
resizeObserver.disconnect();
36+
root.style.setProperty(BANNER_HEIGHT_VAR, '0px');
37+
};
38+
}, []);
39+
40+
return <div ref={ref}>{children}</div>;
41+
}

packages/web/src/app/(app)/layout.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { LogoutEscapeHatch } from "@/app/components/logoutEscapeHatch";
2626
import { GitHubStarToast } from "./components/githubStarToast";
2727
import { getLinkedAccounts } from "@/ee/features/sso/actions";
2828
import { BannerSlot } from "./components/banners/bannerSlot";
29+
import { BannerHeightObserver } from "./components/banners/bannerHeightObserver";
2930
import { getPermissionSyncStatus } from "../api/(server)/ee/permissionSyncStatus/api";
3031
import { OrgRole } from "@sourcebot/db";
3132
import { ServiceErrorException } from "@/lib/serviceError";
@@ -185,15 +186,17 @@ export default async function Layout(props: LayoutProps) {
185186
{sidebar}
186187
<div className="flex-1 min-h-0 flex flex-col pt-2 pb-2 pr-2 pl-2 md:pl-0">
187188
<div className="flex-1 min-h-0 bg-background flex flex-col border border-[#e6e6e6] dark:border-[#1d1d1f] rounded-xl overflow-hidden">
188-
<BannerSlot
189-
role={role}
190-
license={license}
191-
offlineLicense={offlineLicense}
192-
hasPermissionSyncEntitlement={hasPermissionSyncEntitlement}
193-
hasPendingFirstSync={hasPendingFirstSync}
194-
currentVersion={SOURCEBOT_VERSION}
195-
latestVersion={latestVersion}
196-
/>
189+
<BannerHeightObserver>
190+
<BannerSlot
191+
role={role}
192+
license={license}
193+
offlineLicense={offlineLicense}
194+
hasPermissionSyncEntitlement={hasPermissionSyncEntitlement}
195+
hasPendingFirstSync={hasPendingFirstSync}
196+
currentVersion={SOURCEBOT_VERSION}
197+
latestVersion={latestVersion}
198+
/>
199+
</BannerHeightObserver>
197200
<div className="flex-1 min-h-0 overflow-y-auto">
198201
{children}
199202
</div>

packages/web/src/app/globals.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@layer base {
88
html {
99
overflow-y: scroll;
10+
--banner-height: 0px;
1011
}
1112

1213
/* Hide scrollbar but keep functionality */
@@ -16,6 +17,7 @@
1617
}
1718

1819
:root {
20+
--banner-height: 0px;
1921
--background: hsl(0 0% 99%);
2022
--background-secondary: hsl(0, 0%, 98%);
2123
--foreground: hsl(37, 84%, 5%);

packages/web/src/ee/features/chat/components/chatThread/chatThreadListItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ const ChatThreadListItemComponent = forwardRef<HTMLDivElement, ChatThreadListIte
174174
}, [answerPart]);
175175

176176
const rightPanelStyle: CSSProperties = useMemo(() => {
177-
const maxHeight = 'calc(100vh - 215px)';
177+
const maxHeight = 'calc(100vh - 215px - var(--banner-height, 0px))';
178178

179179
return {
180180
height: leftPanelHeight ? `min(${leftPanelHeight}px, ${maxHeight})` : maxHeight,
@@ -323,7 +323,7 @@ const ChatThreadListItemComponent = forwardRef<HTMLDivElement, ChatThreadListIte
323323

324324
return (
325325
<div
326-
className="flex flex-col md:flex-row relative min-h-[calc(100vh-250px)]"
326+
className="flex flex-col md:flex-row relative min-h-[calc(100vh-250px-var(--banner-height,0px))]"
327327
ref={ref}
328328
>
329329
<ResizablePanelGroup

0 commit comments

Comments
 (0)