diff --git a/devtools/projects/ng-devtools/src/lib/application-operations/index.ts b/devtools/projects/ng-devtools/src/lib/application-operations/index.ts index ae00f791819f..b8820e840e96 100644 --- a/devtools/projects/ng-devtools/src/lib/application-operations/index.ts +++ b/devtools/projects/ng-devtools/src/lib/application-operations/index.ts @@ -14,6 +14,9 @@ export abstract class ApplicationOperations { abstract selectDomElement(position: ElementPosition, target: Frame): void; abstract inspect(directivePosition: DirectivePosition, objectPath: string[], target: Frame): void; abstract inspectSignal(position: SignalNodePosition, target: Frame): void; + abstract setSignalBreakpoint(position: SignalNodePosition, target: Frame): void; + abstract removeSignalBreakpoint(position: SignalNodePosition, target: Frame): void; + abstract getActiveSignalBreakpoints(target: Frame): Promise; abstract viewSourceFromRouter(name: string, type: string, target: Frame): void; abstract setStorageItems(items: {[key: string]: unknown}): Promise; abstract getStorageItems(items: string[]): Promise<{[key: string]: unknown}>; diff --git a/devtools/projects/ng-devtools/src/lib/application-services/test-utils/app_operations_mock.ts b/devtools/projects/ng-devtools/src/lib/application-services/test-utils/app_operations_mock.ts index 7e3aff5a4550..937231a55472 100644 --- a/devtools/projects/ng-devtools/src/lib/application-services/test-utils/app_operations_mock.ts +++ b/devtools/projects/ng-devtools/src/lib/application-services/test-utils/app_operations_mock.ts @@ -58,6 +58,18 @@ export class AppOperationsMock extends ApplicationOperations { throw new Error('Method not implemented.'); } + override setSignalBreakpoint(position: SignalNodePosition, target: Frame): void { + throw new Error('Method not implemented.'); + } + + override removeSignalBreakpoint(position: SignalNodePosition, target: Frame): void { + throw new Error('Method not implemented.'); + } + + override getActiveSignalBreakpoints(target: Frame): Promise { + return Promise.resolve([]); + } + override viewSourceFromRouter(name: string, type: string, target: Frame): void { throw new Error('Method not implemented.'); } diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.html b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.html index 445b9ed22e90..2f3affe1bbf0 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.html +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.html @@ -15,7 +15,10 @@ [node]="node" [graph]="signalGraph.graph()!" [element]="signalGraph.element()!" + [hasBreakpoint]="hasBreakpoint(node)" (gotoSource)="gotoSource($event)" + (setBreakpoint)="setBreakpoint($event)" + (removeBreakpoint)="removeBreakpoint($event)" (expandCluster)="expandCluster($event)" (highlightDeps)="highlightDeps($event)" (close)="detailsVisible.set(false)" diff --git a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.ts b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.ts index 87673a31e5cf..944c7f99682d 100644 --- a/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.ts +++ b/devtools/projects/ng-devtools/src/lib/devtools-tabs/directive-explorer/signal-graph-pane/signal-graph-pane.component.ts @@ -9,6 +9,7 @@ import { Component, computed, + effect, inject, input, linkedSignal, @@ -45,6 +46,25 @@ export class SignalGraphPaneComponent { private readonly appOperations = inject(ApplicationOperations); private readonly frameManager = inject(FrameManager); + constructor() { + effect(() => { + const element = this.signalGraph.element(); + const graph = this.signalGraph.graph(); + if (element && graph) { + const frame = this.frameManager.selectedFrame(); + this.appOperations.getActiveSignalBreakpoints(frame!).then((positions) => { + const activeIds = new Set(); + for (const pos of positions) { + if (JSON.stringify(pos.element) === JSON.stringify(element)) { + activeIds.add(pos.signalId); + } + } + this.activeBreakpoints.set(activeIds); + }); + } + }); + } + protected readonly close = output(); // Source for selected node ID. @@ -86,6 +106,18 @@ export class SignalGraphPaneComponent { protected readonly detailsVisible = signal(false); + // Track active breakpoints for the current inspected element. + // Resets when the inspected element changes. + protected readonly activeBreakpoints = linkedSignal>({ + source: () => this.signalGraph.element(), + computation: () => new Set(), + }); + + protected hasBreakpoint(node: DevtoolsSignalGraphNode | undefined): boolean { + if (!node) return false; + return this.activeBreakpoints().has(node.id); + } + protected empty = computed(() => !(this.signalGraph.graph()?.nodes.length! > 0)); onNodeClick(node: DevtoolsSignalGraphNode) { @@ -104,6 +136,38 @@ export class SignalGraphPaneComponent { ); } + setBreakpoint(node: DevtoolsSignalGraphNode) { + const frame = this.frameManager.selectedFrame(); + this.appOperations.setSignalBreakpoint( + { + element: this.signalGraph.element()!, + signalId: node.id, + }, + frame!, + ); + this.activeBreakpoints.update((set) => { + const newSet = new Set(set); + newSet.add(node.id); + return newSet; + }); + } + + removeBreakpoint(node: DevtoolsSignalGraphNode) { + const frame = this.frameManager.selectedFrame(); + this.appOperations.removeSignalBreakpoint( + { + element: this.signalGraph.element()!, + signalId: node.id, + }, + frame!, + ); + this.activeBreakpoints.update((set) => { + const newSet = new Set(set); + newSet.delete(node.id); + return newSet; + }); + } + expandCluster(clusterId: string) { this.visualizer().expandCluster(clusterId); } diff --git a/devtools/projects/ng-devtools/src/lib/shared/signal-details/signal-details.component.html b/devtools/projects/ng-devtools/src/lib/shared/signal-details/signal-details.component.html index fb6e55b44e7e..4e7ad7821e2b 100644 --- a/devtools/projects/ng-devtools/src/lib/shared/signal-details/signal-details.component.html +++ b/devtools/projects/ng-devtools/src/lib/shared/signal-details/signal-details.component.html @@ -16,6 +16,17 @@ > code + } @else if (isClusterNode) {