chrome-devtools-frontend
Version:
Chrome DevTools UI
142 lines (122 loc) • 4.29 kB
text/typescript
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Common from '../../core/common/common.js';
import * as Root from '../../core/root/root.js';
export interface EditorAnchorSignature {
/** 1-based line number for CodeMirror text editor anchors */
lineNumber: number;
/** File path associated with the editor */
filePath?: string;
}
export interface CommentAnchorSignature {
/** Visual logging tree path, e.g. "Panel: elements > Pane: styles > TreeOutline > TreeItem: color" */
vePath: string;
/** Normalized text content of the target node */
textSignature: string;
/** Text content of the parent container VE node for sibling disambiguation */
parentTextSignature?: string;
/** 0-indexed position among siblings sharing the same visual logging path */
siblingIndex?: number;
/** Optional backend RequestId for Network panel elements (`data-network-request-id`) */
networkRequestId?: string;
/** Optional backend NodeId for Elements panel DOM nodes (`data-backend-node-id`) */
backendNodeId?: number;
/** Optional editor anchor coordinates for CodeMirror text editors */
editor?: EditorAnchorSignature;
}
export interface Comment {
author: 'DEVELOPER'|'AGENT';
text: string;
timestamp: number;
}
export interface CommentThread {
id: string;
anchor: CommentAnchorSignature;
comments: Comment[];
status: 'ACTIVE'|'RESOLVED';
changes?: Array<Record<string, unknown>>;
}
export interface CommentManagerCreationOptions {
forceNew?: boolean|null;
}
export const enum Events {
COMMENT_THREADS_CHANGED = 'CommentThreadsChanged',
COMMENT_MODE_CHANGED = 'CommentModeChanged',
}
export interface EventTypes {
[]: CommentThread[];
[]: boolean;
}
/**
* Headless model managing comment thread data, CRUD operations, and comment mode.
*/
export class CommentManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
readonly
/**
* IMPORTANT: This method MUST only be called in MainImpl. All other components
* should receive CommentManager via dependency injection (INJECT or constructor argument).
*/
static instance(opts: CommentManagerCreationOptions = {forceNew: null}): CommentManager {
const {forceNew} = opts;
if (!Root.DevToolsContext.globalInstance().has(CommentManager) || forceNew) {
Root.DevToolsContext.globalInstance().set(CommentManager, new CommentManager());
}
return Root.DevToolsContext.globalInstance().get(CommentManager);
}
static removeInstance(): void {
Root.DevToolsContext.globalInstance().delete(CommentManager);
}
setCommentMode(active: boolean): void {
if (this.
return;
}
this.
this.dispatchEventToListeners(Events.COMMENT_MODE_CHANGED, active);
}
isCommentMode(): boolean {
return this.
}
createCommentThread(
anchor: CommentAnchorSignature,
text: string,
author: 'DEVELOPER'|'AGENT' = 'DEVELOPER',
changes?: Array<Record<string, unknown>>,
): CommentThread {
const id = `comment-${this.
const thread: CommentThread = {
id,
anchor,
comments: [{
author,
text,
timestamp: Date.now(),
}],
status: 'ACTIVE',
changes,
};
this.
this.dispatchEventToListeners(Events.COMMENT_THREADS_CHANGED, this.getCommentThreads());
return thread;
}
getCommentThread(id: string): CommentThread|undefined {
return this.
}
getCommentThreads(): CommentThread[] {
return Array.from(this.
}
removeCommentThread(id: string): void {
if (!this.
return;
}
this.
this.dispatchEventToListeners(Events.COMMENT_THREADS_CHANGED, this.getCommentThreads());
}
clear(): void {
this.setCommentMode(false);
this.
this.dispatchEventToListeners(Events.COMMENT_THREADS_CHANGED, []);
}
}