Move (almost) all widgets into ui/src/widgets

timestamp.ts and vega_view.ts rely on actions/globals and are a bit more
tricky to disentangle from frontend.

Change-Id: I4d0a2ce59a89cd54224acab597cba2b6c14542fe
diff --git a/ui/src/frontend/classnames.ts b/ui/src/base/classnames.ts
similarity index 100%
rename from ui/src/frontend/classnames.ts
rename to ui/src/base/classnames.ts
diff --git a/ui/src/frontend/classnames_unittest.ts b/ui/src/base/classnames_unittest.ts
similarity index 100%
rename from ui/src/frontend/classnames_unittest.ts
rename to ui/src/base/classnames_unittest.ts
diff --git a/ui/src/base/clipboard.ts b/ui/src/base/clipboard.ts
new file mode 100644
index 0000000..acb6dbb
--- /dev/null
+++ b/ui/src/base/clipboard.ts
@@ -0,0 +1,33 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export async function copyToClipboard(text: string): Promise<void> {
+  try {
+    // TODO(hjd): Fix typescript type for navigator.
+    await (navigator as any).clipboard.writeText(text);
+  } catch (err) {
+    console.error(`Failed to copy "${text}" to clipboard: ${err}`);
+  }
+}
+
+export function download(file: File, name?: string): void {
+  const url = URL.createObjectURL(file);
+  const a = document.createElement('a');
+  a.href = url;
+  a.download = name === undefined ? file.name : name;
+  document.body.appendChild(a);
+  a.click();
+  document.body.removeChild(a);
+  URL.revokeObjectURL(url);
+}
diff --git a/ui/src/frontend/icons.ts b/ui/src/base/icons.ts
similarity index 100%
rename from ui/src/frontend/icons.ts
rename to ui/src/base/icons.ts
diff --git a/ui/src/frontend/semantic_icons.ts b/ui/src/base/semantic_icons.ts
similarity index 100%
rename from ui/src/frontend/semantic_icons.ts
rename to ui/src/base/semantic_icons.ts
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index b11f0df..b8ac43c 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -14,6 +14,7 @@
 
 import m from 'mithril';
 
+import {copyToClipboard} from '../base/clipboard';
 import {Trash} from '../base/disposable';
 import {findRef} from '../base/dom_utils';
 import {FuzzyFinder} from '../base/fuzzy';
@@ -30,9 +31,11 @@
 import {setTimestampFormat, TimestampFormat} from '../common/timestamp_format';
 import {raf} from '../core/raf_scheduler';
 import {Command} from '../public';
+import {HotkeyConfig, HotkeyContext} from '../widgets/hotkey_context';
+import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
 
 import {addTab} from './bottom_tab';
-import {copyToClipboard, onClickCopy} from './clipboard';
+import {onClickCopy} from './clipboard';
 import {CookieConsent} from './cookie_consent';
 import {globals} from './globals';
 import {toggleHelp} from './help_modal';
@@ -45,8 +48,6 @@
 import {SqlTables} from './sql_table/well_known_tables';
 import {Topbar} from './topbar';
 import {shareTrace} from './trace_attrs';
-import {HotkeyConfig, HotkeyContext} from './widgets/hotkey_context';
-import {HotkeyGlyphs} from './widgets/hotkey_glyphs';
 
 function renderPermalink(): m.Children {
   const permalink = globals.state.permalink;
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index 3cb4eb5..571fc70 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -14,12 +14,20 @@
 
 import m from 'mithril';
 
+import {Icons} from '../base/semantic_icons';
 import {duration, Time} from '../base/time';
 import {exists} from '../base/utils';
 import {EngineProxy} from '../common/engine';
 import {runQuery} from '../common/queries';
 import {LONG, LONG_NULL, NUM, STR_NULL} from '../common/query_result';
 import {addDebugTrack} from '../tracks/debug/slice_track';
+import {Button} from '../widgets/button';
+import {DetailsShell} from '../widgets/details_shell';
+import {DurationWidget} from '../widgets/duration';
+import {GridLayout, GridLayoutColumn} from '../widgets/grid_layout';
+import {MenuItem, PopupMenu2} from '../widgets/menu';
+import {Section} from '../widgets/section';
+import {Tree, TreeNode} from '../widgets/tree';
 
 import {
   BottomTab,
@@ -29,18 +37,10 @@
 import {FlowPoint, globals} from './globals';
 import {PanelSize} from './panel';
 import {runQueryInNewTab} from './query_result_tab';
-import {Icons} from './semantic_icons';
 import {renderArguments} from './slice_args';
 import {renderDetails} from './slice_details';
 import {getSlice, SliceDetails, SliceRef} from './sql/slice';
 import {asSliceSqlId} from './sql_types';
-import {Button} from './widgets/button';
-import {DetailsShell} from './widgets/details_shell';
-import {DurationWidget} from './widgets/duration';
-import {GridLayout, GridLayoutColumn} from './widgets/grid_layout';
-import {MenuItem, PopupMenu2} from './widgets/menu';
-import {Section} from './widgets/section';
-import {Tree, TreeNode} from './widgets/tree';
 
 interface ContextMenuItem {
   name: string;
diff --git a/ui/src/frontend/clipboard.ts b/ui/src/frontend/clipboard.ts
index 38d0099..b5f51a1 100644
--- a/ui/src/frontend/clipboard.ts
+++ b/ui/src/frontend/clipboard.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {copyToClipboard} from '../base/clipboard';
 import {Actions} from '../common/actions';
 import {QueryResponse} from '../common/queries';
 
@@ -26,15 +27,6 @@
   };
 }
 
-export async function copyToClipboard(text: string): Promise<void> {
-  try {
-    // TODO(hjd): Fix typescript type for navigator.
-    await(navigator as any).clipboard.writeText(text);
-  } catch (err) {
-    console.error(`Failed to copy "${text}" to clipboard: ${err}`);
-  }
-}
-
 export async function queryResponseToClipboard(resp: QueryResponse):
     Promise<void> {
   const lines: string[][] = [];
@@ -49,16 +41,3 @@
   }
   copyToClipboard(lines.map((line) => line.join('\t')).join('\n'));
 }
-
-export function download(file: File, name?: string): void {
-  const url = URL.createObjectURL(file);
-  const a = document.createElement('a');
-  a.href = url;
-  a.download = name === undefined ? file.name : name;
-  document.body.appendChild(a);
-  a.click();
-  document.body.removeChild(a);
-  URL.revokeObjectURL(url);
-}
-
-
diff --git a/ui/src/frontend/counter_panel.ts b/ui/src/frontend/counter_panel.ts
index 118d457..3d9e8c7 100644
--- a/ui/src/frontend/counter_panel.ts
+++ b/ui/src/frontend/counter_panel.ts
@@ -14,13 +14,14 @@
 
 import m from 'mithril';
 
+import {DetailsShell} from '../widgets/details_shell';
+import {DurationWidget} from '../widgets/duration';
+import {GridLayout} from '../widgets/grid_layout';
+import {Section} from '../widgets/section';
+import {Tree, TreeNode} from '../widgets/tree';
+
 import {globals} from './globals';
-import {DetailsShell} from './widgets/details_shell';
-import {DurationWidget} from './widgets/duration';
-import {GridLayout} from './widgets/grid_layout';
-import {Section} from './widgets/section';
 import {Timestamp} from './widgets/timestamp';
