Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
424 changes: 422 additions & 2 deletions ui/src/assets/bigtrace.scss

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions ui/src/bigtrace/help_modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ class BigTraceHelpContent implements m.ClassComponent {
),
m(
'tr',
m('td', keycap('Ctrl'), ' + ', keycap('Enter'), ' (with selection)'),
m('td', 'Execute selection'),
m('td', keycap('Ctrl'), ' + ', keycap('Enter')),
m('td', 'Execute selected text (when text is selected)'),
),
),
m('h2', 'Running commands'),
Expand Down
63 changes: 30 additions & 33 deletions ui/src/bigtrace/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@ import {initLiveReload} from '../core/live_reload';
import {settingsStorage} from './settings/settings_storage';
import {ThemeProvider} from '../frontend/theme_provider';
import {OverlayContainer} from '../widgets/overlay_container';
import {QueryPage} from './pages/query_page';
import {QueryPage, queryRightSidebarToggleFn} from './pages/query_page';
import {HomePage} from './pages/home_page';
import {bigTraceSettingsStorage} from './settings/bigtrace_settings_storage';
import {queryState} from './query/query_state';
import {SettingsPage} from './pages/settings_page';
import {Topbar} from './layout/topbar';
import {BigTraceApp as BigTraceAppSingleton} from './bigtrace_app';
Expand All @@ -38,11 +37,10 @@ import {toggleHelp} from './help_modal';
import {Routes} from './routes';

function getRoot() {
// Works out the root directory where the content should be served from
// e.g. `http://origin/v1.2.3/`.
// Root for serving content, e.g. `http://origin/v1.2.3/`.
const script = document.currentScript as HTMLScriptElement;

// Needed for DOM tests, that do not have script element.
// DOM tests have no script element.
if (script === null) {
return '';
}
Expand All @@ -62,6 +60,9 @@ function setupContentSecurityPolicy() {
`'self'`,
'https://autopush-brush-googleapis.corp.google.com',
'https://brush-googleapis.corp.google.com',
// Local reference / TP backends for dev (perfetto_2/tools/).
'http://localhost:*',
'http://127.0.0.1:*',
],
'img-src': [`'self'`, 'data:', 'blob:'],
'style-src': [
Expand Down Expand Up @@ -177,25 +178,11 @@ class BigTraceLayout implements m.ClassComponent {
}
}

// Root component: routing, theme, hotkeys, and layout.
// Uses m.mount (not m.route) so that all rendering goes through the raf
// scheduler's mount system. m.route() caches the original m.mount and
// bypasses the raf scheduler, which breaks cross-tree redraws for
// portal-based popups (e.g. the omnibox dropdown).
// Root: routing + theme + hotkeys. Uses m.mount (not m.route) because
// m.route bypasses the raf scheduler and breaks portal-based popups.
class BigTraceRoot implements m.ClassComponent {
private prevRoute = '';
private queryInitialQuery: string | undefined;

view(): m.Children {
const route = getCurrentRoute();

// Capture initialQuery on first navigation to /query.
if (route === Routes.QUERY && this.prevRoute !== Routes.QUERY) {
this.queryInitialQuery = queryState.initialQuery;
queryState.initialQuery = undefined;
}
this.prevRoute = route;

const page = this.resolvePage(route);

const theme = settingsStorage.get('theme');
Expand All @@ -222,17 +209,17 @@ class BigTraceRoot implements m.ClassComponent {
}

private resolvePage(route: string): m.Children {
switch (route) {
case Routes.QUERY:
return m(QueryPage, {
useBrushBackend: true,
initialQuery: this.queryInitialQuery,
});
case Routes.SETTINGS:
return m(SettingsPage);
default:
return m(HomePage);
}
return [
// QueryPage stays mounted across route changes to preserve DataGrid
// state (filters, scroll position, sort order).
m(
'div',
{style: {display: route === Routes.QUERY ? 'contents' : 'none'}},
m(QueryPage, {useBigtraceBackend: true}),
),
route === Routes.SETTINGS && m(SettingsPage),
route !== Routes.QUERY && route !== Routes.SETTINGS && m(HomePage),
];
}
}

Expand Down Expand Up @@ -264,11 +251,21 @@ function registerCommands() {
defaultHotkey: '!Mod+B',
});

app.commands.registerCommand({
id: 'bigtrace.ToggleQueryRightSidebar',
name: 'Toggle query right sidebar (History / Stdlib Schemas)',
callback: () => {
queryRightSidebarToggleFn?.();
},
defaultHotkey: '!Mod+Shift+B',
});

app.commands.registerCommand({
id: 'bigtrace.ShowHelp',
name: 'Show help',
callback: () => toggleHelp(),
defaultHotkey: '?',
// '!' prefix fires even when the omnibox has focus.
defaultHotkey: '!?',
});
}

