Skip to content

Commit c8dd1cd

Browse files
committed
ui: Add Zen mode
1 parent dc53863 commit c8dd1cd

File tree

11 files changed

+145
-13
lines changed

11 files changed

+145
-13
lines changed

ui/src/assets/topbar.scss

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@
3535
}
3636
}
3737

38+
// Zen mode: hide topbar by default, show when it has focus or when explicitly shown
39+
&.pf-zen-mode {
40+
display: none;
41+
42+
// Show topbar when explicitly marked to show (has text, pending focus, or non-search mode)
43+
&.pf-zen-mode--show {
44+
display: flex;
45+
}
46+
47+
// Always show topbar when it has focus (user is interacting with omnibox)
48+
&:focus-within {
49+
display: flex;
50+
}
51+
}
52+
3853
&__error-box {
3954
position: absolute;
4055
right: 10px;

ui/src/core/app_impl.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export class AppImpl implements App {
126126
readonly startupCommandsSetting: Setting<CommandInvocation[]>;
127127
readonly enforceStartupCommandAllowlistSetting: Setting<boolean>;
128128
private _isInternalUser?: boolean;
129+
private _zenModeEnabled = false;
129130

130131
// This constructor is invoked only once, when frontend/index.ts invokes
131132
// AppMainImpl.initialize().
@@ -197,6 +198,16 @@ export class AppImpl implements App {
197198
};
198199
}
199200

201+
get zenModeEnabled(): boolean {
202+
return this._zenModeEnabled;
203+
}
204+
205+
toggleZenMode(): void {
206+
this._zenModeEnabled = !this._zenModeEnabled;
207+
// Zen mode controls sidebar existence
208+
this.sidebar.toggleEnabled();
209+
}
210+
200211
openTraceFromFile(file: File) {
201212
return this.openTrace({type: 'FILE', file});
202213
}

ui/src/core/sidebar_manager.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ export type SidebarMenuItemInternal = SidebarMenuItem & {
2020
};
2121

2222
export class SidebarManagerImpl implements SidebarManager {
23-
readonly enabled: boolean;
23+
private _enabled: boolean;
2424
private _visible: boolean;
2525
private lastId = 0;
2626

2727
readonly menuItems = new Registry<SidebarMenuItemInternal>((m) => m.id);
2828

2929
constructor(args: {disabled?: boolean; hidden?: boolean}) {
30-
this.enabled = !args.disabled;
30+
this._enabled = !args.disabled;
3131
this._visible = !args.hidden;
3232
}
3333

@@ -39,12 +39,26 @@ export class SidebarManagerImpl implements SidebarManager {
3939
return this.menuItems.register(itemInt);
4040
}
4141

42+
public get enabled() {
43+
return this._enabled;
44+
}
45+
4246
public get visible() {
4347
return this._visible;
4448
}
4549

50+
public toggleEnabled() {
51+
this._enabled = !this._enabled;
52+
}
53+
4654
public toggleVisibility() {
47-
if (!this.enabled) return;
55+
// When sidebar is disabled, just enable it and make it visible
56+
// (This typically happens when exiting zen mode via sidebar commands)
57+
if (!this._enabled) {
58+
this._enabled = true;
59+
this._visible = true;
60+
return;
61+
}
4862
this._visible = !this._visible;
4963
}
5064
}

ui/src/core/trace_impl.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,14 @@ export class TraceImpl implements Trace, Disposable {
314314
return this.app.isInternalUser;
315315
}
316316

317+
get zenModeEnabled(): boolean {
318+
return this.app.zenModeEnabled;
319+
}
320+
321+
toggleZenMode(): void {
322+
this.app.toggleZenMode();
323+
}
324+
317325
get perfDebugging() {
318326
return this.app.perfDebugging;
319327
}

ui/src/core_plugins/dev.perfetto.CoreCommands/index.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,43 @@ export default class CoreCommands implements PerfettoPlugin {
205205
icon: 'folder_open',
206206
});
207207