-import {Tree, TreeNode} from './widgets/tree';
 
 export class CounterDetailsPanel implements m.ClassComponent {
   view() {
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index 915bd94..5612991 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -32,6 +32,8 @@
 } from '../common/state';
 import {profileType} from '../controller/flamegraph_controller';
 import {raf} from '../core/raf_scheduler';
+import {Button} from '../widgets/button';
+import {DurationWidget} from '../widgets/duration';
 
 import {Flamegraph, NodeRendering} from './flamegraph';
 import {globals} from './globals';
@@ -41,8 +43,6 @@
 import {Router} from './router';
 import {getCurrentTrace} from './sidebar';
 import {convertTraceToPprofAndDownload} from './trace_converter';
-import {Button} from './widgets/button';
-import {DurationWidget} from './widgets/duration';
 
 interface FlamegraphDetailsPanelAttrs {}
 
diff --git a/ui/src/frontend/flow_events_panel.ts b/ui/src/frontend/flow_events_panel.ts
index 28a40cb..2debfba 100644
--- a/ui/src/frontend/flow_events_panel.ts
+++ b/ui/src/frontend/flow_events_panel.ts
@@ -14,13 +14,13 @@
 
 import m from 'mithril';
 
+import {BLANK_CHECKBOX, CHECKBOX} from '../base/icons';
 import {Actions} from '../common/actions';
 import {raf} from '../core/raf_scheduler';
+import {DurationWidget} from '../widgets/duration';
 
 import {Flow, globals} from './globals';
-import {BLANK_CHECKBOX, CHECKBOX} from './icons';
 import {Panel, PanelSize} from './panel';
-import {DurationWidget} from './widgets/duration';
 
 export const ALL_CATEGORIES = '_all_';
 
diff --git a/ui/src/frontend/ftrace_panel.ts b/ui/src/frontend/ftrace_panel.ts
index 84e895f..81f6592 100644
--- a/ui/src/frontend/ftrace_panel.ts
+++ b/ui/src/frontend/ftrace_panel.ts
@@ -18,18 +18,18 @@
 import {Actions} from '../common/actions';
 import {colorForString} from '../common/colorizer';
 import {StringListPatch} from '../common/state';
-
-import {globals} from './globals';
-import {Panel} from './panel';
-import {DetailsShell} from './widgets/details_shell';
+import {DetailsShell} from '../widgets/details_shell';
 import {
   MultiSelectDiff,
   Option as MultiSelectOption,
   PopupMultiSelect,
-} from './widgets/multiselect';
-import {PopupPosition} from './widgets/popup';
+} from '../widgets/multiselect';
+import {PopupPosition} from '../widgets/popup';
+import {VirtualScrollContainer} from '../widgets/virtual_scroll_container';
+
+import {globals} from './globals';
+import {Panel} from './panel';
 import {Timestamp} from './widgets/timestamp';
-import {VirtualScrollContainer} from './widgets/virtual_scroll_container';
 
 const ROW_H = 20;
 const PAGE_SIZE = 250;
diff --git a/ui/src/frontend/generic_slice_details_tab.ts b/ui/src/frontend/generic_slice_details_tab.ts
index b0a6570..46813d2 100644
--- a/ui/src/frontend/generic_slice_details_tab.ts
+++ b/ui/src/frontend/generic_slice_details_tab.ts
@@ -16,14 +16,14 @@
 
 import {ColumnType} from '../common/query_result';
 import {raf} from '../core/raf_scheduler';
+import {DetailsShell} from '../widgets/details_shell';
+import {GridLayout} from '../widgets/grid_layout';
+import {Section} from '../widgets/section';
+import {SqlRef} from '../widgets/sql_ref';
+import {dictToTree, Tree, TreeNode} from '../widgets/tree';
 
 import {BottomTab, bottomTabRegistry, NewBottomTabArgs} from './bottom_tab';
 import {sqlValueToString} from './sql_utils';
