UNPKG

chrome-devtools-frontend

Version:
146 lines (127 loc) • 4.25 kB
// 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'; export interface EditorAnchorSignature { /** 1-based line number for CodeMirror text editor anchors */ lineNumber: number; /** File path associated with the editor */ filePath?: string; } export interface DOMNodeAnchorSignature { /** Backend NodeId for DOM nodes (`data-backend-node-id`) */ backendNodeId: number; /** Target ID associated with the DOM node (`data-target-id`) */ targetId: string; } export interface TimelineAnchorSignature { /** Identifier of the trace to scope comments to a specific recording */ traceId: string; /** Serializable key from Trace.EventsSerializer (e.g. 'r-123', 'p-1-2-3-4', 's-5') */ traceEventKey: string; /** Primary event title or category name */ entryName: string; /** Event start timestamp in microseconds */ startTimeMicro: number; /** Chart location */ chartLocation: 'main'|'network'; /** Event duration in microseconds (omitted for instant markers) */ durationMicro?: number; } 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 DOM node identifiers (`data-backend-node-id`, `data-target-id`) */ node?: DOMNodeAnchorSignature; /** Optional editor anchor coordinates for CodeMirror text editors */ editor?: EditorAnchorSignature; /** Optional timeline flamechart anchor coordinates for Performance panel trace entries */ timeline?: TimelineAnchorSignature; } export interface Comment { author: 'DEVELOPER'|'AGENT'; text: string; timestamp: number; } export interface ChangeRecord { id: string; description: string; timestamp: number; } export type CommentThreadStatus = 'DRAFT'|'ACTIVE'|'RESOLVED'; export const enum Events { CHANGED = 'Changed', } export interface EventTypes { [Events.CHANGED]: void; } export interface CommentThreadOptions { anchor: CommentAnchorSignature; comments?: Comment[]; changes?: ChangeRecord[]; } export class CommentThread extends Common.ObjectWrapper.ObjectWrapper<EventTypes> { static #nextIndex = 1; static resetIndex(): void { CommentThread.#nextIndex = 1; } readonly id: string = crypto.randomUUID(); readonly anchor: CommentAnchorSignature; #savedIndex?: number; comments: Comment[]; status: CommentThreadStatus = 'DRAFT'; transmitted: boolean = false; changes?: ChangeRecord[]; constructor(options: CommentThreadOptions) { super(); this.anchor = options.anchor; this.comments = options.comments ?? []; this.changes = options.changes; } get index(): number { return this.#savedIndex ?? CommentThread.#nextIndex; } save(text?: string, author: 'DEVELOPER'|'AGENT' = 'DEVELOPER'): void { let changed = false; if (text && text.trim().length > 0) { this.comments.push({ author, text: text.trim(), timestamp: Date.now(), }); changed = true; } if (this.status === 'DRAFT') { if (this.#savedIndex === undefined) { this.#savedIndex = CommentThread.#nextIndex++; } this.status = 'ACTIVE'; changed = true; } if (changed) { this.dispatchEventToListeners(Events.CHANGED); } } resolve(replyText?: string): void { if (replyText && replyText.trim().length > 0) { this.comments.push({ author: 'AGENT', text: replyText.trim(), timestamp: Date.now(), }); } if (this.#savedIndex === undefined) { this.#savedIndex = CommentThread.#nextIndex++; } this.status = 'RESOLVED'; this.dispatchEventToListeners(Events.CHANGED); } }