@@ -27,7 +27,13 @@ import {bigTraceSettingsStorage} from './settings/bigtrace_settings_storage';
2727import { queryState } from './query/query_state' ;
2828import { SettingsPage } from './pages/settings_page' ;
2929import { Topbar } from './layout/topbar' ;
30- import { Sidebar , SidebarMenuItem , SIDEBAR_SECTIONS } from './layout/sidebar' ;
30+ import { BigTraceApp as BigTraceAppSingleton } from './bigtrace_app' ;
31+ import { OmniboxMode } from '../core/omnibox_manager' ;
32+ import { Sidebar , SidebarMenuItem } from './layout/sidebar' ;
33+ import { HotkeyConfig , HotkeyContext } from '../widgets/hotkey_context' ;
34+ import { initAssets } from '../base/assets' ;
35+ import { getCurrentRoute , initRouter } from './router' ;
36+ import { Routes } from './routes' ;
3137
3238function getRoot ( ) {
3339 // Works out the root directory where the content should be served from
@@ -82,6 +88,7 @@ function main() {
8288 } ) ;
8389 }
8490 setupContentSecurityPolicy ( ) ;
91+ initAssets ( ) ;
8592 // Settings will be lazy-loaded by the UI components that require them.
8693
8794 // Load the css. The load is asynchronous and the CSS is not ready by the time
@@ -101,7 +108,7 @@ function main() {
101108 document . head . append ( css ) ;
102109
103110 // Add Error handlers for JS error and for uncaught exceptions in promises.
104- addErrorHandler ( ( err : ErrorDetails ) => console . log ( err . message , err . stack ) ) ;
111+ addErrorHandler ( ( err : ErrorDetails ) => console . error ( err . message , err . stack ) ) ;
105112 window . addEventListener ( 'error' , ( e ) => reportError ( e ) ) ;
106113 window . addEventListener ( 'unhandledrejection' , ( e ) => reportError ( e ) ) ;
107114
@@ -117,134 +124,158 @@ function main() {
117124 cssLoadPromise . then ( ( ) => onCssLoaded ( ) ) ;
118125}
119126
120- class BigTraceApp implements m . ClassComponent {
127+ // Allows the sidebar toggle command (registered globally) to reach into the
128+ // BigTraceApp component's local state.
129+ let sidebarToggleFn : ( ( ) => void ) | undefined ;
130+
131+ class BigTraceLayout implements m . ClassComponent {
121132 private sidebarVisible = true ;
122133
123134 oninit ( ) {
124135 bigTraceSettingsStorage . loadSettings ( ) ;
136+ sidebarToggleFn = ( ) => {
137+ this . sidebarVisible = ! this . sidebarVisible ;
138+ } ;
125139 }
126140
127141 view ( vnode : m . Vnode ) {
128- const currentRoute = m . route . get ( ) ;
142+ const currentRoute = getCurrentRoute ( ) ;
129143
130144 const items : SidebarMenuItem [ ] = [
131145 {
132146 section : 'home' ,
133147 text : 'Home' ,
134- href : '#!/' ,
148+ href : `#! ${ Routes . HOME } ` ,
135149 icon : 'home' ,
136- active : currentRoute === '/' || currentRoute === '' ,
150+ active : currentRoute === Routes . HOME || currentRoute === '' ,
137151 onclick : ( ) => { } ,
138152 } ,
139153 {
140154 section : 'query' ,
141155 text : 'Query Editor' ,
142- href : '#!/query' ,
156+ href : `#! ${ Routes . QUERY } ` ,
143157 icon : 'line_style' ,
144- active : currentRoute === '/query' ,
158+ active : currentRoute === Routes . QUERY ,
145159 onclick : ( ) => { } ,
146160 } ,
147161 {
148162 section : 'settings' ,
149163 text : 'Settings' ,
150- href : '#!/settings' ,
164+ href : `#! ${ Routes . SETTINGS } ` ,
151165 icon : 'settings' ,
152- active : currentRoute === '/settings' ,
166+ active : currentRoute === Routes . SETTINGS ,
153167 onclick : ( ) => { } ,
154168 } ,
155169 ] ;
156170
157- const currentItem = items . find ( ( item ) => item . active ) ;
158- const title = currentItem
159- ? `${ SIDEBAR_SECTIONS [ currentItem . section ] . title } > ${ currentItem . text } `
160- : '' ;
161-
162- return m (
163- '.pf-ui-main' ,
164- {
165- style : {
166- display : 'flex' ,
167- height : '100vh' ,
168- overflow : 'hidden' ,
171+ return m ( 'main.pf-ui-main' , [
172+ m ( Sidebar , {
173+ items,
174+ onToggleSidebar : ( ) => {
175+ this . sidebarVisible = ! this . sidebarVisible ;
169176 } ,
170- } ,
171- [
172- // Left Sidebar
173- m ( Sidebar , {
174- items,
175- onToggleSidebar : ( ) => {
176- this . sidebarVisible = ! this . sidebarVisible ;
177- } ,
178- visible : this . sidebarVisible ,
179- } ) ,
180-
181- m (
182- '.pf-main-content' ,
183- {
184- style : {
185- display : 'flex' ,
186- flexDirection : 'column' ,
187- flex : 1 ,
188- overflow : 'hidden' ,
189- } ,
190- } ,
191- [
192- m ( Topbar , {
193- sidebarVisible : this . sidebarVisible ,
194- onToggleSidebar : ( ) => {
195- this . sidebarVisible = ! this . sidebarVisible ;
196- } ,
197- title : title ,
198- } ) ,
199- vnode . children ,
200- ] ,
201- ) ,
202- ] ,
203- ) ;
177+ visible : this . sidebarVisible ,
178+ } ) ,
179+ m ( Topbar , { sidebarVisible : this . sidebarVisible } ) ,
180+ m ( '.pf-ui-main__page-container' , vnode . children ) ,
181+ ] ) ;
204182 }
205183}
206184
207- const BigTraceLayout : m . Component = {
208- view ( vnode ) {
185+ // Root component: routing, theme, hotkeys, and layout.
186+ // Uses m.mount (not m.route) so that all rendering goes through the raf
187+ // scheduler's mount system. m.route() caches the original m.mount and
188+ // bypasses the raf scheduler, which breaks cross-tree redraws for
189+ // portal-based popups (e.g. the omnibox dropdown).
190+ class BigTraceRoot implements m . ClassComponent {
191+ private prevRoute = '' ;
192+ private queryInitialQuery : string | undefined ;
193+
194+ view ( ) : m . Children {
195+ const route = getCurrentRoute ( ) ;
196+
197+ // Capture initialQuery on first navigation to /query.
198+ if ( route === Routes . QUERY && this . prevRoute !== Routes . QUERY ) {
199+ this . queryInitialQuery = queryState . initialQuery ;
200+ queryState . initialQuery = undefined ;
201+ }
202+ this . prevRoute = route ;
203+
204+ const page = this . resolvePage ( route ) ;
205+
209206 const theme = settingsStorage . get ( 'theme' ) ;
210207 const themeValue = theme ? theme . get ( ) : 'light' ;
208+
209+ const commands = BigTraceAppSingleton . instance . commands ;
210+ const hotkeys : HotkeyConfig [ ] = [ ] ;
211+ for ( const { id, defaultHotkey} of commands . commands ) {
212+ if ( defaultHotkey ) {
213+ hotkeys . push ( {
214+ callback : ( ) => commands . runCommand ( id ) ,
215+ hotkey : defaultHotkey ,
216+ } ) ;
217+ }
218+ }
219+
211220 return m ( ThemeProvider , { theme : themeValue as 'dark' | 'light' } , [
212- m ( OverlayContainer , { fillHeight : true } , [ m ( BigTraceApp , vnode . children ) ] ) ,
221+ m (
222+ HotkeyContext ,
223+ { hotkeys, fillHeight : true , focusable : false } ,
224+ m ( OverlayContainer , { fillHeight : true } , [ m ( BigTraceLayout , page ) ] ) ,
225+ ) ,
213226 ] ) ;
214- } ,
215- } ;
227+ }
216228
217- function onCssLoaded ( ) {
218- // Clear all the contents of the initial page
219- document . body . innerHTML = '' ;
229+ private resolvePage ( route : string ) : m . Children {
230+ switch ( route ) {
231+ case Routes . QUERY :
232+ return m ( QueryPage , {
233+ useBrushBackend : true ,
234+ initialQuery : this . queryInitialQuery ,
235+ } ) ;
236+ case Routes . SETTINGS :
237+ return m ( SettingsPage ) ;
238+ default :
239+ return m ( HomePage ) ;
240+ }
241+ }
242+ }
220243
221- m . route . prefix = '#!' ;
222- m . route ( document . body , '/' , {
223- '/' : {
224- render : ( ) => m ( BigTraceLayout , m ( HomePage ) ) ,
225- } ,
226- '/query' : {
227- onmatch : ( ) => {
228- const initialQuery = queryState . initialQuery ;
229- queryState . initialQuery = undefined ;
230- return {
231- view : ( ) =>
232- m (
233- BigTraceLayout ,
234- m ( QueryPage , {
235- useBrushBackend : true ,
236- initialQuery,
237- } ) ,
238- ) ,
239- } ;
240- } ,
244+ function registerCommands ( ) {
245+ const app = BigTraceAppSingleton . instance ;
246+
247+ app . commands . registerCommand ( {
248+ id : 'bigtrace.ToggleTheme' ,
249+ name : 'Toggle UI Theme (Dark/Light)' ,
250+ callback : ( ) => {
251+ const theme = settingsStorage . get ( 'theme' ) ;
252+ if ( theme ) theme . set ( theme . get ( ) === 'light' ? 'dark' : 'light' ) ;
241253 } ,
242- '/settings' : {
243- render : ( ) => m ( BigTraceLayout , m ( SettingsPage ) ) ,
254+ } ) ;
255+
256+ app . commands . registerCommand ( {
257+ id : 'bigtrace.OpenCommandPalette' ,
258+ name : 'Open command palette' ,
259+ callback : ( ) => app . omnibox . setMode ( OmniboxMode . Command ) ,
260+ defaultHotkey : '!Mod+Shift+P' ,
261+ } ) ;
262+
263+ app . commands . registerCommand ( {
264+ id : 'bigtrace.ToggleLeftSidebar' ,
265+ name : 'Toggle left sidebar' ,
266+ callback : ( ) => {
267+ sidebarToggleFn ?.( ) ;
244268 } ,
269+ defaultHotkey : '!Mod+B' ,
245270 } ) ;
271+ }
246272
273+ function onCssLoaded ( ) {
274+ document . body . innerHTML = '' ;
275+ initRouter ( ) ;
276+ m . mount ( document . body , BigTraceRoot ) ;
247277 initLiveReload ( ) ;
278+ registerCommands ( ) ;
248279}
249280
250281main ( ) ;
0 commit comments