chrome-devtools-frontend 1.0.1596260 → 1.0.1596535
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front_end/core/common/Console.ts +6 -6
- package/front_end/core/common/Settings.ts +12 -8
- package/front_end/core/root/DevToolsContext.ts +13 -7
- package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
- package/front_end/core/sdk/FrameManager.ts +7 -8
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +1 -1
- package/front_end/entrypoints/main/MainImpl.ts +1 -2
- package/front_end/foundation/Universe.ts +22 -11
- package/front_end/generated/InspectorBackendCommands.ts +7 -0
- package/front_end/generated/protocol-mapping.d.ts +16 -0
- package/front_end/generated/protocol-proxy-api.d.ts +25 -0
- package/front_end/generated/protocol.ts +71 -0
- package/front_end/models/ai_assistance/AiConversation.ts +6 -95
- package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +25 -4
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +2 -2
- package/front_end/panels/elements/StandaloneStylesContainer.ts +48 -12
- package/front_end/panels/elements/StylePropertiesSection.ts +97 -1
- package/front_end/panels/elements/StylePropertyTreeElement.ts +16 -0
- package/front_end/panels/elements/StylesContainer.ts +1 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +48 -32
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +13 -5
- package/front_end/panels/lighthouse/LighthousePanel.ts +22 -5
- package/front_end/panels/recorder/components/StepView.ts +0 -2
- package/front_end/panels/recorder/components/stepView.css +13 -13
- package/front_end/panels/recorder/components/timelineSection.css +6 -7
- package/front_end/panels/timeline/TimelinePanel.ts +18 -0
- package/front_end/panels/timeline/components/CWVMetrics.ts +339 -0
- package/front_end/panels/timeline/components/Sidebar.ts +17 -0
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +10 -0
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +25 -267
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/cwvMetrics.css +107 -0
- package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +0 -102
- package/front_end/panels/timeline/timeline-meta.ts +11 -0
- package/front_end/panels/timeline/utils/Helpers.ts +5 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +6 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +5 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +14 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +6 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +5 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/package.json +1 -1
- package/front_end/third_party/puppeteer/package/src/api/Page.ts +8 -0
- package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +7 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/text_editor/AutocompleteHistory.ts +89 -5
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Host from '../../core/host/host.js';
|
|
6
6
|
import * as Root from '../../core/root/root.js';
|
|
7
|
-
import * as SDK from '../../core/sdk/sdk.js';
|
|
8
|
-
import * as Trace from '../../models/trace/trace.js';
|
|
7
|
+
import type * as SDK from '../../core/sdk/sdk.js';
|
|
8
|
+
import type * as Trace from '../../models/trace/trace.js';
|
|
9
9
|
import * as Greendev from '../greendev/greendev.js';
|
|
10
|
-
import * as NetworkTimeCalculator from '../network_time_calculator/network_time_calculator.js';
|
|
10
|
+
import type * as NetworkTimeCalculator from '../network_time_calculator/network_time_calculator.js';
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
type AiAgent,
|
|
@@ -26,10 +26,6 @@ import {PerformanceAgent, PerformanceTraceContext} from './agents/PerformanceAge
|
|
|
26
26
|
import {NodeContext, StylingAgent} from './agents/StylingAgent.js';
|
|
27
27
|
import {AiHistoryStorage, ConversationType, type SerializedConversation} from './AiHistoryStorage.js';
|
|
28
28
|
import type {ChangeManager} from './ChangeManager.js';
|
|
29
|
-
import {NetworkRequestFormatter} from './data_formatters/NetworkRequestFormatter.js';
|
|
30
|
-
import {PerformanceInsightFormatter} from './data_formatters/PerformanceInsightFormatter.js';
|
|
31
|
-
import {micros} from './data_formatters/UnitFormatters.js';
|
|
32
|
-
import {AgentFocus} from './performance/AIContext.js';
|
|
33
29
|
|
|
34
30
|
export const NOT_FOUND_IMAGE_DATA = '';
|
|
35
31
|
const MAX_TITLE_LENGTH = 80;
|
|
@@ -274,6 +270,9 @@ export class AiConversation {
|
|
|
274
270
|
if (item.type === ResponseType.SIDE_EFFECT) {
|
|
275
271
|
return {...item, confirm: undefined};
|
|
276
272
|
}
|
|
273
|
+
if (item.type === ResponseType.CONTEXT && item.widgets) {
|
|
274
|
+
return {...item, widgets: undefined};
|
|
275
|
+
}
|
|
277
276
|
return item;
|
|
278
277
|
})
|
|
279
278
|
.filter(history => !!history),
|
|
@@ -342,88 +341,11 @@ export class AiConversation {
|
|
|
342
341
|
}
|
|
343
342
|
}
|
|
344
343
|
|
|
345
|
-
#factsCache = new Map<ExtraContext, Host.AidaClient.RequestFact>();
|
|
346
|
-
|
|
347
|
-
async #createFactsForExtraContext(contexts: ExtraContext[]): Promise<void> {
|
|
348
|
-
for (const context of contexts) {
|
|
349
|
-
const cached = this.#factsCache.get(context);
|
|
350
|
-
if (cached) {
|
|
351
|
-
this.#agent.addFact(cached);
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (context instanceof SDK.DOMModel.DOMNode) {
|
|
356
|
-
const desc = await StylingAgent.describeElement(context);
|
|
357
|
-
|
|
358
|
-
const fact = {
|
|
359
|
-
text: `Relevant HTML element:\n${desc}`,
|
|
360
|
-
metadata: {
|
|
361
|
-
source: 'devtools-floaty',
|
|
362
|
-
score: 1,
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
this.#factsCache.set(context, fact);
|
|
366
|
-
this.#agent.addFact(fact);
|
|
367
|
-
} else if (context instanceof SDK.NetworkRequest.NetworkRequest) {
|
|
368
|
-
const calculator = new NetworkTimeCalculator.NetworkTransferTimeCalculator();
|
|
369
|
-
calculator.updateBoundaries(context);
|
|
370
|
-
const formatter = new NetworkRequestFormatter(context, calculator);
|
|
371
|
-
const desc = await formatter.formatNetworkRequest();
|
|
372
|
-
|
|
373
|
-
const fact = {
|
|
374
|
-
text: `Relevant network request:\n${desc}`,
|
|
375
|
-
metadata: {
|
|
376
|
-
source: 'devtools-floaty',
|
|
377
|
-
score: 1,
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
this.#factsCache.set(context, fact);
|
|
381
|
-
this.#agent.addFact(fact);
|
|
382
|
-
} else if ('insight' in context) {
|
|
383
|
-
const focus = AgentFocus.fromInsight(context.trace, context.insight);
|
|
384
|
-
const formatter = new PerformanceInsightFormatter(
|
|
385
|
-
focus,
|
|
386
|
-
context.insight,
|
|
387
|
-
);
|
|
388
|
-
|
|
389
|
-
const text = `Relevant Performance Insight:\n${formatter.formatInsight()}`;
|
|
390
|
-
const fact = {
|
|
391
|
-
text,
|
|
392
|
-
metadata: {
|
|
393
|
-
source: 'devtools-floaty',
|
|
394
|
-
score: 1,
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
this.#factsCache.set(context, fact);
|
|
398
|
-
this.#agent.addFact(fact);
|
|
399
|
-
} else {
|
|
400
|
-
// Must be a trace event
|
|
401
|
-
const time = Trace.Types.Timing.Micro(
|
|
402
|
-
context.event.ts - context.traceStartTime,
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
const desc = `Trace event: ${context.event.name}
|
|
406
|
-
Time: ${micros(time)}`;
|
|
407
|
-
|
|
408
|
-
const fact = {
|
|
409
|
-
text: `Relevant trace event:\n${desc}`,
|
|
410
|
-
metadata: {
|
|
411
|
-
source: 'devtools-floaty',
|
|
412
|
-
score: 1,
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
this.#factsCache.set(context, fact);
|
|
416
|
-
this.#agent.addFact(fact);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
344
|
async *
|
|
422
345
|
run(
|
|
423
346
|
initialQuery: string,
|
|
424
347
|
options: {
|
|
425
348
|
signal?: AbortSignal,
|
|
426
|
-
extraContext?: ExtraContext[],
|
|
427
349
|
multimodalInput?: MultimodalInput,
|
|
428
350
|
} = {},
|
|
429
351
|
): AsyncGenerator<ResponseData, void, void> {
|
|
@@ -442,9 +364,6 @@ Time: ${micros(time)}`;
|
|
|
442
364
|
void this.addHistoryItem(userQuery);
|
|
443
365
|
yield userQuery;
|
|
444
366
|
|
|
445
|
-
if (options.extraContext) {
|
|
446
|
-
await this.#createFactsForExtraContext(options.extraContext);
|
|
447
|
-
}
|
|
448
367
|
this.#setOriginIfEmpty(this.selectedContext?.getOrigin());
|
|
449
368
|
|
|
450
369
|
if (this.isBlockedByOrigin) {
|
|
@@ -463,7 +382,6 @@ Time: ${micros(time)}`;
|
|
|
463
382
|
initialQuery: string,
|
|
464
383
|
options: {
|
|
465
384
|
signal?: AbortSignal,
|
|
466
|
-
extraContext?: ExtraContext[],
|
|
467
385
|
multimodalInput?: MultimodalInput,
|
|
468
386
|
} = {},
|
|
469
387
|
): AsyncGenerator<ResponseData, void, void> {
|
|
@@ -528,13 +446,6 @@ function isAiAssistanceServerSideLoggingEnabled(): boolean {
|
|
|
528
446
|
return !Root.Runtime.hostConfig.aidaAvailability?.disallowLogging;
|
|
529
447
|
}
|
|
530
448
|
|
|
531
|
-
// TODO: this is the same as the type in UI.Floaty but we cannot use UI
|
|
532
|
-
// here. This is fine for prototyping but if we take this further we can
|
|
533
|
-
// rearchitect.
|
|
534
|
-
type ExtraContext = SDK.DOMModel.DOMNode|SDK.NetworkRequest.NetworkRequest|
|
|
535
|
-
{event: Trace.Types.Events.Event, traceStartTime: Trace.Types.Timing.Micro}|
|
|
536
|
-
{insight: Trace.Insights.Types.InsightModel, trace: Trace.TraceModel.ParsedTrace};
|
|
537
|
-
|
|
538
449
|
function isAiAssistanceContextSelectionAgentEnabled(): boolean {
|
|
539
450
|
return Boolean(Root.Runtime.hostConfig.devToolsAiAssistanceContextSelectionAgent?.enabled);
|
|
540
451
|
}
|
|
@@ -7,6 +7,7 @@ import * as Root from '../../../core/root/root.js';
|
|
|
7
7
|
import type * as SDK from '../../../core/sdk/sdk.js';
|
|
8
8
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
9
9
|
import * as Greendev from '../../greendev/greendev.js';
|
|
10
|
+
import type * as Trace from '../../trace/trace.js';
|
|
10
11
|
import {debugLog, isStructuredLogEnabled} from '../debug.js';
|
|
11
12
|
|
|
12
13
|
export const enum ResponseType {
|
|
@@ -69,6 +70,7 @@ export interface ContextResponse {
|
|
|
69
70
|
type: ResponseType.CONTEXT;
|
|
70
71
|
title: string;
|
|
71
72
|
details: [ContextDetail, ...ContextDetail[]];
|
|
73
|
+
widgets?: AiWidget[];
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export interface TitleResponse {
|
|
@@ -228,8 +230,16 @@ export interface ComputedStyleAiWidget {
|
|
|
228
230
|
properties: string[],
|
|
229
231
|
};
|
|
230
232
|
}
|
|
233
|
+
export interface CoreVitalsAiWidget {
|
|
234
|
+
name: 'CORE_VITALS';
|
|
235
|
+
data: {
|
|
236
|
+
insightSetKey: string,
|
|
237
|
+
parsedTrace: Trace.TraceModel.ParsedTrace,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
231
241
|
// This type will grow as we add more widgets.
|
|
232
|
-
export type AiWidget = ComputedStyleAiWidget;
|
|
242
|
+
export type AiWidget = ComputedStyleAiWidget|CoreVitalsAiWidget;
|
|
233
243
|
|
|
234
244
|
export type FunctionCallHandlerResult<Result> = {
|
|
235
245
|
requiresApproval: true,
|
|
@@ -22,6 +22,7 @@ import {AgentFocus} from '../performance/AIContext.js';
|
|
|
22
22
|
|
|
23
23
|
import {
|
|
24
24
|
AiAgent,
|
|
25
|
+
type AiWidget,
|
|
25
26
|
type ContextResponse,
|
|
26
27
|
ConversationContext,
|
|
27
28
|
type ConversationSuggestions,
|
|
@@ -381,6 +382,18 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
|
381
382
|
return;
|
|
382
383
|
}
|
|
383
384
|
|
|
385
|
+
const widgets: AiWidget[] = [];
|
|
386
|
+
const primaryInsightSet = context.getItem().primaryInsightSet;
|
|
387
|
+
if (primaryInsightSet) {
|
|
388
|
+
widgets.push({
|
|
389
|
+
name: 'CORE_VITALS',
|
|
390
|
+
data: {
|
|
391
|
+
parsedTrace: context.getItem().parsedTrace,
|
|
392
|
+
insightSetKey: primaryInsightSet.id,
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
384
397
|
yield {
|
|
385
398
|
type: ResponseType.CONTEXT,
|
|
386
399
|
title: lockedString(UIStringsNotTranslated.analyzingTrace),
|
|
@@ -390,6 +403,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
|
390
403
|
text: this.#formatter?.formatTraceSummary() ?? '',
|
|
391
404
|
},
|
|
392
405
|
],
|
|
406
|
+
widgets,
|
|
393
407
|
};
|
|
394
408
|
|
|
395
409
|
this.#hasShownAnalyzeTraceContext = true;
|
|
@@ -1714,6 +1714,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1714
1714
|
case AiAssistanceModel.AiAgent.ResponseType.CONTEXT: {
|
|
1715
1715
|
step.title = data.title;
|
|
1716
1716
|
step.contextDetails = data.details;
|
|
1717
|
+
step.widgets = data.widgets;
|
|
1717
1718
|
step.isLoading = false;
|
|
1718
1719
|
commitStep();
|
|
1719
1720
|
break;
|
|
@@ -12,7 +12,8 @@ import type * as Platform from '../../../core/platform/platform.js';
|
|
|
12
12
|
import * as Root from '../../../core/root/root.js';
|
|
13
13
|
import * as SDK from '../../../core/sdk/sdk.js';
|
|
14
14
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
AiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
|
|
16
17
|
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
17
18
|
import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
|
|
18
19
|
import * as Marked from '../../../third_party/marked/marked.js';
|
|
@@ -25,6 +26,8 @@ import * as UI from '../../../ui/legacy/legacy.js';
|
|
|
25
26
|
import * as Lit from '../../../ui/lit/lit.js';
|
|
26
27
|
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
|
27
28
|
import * as Elements from '../../elements/elements.js';
|
|
29
|
+
import * as TimelineComponents from '../../timeline/components/components.js';
|
|
30
|
+
import * as TimelineUtils from '../../timeline/utils/utils.js';
|
|
28
31
|
|
|
29
32
|
import chatMessageStyles from './chatMessage.css.js';
|
|
30
33
|
import {walkthroughTitle, WalkthroughView} from './WalkthroughView.js';
|
|
@@ -696,6 +699,19 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
|
|
|
696
699
|
return {renderedWidget: widget, revealable: new Elements.ElementsPanel.NodeComputedStyles(domNodeForId)};
|
|
697
700
|
}
|
|
698
701
|
|
|
702
|
+
async function makeCoreVitalsWidget(widgetData: CoreVitalsAiWidget): Promise<WidgetMakerResponse|null> {
|
|
703
|
+
const widgetConfig = UI.Widget.widgetConfig(TimelineComponents.CWVMetrics.CWVMetrics, {data: widgetData.data});
|
|
704
|
+
|
|
705
|
+
// clang-format off
|
|
706
|
+
const widget = html`<devtools-widget class="core-vitals-widget" .widgetConfig=${widgetConfig}></devtools-widget>`;
|
|
707
|
+
// clang-format on
|
|
708
|
+
|
|
709
|
+
return {
|
|
710
|
+
renderedWidget: widget,
|
|
711
|
+
revealable: new TimelineUtils.Helpers.RevealableCoreVitals(widgetData.data.insightSetKey)
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
|
|
699
715
|
function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTemplate {
|
|
700
716
|
if (response === null) {
|
|
701
717
|
return Lit.nothing;
|
|
@@ -732,10 +748,11 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
|
|
|
732
748
|
* iterates through the `step.widgets` array. For each widget, it determines
|
|
733
749
|
* the appropriate rendering logic based on the `widgetData.name`.
|
|
734
750
|
*
|
|
735
|
-
* Currently, only 'COMPUTED_STYLES' widgets are supported. For these, the
|
|
736
|
-
* `makeComputedStyleWidget`
|
|
751
|
+
* Currently, only 'COMPUTED_STYLES' and 'CORE_VITALS' widgets are supported. For these, the
|
|
752
|
+
* `makeComputedStyleWidget` and `makeCoreVitalsWidget` functions are called to construct the necessary
|
|
737
753
|
* data and configuration for the `Elements.ComputedStyleWidget.ComputedStyleWidget`
|
|
738
|
-
*
|
|
754
|
+
* and `TimelineComponents.CWVMetrics.CWVMetrics`
|
|
755
|
+
* components. The widget is then rendered using the `<devtools-widget>`
|
|
739
756
|
* custom element, which dynamically instantiates and displays the specified
|
|
740
757
|
* UI.Widget subclass with the provided configuration.
|
|
741
758
|
*
|
|
@@ -752,6 +769,10 @@ async function renderStepWidgets(step: Step): Promise<Lit.LitTemplate> {
|
|
|
752
769
|
const response = await makeComputedStyleWidget(widgetData);
|
|
753
770
|
return renderWidgetResponse(response);
|
|
754
771
|
}
|
|
772
|
+
if (widgetData.name === 'CORE_VITALS') {
|
|
773
|
+
const response = await makeCoreVitalsWidget(widgetData);
|
|
774
|
+
return renderWidgetResponse(response);
|
|
775
|
+
}
|
|
755
776
|
return Lit.nothing;
|
|
756
777
|
}));
|
|
757
778
|
return html`${ui}`;
|
|
@@ -25,7 +25,7 @@ const UIStrings = {
|
|
|
25
25
|
/**
|
|
26
26
|
* @description Title for the walkthrough view.
|
|
27
27
|
*/
|
|
28
|
-
title: '
|
|
28
|
+
title: 'Agent walkthrough',
|
|
29
29
|
/**
|
|
30
30
|
* @description Title for the button that shows the walkthrough when there are no widgets in the walkthrough.
|
|
31
31
|
*/
|
|
@@ -101,7 +101,7 @@ function renderSidebarWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate
|
|
|
101
101
|
<devtools-button
|
|
102
102
|
.data=${{
|
|
103
103
|
variant: Buttons.Button.Variant.TOOLBAR,
|
|
104
|
-
iconName: '
|
|
104
|
+
iconName: 'cross',
|
|
105
105
|
title: i18nString(UIStrings.close),
|
|
106
106
|
jslogContext: 'close-walkthrough',
|
|
107
107
|
} as Buttons.Button.ButtonData}
|
|
@@ -40,7 +40,16 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
|
40
40
|
target);
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
export
|
|
43
|
+
export const enum Events {
|
|
44
|
+
STYLES_UPDATE_COMPLETED = 'StylesUpdateCompleted',
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface EventTypes {
|
|
48
|
+
[Events.STYLES_UPDATE_COMPLETED]: void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class StandaloneStylesContainer extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.VBox>(
|
|
52
|
+
UI.Widget.VBox) implements StylesContainer {
|
|
44
53
|
activeCSSAngle: InlineEditor.CSSAngle.CSSAngle|null = null;
|
|
45
54
|
isEditingStyle = false;
|
|
46
55
|
readonly sectionByElement = new WeakMap<Node, StylePropertiesSection>();
|
|
@@ -49,7 +58,7 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
49
58
|
new Components.Linkifier.Linkifier(23, /* useLinkDecorator */ true);
|
|
50
59
|
|
|
51
60
|
#webCustomData: WebCustomData|undefined;
|
|
52
|
-
|
|
61
|
+
userOperation = false;
|
|
53
62
|
#sections: StylePropertiesSection[] = [];
|
|
54
63
|
#swatchPopoverHelper = new InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper();
|
|
55
64
|
#computedStyleModelInternal = new ComputedStyle.ComputedStyleModel.ComputedStyleModel();
|
|
@@ -58,10 +67,24 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
58
67
|
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
59
68
|
super(element, {useShadowDom: true});
|
|
60
69
|
this.#view = view;
|
|
70
|
+
this.#computedStyleModelInternal.addEventListener(
|
|
71
|
+
ComputedStyle.ComputedStyleModel.Events.CSS_MODEL_CHANGED, this.#onCSSModelChanged, this);
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
async #onCSSModelChanged(
|
|
75
|
+
event: Common.EventTarget.EventTargetEvent<ComputedStyle.ComputedStyleModel.CSSModelChangedEvent>):
|
|
76
|
+
Promise<void> {
|
|
77
|
+
// We only recreate sections if this update is more than an "edit" operation.
|
|
78
|
+
// Sections will pull their own updates in the case of an "edit".
|
|
79
|
+
if (event?.data && 'edit' in event.data && event?.data.edit) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this.isEditingStyle || this.userOperation) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.requestUpdate();
|
|
65
88
|
}
|
|
66
89
|
|
|
67
90
|
get webCustomData(): WebCustomData|undefined {
|
|
@@ -73,10 +96,12 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
async #updateSections(): Promise<void> {
|
|
99
|
+
for (const section of this.#sections) {
|
|
100
|
+
section.dispose();
|
|
101
|
+
}
|
|
76
102
|
const node = this.node();
|
|
77
103
|
if (!node) {
|
|
78
104
|
this.#sections = [];
|
|
79
|
-
this.requestUpdate();
|
|
80
105
|
return;
|
|
81
106
|
}
|
|
82
107
|
|
|
@@ -106,9 +131,8 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
106
131
|
}
|
|
107
132
|
|
|
108
133
|
override async performUpdate(): Promise<void> {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
134
|
+
this.hideAllPopovers();
|
|
135
|
+
this.node()?.domModel().cssModel().discardCachedMatchedCascade();
|
|
112
136
|
|
|
113
137
|
await this.#updateSections();
|
|
114
138
|
|
|
@@ -116,6 +140,11 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
116
140
|
sections: this.#sections,
|
|
117
141
|
};
|
|
118
142
|
this.#view(viewInput, undefined, this.contentElement);
|
|
143
|
+
this.#onUpdateFinished();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#onUpdateFinished(): void {
|
|
147
|
+
this.dispatchEventToListeners(Events.STYLES_UPDATE_COMPLETED);
|
|
119
148
|
}
|
|
120
149
|
|
|
121
150
|
swatchPopoverHelper(): InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper {
|
|
@@ -154,6 +183,7 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
154
183
|
}
|
|
155
184
|
|
|
156
185
|
if (this.isEditingStyle) {
|
|
186
|
+
this.#onUpdateFinished();
|
|
157
187
|
return;
|
|
158
188
|
}
|
|
159
189
|
|
|
@@ -161,6 +191,7 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
161
191
|
section.update(section === editedSection);
|
|
162
192
|
}
|
|
163
193
|
this.swatchPopoverHelper().reposition();
|
|
194
|
+
this.#onUpdateFinished();
|
|
164
195
|
}
|
|
165
196
|
|
|
166
197
|
filterRegex(): RegExp|null {
|
|
@@ -172,16 +203,19 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
172
203
|
}
|
|
173
204
|
|
|
174
205
|
setUserOperation(userOperation: boolean): void {
|
|
175
|
-
this
|
|
206
|
+
this.userOperation = userOperation;
|
|
176
207
|
}
|
|
177
208
|
|
|
178
209
|
forceUpdate(): void {
|
|
179
|
-
this.hideAllPopovers();
|
|
180
210
|
this.requestUpdate();
|
|
181
211
|
}
|
|
182
212
|
|
|
183
213
|
hideAllPopovers(): void {
|
|
184
214
|
this.#swatchPopoverHelper.hide();
|
|
215
|
+
if (this.activeCSSAngle) {
|
|
216
|
+
this.activeCSSAngle.minify();
|
|
217
|
+
this.activeCSSAngle = null;
|
|
218
|
+
}
|
|
185
219
|
}
|
|
186
220
|
|
|
187
221
|
allSections(): StylePropertiesSection[] {
|
|
@@ -246,9 +280,11 @@ export class StandaloneStylesContainer extends UI.Widget.VBox implements StylesC
|
|
|
246
280
|
jumpToDeclaration(_valueSource: SDK.CSSMatchedStyles.CSSValueSource): void {
|
|
247
281
|
}
|
|
248
282
|
|
|
249
|
-
addStyleUpdateListener(
|
|
283
|
+
addStyleUpdateListener(listener: () => void): void {
|
|
284
|
+
this.addEventListener(Events.STYLES_UPDATE_COMPLETED, listener);
|
|
250
285
|
}
|
|
251
286
|
|
|
252
|
-
removeStyleUpdateListener(
|
|
287
|
+
removeStyleUpdateListener(listener: () => void): void {
|
|
288
|
+
this.removeEventListener(Events.STYLES_UPDATE_COMPLETED, listener);
|
|
253
289
|
}
|
|
254
290
|
}
|
|
@@ -43,6 +43,7 @@ import * as SDK from '../../core/sdk/sdk.js';
|
|
|
43
43
|
import * as Protocol from '../../generated/protocol.js';
|
|
44
44
|
import * as Badges from '../../models/badges/badges.js';
|
|
45
45
|
import * as Bindings from '../../models/bindings/bindings.js';
|
|
46
|
+
import * as ComputedStyle from '../../models/computed_style/computed_style.js';
|
|
46
47
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
47
48
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
48
49
|
import * as Tooltips from '../../ui/components/tooltips/tooltips.js';
|
|
@@ -55,7 +56,7 @@ import {FontEditorSectionManager} from './ColorSwatchPopoverIcon.js';
|
|
|
55
56
|
import * as ElementsComponents from './components/components.js';
|
|
56
57
|
import {ElementsPanel} from './ElementsPanel.js';
|
|
57
58
|
import stylePropertiesTreeOutlineStyles from './stylePropertiesTreeOutline.css.js';
|
|
58
|
-
import {type Context, StylePropertyTreeElement} from './StylePropertyTreeElement.js';
|
|
59
|
+
import {type Context, GhostStylePropertyTreeElement, StylePropertyTreeElement} from './StylePropertyTreeElement.js';
|
|
59
60
|
import type {StylesContainer} from './StylesContainer.js';
|
|
60
61
|
|
|
61
62
|
const UIStrings = {
|
|
@@ -166,6 +167,8 @@ export class StylePropertiesSection {
|
|
|
166
167
|
static #nextSectionTooltipIdPrefix = 0;
|
|
167
168
|
readonly sectionTooltipIdPrefix = StylePropertiesSection.#nextSectionTooltipIdPrefix++;
|
|
168
169
|
|
|
170
|
+
private ghostStyleTreeElements: GhostStylePropertyTreeElement[] = [];
|
|
171
|
+
|
|
169
172
|
constructor(
|
|
170
173
|
stylesContainer: StylesContainer, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
|
|
171
174
|
style: SDK.CSSStyleDeclaration.CSSStyleDeclaration, sectionIdx: number, computedStyles: Map<string, string>|null,
|
|
@@ -323,12 +326,55 @@ export class StylePropertiesSection {
|
|
|
323
326
|
this.#isHidden = false;
|
|
324
327
|
this.markSelectorMatches();
|
|
325
328
|
this.onpopulate();
|
|
329
|
+
|
|
330
|
+
this.stylesContainer.computedStyleModel().addEventListener(
|
|
331
|
+
ComputedStyle.ComputedStyleModel.Events.CSS_MODEL_CHANGED, this.#onCSSModelChanged, this);
|
|
326
332
|
}
|
|
327
333
|
|
|
328
334
|
setComputedStyles(computedStyles: Map<string, string>|null): void {
|
|
329
335
|
this.computedStyles = computedStyles;
|
|
330
336
|
}
|
|
331
337
|
|
|
338
|
+
#onCSSModelChanged(event: Common.EventTarget.EventTargetEvent<ComputedStyle.ComputedStyleModel.CSSModelChangedEvent>):
|
|
339
|
+
void {
|
|
340
|
+
const edit = event?.data && 'edit' in event.data ? event.data.edit : null;
|
|
341
|
+
if (edit) {
|
|
342
|
+
this.styleSheetEdited(edit);
|
|
343
|
+
void this.refreshComputedValues();
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (this.stylesContainer.isEditingStyle || this.stylesContainer.userOperation) {
|
|
348
|
+
void this.refreshComputedValues();
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async refreshComputedValues(): Promise<void> {
|
|
353
|
+
const node = this.stylesContainer.node();
|
|
354
|
+
if (!node) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const cssModel = node.domModel().cssModel();
|
|
359
|
+
const computedStyleModel = this.stylesContainer.computedStyleModel();
|
|
360
|
+
|
|
361
|
+
const matchedStyles = await cssModel.cachedMatchedCascadeForNode(node);
|
|
362
|
+
const parentNodeId = matchedStyles?.getParentLayoutNodeId();
|
|
363
|
+
|
|
364
|
+
const [computedStyles, parentsComputedStyles] = await Promise.all([
|
|
365
|
+
computedStyleModel.fetchComputedStyle(),
|
|
366
|
+
parentNodeId ? cssModel.getComputedStyle(parentNodeId) : null,
|
|
367
|
+
]);
|
|
368
|
+
|
|
369
|
+
if (computedStyles) {
|
|
370
|
+
this.setComputedStyles(computedStyles.computedStyle);
|
|
371
|
+
}
|
|
372
|
+
if (parentsComputedStyles) {
|
|
373
|
+
this.setParentsComputedStyles(parentsComputedStyles);
|
|
374
|
+
}
|
|
375
|
+
this.updateAuthoringHint();
|
|
376
|
+
}
|
|
377
|
+
|
|
332
378
|
setParentsComputedStyles(parentsComputedStyles: Map<string, string>|null): void {
|
|
333
379
|
this.parentsComputedStyles = parentsComputedStyles;
|
|
334
380
|
}
|
|
@@ -349,6 +395,11 @@ export class StylePropertiesSection {
|
|
|
349
395
|
}
|
|
350
396
|
}
|
|
351
397
|
|
|
398
|
+
dispose(): void {
|
|
399
|
+
this.stylesContainer.computedStyleModel().removeEventListener(
|
|
400
|
+
ComputedStyle.ComputedStyleModel.Events.CSS_MODEL_CHANGED, this.#onCSSModelChanged, this);
|
|
401
|
+
}
|
|
402
|
+
|
|
352
403
|
setSectionIdx(sectionIdx: number): void {
|
|
353
404
|
this.sectionIdx = sectionIdx;
|
|
354
405
|
this.onpopulate();
|
|
@@ -778,6 +829,51 @@ export class StylePropertiesSection {
|
|
|
778
829
|
return;
|
|
779
830
|
}
|
|
780
831
|
|
|
832
|
+
clearGhostStyleTreeElements(): void {
|
|
833
|
+
// Clear existing ghost elements
|
|
834
|
+
for (const ghost of this.ghostStyleTreeElements) {
|
|
835
|
+
this.propertiesTreeOutline.removeChild(ghost);
|
|
836
|
+
}
|
|
837
|
+
this.ghostStyleTreeElements = [];
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
renderGhostStyleTreeElements(suggestion: string): void {
|
|
841
|
+
this.clearGhostStyleTreeElements();
|
|
842
|
+
if (!suggestion) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
const suggestionLines = suggestion.split(';').map(line => line.trim());
|
|
847
|
+
for (const line of suggestionLines) {
|
|
848
|
+
const colonIdx = line.indexOf(':');
|
|
849
|
+
if (colonIdx === -1) {
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
const name = line.substring(0, colonIdx).trim();
|
|
854
|
+
const value = line.substring(colonIdx + 1).trim();
|
|
855
|
+
if (!name || !value) {
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Create a fake property attached to this style
|
|
860
|
+
const fakeProperty = new SDK.CSSProperty.CSSProperty(
|
|
861
|
+
this.styleInternal,
|
|
862
|
+
this.styleInternal.allProperties().length,
|
|
863
|
+
name,
|
|
864
|
+
value,
|
|
865
|
+
false,
|
|
866
|
+
false,
|
|
867
|
+
true,
|
|
868
|
+
false,
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
const ghost = new GhostStylePropertyTreeElement(this.stylesContainer, this, this.matchedStyles, fakeProperty);
|
|
872
|
+
this.propertiesTreeOutline.appendChild(ghost);
|
|
873
|
+
this.ghostStyleTreeElements.push(ghost);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
781
877
|
private onNewRuleClick(event: Common.EventTarget.EventTargetEvent<Event>): void {
|
|
782
878
|
event.data.consume();
|
|
783
879
|
const rule = this.styleInternal.parentRule;
|
|
@@ -3548,6 +3548,22 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
3548
3548
|
override isEventWithinDisclosureTriangle(event: Event): boolean {
|
|
3549
3549
|
return event.target === this.expandElement;
|
|
3550
3550
|
}
|
|
3551
|
+
|
|
3552
|
+
showGhostTextInValue(text: string): void {
|
|
3553
|
+
if (!this.valueElement) {
|
|
3554
|
+
return;
|
|
3555
|
+
}
|
|
3556
|
+
this.clearGhostTextInValue();
|
|
3557
|
+
this.valueElement.createChild('span', 'ghost-value-prediction').textContent = text;
|
|
3558
|
+
}
|
|
3559
|
+
|
|
3560
|
+
clearGhostTextInValue(): void {
|
|
3561
|
+
if (!this.valueElement) {
|
|
3562
|
+
return;
|
|
3563
|
+
}
|
|
3564
|
+
const ghostElement = this.valueElement.querySelector('.ghost-value-prediction');
|
|
3565
|
+
ghostElement?.remove();
|
|
3566
|
+
}
|
|
3551
3567
|
}
|
|
3552
3568
|
|
|
3553
3569
|
export class GhostStylePropertyTreeElement extends StylePropertyTreeElement {
|
|
@@ -17,6 +17,7 @@ export interface StylesContainer {
|
|
|
17
17
|
activeCSSAngle: InlineEditor.CSSAngle.CSSAngle|null;
|
|
18
18
|
readonly webCustomData: WebCustomData|undefined;
|
|
19
19
|
isEditingStyle: boolean;
|
|
20
|
+
userOperation: boolean;
|
|
20
21
|
readonly sectionByElement: WeakMap<Node, StylePropertiesSection>;
|
|
21
22
|
readonly element: HTMLElement;
|
|
22
23
|
readonly linkifier: Components.Linkifier.Linkifier;
|