2021-12-09 21:37:01 -05:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google LLC All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
2024-09-20 09:23:15 -06:00
|
|
|
* found in the LICENSE file at https://angular.dev/license
|
2021-12-09 21:37:01 -05:00
|
|
|
*/
|
|
|
|
|
|
2025-07-12 21:01:49 +02:00
|
|
|
import {
|
|
|
|
|
ChangeDetectionStrategy,
|
|
|
|
|
Component,
|
|
|
|
|
computed,
|
|
|
|
|
inject,
|
|
|
|
|
input,
|
|
|
|
|
output,
|
|
|
|
|
signal,
|
|
|
|
|
} from '@angular/core';
|
2024-01-19 22:52:14 +01:00
|
|
|
import {MatIcon} from '@angular/material/icon';
|
|
|
|
|
import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu';
|
|
|
|
|
import {MatSlideToggle} from '@angular/material/slide-toggle';
|
|
|
|
|
import {MatTabLink, MatTabNav, MatTabNavPanel} from '@angular/material/tabs';
|
|
|
|
|
import {MatTooltip} from '@angular/material/tooltip';
|
2025-05-08 17:51:51 +03:00
|
|
|
import {
|
|
|
|
|
ComponentExplorerView,
|
|
|
|
|
Events,
|
|
|
|
|
MessageBus,
|
|
|
|
|
Route,
|
|
|
|
|
SerializedInjector,
|
|
|
|
|
SerializedProviderRecord,
|
|
|
|
|
} from '../../../../protocol';
|
2021-12-09 00:44:17 -05:00
|
|
|
|
2024-03-04 22:55:01 -05:00
|
|
|
import {ApplicationEnvironment, Frame, TOP_LEVEL_FRAME_ID} from '../application-environment/index';
|
2025-01-15 14:42:01 +02:00
|
|
|
import {FrameManager} from '../application-services/frame_manager';
|
|
|
|
|
import {ThemeService} from '../application-services/theme_service';
|
2021-12-09 00:44:17 -05:00
|
|
|
|
|
|
|
|
import {DirectiveExplorerComponent} from './directive-explorer/directive-explorer.component';
|
2024-01-19 22:52:14 +01:00
|
|
|
import {InjectorTreeComponent} from './injector-tree/injector-tree.component';
|
|
|
|
|
import {ProfilerComponent} from './profiler/profiler.component';
|
|
|
|
|
import {RouterTreeComponent} from './router-tree/router-tree.component';
|
2025-07-18 14:15:07 +02:00
|
|
|
import {TransferStateComponent} from './transfer-state/transfer-state.component';
|
2021-12-09 00:44:17 -05:00
|
|
|
import {TabUpdate} from './tab-update/index';
|
2025-07-15 15:52:20 +03:00
|
|
|
import {Settings} from '../application-services/settings';
|
2025-05-13 19:19:35 +03:00
|
|
|
import {SUPPORTED_APIS} from '../application-providers/supported_apis';
|
2025-11-06 11:36:28 +02:00
|
|
|
import {ButtonComponent} from '../shared/button/button.component';
|
2020-01-27 13:40:18 -05:00
|
|
|
|
2025-07-18 14:15:07 +02:00
|
|
|
type Tab = 'Components' | 'Profiler' | 'Router Tree' | 'Injector Tree' | 'Transfer State';
|
2023-12-04 22:19:43 +01:00
|
|
|
|
2020-01-27 13:40:18 -05:00
|
|
|
@Component({
|
|
|
|
|
selector: 'ng-devtools-tabs',
|
|
|
|
|
templateUrl: './devtools-tabs.component.html',
|
2020-03-28 23:28:36 -04:00
|
|
|
styleUrls: ['./devtools-tabs.component.scss'],
|
2024-01-19 22:52:14 +01:00
|
|
|
imports: [
|
|
|
|
|
MatTabNav,
|
|
|
|
|
MatTabNavPanel,
|
|
|
|
|
MatTooltip,
|
|
|
|
|
MatIcon,
|
|
|
|
|
MatMenu,
|
|
|
|
|
MatMenuItem,
|
|
|
|
|
MatMenuTrigger,
|
|
|
|
|
MatTabLink,
|
|
|
|
|
DirectiveExplorerComponent,
|
|
|
|
|
ProfilerComponent,
|
|
|
|
|
RouterTreeComponent,
|
|
|
|
|
InjectorTreeComponent,
|
2025-07-18 14:15:07 +02:00
|
|
|
TransferStateComponent,
|
2024-01-19 22:52:14 +01:00
|
|
|
MatSlideToggle,
|
2025-11-06 11:36:28 +02:00
|
|
|
ButtonComponent,
|
2024-01-19 22:52:14 +01:00
|
|
|
],
|
|
|
|
|
providers: [TabUpdate],
|
2025-07-12 21:01:49 +02:00
|
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
2020-01-27 13:40:18 -05:00
|
|
|
})
|
2024-07-30 12:55:46 +05:30
|
|
|
export class DevToolsTabsComponent {
|
2025-11-17 10:19:55 +06:00
|
|
|
public readonly frameManager = inject(FrameManager);
|
|
|
|
|
protected readonly themeService = inject(ThemeService);
|
2025-07-15 15:52:20 +03:00
|
|
|
private readonly tabUpdate = inject(TabUpdate);
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly messageBus = inject<MessageBus<Events>>(MessageBus);
|
|
|
|
|
protected readonly settings = inject(Settings);
|
2025-05-13 19:19:35 +03:00
|
|
|
protected readonly applicationEnvironment = inject(ApplicationEnvironment);
|
|
|
|
|
protected readonly supportedApis = inject(SUPPORTED_APIS);
|
2025-07-15 15:52:20 +03:00
|
|
|
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly isHydrationEnabled = input(false);
|
2024-07-30 12:55:46 +05:30
|
|
|
readonly frameSelected = output<Frame>();
|
2020-08-08 16:43:42 -04:00
|
|
|
|
2024-07-30 12:55:46 +05:30
|
|
|
readonly inspectorRunning = signal(false);
|
2025-07-15 15:52:20 +03:00
|
|
|
|
|
|
|
|
protected readonly showCommentNodes = this.settings.showCommentNodes;
|
|
|
|
|
protected readonly timingAPIEnabled = this.settings.timingAPIEnabled;
|
2025-10-07 04:52:55 +00:00
|
|
|
protected readonly signalGraphEnabled = () => this.supportedApis().signals;
|
2025-07-28 14:52:46 +03:00
|
|
|
protected readonly transferStateEnabled = this.settings.transferStateEnabled;
|
2025-09-04 12:13:30 +02:00
|
|
|
protected readonly activeTab = this.settings.activeTab;
|
2024-01-25 19:35:36 -05:00
|
|
|
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly componentExplorerView = signal<ComponentExplorerView | null>(null);
|
|
|
|
|
protected readonly providers = signal<SerializedProviderRecord[]>([]);
|
2024-07-30 12:55:46 +05:30
|
|
|
readonly routes = signal<Route[]>([]);
|
|
|
|
|
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly tabs = computed<Tab[]>(() => {
|
2025-03-27 17:26:53 +02:00
|
|
|
const supportedApis = this.supportedApis();
|
|
|
|
|
const tabs: Tab[] = ['Components'];
|
|
|
|
|
|
|
|
|
|
if (supportedApis.profiler) {
|
|
|
|
|
tabs.push('Profiler');
|
|
|
|
|
}
|
|
|
|
|
if (supportedApis.dependencyInjection) {
|
|
|
|
|
tabs.push('Injector Tree');
|
|
|
|
|
}
|
2026-01-29 15:45:58 -05:00
|
|
|
if (supportedApis.routes && this.routes().length > 0) {
|
2025-03-27 17:26:53 +02:00
|
|
|
tabs.push('Router Tree');
|
|
|
|
|
}
|
2025-07-28 14:52:46 +03:00
|
|
|
if (supportedApis.transferState && this.transferStateEnabled()) {
|
2025-07-18 14:15:07 +02:00
|
|
|
tabs.push('Transfer State');
|
|
|
|
|
}
|
2025-03-27 17:26:53 +02:00
|
|
|
|
|
|
|
|
return tabs;
|
2024-07-30 12:55:46 +05:30
|
|
|
});
|
2024-03-04 22:55:01 -05:00
|
|
|
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly profilingNotificationsSupported = Boolean(
|
2024-07-30 12:55:46 +05:30
|
|
|
(window.chrome?.devtools as any)?.performance?.onProfilingStarted,
|
|
|
|
|
);
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly TOP_LEVEL_FRAME_ID = TOP_LEVEL_FRAME_ID;
|
2024-03-04 22:55:01 -05:00
|
|
|
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly angularVersion = input<string | undefined>();
|
2024-07-30 12:55:46 +05:30
|
|
|
readonly majorAngularVersion = computed(() => {
|
2024-04-05 18:24:43 -04:00
|
|
|
const version = this.angularVersion();
|
|
|
|
|
if (!version) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return parseInt(version.toString().split('.')[0], 10);
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-17 10:19:55 +06:00
|
|
|
protected readonly extensionVersion = signal('dev-build');
|
2024-05-06 16:16:22 -04:00
|
|
|
|
2024-07-30 12:55:46 +05:30
|
|
|
constructor() {
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.on('updateRouterTree', (routes: any[]) => {
|
2024-07-30 12:55:46 +05:30
|
|
|
this.routes.set(routes || []);
|
2021-03-11 00:41:50 -05:00
|
|
|
});
|
2023-08-30 18:41:16 -04:00
|
|
|
|
2025-04-02 13:33:28 +03:00
|
|
|
// Change the tab to Components, if an element is selected via the inspector.
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.on('selectComponent', () => {
|
2025-04-02 13:33:28 +03:00
|
|
|
if (this.activeTab() !== 'Components') {
|
|
|
|
|
this.changeTab('Components');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.on('latestComponentExplorerView', (view: ComponentExplorerView) => {
|
2025-05-08 17:51:51 +03:00
|
|
|
this.componentExplorerView.set(view);
|
|
|
|
|
});
|
|
|
|
|
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.on(
|
2025-05-08 17:51:51 +03:00
|
|
|
'latestInjectorProviders',
|
|
|
|
|
(_: SerializedInjector, providers: SerializedProviderRecord[]) => {
|
|
|
|
|
this.providers.set(providers);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2024-07-30 12:55:46 +05:30
|
|
|
if (typeof chrome !== 'undefined' && chrome.runtime !== undefined) {
|
|
|
|
|
this.extensionVersion.set(chrome.runtime.getManifest().version);
|
2024-05-06 16:16:22 -04:00
|
|
|
}
|
2021-03-11 00:41:50 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-20 11:38:03 +01:00
|
|
|
emitSelectedFrame(event: Event): void {
|
|
|
|
|
const frameId = (event.target as HTMLInputElement).value;
|
2024-11-22 14:19:00 +05:30
|
|
|
const frame = this.frameManager.frames().find((frame) => frame.id === parseInt(frameId, 10));
|
2024-07-30 12:55:46 +05:30
|
|
|
this.frameSelected.emit(frame!);
|
2021-02-13 05:22:04 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-27 17:26:53 +02:00
|
|
|
changeTab(tab: Tab): void {
|
2024-07-30 12:55:46 +05:30
|
|
|
this.activeTab.set(tab);
|
2024-08-28 11:38:31 +05:30
|
|
|
this.tabUpdate.notify(tab);
|
2021-02-07 08:15:42 -05:00
|
|
|
if (tab === 'Router Tree') {
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.emit('getRoutes');
|
2021-02-07 08:15:42 -05:00
|
|
|
}
|
2021-02-13 05:22:04 -05:00
|
|
|
}
|
|
|
|
|
|
2020-01-28 19:29:23 -05:00
|
|
|
toggleInspector(): void {
|
2020-02-06 17:41:19 -05:00
|
|
|
this.toggleInspectorState();
|
|
|
|
|
this.emitInspectorEvent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emitInspectorEvent(): void {
|
2024-07-30 12:55:46 +05:30
|
|
|
if (this.inspectorRunning()) {
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.emit('inspectorStart');
|
2020-01-27 13:40:18 -05:00
|
|
|
} else {
|
2025-07-15 15:52:20 +03:00
|
|
|
this.messageBus.emit('inspectorEnd');
|
|
|
|
|
this.messageBus.emit('removeHighlightOverlay');
|
2020-01-27 13:40:18 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-06 17:41:19 -05:00
|
|
|
toggleInspectorState(): void {
|
2024-07-30 12:55:46 +05:30
|
|
|
this.inspectorRunning.update((state) => !state);
|
2020-02-06 17:41:19 -05:00
|
|
|
}
|
|
|
|
|
|
2023-08-30 18:41:16 -04:00
|
|
|
toggleTimingAPI(): void {
|
2024-07-30 12:55:46 +05:30
|
|
|
this.timingAPIEnabled.update((state) => !state);
|
|
|
|
|
this.timingAPIEnabled()
|
2025-07-15 15:52:20 +03:00
|
|
|
? this.messageBus.emit('enableTimingAPI')
|
|
|
|
|
: this.messageBus.emit('disableTimingAPI');
|
2020-05-06 23:21:32 -04:00
|
|
|
}
|
2024-10-15 14:39:26 -04:00
|
|
|
|
2025-07-18 14:15:07 +02:00
|
|
|
protected setTransferStateTab(enabled: boolean): void {
|
2025-07-28 14:52:46 +03:00
|
|
|
this.transferStateEnabled.set(enabled);
|
2025-07-18 14:15:07 +02:00
|
|
|
if (!enabled && this.activeTab() === 'Transfer State') {
|
|
|
|
|
this.activeTab.set('Components');
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-27 13:40:18 -05:00
|
|
|
}
|