Skip to content

Commit 9223197

Browse files
committed
fix(spotlight): center over content area excluding sidebar
1 parent ff986c3 commit 9223197

3 files changed

Lines changed: 39 additions & 4 deletions

File tree

src/engines/ChatPanel/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ const ChatPanel: React.FC<ChatPanelProps> = memo(
723723
<div
724724
aria-hidden
725725
data-testid="launchpad-workstation-rail-placeholder"
726+
data-workstation-trail-track
726727
className={`h-full shrink-0 ${resolveFocusedChatWorkstationRailTrackClass(true)}`}
727728
/>
728729
) : null

src/modules/shared/layouts/FocusedChatWorkstationRail/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ export function FocusedChatWorkstationRail({
578578
has no drag handle or continuously resizable width. */}
579579
<div
580580
data-workstation-pane-control
581+
data-workstation-trail-track
581582
className={`relative flex h-full shrink-0 flex-col items-start transition-[width] duration-200 ease-out motion-reduce:transition-none ${resolveFocusedChatWorkstationRailTrackClass(
582583
collapsed
583584
)}`}

src/scaffold/GlobalSpotlight/shell/SpotlightShellChrome.tsx

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,26 @@
22
* SpotlightShellChrome
33
*
44
* Low-level chrome for SpotlightShell: simple panel + optional portal +
5-
* backdrop + viewport-centered positioning + footer slot beneath the panel.
5+
* backdrop + content-area-centered positioning + footer slot beneath the
6+
* panel. Horizontal centering excludes the docked layout sidebar and the
7+
* focused-chat workstation trail, so the spotlight sits over the visible
8+
* content rather than the full viewport.
69
*
710
* This is a direct merge of the previous SelectorContainer + SpotlightPortal
811
* layer. Only consumed by SpotlightShell; palettes never see this component.
912
*/
1013
import { useAtomValue } from "jotai";
11-
import React, { useEffect, useRef } from "react";
14+
import React, { useEffect, useMemo, useRef } from "react";
1215
import { createPortal } from "react-dom";
16+
import { useLocation } from "react-router-dom";
1317

18+
import { getSidebarId } from "@src/config/sidebarRegistry";
1419
import { CODEMIRROR_STYLE_NONCE } from "@src/features/CodeMirror/config/nonce";
1520
import { useOverlayLayer } from "@src/store/ui/overlayLayerAtom";
21+
import {
22+
sidebarCollapsedAtom,
23+
sidebarWidthAtom,
24+
} from "@src/store/ui/sidebarAtom";
1625
import { spotlightPlacementAtom } from "@src/store/ui/uiAtom";
1726

1827
import { SPOTLIGHT_CONFIG } from "../constants";
@@ -43,6 +52,30 @@ export const SpotlightShellChrome: React.FC<SpotlightShellChromeProps> = ({
4352
}) => {
4453
const inputHostRef = useRef<HTMLDivElement | null>(null);
4554
const spotlightPlacement = useAtomValue(spotlightPlacementAtom);
55+
const location = useLocation();
56+
const sidebarWidth = useAtomValue(sidebarWidthAtom);
57+
const sidebarCollapsed = useAtomValue(sidebarCollapsedAtom);
58+
59+
// Routes without a docked layout sidebar (and collapsed sidebars) reserve
60+
// no horizontal space, so they contribute no centering inset.
61+
const sidebarInset =
62+
getSidebarId(location.pathname) !== null && !sidebarCollapsed
63+
? sidebarWidth
64+
: 0;
65+
66+
// The focused-chat workstation trail (live rail or launchpad placeholder)
67+
// reserves width at the right edge of the content area. Its visibility
68+
// mixes chat-focus state, tab type, a container query, and rail-local
69+
// collapse state that no atom exposes, so the rendered track is measured
70+
// instead of mirrored. Measured once per open — the backdrop prevents the
71+
// trail from changing while the spotlight is up.
72+
const trailInset = useMemo(() => {
73+
if (!isOpen || !asPortal) return 0;
74+
const track = document.querySelector<HTMLElement>(
75+
"[data-workstation-trail-track]"
76+
);
77+
return track ? track.getBoundingClientRect().width : 0;
78+
}, [isOpen, asPortal]);
4679

4780
useOverlayLayer(isOpen && asPortal);
4881

@@ -147,13 +180,13 @@ export const SpotlightShellChrome: React.FC<SpotlightShellChromeProps> = ({
147180
spotlightPlacement === "center"
148181
? "50%"
149182
: SPOTLIGHT_CONFIG.topOffset,
150-
left: "50%",
183+
left: `calc(50% + ${(sidebarInset - trailInset) / 2}px)`,
151184
transform:
152185
spotlightPlacement === "center"
153186
? "translate(-50%, -50%)"
154187
: "translateX(-50%)",
155188
zIndex: SPOTLIGHT_CONFIG.containerZIndex,
156-
width: `min(${width}px, calc(100vw - 160px))`,
189+
width: `min(${width}px, calc(100vw - ${sidebarInset + trailInset}px - 160px))`,
157190
}}
158191
onClick={(event) => event.stopPropagation()}
159192
>

0 commit comments

Comments
 (0)