208+
ctx.commands.registerCommand({
209+
id: 'dev.perfetto.ToggleSidebar',
210+
name: 'Toggle sidebar',
211+
callback: () => {
212+
const app = AppImpl.instance;
213+
// If in zen mode, exit zen mode instead of just toggling sidebar
214+
if (app.zenModeEnabled) {
215+
app.toggleZenMode();
216+
} else {
217+
app.sidebar.toggleEnabled();
218+
}
219+
},
220+
});
221+
222+
ctx.commands.registerCommand({
223+
id: 'dev.perfetto.ToggleSidebarVisibility',
224+
name: 'Toggle sidebar visibility',
225+
callback: () => {
226+
const app = AppImpl.instance;
227+
// If in zen mode, exit zen mode instead of just toggling visibility
228+
if (app.zenModeEnabled) {
229+
app.toggleZenMode();
230+
} else {
231+
app.sidebar.toggleVisibility();
232+
}
233+
},
234+
});
235+
236+
ctx.commands.registerCommand({
237+
id: 'dev.perfetto.ToggleZenMode',
238+
name: 'Toggle zen mode',
239+
defaultHotkey: 'Shift+Z',
240+
callback: () => {
241+
AppImpl.instance.toggleZenMode();
242+
},
243+
});
244+
208245
const OPEN_LEGACY_COMMAND_ID = 'dev.perfetto.OpenTraceInLegacyUi';
209246
ctx.commands.registerCommand({
210247
id: OPEN_LEGACY_COMMAND_ID,

ui/src/frontend/omnibox.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ export class Omnibox implements m.ClassComponent<OmniboxAttrs> {
7575
}
7676

7777
private renderPromptOmnibox(): m.Children {
78-
const omnibox = AppImpl.instance.omnibox;
78+
const app = AppImpl.instance;
79+
const omnibox = app.omnibox;
7980
const prompt = assertExists(omnibox.pendingPrompt);
8081

8182
let options: OmniboxOption[] | undefined = undefined;
@@ -120,7 +121,8 @@ export class Omnibox implements m.ClassComponent<OmniboxAttrs> {
120121

121122
private renderCommandOmnibox(): m.Children {
122123
// Fuzzy-filter commands by the filter string.
123-
const {commands, omnibox} = AppImpl.instance;
124+
const app = AppImpl.instance;
125+
const {commands, omnibox} = app;
124126
const filteredCmds = commands.fuzzyFilterCommands(omnibox.text);
125127

126128
// Create an array of commands with attached heuristics from the recent

ui/src/frontend/timeline_page/timeline_page.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ class TimelinePage implements m.ClassComponent<TimelinePageAttrs> {
5757

5858
view({attrs}: m.CVnode<TimelinePageAttrs>) {
5959
const {trace} = attrs;
60+
const zenMode = AppImpl.instance.zenModeEnabled;
61+
const showOverview = OVERVIEW_PANEL_FLAG.get() && !zenMode;
6062
return m(
6163
'.pf-timeline-page',
6264
m(
6365
TabPanel,
6466
{trace},
65-
OVERVIEW_PANEL_FLAG.get() &&
67+
showOverview &&
6668
m(Minimap, {
6769
trace,
6870
className: 'pf-timeline-page__overview',

ui/src/frontend/topbar.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,18 @@ class TraceErrorIcon implements m.ClassComponent<TraceImplAttrs> {
7272

7373
export interface TopbarAttrs {
7474
readonly trace?: TraceImpl;
75+
readonly className?: string;
7576
}
7677

7778
export class Topbar implements m.ClassComponent<TopbarAttrs> {
7879
view({attrs}: m.Vnode<TopbarAttrs>) {
79-
const {trace} = attrs;
80+
const {trace, className} = attrs;
8081
return m(
8182
'.pf-topbar',
8283
{
8384
className: classNames(
8485
!AppImpl.instance.sidebar.visible && 'pf-topbar--hide-sidebar',
86+
className,
8587
),
8688
},
8789
m(Omnibox, {trace}),

ui/src/frontend/ui_main.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
// limitations under the License.
1414

1515
import m from 'mithril';
16+
import {classNames} from '../base/classnames';
1617
import {AppImpl} from '../core/app_impl';
1718
import {CookieConsent} from '../core/cookie_consent';
1819
import {featureFlags} from '../core/feature_flags';
20+
import {OmniboxMode} from '../core/omnibox_manager';
1921
import {LinearProgress} from '../widgets/linear_progress';
2022
import {maybeRenderFullscreenModalDialog} from '../widgets/modal';
2123
import {initCssConstants} from './css_constants';
@@ -57,17 +59,33 @@ export class UiMain implements m.ClassComponent {
5759
(trace?.engine.numRequestsPending ?? 0) > 0 ||
5860
taskTracker.hasPendingTasks();
5961

62+
const zenMode = app.zenModeEnabled;
63+
const showStatusBar = showStatusBarFlag.get() && !zenMode;
64+
65+
// In zen mode, topbar visibility is controlled by CSS classes
66+
// Show topbar when: not in search mode, has text, or should focus
67+
const shouldShowTopbarInZenMode =
68+
app.omnibox.mode !== OmniboxMode.Search ||
69+
app.omnibox.text.length > 0 ||
70+
app.omnibox.focusOmniboxNextRender;
71+
6072
return m('main.pf-ui-main', [
61-
m(Sidebar),
62-
m(Topbar, {trace}),
73+
app.sidebar.enabled && m(Sidebar),
74+
m(Topbar, {
75+
trace,
76+
className: classNames(
77+
zenMode && 'pf-zen-mode',
78+
zenMode && shouldShowTopbarInZenMode && 'pf-zen-mode--show',
79+
),
80+
}),
6381
m(LinearProgress, {
6482
className: 'pf-ui-main__loading',
6583
state: isSomethingLoading ? 'indeterminate' : 'none',
6684
}),
6785
m('.pf-ui-main__page-container', app.pages.renderPageForCurrentRoute()),
6886
m(CookieConsent),
6987
maybeRenderFullscreenModalDialog(),
70-
showStatusBarFlag.get() && renderStatusBar(trace),
88+
showStatusBar && renderStatusBar(trace),
7189
app.perfDebugging.renderPerfStats(),
7290
]);
7391
}

ui/src/public/app.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ export interface App {
6161
// certain internal links or expose certain experimental features by default.
6262
readonly isInternalUser: boolean;
6363

64+
// True if zen mode is enabled. Zen mode hides the sidebar, overview,
65+
// and status bar to provide a distraction-free viewing experience.
66+
readonly zenModeEnabled: boolean;
67+
68+
/**
69+
* Toggle zen mode on/off. Zen mode hides the sidebar, overview,
70+
* and status bar to provide a distraction-free viewing experience.
71+
*/
72+
toggleZenMode(): void;
73+
6474
/**
6575
* Navigate to a new page.
6676
*/

0 commit comments

Comments
 (0)