-import {DetailsShell} from './widgets/details_shell';
-import {GridLayout} from './widgets/grid_layout';
-import {Section} from './widgets/section';
-import {SqlRef} from './widgets/sql_ref';
-import {dictToTree, Tree, TreeNode} from './widgets/tree';
 
 export interface ColumnConfig {
   displayName?: string;
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 3f93caf..46c26a3 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -16,6 +16,7 @@
 import m from 'mithril';
 
 import {raf} from '../core/raf_scheduler';
+import {Spinner} from '../widgets/spinner';
 
 import {globals} from './globals';
 import {
@@ -25,7 +26,6 @@
 } from './keyboard_layout_map';
 import {showModal} from './modal';
 import {KeyMapping} from './pan_and_zoom_handler';
-import {Spinner} from './widgets/spinner';
 
 export function toggleHelp() {
   globals.logging.logEvent('User Actions', 'Show help');
diff --git a/ui/src/frontend/home_page.ts b/ui/src/frontend/home_page.ts
index a45737b..99a3c03 100644
--- a/ui/src/frontend/home_page.ts
+++ b/ui/src/frontend/home_page.ts
@@ -15,11 +15,11 @@
 import m from 'mithril';
 
 import {channelChanged, getNextChannel, setChannel} from '../common/channels';
+import {Anchor} from '../widgets/anchor';
+import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
 
-import {Anchor} from './anchor';
 import {globals} from './globals';
 import {createPage} from './pages';
-import {HotkeyGlyphs} from './widgets/hotkey_glyphs';
 
 export class Hints implements m.ClassComponent {
   view() {
diff --git a/ui/src/frontend/logs_filters.ts b/ui/src/frontend/logs_filters.ts
index 9ec5da2..d416659 100644
--- a/ui/src/frontend/logs_filters.ts
+++ b/ui/src/frontend/logs_filters.ts
@@ -15,11 +15,11 @@
 import m from 'mithril';
 
 import {Actions} from '../common/actions';
+import {Button} from '../widgets/button';
+import {Select} from '../widgets/select';
+import {TextInput} from '../widgets/text_input';
 
 import {globals} from './globals';
-import {Button} from './widgets/button';
-import {Select} from './widgets/select';
-import {TextInput} from './widgets/text_input';
 
 export const LOG_PRIORITIES =
     ['-', '-', 'Verbose', 'Debug', 'Info', 'Warn', 'Error', 'Fatal'];
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index d1c981c..6c47f48 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -24,14 +24,14 @@
   LogEntriesKey,
 } from '../common/logs';
 import {raf} from '../core/raf_scheduler';
+import {DetailsShell} from '../widgets/details_shell';
+import {VirtualScrollContainer} from '../widgets/virtual_scroll_container';
 
 import {SELECTED_LOG_ROWS_COLOR} from './css_constants';
 import {globals} from './globals';
 import {LOG_PRIORITIES, LogsFilters} from './logs_filters';
 import {Panel} from './panel';
-import {DetailsShell} from './widgets/details_shell';
 import {Timestamp} from './widgets/timestamp';
-import {VirtualScrollContainer} from './widgets/virtual_scroll_container';
 
 const ROW_H = 20;
 
diff --git a/ui/src/frontend/metrics_page.ts b/ui/src/frontend/metrics_page.ts
index b1586a0..1a022e8 100644
--- a/ui/src/frontend/metrics_page.ts
+++ b/ui/src/frontend/metrics_page.ts
@@ -27,11 +27,11 @@
 import {STR} from '../common/query_result';
 import {raf} from '../core/raf_scheduler';
 import {MetricVisualisation} from '../public';
+import {Select} from '../widgets/select';
+import {Spinner} from '../widgets/spinner';
 
 import {globals} from './globals';
 import {createPage} from './pages';
-import {Select} from './widgets/select';
-import {Spinner} from './widgets/spinner';
 import {VegaView} from './widgets/vega_view';
 
 type Format = 'json'|'prototext'|'proto';
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 362a1ae..9ddaef0 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -14,11 +14,13 @@
 
 import m from 'mithril';
 
+import {Icons} from '../base/semantic_icons';
 import {Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {randomColor} from '../common/colorizer';
 import {AreaNote, Note} from '../common/state';
 import {raf} from '../core/raf_scheduler';
+import {Button} from '../widgets/button';
 
 import {
   BottomTab,
@@ -35,9 +37,7 @@
   timeScaleForVisibleWindow,
 } from './gridline_helper';
 import {Panel, PanelSize} from './panel';
-import {Icons} from './semantic_icons';
 import {isTraceLoaded} from './sidebar';
-import {Button} from './widgets/button';
 import {Timestamp} from './widgets/timestamp';
 
 const FLAG_WIDTH = 16;
diff --git a/ui/src/frontend/omnibox.ts b/ui/src/frontend/omnibox.ts
index 7975ad1..fc36b56 100644
--- a/ui/src/frontend/omnibox.ts
+++ b/ui/src/frontend/omnibox.ts
@@ -14,14 +14,13 @@
 
 import m from 'mithril';
 
+import {classNames} from '../base/classnames';
 import {FuzzySegment} from '../base/fuzzy';
 import {exists} from '../base/utils';
 import {raf} from '../core/raf_scheduler';
-
-import {classNames} from './classnames';
-import {EmptyState} from './widgets/empty_state';
-import {KeycapGlyph} from './widgets/hotkey_glyphs';
-import {Popup} from './widgets/popup';
+import {EmptyState} from '../widgets/empty_state';
+import {KeycapGlyph} from '../widgets/hotkey_glyphs';
+import {Popup} from '../widgets/popup';
 
 interface OmniboxOptionRowAttrs {
   // Human readable display name for the option.
diff --git a/ui/src/frontend/pivot_table.ts b/ui/src/frontend/pivot_table.ts
index 613c97f..9a7bb15 100644
--- a/ui/src/frontend/pivot_table.ts
+++ b/ui/src/frontend/pivot_table.ts
@@ -28,6 +28,7 @@
   SortDirection,
 } from '../common/state';
 import {raf} from '../core/raf_scheduler';
+import {DurationWidget} from '../widgets/duration';
 
 import {addTab} from './bottom_tab';
 import {globals} from './globals';
@@ -51,7 +52,6 @@
 import {SqlTableTab} from './sql_table/tab';
 import {SqlTables} from './sql_table/well_known_tables';
 import {AttributeModalHolder} from './tables/attribute_modal_holder';
-import {DurationWidget} from './widgets/duration';
 
 interface PathItem {
   tree: PivotTree;
diff --git a/ui/src/frontend/query_history.ts b/ui/src/frontend/query_history.ts
index 457ce63..d094bae 100644
--- a/ui/src/frontend/query_history.ts
+++ b/ui/src/frontend/query_history.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {STAR} from './icons';
+import {STAR} from '../base/icons';
 
 import {
   arrayOf,
@@ -25,7 +25,7 @@
   ValidatedType,
 } from '../base/validators';
 import {assertTrue} from '../base/logging';
-import {Icon} from './widgets/icon';
+import {Icon} from '../widgets/icon';
 import {raf} from '../core/raf_scheduler';
 
 const QUERY_HISTORY_KEY = 'queryHistory';
diff --git a/ui/src/frontend/query_page.ts b/ui/src/frontend/query_page.ts
index 9ae3043..97a655d 100644
--- a/ui/src/frontend/query_page.ts
+++ b/ui/src/frontend/query_page.ts
@@ -21,6 +21,8 @@
 import {EngineProxy} from '../common/engine';
 import {QueryResponse, runQuery} from '../common/queries';
 import {raf} from '../core/raf_scheduler';
+import {Callout} from '../widgets/callout';
+import {Editor} from '../widgets/editor';
 
 import {addTab} from './bottom_tab';
 import {globals} from './globals';
@@ -28,8 +30,6 @@
 import {QueryHistoryComponent, queryHistoryStorage} from './query_history';
 import {QueryResultTab} from './query_result_tab';
 import {QueryTable} from './query_table';
-import {Callout} from './widgets/callout';
-import {Editor} from './widgets/editor';
 
 interface QueryPageState {
   enteredText: string;
diff --git a/ui/src/frontend/query_result_tab.ts b/ui/src/frontend/query_result_tab.ts
index b3e8baf..77de912 100644
--- a/ui/src/frontend/query_result_tab.ts
+++ b/ui/src/frontend/query_result_tab.ts
@@ -23,6 +23,9 @@
   AddDebugTrackMenu,
   uuidToViewName,
 } from '../tracks/debug/add_debug_track_menu';
+import {Button} from '../widgets/button';
+import {PopupMenu2} from '../widgets/menu';
+import {PopupPosition} from '../widgets/popup';
 
 import {
   addTab,
@@ -32,9 +35,6 @@
   NewBottomTabArgs,
 } from './bottom_tab';
 import {QueryTable} from './query_table';
-import {Button} from './widgets/button';
-import {PopupMenu2} from './widgets/menu';
-import {PopupPosition} from './widgets/popup';
 
 export function runQueryInNewTab(query: string, title: string, tag?: string) {
   return addTab({
diff --git a/ui/src/frontend/query_table.ts b/ui/src/frontend/query_table.ts
index 7849b49..1f49b99 100644
--- a/ui/src/frontend/query_table.ts
+++ b/ui/src/frontend/query_table.ts
@@ -16,21 +16,22 @@
 import m from 'mithril';
 
 import {BigintMath} from '../base/bigint_math';
+import {copyToClipboard} from '../base/clipboard';
 import {Duration, Time} from '../base/time';
 import {Actions} from '../common/actions';
 import {QueryResponse} from '../common/queries';
 import {Row} from '../common/query_result';
+import {Anchor} from '../widgets/anchor';
+import {Button} from '../widgets/button';
+import {Callout} from '../widgets/callout';
+import {DetailsShell} from '../widgets/details_shell';
 
-import {Anchor} from './anchor';
-import {copyToClipboard, queryResponseToClipboard} from './clipboard';
+import {queryResponseToClipboard} from './clipboard';
 import {downloadData} from './download_utils';
 import {globals} from './globals';
 import {Panel} from './panel';
 import {Router} from './router';
 import {reveal} from './scroll_helper';
-import {Button} from './widgets/button';
-import {Callout} from './widgets/callout';
-import {DetailsShell} from './widgets/details_shell';
 
 interface QueryTableRowAttrs {
   row: Row;
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/frontend/record_widgets.ts
index bffe793..e4489d9 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/frontend/record_widgets.ts
@@ -15,11 +15,11 @@
 import {Draft, produce} from 'immer';
 import m from 'mithril';
 
+import {copyToClipboard} from '../base/clipboard';
 import {assertExists} from '../base/logging';
 import {Actions} from '../common/actions';
 import {RecordConfig} from '../controller/record_config_types';
 
-import {copyToClipboard} from './clipboard';
 import {globals} from './globals';
 
 export declare type Setter<T> = (draft: Draft<RecordConfig>, val: T) => void;
diff --git a/ui/src/frontend/recording/chrome_settings.ts b/ui/src/frontend/recording/chrome_settings.ts
index c517b05..ca38f0f 100644
--- a/ui/src/frontend/recording/chrome_settings.ts
+++ b/ui/src/frontend/recording/chrome_settings.ts
@@ -18,6 +18,12 @@
 import {Actions} from '../../common/actions';
 import {DataSource} from '../../common/recordingV2/recording_interfaces_v2';
 import {getBuiltinChromeCategoryList, isChromeTarget} from '../../common/state';
+import {
+  MultiSelect,
+  MultiSelectDiff,
+  Option as MultiSelectOption,
+} from '../../widgets/multiselect';
+import {Section} from '../../widgets/section';
 import {globals} from '../globals';
 import {
   CategoryGetter,
@@ -25,12 +31,6 @@
   Toggle,
   ToggleAttrs,
 } from '../record_widgets';
-import {
-  MultiSelect,
-  MultiSelectDiff,
-  Option as MultiSelectOption,
-} from '../widgets/multiselect';
-import {Section} from '../widgets/section';
 
 import {RecordingSectionAttrs} from './recording_sections';
 
diff --git a/ui/src/frontend/slice_args.ts b/ui/src/frontend/slice_args.ts
index 3902f51..5bb8a8c 100644
--- a/ui/src/frontend/slice_args.ts
+++ b/ui/src/frontend/slice_args.ts
@@ -14,23 +14,23 @@
 
 import m from 'mithril';
 
+import {Icons} from '../base/semantic_icons';
 import {sqliteString} from '../base/string_utils';
 import {exists} from '../base/utils';
 import {Actions} from '../common/actions';
 import {EngineProxy} from '../common/engine';
 import {ArgNode, convertArgsToTree, Key} from '../controller/args_parser';
+import {Anchor} from '../widgets/anchor';
+import {MenuItem, PopupMenu2} from '../widgets/menu';
+import {Section} from '../widgets/section';
+import {Tree, TreeNode} from '../widgets/tree';
 
-import {Anchor} from './anchor';
 import {addTab} from './bottom_tab';
 import {globals} from './globals';
-import {Icons} from './semantic_icons';
 import {Arg} from './sql/args';
 import {SliceDetails} from './sql/slice';
 import {SqlTableTab} from './sql_table/tab';
 import {SqlTables} from './sql_table/well_known_tables';
-import {MenuItem, PopupMenu2} from './widgets/menu';
-import {Section} from './widgets/section';
-import {Tree, TreeNode} from './widgets/tree';
 
 // Renders slice arguments (key/value pairs) into a Tree widget.
 export function renderArguments(
diff --git a/ui/src/frontend/slice_details.ts b/ui/src/frontend/slice_details.ts
index 16075cf..1f782a7 100644
--- a/ui/src/frontend/slice_details.ts
+++ b/ui/src/frontend/slice_details.ts
@@ -18,20 +18,20 @@
 import {sqliteString} from '../base/string_utils';
 import {Duration, duration, time} from '../base/time';
 import {exists} from '../base/utils';
+import {Anchor} from '../widgets/anchor';
+import {DurationWidget} from '../widgets/duration';
+import {MenuItem, PopupMenu2} from '../widgets/menu';
+import {Section} from '../widgets/section';
+import {SqlRef} from '../widgets/sql_ref';
+import {Tree, TreeNode} from '../widgets/tree';
 
-import {Anchor} from './anchor';
 import {addTab} from './bottom_tab';
 import {globals} from './globals';
 import {SliceDetails} from './sql/slice';
 import {SqlTableTab} from './sql_table/tab';
 import {SqlTables} from './sql_table/well_known_tables';
 import {getProcessName, getThreadName} from './thread_and_process_info';
-import {DurationWidget} from './widgets/duration';
-import {MenuItem, PopupMenu2} from './widgets/menu';
-import {Section} from './widgets/section';
-import {SqlRef} from './widgets/sql_ref';
 import {Timestamp} from './widgets/timestamp';
-import {Tree, TreeNode} from './widgets/tree';
 
 function computeDuration(ts: time, dur: duration): m.Children {
   if (dur === -1n) {
diff --git a/ui/src/frontend/slice_details_panel.ts b/ui/src/frontend/slice_details_panel.ts
index c0d6675..28bd796 100644
--- a/ui/src/frontend/slice_details_panel.ts
+++ b/ui/src/frontend/slice_details_panel.ts
@@ -16,18 +16,18 @@
 
 import {Actions} from '../common/actions';
 import {translateState} from '../common/thread_state';
+import {Anchor} from '../widgets/anchor';
+import {DetailsShell} from '../widgets/details_shell';
+import {DurationWidget} from '../widgets/duration';
+import {GridLayout} from '../widgets/grid_layout';
+import {Section} from '../widgets/section';
+import {SqlRef} from '../widgets/sql_ref';
+import {Tree, TreeNode} from '../widgets/tree';
 
-import {Anchor} from './anchor';
 import {globals, SliceDetails, ThreadDesc} from './globals';
 import {scrollToTrackAndTs} from './scroll_helper';
 import {SlicePanel} from './slice_panel';
-import {DetailsShell} from './widgets/details_shell';
-import {DurationWidget} from './widgets/duration';
-import {GridLayout} from './widgets/grid_layout';
-import {Section} from './widgets/section';
-import {SqlRef} from './widgets/sql_ref';
 import {Timestamp} from './widgets/timestamp';
-import {Tree, TreeNode} from './widgets/tree';
 
 export class SliceDetailsPanel extends SlicePanel {
   view() {
diff --git a/ui/src/frontend/slice_panel.ts b/ui/src/frontend/slice_panel.ts
index 14601b1..4dafb3a 100644
--- a/ui/src/frontend/slice_panel.ts
+++ b/ui/src/frontend/slice_panel.ts
@@ -15,10 +15,10 @@
 import m from 'mithril';
 
 import {duration, time} from '../base/time';
+import {DurationWidget} from '../widgets/duration';
 
 import {globals, SliceDetails} from './globals';
 import {Panel} from './panel';
-import {DurationWidget} from './widgets/duration';
 
 // To display process or thread, we want to concatenate their name with ID, but
 // either can be undefined and all the cases need to be considered carefully to
diff --git a/ui/src/frontend/sql/slice.ts b/ui/src/frontend/sql/slice.ts
index d096723..8247487 100644
--- a/ui/src/frontend/sql/slice.ts
+++ b/ui/src/frontend/sql/slice.ts
@@ -15,6 +15,7 @@
 import m from 'mithril';
 
 import {BigintMath} from '../../base/bigint_math';
+import {Icons} from '../../base/semantic_icons';
 import {duration, Time, time} from '../../base/time';
 import {exists} from '../../base/utils';
 import {Actions} from '../../common/actions';
@@ -26,10 +27,9 @@
   STR,
   STR_NULL,
 } from '../../common/query_result';
-import {Anchor} from '../anchor';
+import {Anchor} from '../../widgets/anchor';
 import {globals} from '../globals';
 import {focusHorizontalRange, verticalScrollToTrack} from '../scroll_helper';
-import {Icons} from '../semantic_icons';
 import {
   asArgSetId,
   asSliceSqlId,
diff --git a/ui/src/frontend/sql_table/argument_selector.ts b/ui/src/frontend/sql_table/argument_selector.ts
index 9951487..a340ae4 100644
--- a/ui/src/frontend/sql_table/argument_selector.ts
+++ b/ui/src/frontend/sql_table/argument_selector.ts
@@ -17,13 +17,13 @@
 import {EngineProxy} from '../../common/engine';
 import {STR} from '../../common/query_result';
 import {raf} from '../../core/raf_scheduler';
+import {FilterableSelect} from '../../widgets/select';
+import {Spinner} from '../../widgets/spinner';
 import {
   constraintsToQueryPrefix,
   constraintsToQuerySuffix,
   SQLConstraints,
 } from '../sql_utils';
-import {FilterableSelect} from '../widgets/select';
-import {Spinner} from '../widgets/spinner';
 
 import {argColumn} from './column';
 import {ArgSetIdColumn} from './table_description';
diff --git a/ui/src/frontend/sql_table/render_cell.ts b/ui/src/frontend/sql_table/render_cell.ts
index 19c0b28..e5d5b1c 100644
--- a/ui/src/frontend/sql_table/render_cell.ts
+++ b/ui/src/frontend/sql_table/render_cell.ts
@@ -14,17 +14,17 @@
 
 import m from 'mithril';
 
+import {copyToClipboard} from '../../base/clipboard';
+import {Icons} from '../../base/semantic_icons';
 import {sqliteString} from '../../base/string_utils';
 import {duration, Duration, Time} from '../../base/time';
 import {Row, SqlValue} from '../../common/query_result';
-import {Anchor} from '../anchor';
-import {copyToClipboard} from '../clipboard';
-import {Icons} from '../semantic_icons';
+import {Anchor} from '../../widgets/anchor';
+import {Err} from '../../widgets/error';
+import {MenuItem, PopupMenu2} from '../../widgets/menu';
 import {SliceRef} from '../sql/slice';
 import {asSliceSqlId} from '../sql_types';
 import {sqlValueToString} from '../sql_utils';
-import {Err} from '../widgets/error';
-import {MenuItem, PopupMenu2} from '../widgets/menu';
 import {Timestamp} from '../widgets/timestamp';
 
 import {Column} from './column';
diff --git a/ui/src/frontend/sql_table/tab.ts b/ui/src/frontend/sql_table/tab.ts
index a210082..449b8e4 100644
--- a/ui/src/frontend/sql_table/tab.ts
+++ b/ui/src/frontend/sql_table/tab.ts
@@ -14,14 +14,14 @@
 
 import m from 'mithril';
 
+import {copyToClipboard} from '../../base/clipboard';
+import {Icons} from '../../base/semantic_icons';
 import {exists} from '../../base/utils';
 import {AddDebugTrackMenu} from '../../tracks/debug/add_debug_track_menu';
+import {Button} from '../../widgets/button';
+import {DetailsShell} from '../../widgets/details_shell';
+import {Popup, PopupPosition} from '../../widgets/popup';
 import {BottomTab, bottomTabRegistry, NewBottomTabArgs} from '../bottom_tab';
-import {copyToClipboard} from '../clipboard';
-import {Icons} from '../semantic_icons';
-import {Button} from '../widgets/button';
-import {DetailsShell} from '../widgets/details_shell';
-import {Popup, PopupPosition} from '../widgets/popup';
 
 import {SqlTableState} from './state';
 import {SqlTable} from './table';
diff --git a/ui/src/frontend/sql_table/table.ts b/ui/src/frontend/sql_table/table.ts
index 49e4d22..92f8fc9 100644
--- a/ui/src/frontend/sql_table/table.ts
+++ b/ui/src/frontend/sql_table/table.ts
@@ -14,14 +14,14 @@
 
 import m from 'mithril';
 
+import {Icons} from '../../base/semantic_icons';
 import {EngineProxy} from '../../common/engine';
 import {Row} from '../../common/query_result';
-import {Anchor} from '../anchor';
-import {Icons} from '../semantic_icons';
-import {BasicTable} from '../widgets/basic_table';
-import {Button} from '../widgets/button';
-import {MenuDivider, MenuItem, PopupMenu2} from '../widgets/menu';
-import {Spinner} from '../widgets/spinner';
+import {Anchor} from '../../widgets/anchor';
+import {BasicTable} from '../../widgets/basic_table';
+import {Button} from '../../widgets/button';
+import {MenuDivider, MenuItem, PopupMenu2} from '../../widgets/menu';
+import {Spinner} from '../../widgets/spinner';
 
 import {ArgumentSelector} from './argument_selector';
 import {argColumn, Column, columnFromSqlTableColumn} from './column';
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index d2b6ff5..687c15a 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -14,6 +14,7 @@
 
 import m from 'mithril';
 
+import {Icons} from '../base/semantic_icons';
 import {
   duration,
   Time,
@@ -23,11 +24,10 @@
 import {EngineProxy} from '../common/engine';
 import {LONG, NUM, NUM_NULL, STR_NULL} from '../common/query_result';
 import {translateState} from '../common/thread_state';
+import {Anchor} from '../widgets/anchor';
 
-import {Anchor} from './anchor';
 import {globals} from './globals';
 import {scrollToTrackAndTs} from './scroll_helper';
-import {Icons} from './semantic_icons';
 import {
   asUtid,
   SchedSqlId,
diff --git a/ui/src/frontend/thread_state_tab.ts b/ui/src/frontend/thread_state_tab.ts
index 6745a46..1a5f542 100644
--- a/ui/src/frontend/thread_state_tab.ts
+++ b/ui/src/frontend/thread_state_tab.ts
@@ -18,8 +18,15 @@
 import {runQuery} from '../common/queries';
 import {raf} from '../core/raf_scheduler';
 import {addDebugTrack} from '../tracks/debug/slice_track';
+import {Anchor} from '../widgets/anchor';
+import {Button} from '../widgets/button';
+import {DetailsShell} from '../widgets/details_shell';
+import {DurationWidget} from '../widgets/duration';
+import {GridLayout} from '../widgets/grid_layout';
+import {Section} from '../widgets/section';
+import {SqlRef} from '../widgets/sql_ref';
+import {Tree, TreeNode} from '../widgets/tree';
 
-import {Anchor} from './anchor';
 import {BottomTab, bottomTabRegistry, NewBottomTabArgs} from './bottom_tab';
 import {SchedSqlId, ThreadStateSqlId} from './sql_types';
 import {
@@ -35,14 +42,7 @@
   ThreadState,
   ThreadStateRef,
 } from './thread_state';
-import {Button} from './widgets/button';
-import {DetailsShell} from './widgets/details_shell';
-import {DurationWidget} from './widgets/duration';
-import {GridLayout} from './widgets/grid_layout';
-import {Section} from './widgets/section';
-import {SqlRef} from './widgets/sql_ref';
 import {Timestamp} from './widgets/timestamp';
-import {Tree, TreeNode} from './widgets/tree';
 
 interface ThreadStateTabConfig {
   // Id into |thread_state| sql table.
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 7c75be7..70aed74 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
+import {classNames} from '../base/classnames';
 import {raf} from '../core/raf_scheduler';
 import {VERSION} from '../gen/perfetto_version';
 
-import {classNames} from './classnames';
 import {globals} from './globals';
 import {taskTracker} from './task_tracker';
 
diff --git a/ui/src/frontend/trace_converter.ts b/ui/src/frontend/trace_converter.ts
index 97acbd4..a08f622 100644
--- a/ui/src/frontend/trace_converter.ts
+++ b/ui/src/frontend/trace_converter.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {download} from '../base/clipboard';
 import {time} from '../base/time';
 import {Actions} from '../common/actions';
 import {
@@ -19,7 +20,6 @@
   ConversionJobStatus,
 } from '../common/conversion_jobs';
 
-import {download} from './clipboard';
 import {maybeShowErrorDialog} from './error_dialog';
 import {globals} from './globals';
 import {openBufferWithLegacyTraceViewer} from './legacy_trace_viewer';
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index 6fe93c8..3be23a3 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -15,6 +15,13 @@
 import {hex} from 'color-convert';
 import m from 'mithril';
 
+import {
+  BLANK_CHECKBOX,
+  CHECKBOX,
+  EXPAND_DOWN,
+  EXPAND_UP,
+  INDETERMINATE_CHECKBOX,
+} from '../base/icons';
 import {assertExists} from '../base/logging';
 import {Actions} from '../common/actions';
 import {
@@ -25,13 +32,6 @@
 
 import {globals} from './globals';
 import {drawGridLines} from './gridline_helper';
-import {
-  BLANK_CHECKBOX,
-  CHECKBOX,
-  EXPAND_DOWN,
-  EXPAND_UP,
-  INDETERMINATE_CHECKBOX,
-} from './icons';
 import {Panel, PanelSize} from './panel';
 import {Track} from './track';
 import {TrackChips, TrackContent} from './track_panel';
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 0bef093..d08ad8b 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -15,6 +15,7 @@
 import {hex} from 'color-convert';
 import m from 'mithril';
 
+import {BLANK_CHECKBOX, CHECKBOX, PIN} from '../base/icons';
 import {duration, Span, time} from '../base/time';
 import {Actions} from '../common/actions';
 import {pluginManager} from '../common/plugins';
@@ -27,7 +28,6 @@
 import {PerfettoMouseEvent} from './events';
 import {globals} from './globals';
 import {drawGridLines} from './gridline_helper';
-import {BLANK_CHECKBOX, CHECKBOX, PIN} from './icons';
 import {Panel, PanelSize} from './panel';
 import {verticalScrollToTrack} from './scroll_helper';
 import {PxSpan, TimeScale} from './time_scale';
diff --git a/ui/src/frontend/value.ts b/ui/src/frontend/value.ts
index bf1f769..7e6688d 100644
--- a/ui/src/frontend/value.ts
+++ b/ui/src/frontend/value.ts
@@ -14,8 +14,9 @@
 
 import m from 'mithril';
 
+import {Tree, TreeNode} from '../widgets/tree';
+
 import {PopupMenuButton, PopupMenuItem} from './popup_menu';
-import {Tree, TreeNode} from './widgets/tree';
 
 // This file implements a component for rendering JSON-like values (with
 // customisation options like context menu and action buttons).
diff --git a/ui/src/frontend/viz_page.ts b/ui/src/frontend/viz_page.ts
index d8fc532..8f71e82 100644
--- a/ui/src/frontend/viz_page.ts
+++ b/ui/src/frontend/viz_page.ts
@@ -16,10 +16,10 @@
 
 import {EngineProxy} from '../common/engine';
 import {raf} from '../core/raf_scheduler';
+import {Editor} from '../widgets/editor';
 
 import {globals} from './globals';
 import {createPage} from './pages';
-import {Editor} from './widgets/editor';
 import {VegaView} from './widgets/vega_view';
 
 function getEngine(): EngineProxy|undefined {
diff --git a/ui/src/frontend/widgets/timestamp.ts b/ui/src/frontend/widgets/timestamp.ts
index aeadaa4..ed93612 100644
--- a/ui/src/frontend/widgets/timestamp.ts
+++ b/ui/src/frontend/widgets/timestamp.ts
@@ -14,15 +14,14 @@
 
 import m from 'mithril';
 
+import {copyToClipboard} from '../../base/clipboard';
+import {Icons} from '../../base/semantic_icons';
 import {time, Time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {TimestampFormat, timestampFormat} from '../../common/timestamp_format';
-import {Anchor} from '../anchor';
-import {copyToClipboard} from '../clipboard';
+import {Anchor} from '../../widgets/anchor';
+import {MenuItem, PopupMenu2} from '../../widgets/menu';
 import {globals} from '../globals';
-import {Icons} from '../semantic_icons';
-
-import {MenuItem, PopupMenu2} from './menu';
 
 // import {MenuItem, PopupMenu2} from './menu';
 
diff --git a/ui/src/frontend/widgets/vega_view.ts b/ui/src/frontend/widgets/vega_view.ts
index fb6b3d1..5dd3a1a 100644
--- a/ui/src/frontend/widgets/vega_view.ts
+++ b/ui/src/frontend/widgets/vega_view.ts
@@ -23,8 +23,7 @@
 import {getErrorMessage} from '../../common/errors';
 import {QueryError} from '../../common/query_result';
 import {scheduleFullRedraw} from '../../widgets/raf';
-
-import {Spinner} from './spinner';
+import {Spinner} from '../../widgets/spinner';
 
 function isVegaLite(spec: unknown): boolean {
   if (typeof spec === 'object') {
diff --git a/ui/src/frontend/widgets_page.ts b/ui/src/frontend/widgets_page.ts
index 4d088da..09ec172 100644
--- a/ui/src/frontend/widgets_page.ts
+++ b/ui/src/frontend/widgets_page.ts
@@ -14,37 +14,37 @@
 
 import m from 'mithril';
 
+import {classNames} from '../base/classnames';
 import {Hotkey, Platform} from '../base/hotkeys';
+import {LIBRARY_ADD_CHECK} from '../base/icons';
+import {Icons} from '../base/semantic_icons';
 import {raf} from '../core/raf_scheduler';
-
-import {Anchor} from './anchor';
-import {classNames} from './classnames';
-import {LIBRARY_ADD_CHECK} from './icons';
-import {createPage} from './pages';
-import {PopupMenuButton} from './popup_menu';
-import {Icons} from './semantic_icons';
-import {TableShowcase} from './tables/table_showcase';
-import {Button} from './widgets/button';
-import {Callout} from './widgets/callout';
-import {Checkbox} from './widgets/checkbox';
-import {Editor} from './widgets/editor';
-import {EmptyState} from './widgets/empty_state';
-import {Form, FormLabel} from './widgets/form';
-import {HotkeyGlyphs} from './widgets/hotkey_glyphs';
-import {Icon} from './widgets/icon';
-import {Menu, MenuDivider, MenuItem, PopupMenu2} from './widgets/menu';
+import {Anchor} from '../widgets/anchor';
+import {Button} from '../widgets/button';
+import {Callout} from '../widgets/callout';
+import {Checkbox} from '../widgets/checkbox';
+import {Editor} from '../widgets/editor';
+import {EmptyState} from '../widgets/empty_state';
+import {Form, FormLabel} from '../widgets/form';
+import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
+import {Icon} from '../widgets/icon';
+import {Menu, MenuDivider, MenuItem, PopupMenu2} from '../widgets/menu';
 import {
   MultiSelect,
   MultiSelectDiff,
   PopupMultiSelect,
-} from './widgets/multiselect';
-import {Popup, PopupPosition} from './widgets/popup';
-import {Portal} from './widgets/portal';
-import {FilterableSelect, Select} from './widgets/select';
-import {Spinner} from './widgets/spinner';
-import {Switch} from './widgets/switch';
-import {TextInput} from './widgets/text_input';
-import {LazyTreeNode, Tree, TreeNode} from './widgets/tree';
+} from '../widgets/multiselect';
+import {Popup, PopupPosition} from '../widgets/popup';
+import {Portal} from '../widgets/portal';
+import {FilterableSelect, Select} from '../widgets/select';
+import {Spinner} from '../widgets/spinner';
+import {Switch} from '../widgets/switch';
+import {TextInput} from '../widgets/text_input';
+import {LazyTreeNode, Tree, TreeNode} from '../widgets/tree';
+
+import {createPage} from './pages';
+import {PopupMenuButton} from './popup_menu';
+import {TableShowcase} from './tables/table_showcase';
 import {VegaView} from './widgets/vega_view';
 
 const DATA_ENGLISH_LETTER_FREQUENCY = {
diff --git a/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts
index b77beaa..e51a697 100644
--- a/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/event_latency_details_panel.ts
@@ -29,10 +29,10 @@
 import {renderDetails} from '../../frontend/slice_details';
 import {getSlice, SliceDetails, sliceRef} from '../../frontend/sql/slice';
 import {asSliceSqlId} from '../../frontend/sql_types';
-import {DetailsShell} from '../../frontend/widgets/details_shell';
-import {GridLayout, GridLayoutColumn} from '../../frontend/widgets/grid_layout';
-import {Section} from '../../frontend/widgets/section';
-import {Tree, TreeNode} from '../../frontend/widgets/tree';
+import {DetailsShell} from '../../widgets/details_shell';
+import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
+import {Tree, TreeNode} from '../../widgets/tree';
 
 import {
   getScrollJankSlices,
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
index 5f0d377..6e3154f 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_details_panel.ts
@@ -33,13 +33,13 @@
   Table,
   TableData,
 } from '../../frontend/tables/table';
-import {DetailsShell} from '../../frontend/widgets/details_shell';
-import {DurationWidget} from '../../frontend/widgets/duration';
-import {GridLayout, GridLayoutColumn} from '../../frontend/widgets/grid_layout';
-import {Section} from '../../frontend/widgets/section';
-import {SqlRef} from '../../frontend/widgets/sql_ref';
 import {Timestamp} from '../../frontend/widgets/timestamp';
-import {dictToTreeNodes, Tree} from '../../frontend/widgets/tree';
+import {DetailsShell} from '../../widgets/details_shell';
+import {DurationWidget} from '../../widgets/duration';
+import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
+import {SqlRef} from '../../widgets/sql_ref';
+import {dictToTreeNodes, Tree} from '../../widgets/tree';
 
 import {
   getScrollJankSlices,
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts
index e4ea041..00d8d8c 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_slice.ts
@@ -14,19 +14,19 @@
 
 import m from 'mithril';
 
+import {Icons} from '../../base/semantic_icons';
 import {duration, time, Time} from '../../base/time';
 import {Actions} from '../../common/actions';
 import {EngineProxy} from '../../common/engine';
 import {LONG, NUM} from '../../common/query_result';
-import {Anchor} from '../../frontend/anchor';
 import {globals} from '../../frontend/globals';
 import {scrollToTrackAndTs} from '../../frontend/scroll_helper';
-import {Icons} from '../../frontend/semantic_icons';
 import {SliceSqlId} from '../../frontend/sql_types';
 import {
   constraintsToQuerySuffix,
   SQLConstraints,
 } from '../../frontend/sql_utils';
+import {Anchor} from '../../widgets/anchor';
 
 import {EventLatencyTrack} from './event_latency_track';
 import {ScrollJankPluginState, ScrollJankTrackSpec} from './index';
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
index 5547b38..9eb2a94 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
@@ -30,13 +30,13 @@
 import {getSlice, SliceDetails} from '../../frontend/sql/slice';
 import {asSliceSqlId} from '../../frontend/sql_types';
 import {sqlValueToString} from '../../frontend/sql_utils';
-import {DetailsShell} from '../../frontend/widgets/details_shell';
-import {DurationWidget} from '../../frontend/widgets/duration';
-import {GridLayout, GridLayoutColumn} from '../../frontend/widgets/grid_layout';
-import {Section} from '../../frontend/widgets/section';
-import {SqlRef} from '../../frontend/widgets/sql_ref';
 import {Timestamp} from '../../frontend/widgets/timestamp';
-import {dictToTreeNodes, Tree, TreeNode} from '../../frontend/widgets/tree';
+import {DetailsShell} from '../../widgets/details_shell';
+import {DurationWidget} from '../../widgets/duration';
+import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
+import {SqlRef} from '../../widgets/sql_ref';
+import {dictToTreeNodes, Tree, TreeNode} from '../../widgets/tree';
 
 import {EventLatencyTrack} from './event_latency_track';
 import {
diff --git a/ui/src/tracks/counter/index.ts b/ui/src/tracks/counter/index.ts
index 7f8c20b..d0c1bba 100644
--- a/ui/src/tracks/counter/index.ts
+++ b/ui/src/tracks/counter/index.ts
@@ -23,8 +23,6 @@
 import {checkerboardExcept} from '../../frontend/checkerboard';
 import {globals} from '../../frontend/globals';
 import {NewTrackArgs, Track} from '../../frontend/track';
-import {Button} from '../../frontend/widgets/button';
-import {MenuItem, PopupMenu2} from '../../frontend/widgets/menu';
 import {
   LONG,
   LONG_NULL,
@@ -36,6 +34,8 @@
   TracePluginContext,
   TrackInfo,
 } from '../../public';
+import {Button} from '../../widgets/button';
+import {MenuItem, PopupMenu2} from '../../widgets/menu';
 
 export const COUNTER_TRACK_KIND = 'CounterTrack';
 
diff --git a/ui/src/tracks/debug/add_debug_track_menu.ts b/ui/src/tracks/debug/add_debug_track_menu.ts
index b7355c1..e7712a0 100644
--- a/ui/src/tracks/debug/add_debug_track_menu.ts
+++ b/ui/src/tracks/debug/add_debug_track_menu.ts
@@ -16,9 +16,9 @@
 
 import {findRef} from '../../base/dom_utils';
 import {EngineProxy} from '../../common/engine';
-import {Form, FormLabel} from '../../frontend/widgets/form';
-import {Select} from '../../frontend/widgets/select';
-import {TextInput} from '../../frontend/widgets/text_input';
+import {Form, FormLabel} from '../../widgets/form';
+import {Select} from '../../widgets/select';
+import {TextInput} from '../../widgets/text_input';
 
 import {addDebugTrack, SliceColumns, SqlDataSource} from './slice_track';
 
diff --git a/ui/src/tracks/debug/details_tab.ts b/ui/src/tracks/debug/details_tab.ts
index ab4d61d..d391f20 100644
--- a/ui/src/tracks/debug/details_tab.ts
+++ b/ui/src/tracks/debug/details_tab.ts
@@ -14,8 +14,6 @@
 
 import m from 'mithril';
 
-import {GridLayout} from '../..//frontend/widgets/grid_layout';
-import {Section} from '../..//frontend/widgets/section';
 import {duration, Time, time} from '../../base/time';
 import {
   ColumnType,
@@ -52,15 +50,17 @@
   ThreadState,
   threadStateRef,
 } from '../../frontend/thread_state';
-import {DetailsShell} from '../../frontend/widgets/details_shell';
-import {DurationWidget} from '../../frontend/widgets/duration';
 import {Timestamp} from '../../frontend/widgets/timestamp';
+import {DetailsShell} from '../../widgets/details_shell';
+import {DurationWidget} from '../../widgets/duration';
+import {GridLayout} from '../../widgets/grid_layout';
+import {Section} from '../../widgets/section';
 import {
   dictToTree,
   dictToTreeNodes,
   Tree,
   TreeNode,
-} from '../../frontend/widgets/tree';
+} from '../../widgets/tree';
 
 import {ARG_PREFIX} from './add_debug_track_menu';
 
diff --git a/ui/src/frontend/anchor.ts b/ui/src/widgets/anchor.ts
similarity index 100%
rename from ui/src/frontend/anchor.ts
rename to ui/src/widgets/anchor.ts
diff --git a/ui/src/frontend/widgets/basic_table.ts b/ui/src/widgets/basic_table.ts
similarity index 100%
rename from ui/src/frontend/widgets/basic_table.ts
rename to ui/src/widgets/basic_table.ts
diff --git a/ui/src/frontend/widgets/button.ts b/ui/src/widgets/button.ts
similarity index 98%
rename from ui/src/frontend/widgets/button.ts
rename to ui/src/widgets/button.ts
index ac86e13..3446613 100644
--- a/ui/src/frontend/widgets/button.ts
+++ b/ui/src/widgets/button.ts
@@ -13,7 +13,9 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {classNames} from '../classnames';
+
+import {classNames} from '../base/classnames';
+
 import {Icon} from './icon';
 import {Popup} from './popup';
 
diff --git a/ui/src/frontend/widgets/callout.ts b/ui/src/widgets/callout.ts
similarity index 100%
rename from ui/src/frontend/widgets/callout.ts
rename to ui/src/widgets/callout.ts
diff --git a/ui/src/frontend/widgets/checkbox.ts b/ui/src/widgets/checkbox.ts
similarity index 97%
rename from ui/src/frontend/widgets/checkbox.ts
rename to ui/src/widgets/checkbox.ts
index 894b633..e15adfd 100644
--- a/ui/src/frontend/widgets/checkbox.ts
+++ b/ui/src/widgets/checkbox.ts
@@ -13,7 +13,8 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {classNames} from '../classnames';
+
+import {classNames} from '../base/classnames';
 
 export interface CheckboxAttrs {
   // Optional text to show to the right of the checkbox.
diff --git a/ui/src/frontend/widgets/details_shell.ts b/ui/src/widgets/details_shell.ts
similarity index 96%
rename from ui/src/frontend/widgets/details_shell.ts
rename to ui/src/widgets/details_shell.ts
index 8b6aa41..90ba707 100644
--- a/ui/src/frontend/widgets/details_shell.ts
+++ b/ui/src/widgets/details_shell.ts
@@ -13,7 +13,8 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {classNames} from '../classnames';
+
+import {classNames} from '../base/classnames';
 
 interface DetailsShellAttrs {
   title: m.Children;
diff --git a/ui/src/frontend/widgets/duration.ts b/ui/src/widgets/duration.ts
similarity index 94%
rename from ui/src/frontend/widgets/duration.ts
rename to ui/src/widgets/duration.ts
index b8a16eb..2ae19cb 100644
--- a/ui/src/frontend/widgets/duration.ts
+++ b/ui/src/widgets/duration.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {Duration, duration} from '../../base/time';
+import {Duration, duration} from '../base/time';
 
 interface DurationWidgetAttrs {
   dur: duration;
diff --git a/ui/src/frontend/widgets/editor.ts b/ui/src/widgets/editor.ts
similarity index 100%
rename from ui/src/frontend/widgets/editor.ts
rename to ui/src/widgets/editor.ts
diff --git a/ui/src/frontend/widgets/empty_state.ts b/ui/src/widgets/empty_state.ts
similarity index 100%
rename from ui/src/frontend/widgets/empty_state.ts
rename to ui/src/widgets/empty_state.ts
diff --git a/ui/src/frontend/widgets/error.ts b/ui/src/widgets/error.ts
similarity index 100%
rename from ui/src/frontend/widgets/error.ts
rename to ui/src/widgets/error.ts
diff --git a/ui/src/frontend/widgets/form.ts b/ui/src/widgets/form.ts
similarity index 98%
rename from ui/src/frontend/widgets/form.ts
rename to ui/src/widgets/form.ts
index bbe3aa7..e654814 100644
--- a/ui/src/frontend/widgets/form.ts
+++ b/ui/src/widgets/form.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {classNames} from '../classnames';
+import {classNames} from '../base/classnames';
 
 import {Button} from './button';
 import {Popup} from './popup';
diff --git a/ui/src/frontend/widgets/grid_layout.ts b/ui/src/widgets/grid_layout.ts
similarity index 100%
rename from ui/src/frontend/widgets/grid_layout.ts
rename to ui/src/widgets/grid_layout.ts
diff --git a/ui/src/frontend/widgets/hotkey_context.ts b/ui/src/widgets/hotkey_context.ts
similarity index 96%
rename from ui/src/frontend/widgets/hotkey_context.ts
rename to ui/src/widgets/hotkey_context.ts
index 0ac7b98..91ad8ab 100644
--- a/ui/src/frontend/widgets/hotkey_context.ts
+++ b/ui/src/widgets/hotkey_context.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {checkHotkey, Hotkey} from '../../base/hotkeys';
+import {checkHotkey, Hotkey} from '../base/hotkeys';
 
 export interface HotkeyConfig {
   hotkey: Hotkey;
diff --git a/ui/src/frontend/widgets/hotkey_glyphs.ts b/ui/src/widgets/hotkey_glyphs.ts
similarity index 98%
rename from ui/src/frontend/widgets/hotkey_glyphs.ts
rename to ui/src/widgets/hotkey_glyphs.ts
index 875aafe..17573d8 100644
--- a/ui/src/frontend/widgets/hotkey_glyphs.ts
+++ b/ui/src/widgets/hotkey_glyphs.ts
@@ -20,7 +20,7 @@
   Key,
   parseHotkey,
   Platform,
-} from '../../base/hotkeys';
+} from '../base/hotkeys';
 
 import {Icon} from './icon';
 
diff --git a/ui/src/frontend/widgets/icon.ts b/ui/src/widgets/icon.ts
similarity index 96%
rename from ui/src/frontend/widgets/icon.ts
rename to ui/src/widgets/icon.ts
index cfe990a..55ada9e 100644
--- a/ui/src/frontend/widgets/icon.ts
+++ b/ui/src/widgets/icon.ts
@@ -13,7 +13,8 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {classNames} from '../classnames';
+
+import {classNames} from '../base/classnames';
 
 export interface IconAttrs {
   // The material icon name.
diff --git a/ui/src/frontend/widgets/menu.ts b/ui/src/widgets/menu.ts
similarity index 97%
rename from ui/src/frontend/widgets/menu.ts
rename to ui/src/widgets/menu.ts
index 4f2d1c7..642b085 100644
--- a/ui/src/frontend/widgets/menu.ts
+++ b/ui/src/widgets/menu.ts
@@ -14,8 +14,8 @@
 
 import m from 'mithril';
 
-import {hasChildren} from '../../base/mithril_utils';
-import {classNames} from '../classnames';
+import {classNames} from '../base/classnames';
+import {hasChildren} from '../base/mithril_utils';
 
 import {Icon} from './icon';
 import {Popup, PopupAttrs, PopupPosition} from './popup';
diff --git a/ui/src/frontend/widgets/multiselect.ts b/ui/src/widgets/multiselect.ts
similarity index 98%
rename from ui/src/frontend/widgets/multiselect.ts
rename to ui/src/widgets/multiselect.ts
index c537868..c501954 100644
--- a/ui/src/frontend/widgets/multiselect.ts
+++ b/ui/src/widgets/multiselect.ts
@@ -14,13 +14,13 @@
 
 import m from 'mithril';
 
-import {scheduleFullRedraw} from '../../widgets/raf';
-import {DESELECT, SELECT_ALL} from '../icons';
+import {DESELECT, SELECT_ALL} from '../base/icons';
 
 import {Button} from './button';
 import {Checkbox} from './checkbox';
 import {EmptyState} from './empty_state';
 import {Popup, PopupPosition} from './popup';
+import {scheduleFullRedraw} from './raf';
 import {TextInput} from './text_input';
 
 export interface Option {
diff --git a/ui/src/frontend/widgets/popup.ts b/ui/src/widgets/popup.ts
similarity index 97%
rename from ui/src/frontend/widgets/popup.ts
rename to ui/src/widgets/popup.ts
index ba19fa6..8598d3d 100644
--- a/ui/src/frontend/widgets/popup.ts
+++ b/ui/src/widgets/popup.ts
@@ -16,10 +16,10 @@
 import type {Modifier, StrictModifiers} from '@popperjs/core';
 import m from 'mithril';
 import {MountOptions, Portal, PortalAttrs} from './portal';
-import {classNames} from '../classnames';
-import {findRef, isOrContains, toHTMLElement} from '../../base/dom_utils';
-import {assertExists} from '../../base/logging';
-import {scheduleFullRedraw} from '../../widgets/raf';
+import {classNames} from '../base/classnames';
+import {findRef, isOrContains, toHTMLElement} from '../base/dom_utils';
+import {assertExists} from '../base/logging';
+import {scheduleFullRedraw} from './raf';
 
 type CustomModifier = Modifier<'sameWidth', {}>;
 type ExtendedModifiers = StrictModifiers|CustomModifier;
diff --git a/ui/src/frontend/widgets/portal.ts b/ui/src/widgets/portal.ts
similarity index 100%
rename from ui/src/frontend/widgets/portal.ts
rename to ui/src/widgets/portal.ts
diff --git a/ui/src/frontend/widgets/section.ts b/ui/src/widgets/section.ts
similarity index 100%
rename from ui/src/frontend/widgets/section.ts
rename to ui/src/widgets/section.ts
diff --git a/ui/src/frontend/widgets/select.ts b/ui/src/widgets/select.ts
similarity index 96%
rename from ui/src/frontend/widgets/select.ts
rename to ui/src/widgets/select.ts
index abae62d..8d4cca5 100644
--- a/ui/src/frontend/widgets/select.ts
+++ b/ui/src/widgets/select.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
-import {exists} from '../../base/utils';
-import {scheduleFullRedraw} from '../../widgets/raf';
+import {exists} from '../base/utils';
 
 import {Menu, MenuItem} from './menu';
+import {scheduleFullRedraw} from './raf';
 import {TextInput} from './text_input';
 
 export interface SelectAttrs {
diff --git a/ui/src/frontend/widgets/spinner.ts b/ui/src/widgets/spinner.ts
similarity index 95%
rename from ui/src/frontend/widgets/spinner.ts
rename to ui/src/widgets/spinner.ts
index b1beb9f..8eab55a 100644
--- a/ui/src/frontend/widgets/spinner.ts
+++ b/ui/src/widgets/spinner.ts
@@ -13,7 +13,8 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {classNames} from '../classnames';
+
+import {classNames} from '../base/classnames';
 
 interface SpinnerAttrs {
   // Whether to use an ease-in-ease-out animation rather than a linear one.
diff --git a/ui/src/frontend/widgets/sql_ref.ts b/ui/src/widgets/sql_ref.ts
similarity index 92%
rename from ui/src/frontend/widgets/sql_ref.ts
rename to ui/src/widgets/sql_ref.ts
index 1882fc5..85a788d 100644
--- a/ui/src/frontend/widgets/sql_ref.ts
+++ b/ui/src/widgets/sql_ref.ts
@@ -14,10 +14,10 @@
 
 import m from 'mithril';
 
-import {Anchor} from '../anchor';
-import {copyToClipboard} from '../clipboard';
-import {Icons} from '../semantic_icons';
+import {copyToClipboard} from '../base/clipboard';
+import {Icons} from '../base/semantic_icons';
 
+import {Anchor} from './anchor';
 import {MenuItem, PopupMenu2} from './menu';
 
 // This widget provides common styling and popup menu options for a SQL row,
diff --git a/ui/src/frontend/widgets/switch.ts b/ui/src/widgets/switch.ts
similarity index 96%
rename from ui/src/frontend/widgets/switch.ts
rename to ui/src/widgets/switch.ts
index 97375f5..6aae496 100644
--- a/ui/src/frontend/widgets/switch.ts
+++ b/ui/src/widgets/switch.ts
@@ -13,7 +13,8 @@
 // limitations under the License.
 
 import m from 'mithril';
-import {classNames} from '../classnames';
+
+import {classNames} from '../base/classnames';
 
 export interface SwitchAttrs {
   // Optional text to show to the right of the switch.
diff --git a/ui/src/frontend/widgets/text_input.ts b/ui/src/widgets/text_input.ts
similarity index 96%
rename from ui/src/frontend/widgets/text_input.ts
rename to ui/src/widgets/text_input.ts
index c3911c3..5e68afd 100644
--- a/ui/src/frontend/widgets/text_input.ts
+++ b/ui/src/widgets/text_input.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {classNames} from '../classnames';
+import {classNames} from '../base/classnames';
 
 export interface TextInputAttrs {
   [htmlAttrs: string]: any;
diff --git a/ui/src/frontend/widgets/tree.ts b/ui/src/widgets/tree.ts
similarity index 97%
rename from ui/src/frontend/widgets/tree.ts
rename to ui/src/widgets/tree.ts
index 285a772..e046a35 100644
--- a/ui/src/frontend/widgets/tree.ts
+++ b/ui/src/widgets/tree.ts
@@ -14,9 +14,10 @@
 
 import m from 'mithril';
 
-import {hasChildren} from '../../base/mithril_utils';
-import {scheduleFullRedraw} from '../../widgets/raf';
-import {classNames} from '../classnames';
+import {classNames} from '../base/classnames';
+import {hasChildren} from '../base/mithril_utils';
+
+import {scheduleFullRedraw} from './raf';
 
 // Heirachical tree layout but right values are horizontally aligned.
 // Example:
diff --git a/ui/src/frontend/widgets/virtual_scroll_container.ts b/ui/src/widgets/virtual_scroll_container.ts
similarity index 95%
rename from ui/src/frontend/widgets/virtual_scroll_container.ts
rename to ui/src/widgets/virtual_scroll_container.ts
index cfcbef7..2f32d0a 100644
--- a/ui/src/frontend/widgets/virtual_scroll_container.ts
+++ b/ui/src/widgets/virtual_scroll_container.ts
@@ -14,7 +14,7 @@
 
 import m from 'mithril';
 
-import {findRef, toHTMLElement} from '../../base/dom_utils';
+import {findRef, toHTMLElement} from '../base/dom_utils';
 
 interface VirtualScrollContainerAttrs {
   // Called when the scrolling element is created, updates, or scrolls.