[web] Add a focus mode to the editor (#32245)

* Basic focus mode implementation

* Exit button

* Toggle between editor pdf mode

* Keep the rail and file tree mounted for editor tabs to work properly

* Hide Switch to editor button in focus mode

* Show shortcuts in layout menu

* Gate the feature behind a focus-mode feature flag

* Add split test badges in the focus mode dropdown item and top bar

* Add analytics tracking for focusMode changes

* Cleanup leftover IdeView value

* Allow passing focusMode value in layout test provider

* Remove redundant listener for focus mode shortcut

* Update focus mode and full project search listeners to use the event key

* Post-rebase cleanup

---------

Co-authored-by: Anna Fields <acfields11@gmail.com>
GitOrigin-RevId: 207f8d136fa6662558c474cca4a867a9842191e7
This commit is contained in:
Alexandre Bourdin
2026-06-08 08:06:05 +00:00
committed by Copybot
co-authored by Anna Fields
parent 757735b075
commit c8a3bdd13f
15 changed files with 223 additions and 25 deletions
@@ -12,10 +12,12 @@ export default function RailPanel({
isReviewPanelOpen,
isHistoryView,
railTabs,
focusMode,
}: {
isReviewPanelOpen: boolean
isHistoryView: boolean
railTabs: RailElement[]
focusMode: boolean
}) {
const { selectedTab, panelRef, handlePaneExpand, handlePaneCollapse } =
useRailContext()
@@ -35,7 +37,7 @@ export default function RailPanel({
return (
<Panel
id={`ide-redesign-sidebar-panel-${isHistoryView ? 'file-tree' : selectedTab}`}
className={classNames({ hidden: isReviewPanelOpen })}
className={classNames({ hidden: isReviewPanelOpen || focusMode })}
order={1}
defaultSize={selectedTab === 'workbench' ? 20 : 15}
minSize={5}
@@ -6,15 +6,17 @@ import { useTranslation } from 'react-i18next'
export default function RailResizeHandle({
isReviewPanelOpen,
focusMode,
}: {
isReviewPanelOpen: boolean
focusMode: boolean
}) {
const { isOpen, setIsOpen, togglePane, setResizing } = useRailContext()
const { t } = useTranslation()
return (
<HorizontalResizeHandle
className={classNames({ hidden: isReviewPanelOpen })}
className={classNames({ hidden: isReviewPanelOpen || focusMode })}
resizable
hitAreaMargins={{ coarse: 0, fine: 0 }}
onDoubleClick={togglePane}
@@ -73,7 +73,7 @@ export const RailLayout = () => {
const gitBridgeEnabled = getMeta('ol-gitBridgeEnabled')
const { isOverleaf } = getMeta('ol-ExposedSettings')
const { view, setLeftMenuShown } = useLayoutContext()
const { view, setLeftMenuShown, focusMode } = useLayoutContext()
const { markMessagesAsRead } = useChatContext()
@@ -271,7 +271,9 @@ export const RailLayout = () => {
But it should be identified as a navigation landmark.
Therefore, we nest them: the parent <nav> is the landmark, and its child gets the "role="tablist"". */}
<nav
className={classNames('ide-rail', { hidden: isHistoryView })}
className={classNames('ide-rail', {
hidden: isHistoryView || focusMode,
})}
aria-label={t('sidebar')}
>
<Nav activeKey={selectedTab} className="ide-rail-tabs-nav">
@@ -306,17 +308,20 @@ export const RailLayout = () => {
</nav>
</Nav>
</nav>
{moduleRailPopovers
.filter(shouldIncludeElement)
.map(({ key, Component, ref }) => (
<Component key={key} ref={ref} />
))}
{!focusMode &&
moduleRailPopovers
.filter(shouldIncludeElement)
.map(({ key, Component, ref }) => <Component key={key} ref={ref} />)}
<RailPanel
isReviewPanelOpen={isReviewPanelOpen}
isHistoryView={isHistoryView}
railTabs={railTabs}
focusMode={focusMode}
/>
<RailResizeHandle
isReviewPanelOpen={isReviewPanelOpen}
focusMode={focusMode}
/>
<RailResizeHandle isReviewPanelOpen={isReviewPanelOpen} />
<RailModals />
</TabContainer>
)