Move the DebugSignalGraph, DebugSignalGraphEdge, and DebugSignalGraphNode interfaces from packages/core/src/render3/util/signal_debug.ts into the packages/core/primitives/devtools/src package. This decouples the signal graph debug types from runtime render3 utilities and allows devtools and internal core tooling to import them directly from primitives as type-only exports.
Before SVG hosts could be skipped because the highlighter checked for HTMLElement. A case like <foreignObject appSvgDemo> would not get a proper overlay. The highlighter now works with Element so SVG hosts can be found and measured.
Before Directive only hosts could be skipped because the inspected-page lookup and directive explorer matched component hosts and component ids only. A case like <a routerLink="/todos">Todos</a> could highlight the parent component instead of the anchor, and the RouterLink only tree node could fail to select, highlight, or request an overlay. Id 0 could also miss the highlighted state because the tree treated it as absent. The lookup now stops at the nearest component or directive host, and tree matching accepts component and directive ids using null as the absent state.
Selected overlays could fail to reappear for the same element because unhighlighting removed the overlay but left the selected element cached. selected-element cache is now reused only while its overlay is still connected, and unhighlighting clears the cached element.
Overlay geometry used integer coercion. An SVG or transformed element with a 0.5px bounding box could get a 0px overlay. Overlay positioning now preserves fractional DOMRect values.
Add an architecture doc tracing how the Angular DevTools panel, background worker, content scripts, and backend connect per tab and relay messages, with diagrams for the topology and boot sequence.
Enables direct navigation to Angular components from the Chrome Performance panel's flame graph.
This feature integrates Angular DevTools with Chrome's `setOpenResourceHandler` by registering a custom URL scheme (`angular-devtools://component/{instanceId}`).
Closes#63960
- Rename `DirectiveForestHooks` to `DirectiveForestManager`
- Keep profiler reference standalone; move it out from `DirectiveForestHooks`/`DirectiveForestManager`
- Convert profiler-specific `IdentityTracker` behavior to a more generalized one
- Separate timing API functionality from the `DirectiveForestHooks`/`DirectiveForestManager` initialization fn
- Reorganize files; rename `/hooks` to `/profiling`
- Use concrete types for directive and component instances (incomplete coverage; based on `any` at the moment)
- Other more minor changes
The rules_angular bump in #69410 made ts_project require every `deps`
entry to provide the JsInfo provider. `esbuild_base` listed
`//packages:package_json` in its deps, but `esbuild-base.config.mts` never
imports or reads package.json, so the dependency is unnecessary and now
breaks analysis (`//packages:package_json` is a `copy_to_bin` target that
provides only DefaultInfo). Remove it.
Add an internals doc covering how the injector tree is built from the directive forest's resolution paths, rendered with d3, and how injector data is read from the DI debug APIs.
The change can be treated as a continuation of #68535.
- Make the table header sticky
- Reduce slightly the table density in order to equalize it with the rest of the UI
- Reduce the font size of the type pills
- Change the position of the filter clear button to match with the rest of the filter inputs
- Add a "No such key" label when there are no data rows as a result of filtering
Improve the name of the public methods of the visualizers by using common terms like "highlighted". This should, hopefully, clear up some ambiguities that came with the term "selected". Additionally, clean up some CSS class names.
This refactoring renames the 'element' (tagName string) property of DevToolsNode to 'tagName' for clarity, as 'element' was ambiguous.
It also restores 'nativeElement' as a flat property (undoing the nesting introduced previously) because there are cases (like control flow blocks) where a tag name is present without an associated DOM node, making nesting inappropriate.
Convert the router details panel in DevTools to use a description list (<dl>) instead of a table. This is semantically more correct for key-value metadata and improves accessibility.
- promote to default-on (remove opt-in setting and gear-menu toggle)
- replace the <pre> value cell with a collapsible JSON tree
- add key filter and column sort (size sorts by raw bytes)
- show an educational empty state when the app has no transfer state
- iterate every root in the directive forest and merge their state
- guard size calculation against undefined and circular references
- register listener once with DestroyRef cleanup; add a loading timeout
- distinguish array/string badge colors via theme-aware tokens
- responsive value cell width; flex-based table scroll
- add a component spec; seed the harness fixture with more demo data
This change is based on the presumption that a signal graph can be significantly large memory-wise sometimes. This is the reason why we don't send the full graph to the FE but rather serialize its values and then lazy load them when they are needed, that is, during value inspection.
Implement singal graph nodes search for easier navigation within graphs with large sets of nodes. The search provides filtering by node type via `type:<SIGNAL_TYPE>` syntax.
Remove unnecessary `async`/`Promise<void>` from `serializeTransferState` and the
redundant `async () =>` wrapper in `provideAppInitializer` — the function contains
no async operations, so this is a zero behavior change cleanup.
Split the `ng` global interface into two interfaces:
* `ExternalCoreGlobalUtils` includes all the functionality which has been shipped in a long-lived Angular version externally and which is subject to the versioning constraints described above.
* `InternalCoreGlobalUtils` includes internal-only functionality which has **not** been shipped in a long-lived Angular version.
This split means that all APIs in `InternalCoreGlobalUtils` can be iterated and evolved at a much faster pace. Angular DevTools can support those features, and we can make breaking changes more-or-less whenever we want. The downside is that external Angular developers cannot take advantage of those APIs or else we would be subject to the same versioning constraint we're trying to avoid here.
This means we can use `InternalCoreGlobalUtils` as a kind of "beta" channel for new DevTools APIs. Once that functionality is validated and the design is stabilized, the feature can be moved into `ExternalCoreGlobalUtils` and made available for external Angular developers when we're ready to commit to the long-lived version constraint. This will hopefully help us strike a better balance between iterating on new APIs quickly and maintaining stable APIs for external Angular users.
Make the `hydration`` property of `DevToolsNode`` optional as frameworks like Client-Only Wiz don't have the same hydration concept as Angular and will not set one for nodes returned from `getComponentForest`.
The PR addresses a missing step in the clustering phase of the signal graph processing on the DevTools frontend. Cluster-to-cluster relationship were missing from the graph, so the change fixes that.
The Angular DevTools Extension doesn't use the `element` property in Client-Only Wiz apps. Once the new `ng.getComponentForest`` function is implemented, Client-Only Wiz could provide `ComponentTreeNode`s without an `element` property. This change supports that case by making the `element` property optional.
Set default values for the `directives`, `element`, `hydration`, and `component.isElement` properties of every `ComponentTreeNode` returned by `ng.getComponentForest` for non-Angular apps.
Add an optional injector property to the DevToolsNode and IndexedNode types. Features that require an injector use the injector stored on the node, if it exists, and fall back to getInjectorFromElementNode if it doesn't.
A new ng global function, ng.getComponentForest, is being added to allow the framework to provide the component forest without writing metadata to the DOM. This will allow devtools to display components that don't render any DOM.
Replace the manual injectorsSeen cleanup mechanism with WeakRef and FinalizationRegistry. The old approach worked but coupled cleanup to the UI change detection and required tracking seen injectors across traversal. WeakRef lets the browser handle this naturally, removing the injectorsSeen set and the manual cleanup loop.
In the context of AOT tests, component with defer blocks no longer throw on instanciation if the component is not overridden (with `overrideComponent`)
Prior to this change, all components with a `@defer` block would throw if `compileComponents` was not invoked.
In none-JIT apps, this change makes `compileComponents()` uneccesary.
Client-Only Wiz doesn't have the concept of a directive. Once the new ng.getComponentForest function is implemented, Client-Only Wiz cowilluld provide `ComponentTreeNode`s without a `directives`` property. This change supports that case by making the `directives` property optional.
The "Log to console" button in the prop-actions-menu component incorrectly logs the entire props object rather than the value of the individual prop with which the button is associated. Passing the `node` to the logValue function fixes the prop lookup logic and logs the appropriate prop value to the console.
The IdentityTracker singleton never deleted entries from its internal
maps (_currentDirectiveId, _currentDirectivePosition, isComponent) for
destroyed directives. This caused the maps to grow monotonically for
the entire DevTools session, retaining references to destroyed component
instances and preventing garbage collection.
The cleanup was intentionally commented out because the profiler needs
to resolve IDs and positions of removed components during recording.
Introduce `setProfilingActive()` to gate cleanup: when profiling is
inactive, removed entries are deleted immediately during `index()`.
When profiling is active, removals are deferred into a `_pendingRemovals`
set and flushed once profiling stops via `capture.ts` start/stop calls.