Skip to content

Commit 9a8d33b

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
Add documentation about the Revealer system
Bug: none Change-Id: Ie3e60269dcd54c67f32496e108cc3d3ea6790564 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7623115 Reviewed-by: Alina Varkki <alinavarkki@chromium.org> Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> Auto-Submit: Jack Franklin <jacktfranklin@chromium.org>
1 parent ae921e7 commit 9a8d33b

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

front_end/core/common/README.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,129 @@ Common.Settings.registerSettingExtension({
3131

3232
A controller for the each setting is added to the 'preferences' tab if they are visible, that is, they have a `title` and a `category`.
3333
They can also be set with the command menu if they have `options` and a `category` (options’ names are added as commands).
34+
35+
---
36+
37+
# The DevTools Revealer System
38+
39+
The `Revealer.ts` module provides a centralized system that allows different parts of the DevTools UI to "reveal" or display various types of objects without being tightly coupled to one another. It's a powerful decoupling mechanism that makes the codebase more modular, extensible, and performant.
40+
41+
For example, if you click on a CSS file link in the **Console**, this system is responsible for telling the **Sources** panel to open and display that file, without the Console needing a direct reference to the Sources panel.
42+
43+
## Core Concepts
44+
45+
1. **`Revealer<T>` Interface**: This is the fundamental contract. A "Revealer" is any object (typically a UI panel or view) that knows how to display a specific type of data (`T`). It must implement a single method: `reveal(revealable: T): Promise<void>`.
46+
47+
2. **"Revealable" Object**: This is any object you want to show to the user. It can be a source code file (`SDK.SourceCode.UISourceCode`), a network request (`SDK.NetworkRequest.NetworkRequest`), a DOM node (`SDK.DOMModel.DOMNode`), or any other custom data type.
48+
49+
3. **`RevealerRegistry`**: This is a singleton class that acts as a central directory. It holds a list of all available `Revealer`s and maps them to the data types they can handle.
50+
51+
4. **`RevealerRegistration<T>`**: This is a configuration object used to register a `Revealer` with the `RevealerRegistry`. It contains three key pieces of information:
52+
* `contextTypes`: A function that returns an array of classes (constructors) that the `Revealer` can handle.
53+
* `loadRevealer`: An asynchronous function that returns a promise, which resolves to an instance of the `Revealer`. This allows for the lazy-loading of UI panels, improving initial application performance.
54+
* `destination` (optional): A user-friendly, localized string that describes where the object will be revealed (e.g., "Network panel", "Elements panel"). This is used for UI text, such as in context menus ("Reveal in...").
55+
56+
---
57+
58+
## How to Create a New Revealer
59+
60+
Here are the steps to implement a new revealer that can take the user to a specific place in the DevTools UI.
61+
62+
### Step 1: Define the Object to be Revealed
63+
64+
First, ensure you have a class or data type that you want to make "revealable."
65+
66+
```ts
67+
// front_end/models/my_app/MyApp.ts
68+
export class MyDataObject {
69+
id: string;
70+
constructor(id: string) {
71+
this.id = id;
72+
}
73+
}
74+
```
75+
76+
### Step 2: Implement the `Revealer` Interface in Your UI Panel
77+
78+
The UI component that will display the object (e.g., your panel or widget) must implement the `Common.Revealer.Revealer<T>` interface for your specific data type.
79+
80+
```ts
81+
// front_end/panels/my_panel/MyPanel.ts
82+
import * as Common from '../../core/common/common.js';
83+
import * as UI from '../../ui/legacy/legacy.js';
84+
import * as MyApp from '../../models/my_app/my_app.js';
85+
86+
// 1. Implement the interface for your specific data type.
87+
export class MyPanel extends UI.Panel.Panel implements Common.Revealer.Revealer<MyApp.MyDataObject> {
88+
// ... other panel implementation ...
89+
90+
// 2. This is the required method from the interface.
91+
async reveal(dataObject: MyApp.MyDataObject): Promise<void> {
92+
// This is where you put your panel's custom logic to show the object.
93+
94+
// First, ensure this panel is visible to the user.
95+
await UI.ViewManager.ViewManager.instance().showView('my-panel-view-id');
96+
97+
// Now, highlight the specific item within your panel.
98+
console.log(`Revealing data object with ID: ${dataObject.id}`);
99+
// e.g., this.selectItemInUI(dataObject.id);
100+
}
101+
}
102+
```
103+
104+
### Step 3: Register Your Panel as a `Revealer`
105+
106+
This is the most important step. You must tell the central `RevealerRegistry` that `MyPanel` knows how to handle `MyDataObject`. This registration is typically done in a module's `-meta.ts` or `-legacy.ts` configuration file, which is executed at startup.
107+
108+
```ts
109+
// front_end/panels/my_panel/my_panel-meta.ts
110+
import * as Common from '../../core/common/common.js';
111+
import * as i18n from '../../core/i18n/i18n.js';
112+
113+
const UIStrings = {
114+
/**
115+
* @description The name of the panel that reveals our custom data objects.
116+
*/
117+
myPanel: 'My Awesome Panel',
118+
};
119+
const str_ = i18n.i18n.registerUIStrings('panels/my_panel/my_panel-meta.ts', UIStrings);
120+
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
121+
122+
Common.Revealer.registerRevealer({
123+
// 1. Specify the data type(s) this revealer can handle.
124+
contextTypes() {
125+
// Use a dynamic import to avoid circular dependencies at module load time.
126+
const {MyApp} = await import('../../models/my_app/my_app.js');
127+
return [
128+
MyApp.MyDataObject,
129+
];
130+
},
131+
132+
// 2. Provide a function to load and return an instance of your panel.
133+
// This is what makes lazy loading possible.
134+
async loadRevealer() {
135+
const {MyPanel} = await import('./MyPanel.js');
136+
// If your panel is a singleton, return its instance, otherwise create a new one.
137+
return MyPanel.instance();
138+
},
139+
140+
// 3. (Optional) Provide a user-facing destination name for context menus.
141+
destination: i18nLazyString(UIStrings.myPanel),
142+
});
143+
```
144+
145+
### Step 4: Trigger the Reveal from Anywhere
146+
147+
Now that your revealer is registered, any other part of the DevTools codebase can ask to reveal an instance of `MyDataObject` without needing to know anything about `MyPanel`.
148+
149+
```ts
150+
// In some other file, e.g., a context menu action.
151+
import * as Common from '../../core/common/common.js';
152+
import * as MyApp from '../../models/my_app/my_app.js';
153+
154+
// Create an instance of the object you want to reveal.
155+
const myObject = new MyApp.MyDataObject('abc-123');
156+
157+
// Call the global reveal function.
158+
await Common.Revealer.reveal(myObject);
159+
```

0 commit comments

Comments
 (0)