Expand Down
29 changes: 19 additions & 10 deletions ui/src/bigtrace/layout/omnibox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ export class Omnibox implements m.ClassComponent {

private renderCommandOmnibox(): m.Children {
const {commands, omnibox} = BigTraceApp.instance;
const allCmds = commands.getCommands();
// OpenCommandPalette is a no-op when invoked from inside itself.
const allCmds = commands
.getCommands()
.filter((c) => c.id !== 'bigtrace.OpenCommandPalette');
const filteredCmds = fuzzyFilterCommands(allCmds, omnibox.text);

const commandsWithHeuristics = filteredCmds.map((cmd) => {
Expand All @@ -118,13 +121,11 @@ export class Omnibox implements m.ClassComponent {
};
});

const sorted = commandsWithHeuristics.sort((a, b) => {
if (b.recentsIndex === a.recentsIndex) {
return 0;
} else {
return b.recentsIndex - a.recentsIndex;
}
});
// Sort by recentsIndex descending — used commands (>=0) above
// never-used (-1).
const sorted = commandsWithHeuristics.sort(
(a, b) => b.recentsIndex - a.recentsIndex,
);

const options = sorted.map(({recentsIndex, cmd}): OmniboxOption => {
const {segments, id, defaultHotkey, source} = cmd;
Expand Down Expand Up @@ -223,13 +224,15 @@ export class Omnibox implements m.ClassComponent {
}
return m(OmniboxWidget, {
value: omnibox.text,
placeholder: `Search or type ${hints.join(', ')}`,
// Don't say "Search" — search submit is a no-op in BigTrace.
placeholder: `Type ${hints.join(', ')}`,
inputRef: OMNIBOX_INPUT_REF,
onInput: (value, _prev) => {
if (value === '>') {
omnibox.setMode(OmniboxMode.Command);
return;
}
// Check registered mode triggers.
if (value.length === 1 && omnibox.registeredModes.has(value)) {
omnibox.activateRegisteredMode(value);
return;
Expand All @@ -242,6 +245,8 @@ export class Omnibox implements m.ClassComponent {
}
},
onSubmit: (_value, _mod, _shift) => {
// BigTrace has no trace-level search; submitting from the search
// omnibox is a no-op other than blurring the input.
if (this.omniboxInputEl) {
this.omniboxInputEl.blur();
}
Expand Down Expand Up @@ -591,7 +596,11 @@ class OmniboxWidget implements m.ClassComponent<OmniboxWidgetAttrs> {
}
}

function fuzzyFilterCommands(commands: readonly Command[], searchTerm: string) {
// Returns Commands annotated with `segments` for highlighted rendering.
function fuzzyFilterCommands(
commands: readonly Command[],
searchTerm: string,
): Array<Command & {segments: FuzzySegment[]}> {
const finder = new FuzzyFinder(commands, ({name}) => name);
return finder.find(searchTerm).map((result) => {
return {segments: result.segments, ...result.item};
Expand Down
9 changes: 6 additions & 3 deletions ui/src/bigtrace/layout/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ import {assetSrc} from '../../base/assets';
import {Icon} from '../../widgets/icon';
import {getOrCreate} from '../../base/utils';
import {classNames} from '../../base/classnames';
import {setRoute} from '../router';
import {Routes} from '../routes';

const SIDEBAR_SECTIONS = {
bigtrace: {
title: 'BigTrace',
summary: 'Query and analyze large traces',
defaultCollapsed: false,
},
} as const;
Expand Down Expand Up @@ -57,14 +58,16 @@ export class Sidebar implements m.ClassComponent<SidebarAttrs> {
m(
'h1',
{
// Title clicks go home; setRoute keeps history/back working.
style: {
margin: 0,
fontSize: '18px',
fontWeight: 'bold',
display: 'flex',
alignItems: 'center',
gap: '8px',
cursor: 'pointer',
},
title: 'Go to BigTrace home',
onclick: () => setRoute(Routes.HOME),
},
m('img', {
src: assetSrc('assets/logo-128.png'),
Expand Down
Loading