chrome-devtools-frontend 1.0.1515988 → 1.0.1516909
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/host/GdpClient.ts +1 -1
- package/front_end/core/host/UserMetrics.ts +5 -1
- package/front_end/entrypoints/main/MainImpl.ts +1 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +4 -5
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +24 -8
- package/front_end/models/badges/UserBadges.ts +38 -3
- package/front_end/models/formatter/FormatterWorkerPool.ts +3 -3
- package/front_end/panels/common/BadgeNotification.ts +46 -10
- package/front_end/panels/elements/ComputedStyleWidget.ts +1 -2
- package/front_end/panels/elements/LayoutPane.ts +1 -1
- package/front_end/panels/network/NetworkLogView.ts +5 -1
- package/front_end/panels/search/SearchResultsPane.ts +32 -47
- package/front_end/panels/search/SearchView.ts +25 -48
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/sources/OutlineQuickOpen.ts +3 -1
- package/front_end/panels/timeline/TimelineUIUtils.ts +13 -8
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js +0 -20
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +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 +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +2 -23
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js +0 -20
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/package.json +1 -1
- package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +1 -21
- package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
- package/front_end/ui/components/text_editor/config.ts +30 -1
- package/front_end/ui/components/tooltips/Tooltip.ts +1 -3
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- package/package.json +1 -1
@@ -80,7 +80,7 @@ function normalizeBadgeName(name: string): string {
|
|
80
80
|
export const GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK = 'https://developers.google.com/profile/u/me';
|
81
81
|
|
82
82
|
async function makeHttpRequest<R extends object>(request: DispatchHttpRequestRequest): Promise<R|null> {
|
83
|
-
if (!Root.Runtime.hostConfig.devToolsGdpProfiles?.enabled) {
|
83
|
+
if (!Root.Runtime.hostConfig.devToolsGdpProfiles?.enabled || Root.Runtime.hostConfig.isOffTheRecord) {
|
84
84
|
return null;
|
85
85
|
}
|
86
86
|
|
@@ -507,7 +507,11 @@ export enum Action {
|
|
507
507
|
AiAssistanceOpenedFromPerformanceInsight = 182,
|
508
508
|
AiAssistanceOpenedFromPerformanceFullButton = 183,
|
509
509
|
AiCodeCompletionResponseServedFromCache = 184,
|
510
|
-
|
510
|
+
AiCodeCompletionRequestTriggered = 185,
|
511
|
+
AiCodeCompletionSuggestionDisplayed = 186,
|
512
|
+
AiCodeCompletionSuggestionAccepted = 187,
|
513
|
+
AiCodeCompletionError = 188,
|
514
|
+
MAX_VALUE = 189,
|
511
515
|
/* eslint-enable @typescript-eslint/naming-convention */
|
512
516
|
}
|
513
517
|
|
@@ -516,7 +516,7 @@ export class MainImpl {
|
|
516
516
|
this.#registerMessageSinkListener();
|
517
517
|
|
518
518
|
// Initialize `GDPClient` and `UserBadges` for Google Developer Program integration
|
519
|
-
if (Root.Runtime.hostConfig.devToolsGdpProfiles?.enabled) {
|
519
|
+
if (Root.Runtime.hostConfig.devToolsGdpProfiles?.enabled && !Root.Runtime.hostConfig.isOffTheRecord) {
|
520
520
|
void Host.GdpClient.GdpClient.instance().initialize();
|
521
521
|
void Badges.UserBadges.instance().initialize();
|
522
522
|
Badges.UserBadges.instance().addEventListener(Badges.Events.BADGE_TRIGGERED, async ev => {
|
@@ -264,7 +264,7 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
264
264
|
#formatter: PerformanceTraceFormatter|null = null;
|
265
265
|
#lastInsightForEnhancedQuery: Trace.Insights.Types.InsightModel|undefined;
|
266
266
|
#eventsSerializer = new Trace.EventsSerializer.EventsSerializer();
|
267
|
-
#
|
267
|
+
#hasShownAnalyzeTraceContext = false;
|
268
268
|
|
269
269
|
/**
|
270
270
|
* Cache of all function calls made by the agent. This allows us to include (as a
|
@@ -336,13 +336,10 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
336
336
|
return;
|
337
337
|
}
|
338
338
|
|
339
|
-
|
340
|
-
if (this.#lastFocusHandledForContextDetails === focus) {
|
339
|
+
if (this.#hasShownAnalyzeTraceContext) {
|
341
340
|
return;
|
342
341
|
}
|
343
342
|
|
344
|
-
this.#lastFocusHandledForContextDetails = focus;
|
345
|
-
|
346
343
|
yield {
|
347
344
|
type: ResponseType.CONTEXT,
|
348
345
|
title: lockedString(UIStringsNotTranslated.analyzingTrace),
|
@@ -353,6 +350,8 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
|
|
353
350
|
},
|
354
351
|
],
|
355
352
|
};
|
353
|
+
|
354
|
+
this.#hasShownAnalyzeTraceContext = true;
|
356
355
|
}
|
357
356
|
|
358
357
|
#callTreeContextSet = new WeakSet();
|
@@ -159,8 +159,10 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
159
159
|
}
|
160
160
|
|
161
161
|
#debouncedRequestAidaSuggestion = Common.Debouncer.debounce(
|
162
|
-
(prefix: string, suffix: string,
|
163
|
-
|
162
|
+
(prefix: string, suffix: string, cursorPositionAtRequest: number,
|
163
|
+
inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage) => {
|
164
|
+
void this.#requestAidaSuggestion(
|
165
|
+
this.#buildRequest(prefix, suffix, inferenceLanguage), cursorPositionAtRequest);
|
164
166
|
},
|
165
167
|
AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS);
|
166
168
|
|
@@ -288,12 +290,15 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
288
290
|
};
|
289
291
|
}
|
290
292
|
|
291
|
-
async #requestAidaSuggestion(request: Host.AidaClient.CompletionRequest,
|
293
|
+
async #requestAidaSuggestion(request: Host.AidaClient.CompletionRequest, cursorPositionAtRequest: number):
|
294
|
+
Promise<void> {
|
292
295
|
const startTime = performance.now();
|
293
296
|
this.dispatchEventToListeners(Events.REQUEST_TRIGGERED, {});
|
297
|
+
// Registering AiCodeCompletionRequestTriggered metric even if the request is served from cache
|
298
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionRequestTriggered);
|
294
299
|
|
295
300
|
try {
|
296
|
-
const sampleResponse = await this.#generateSampleForRequest(request,
|
301
|
+
const sampleResponse = await this.#generateSampleForRequest(request, cursorPositionAtRequest);
|
297
302
|
if (!sampleResponse) {
|
298
303
|
this.dispatchEventToListeners(Events.RESPONSE_RECEIVED, {});
|
299
304
|
return;
|
@@ -308,12 +313,19 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
308
313
|
} = sampleResponse;
|
309
314
|
const remainingDelay = Math.max(DELAY_BEFORE_SHOWING_RESPONSE_MS - (performance.now() - startTime), 0);
|
310
315
|
this.#renderingTimeout = window.setTimeout(() => {
|
316
|
+
const currentCursorPosition = this.#editor.editor.state.selection.main.head;
|
317
|
+
if (currentCursorPosition !== cursorPositionAtRequest) {
|
318
|
+
this.dispatchEventToListeners(Events.RESPONSE_RECEIVED, {});
|
319
|
+
return;
|
320
|
+
}
|
311
321
|
this.#editor.dispatch({
|
312
322
|
effects: TextEditor.Config.setAiAutoCompleteSuggestion.of({
|
313
323
|
text: suggestionText,
|
314
|
-
from:
|
324
|
+
from: cursorPositionAtRequest,
|
315
325
|
rpcGlobalId,
|
316
326
|
sampleId,
|
327
|
+
startTime,
|
328
|
+
onImpression: this.#registerUserImpression.bind(this),
|
317
329
|
})
|
318
330
|
});
|
319
331
|
|
@@ -326,12 +338,13 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
326
338
|
this.#registerUserImpression(rpcGlobalId, sampleId, latency);
|
327
339
|
}
|
328
340
|
|
329
|
-
debugLog('Suggestion dispatched to the editor', suggestionText, 'at cursor position',
|
341
|
+
debugLog('Suggestion dispatched to the editor', suggestionText, 'at cursor position', cursorPositionAtRequest);
|
330
342
|
this.dispatchEventToListeners(Events.RESPONSE_RECEIVED, {citations});
|
331
343
|
}, remainingDelay);
|
332
344
|
} catch (e) {
|
333
345
|
debugLog('Error while fetching code completion suggestions from AIDA', e);
|
334
346
|
this.dispatchEventToListeners(Events.RESPONSE_RECEIVED, {});
|
347
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionError);
|
335
348
|
}
|
336
349
|
}
|
337
350
|
|
@@ -418,6 +431,7 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
418
431
|
},
|
419
432
|
});
|
420
433
|
debugLog('Registered user impression with latency {seconds:', seconds, ', nanos:', nanos, '}');
|
434
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionSuggestionDisplayed);
|
421
435
|
}
|
422
436
|
|
423
437
|
registerUserAcceptance(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId: number): void {
|
@@ -433,11 +447,13 @@ export class AiCodeCompletion extends Common.ObjectWrapper.ObjectWrapper<EventTy
|
|
433
447
|
},
|
434
448
|
});
|
435
449
|
debugLog('Registered user acceptance');
|
450
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionSuggestionAccepted);
|
436
451
|
}
|
437
452
|
|
438
453
|
onTextChanged(
|
439
|
-
prefix: string, suffix: string,
|
440
|
-
|
454
|
+
prefix: string, suffix: string, cursorPositionAtRequest: number,
|
455
|
+
inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage): void {
|
456
|
+
this.#debouncedRequestAidaSuggestion(prefix, suffix, cursorPositionAtRequest, inferenceLanguage);
|
441
457
|
}
|
442
458
|
|
443
459
|
remove(): void {
|
@@ -22,6 +22,9 @@ export interface EventTypes {
|
|
22
22
|
[Events.BADGE_TRIGGERED]: Badge;
|
23
23
|
}
|
24
24
|
|
25
|
+
const SNOOZE_TIME_MS = 24 * 60 * 60 * 1000; // 24 hours
|
26
|
+
const MAX_SNOOZE_COUNT = 3;
|
27
|
+
|
25
28
|
let userBadgesInstance: UserBadges|undefined = undefined;
|
26
29
|
export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
27
30
|
readonly #badgeActionEventTarget = new Common.ObjectWrapper.ObjectWrapper<BadgeActionEvents>();
|
@@ -29,6 +32,10 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
29
32
|
#receiveBadgesSetting: Common.Settings.Setting<Boolean>;
|
30
33
|
#allBadges: Badge[];
|
31
34
|
|
35
|
+
#starterBadgeSnoozeCount: Common.Settings.Setting<number>;
|
36
|
+
#starterBadgeLastSnoozedTimestamp: Common.Settings.Setting<number>;
|
37
|
+
#starterBadgeDismissed: Common.Settings.Setting<boolean>;
|
38
|
+
|
32
39
|
static readonly BADGE_REGISTRY: BadgeClass[] = [
|
33
40
|
StarterBadge,
|
34
41
|
SpeedsterBadge,
|
@@ -43,6 +50,13 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
43
50
|
this.#receiveBadgesSetting = Common.Settings.Settings.instance().moduleSetting('receive-gdp-badges');
|
44
51
|
this.#receiveBadgesSetting.addChangeListener(this.#reconcileBadges, this);
|
45
52
|
|
53
|
+
this.#starterBadgeSnoozeCount = Common.Settings.Settings.instance().createSetting(
|
54
|
+
'starter-badge-snooze-count', 0, Common.Settings.SettingStorageType.SYNCED);
|
55
|
+
this.#starterBadgeLastSnoozedTimestamp = Common.Settings.Settings.instance().createSetting(
|
56
|
+
'starter-badge-last-snoozed-timestamp', 0, Common.Settings.SettingStorageType.SYNCED);
|
57
|
+
this.#starterBadgeDismissed = Common.Settings.Settings.instance().createSetting(
|
58
|
+
'starter-badge-dismissed', false, Common.Settings.SettingStorageType.SYNCED);
|
59
|
+
|
46
60
|
this.#allBadges = UserBadges.BADGE_REGISTRY.map(badgeCtor => new badgeCtor({
|
47
61
|
onTriggerBadge: this.#onTriggerBadge.bind(this),
|
48
62
|
badgeActionEventTarget: this.#badgeActionEventTarget,
|
@@ -60,6 +74,15 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
60
74
|
return await this.#reconcileBadges();
|
61
75
|
}
|
62
76
|
|
77
|
+
snoozeStarterBadge(): void {
|
78
|
+
this.#starterBadgeSnoozeCount.set(this.#starterBadgeSnoozeCount.get() + 1);
|
79
|
+
this.#starterBadgeLastSnoozedTimestamp.set(Date.now());
|
80
|
+
}
|
81
|
+
|
82
|
+
dismissStarterBadge(): void {
|
83
|
+
this.#starterBadgeDismissed.set(true);
|
84
|
+
}
|
85
|
+
|
63
86
|
recordAction(action: BadgeAction): void {
|
64
87
|
// `Common.ObjectWrapper.ObjectWrapper` does not allow passing unions to
|
65
88
|
// the `dispatchEventToListeners` and `action` in this case is a union.
|
@@ -79,7 +102,8 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
79
102
|
const gdpProfile = await Host.GdpClient.GdpClient.instance().getProfile();
|
80
103
|
const receiveBadgesSettingEnabled = Boolean(this.#receiveBadgesSetting.get());
|
81
104
|
// If there is a GDP profile and the user has enabled receiving badges, we award the starter badge as well.
|
82
|
-
if (gdpProfile && receiveBadgesSettingEnabled)
|
105
|
+
if (gdpProfile && receiveBadgesSettingEnabled && !this.#isStarterBadgeDismissed() &&
|
106
|
+
!this.#isStarterBadgeSnoozed()) {
|
83
107
|
shouldAwardBadge = true;
|
84
108
|
}
|
85
109
|
}
|
@@ -101,7 +125,17 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
101
125
|
});
|
102
126
|
}
|
103
127
|
|
104
|
-
|
128
|
+
#isStarterBadgeDismissed(): boolean {
|
129
|
+
return this.#starterBadgeDismissed.get();
|
130
|
+
}
|
131
|
+
|
132
|
+
#isStarterBadgeSnoozed(): boolean {
|
133
|
+
const snoozeCount = this.#starterBadgeSnoozeCount.get();
|
134
|
+
const lastSnoozed = this.#starterBadgeLastSnoozedTimestamp.get();
|
135
|
+
const snoozedRecently = (Date.now() - lastSnoozed) < SNOOZE_TIME_MS;
|
136
|
+
return snoozeCount >= MAX_SNOOZE_COUNT || snoozedRecently;
|
137
|
+
}
|
138
|
+
|
105
139
|
async #reconcileBadges(): Promise<void> {
|
106
140
|
const syncInfo = await new Promise<Host.InspectorFrontendHostAPI.SyncInformation>(
|
107
141
|
resolve => Host.InspectorFrontendHost.InspectorFrontendHostInstance.getSyncInformation(resolve));
|
@@ -148,7 +182,8 @@ export class UserBadges extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
148
182
|
continue;
|
149
183
|
}
|
150
184
|
|
151
|
-
const shouldActivateStarterBadge = badge.isStarterBadge && isEligibleToCreateProfile
|
185
|
+
const shouldActivateStarterBadge = badge.isStarterBadge && isEligibleToCreateProfile &&
|
186
|
+
!this.#isStarterBadgeDismissed() && !this.#isStarterBadgeSnoozed();
|
152
187
|
const shouldActivateActivityBasedBadge =
|
153
188
|
!badge.isStarterBadge && Boolean(gdpProfile) && receiveBadgesSettingEnabled;
|
154
189
|
if (shouldActivateStarterBadge || shouldActivateActivityBasedBadge) {
|
@@ -7,8 +7,6 @@ import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterA
|
|
7
7
|
|
8
8
|
export {DefinitionKind, type ScopeTreeNode} from '../../entrypoints/formatter_worker/FormatterActions.js';
|
9
9
|
|
10
|
-
const MAX_WORKERS = Math.max(2, navigator.hardwareConcurrency - 1);
|
11
|
-
|
12
10
|
let formatterWorkerPoolInstance: FormatterWorkerPool;
|
13
11
|
|
14
12
|
export class FormatterWorkerPool {
|
@@ -37,12 +35,14 @@ export class FormatterWorkerPool {
|
|
37
35
|
}
|
38
36
|
|
39
37
|
private processNextTask(): void {
|
38
|
+
const maxWorkers = Math.max(2, navigator.hardwareConcurrency - 1);
|
39
|
+
|
40
40
|
if (!this.taskQueue.length) {
|
41
41
|
return;
|
42
42
|
}
|
43
43
|
|
44
44
|
let freeWorker = [...this.workerTasks.keys()].find(worker => !this.workerTasks.get(worker));
|
45
|
-
if (!freeWorker && this.workerTasks.size <
|
45
|
+
if (!freeWorker && this.workerTasks.size < maxWorkers) {
|
46
46
|
freeWorker = this.createWorker();
|
47
47
|
}
|
48
48
|
if (!freeWorker) {
|
@@ -69,6 +69,8 @@ const lockedString = i18n.i18n.lockedString;
|
|
69
69
|
|
70
70
|
const LEFT_OFFSET = 5;
|
71
71
|
const BOTTOM_OFFSET = 5;
|
72
|
+
const AUTO_CLOSE_TIME_IN_MS = 30000;
|
73
|
+
|
72
74
|
export interface BadgeNotificationAction {
|
73
75
|
label: string;
|
74
76
|
jslogContext?: string;
|
@@ -80,10 +82,11 @@ export interface BadgeNotificationProperties {
|
|
80
82
|
message: HTMLElement|string;
|
81
83
|
imageUri: string;
|
82
84
|
actions: BadgeNotificationAction[];
|
85
|
+
isStarterBadge: boolean;
|
83
86
|
}
|
84
87
|
|
85
88
|
export interface ViewInput extends BadgeNotificationProperties {
|
86
|
-
|
89
|
+
onDismissClick: () => void;
|
87
90
|
}
|
88
91
|
|
89
92
|
// clang-format off
|
@@ -101,7 +104,7 @@ const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement)
|
|
101
104
|
|
102
105
|
const crossButton = html`<devtools-button
|
103
106
|
class="dismiss notification-button"
|
104
|
-
@click=${input.
|
107
|
+
@click=${input.onDismissClick}
|
105
108
|
jslog=${VisualLogging.action('badge-notification.dismiss').track({click: true})}
|
106
109
|
aria-label=${i18nString(UIStrings.close)}
|
107
110
|
.iconName=${'cross'}
|
@@ -138,9 +141,10 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
138
141
|
message: HTMLElement|string = '';
|
139
142
|
imageUri = '';
|
140
143
|
actions: BadgeNotificationAction[] = [];
|
144
|
+
isStarterBadge = false;
|
141
145
|
|
146
|
+
#autoCloseTimeout?: number;
|
142
147
|
#view: View;
|
143
|
-
|
144
148
|
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
145
149
|
super(element);
|
146
150
|
this.#view = view;
|
@@ -170,12 +174,18 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
170
174
|
this.message = properties.message;
|
171
175
|
this.imageUri = properties.imageUri;
|
172
176
|
this.actions = properties.actions;
|
177
|
+
this.isStarterBadge = properties.isStarterBadge;
|
173
178
|
this.requestUpdate();
|
174
179
|
this.show(document.body);
|
175
180
|
|
176
181
|
void this.updateComplete.then(() => {
|
177
182
|
this.#positionNotification();
|
178
183
|
});
|
184
|
+
|
185
|
+
if (this.#autoCloseTimeout) {
|
186
|
+
window.clearTimeout(this.#autoCloseTimeout);
|
187
|
+
}
|
188
|
+
this.#autoCloseTimeout = window.setTimeout(this.#onAutoClose, AUTO_CLOSE_TIME_IN_MS);
|
179
189
|
}
|
180
190
|
|
181
191
|
async #presentStarterBadge(badge: Badges.Badge): Promise<void> {
|
@@ -201,17 +211,21 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
201
211
|
actions: [
|
202
212
|
{
|
203
213
|
label: i18nString(UIStrings.remindMeLater),
|
204
|
-
onClick: () => {
|
214
|
+
onClick: () => {
|
215
|
+
this.detach();
|
216
|
+
Badges.UserBadges.instance().snoozeStarterBadge();
|
217
|
+
},
|
205
218
|
},
|
206
219
|
{
|
207
220
|
label: i18nString(UIStrings.receiveBadges),
|
208
221
|
onClick: () => {
|
209
|
-
this
|
222
|
+
this.detach();
|
210
223
|
revealBadgeSettings();
|
211
224
|
}
|
212
225
|
}
|
213
226
|
],
|
214
227
|
imageUri: badge.imageUri,
|
228
|
+
isStarterBadge: true,
|
215
229
|
});
|
216
230
|
return;
|
217
231
|
}
|
@@ -223,17 +237,21 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
223
237
|
actions: [
|
224
238
|
{
|
225
239
|
label: i18nString(UIStrings.remindMeLater),
|
226
|
-
onClick: () => {
|
240
|
+
onClick: () => {
|
241
|
+
this.detach();
|
242
|
+
Badges.UserBadges.instance().snoozeStarterBadge();
|
243
|
+
},
|
227
244
|
},
|
228
245
|
{
|
229
246
|
label: i18nString(UIStrings.createProfile),
|
230
247
|
onClick: () => {
|
231
|
-
this
|
248
|
+
this.detach();
|
232
249
|
GdpSignUpDialog.GdpSignUpDialog.show();
|
233
250
|
}
|
234
251
|
}
|
235
252
|
],
|
236
253
|
imageUri: badge.imageUri,
|
254
|
+
isStarterBadge: true,
|
237
255
|
});
|
238
256
|
}
|
239
257
|
|
@@ -244,7 +262,7 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
244
262
|
{
|
245
263
|
label: i18nString(UIStrings.manageSettings),
|
246
264
|
onClick: () => {
|
247
|
-
this
|
265
|
+
this.detach();
|
248
266
|
revealBadgeSettings();
|
249
267
|
},
|
250
268
|
},
|
@@ -256,11 +274,28 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
256
274
|
}
|
257
275
|
],
|
258
276
|
imageUri: badge.imageUri,
|
277
|
+
isStarterBadge: badge.isStarterBadge,
|
259
278
|
});
|
260
279
|
}
|
261
280
|
|
262
|
-
|
281
|
+
override onDetach(): void {
|
282
|
+
window.clearTimeout(this.#autoCloseTimeout);
|
283
|
+
}
|
284
|
+
|
285
|
+
#onDismissClick = (): void => {
|
286
|
+
this.detach();
|
287
|
+
|
288
|
+
if (this.isStarterBadge) {
|
289
|
+
Badges.UserBadges.instance().dismissStarterBadge();
|
290
|
+
}
|
291
|
+
};
|
292
|
+
|
293
|
+
#onAutoClose = (): void => {
|
263
294
|
this.detach();
|
295
|
+
|
296
|
+
if (this.isStarterBadge) {
|
297
|
+
Badges.UserBadges.instance().snoozeStarterBadge();
|
298
|
+
}
|
264
299
|
};
|
265
300
|
|
266
301
|
override wasShown(): void {
|
@@ -273,7 +308,8 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
273
308
|
message: this.message,
|
274
309
|
imageUri: this.imageUri,
|
275
310
|
actions: this.actions,
|
276
|
-
|
311
|
+
isStarterBadge: this.isStarterBadge,
|
312
|
+
onDismissClick: this.#onDismissClick,
|
277
313
|
};
|
278
314
|
this.#view(viewInput, undefined, this.contentElement);
|
279
315
|
}
|
@@ -205,7 +205,6 @@ class ColorRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.ColorMatc
|
|
205
205
|
swatch.renderColor(color);
|
206
206
|
const valueElement = document.createElement('span');
|
207
207
|
valueElement.textContent = match.text;
|
208
|
-
swatch.append(valueElement);
|
209
208
|
|
210
209
|
swatch.addEventListener(
|
211
210
|
InlineEditor.ColorSwatch.ColorChangedEvent.eventName, (event: InlineEditor.ColorSwatch.ColorChangedEvent) => {
|
@@ -214,7 +213,7 @@ class ColorRenderer extends rendererBase(SDK.CSSPropertyParserMatchers.ColorMatc
|
|
214
213
|
});
|
215
214
|
|
216
215
|
context.addControl('color', swatch);
|
217
|
-
return [swatch];
|
216
|
+
return [swatch, valueElement];
|
218
217
|
}
|
219
218
|
|
220
219
|
matcher(): SDK.CSSPropertyParserMatchers.ColorMatcher {
|
@@ -284,7 +284,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
284
284
|
render(html`
|
285
285
|
<div style="min-width: min-content;" jslog=${VisualLogging.pane('layout').track({resize: true})}>
|
286
286
|
<style>${layoutPaneStyles}</style>
|
287
|
-
<style
|
287
|
+
<style>@scope to (devtools-widget > *) { ${UI.inspectorCommonStyles} }</style>
|
288
288
|
<details open>
|
289
289
|
<summary class="header"
|
290
290
|
@keydown=${input.onSummaryKeyDown}
|
@@ -2391,6 +2391,10 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
|
|
2391
2391
|
Lastly we replace new lines with ^ and TWO new lines because the first
|
2392
2392
|
new line is there to enact the escape command the second is the character
|
2393
2393
|
to escape (in this case new line).
|
2394
|
+
|
2395
|
+
All other whitespace characters are replaced with a single space, as there
|
2396
|
+
is no way to enter their literal values in a command line, and they do break
|
2397
|
+
the command allowing for injection.
|
2394
2398
|
*/
|
2395
2399
|
const encapsChars = '^"';
|
2396
2400
|
return encapsChars +
|
@@ -2398,7 +2402,7 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
|
|
2398
2402
|
.replace(/"/g, '\\"')
|
2399
2403
|
.replace(/[^a-zA-Z0-9\s_\-:=+~'\/.',?;()*`]/g, '^$&')
|
2400
2404
|
.replace(/%(?=[a-zA-Z0-9_])/g, '%^')
|
2401
|
-
.replace(/[^\S \r\n]/g, '
|
2405
|
+
.replace(/[^\S \r\n]/g, ' ')
|
2402
2406
|
.replace(/\r?\n|\r/g, '^\n\n') +
|
2403
2407
|
encapsChars;
|
2404
2408
|
}
|
@@ -2,14 +2,15 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
/* eslint-disable rulesdir/no-imperative-dom-api */
|
5
|
+
/* eslint-disable rulesdir/no-lit-render-outside-of-view */
|
5
6
|
|
6
7
|
import * as Common from '../../core/common/common.js';
|
7
8
|
import * as i18n from '../../core/i18n/i18n.js';
|
8
9
|
import * as Platform from '../../core/platform/platform.js';
|
9
10
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
10
11
|
import type * as Workspace from '../../models/workspace/workspace.js';
|
11
|
-
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
12
12
|
import * as UI from '../../ui/legacy/legacy.js';
|
13
|
+
import {html, render} from '../../ui/lit/lit.js';
|
13
14
|
|
14
15
|
import searchResultsPaneStyles from './searchResultsPane.css.js';
|
15
16
|
import type {SearchResult} from './SearchScope.js';
|
@@ -137,31 +138,23 @@ export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
|
|
137
138
|
|
138
139
|
private updateSearchMatches(): void {
|
139
140
|
this.listItemElement.classList.add('search-result');
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
141
|
+
// clang-format off
|
142
|
+
render(html`
|
143
|
+
<span class="search-result-file-name">${this.searchResult.label()}
|
144
|
+
<span class="search-result-dash">${'\u2014'}</span>
|
145
|
+
<span class="search-result-qualifier">${this.searchResult.description()}</span>
|
146
|
+
</span>
|
147
|
+
<span class="search-result-matches-count"
|
148
|
+
aria-label=${i18nString(UIStrings.matchesCountS, {PH1: this.searchResult.matchesCount()})}>
|
149
|
+
${this.searchResult.matchesCount()}
|
150
|
+
</span>`,
|
151
|
+
this.listItemElement);
|
152
|
+
// clang-format on
|
144
153
|
|
145
154
|
this.tooltip = this.searchResult.description();
|
146
|
-
this.listItemElement.appendChild(fileNameSpan);
|
147
|
-
const matchesCountSpan = document.createElement('span');
|
148
|
-
matchesCountSpan.className = 'search-result-matches-count';
|
149
|
-
|
150
|
-
matchesCountSpan.textContent = `${this.searchResult.matchesCount()}`;
|
151
|
-
UI.ARIAUtils.setLabel(
|
152
|
-
matchesCountSpan, i18nString(UIStrings.matchesCountS, {PH1: this.searchResult.matchesCount()}));
|
153
|
-
|
154
|
-
this.listItemElement.appendChild(matchesCountSpan);
|
155
155
|
if (this.expanded) {
|
156
156
|
this.updateMatchesUI();
|
157
157
|
}
|
158
|
-
|
159
|
-
function span(text: string, className: string): Element {
|
160
|
-
const span = document.createElement('span');
|
161
|
-
span.className = className;
|
162
|
-
span.textContent = text;
|
163
|
-
return span;
|
164
|
-
}
|
165
158
|
}
|
166
159
|
|
167
160
|
private appendSearchMatches(fromIndex: number, toIndex: number): void {
|
@@ -195,28 +188,29 @@ export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
|
|
195
188
|
({lineSegment: lineContent, matchRanges} = lineSegmentForMultipleMatches(lineContent, matchRanges));
|
196
189
|
}
|
197
190
|
|
198
|
-
const anchor = Components.Linkifier.Linkifier.linkifyRevealable(
|
199
|
-
searchResult.matchRevealable(i), '', undefined, undefined, undefined, 'search-match');
|
200
|
-
anchor.classList.add('search-match-link');
|
201
|
-
anchor.tabIndex = 0;
|
202
|
-
const labelSpan = document.createElement('span');
|
203
|
-
labelSpan.classList.add('search-match-line-number');
|
204
191
|
const resultLabel = searchResult.matchLabel(i);
|
205
|
-
labelSpan.textContent = resultLabel;
|
206
|
-
if (typeof resultLabel === 'number' && !isNaN(resultLabel)) {
|
207
|
-
UI.ARIAUtils.setLabel(labelSpan, i18nString(UIStrings.lineS, {PH1: resultLabel}));
|
208
|
-
} else {
|
209
|
-
UI.ARIAUtils.setLabel(labelSpan, resultLabel);
|
210
|
-
}
|
211
|
-
anchor.appendChild(labelSpan);
|
212
|
-
|
213
|
-
const contentSpan = this.createContentSpan(lineContent, matchRanges);
|
214
|
-
anchor.appendChild(contentSpan);
|
215
192
|
|
216
193
|
const searchMatchElement = new UI.TreeOutline.TreeElement();
|
217
194
|
this.appendChild(searchMatchElement);
|
195
|
+
// clang-format off
|
196
|
+
render(html`
|
197
|
+
<button class="devtools-link text-button link-style search-match-link"
|
198
|
+
jslog="Link; context: search-match; track: click" role="link" tabindex="0"
|
199
|
+
@click=${() => void Common.Revealer.reveal(searchResult.matchRevealable(i))}>
|
200
|
+
<span class="search-match-line-number"
|
201
|
+
aria-label=${typeof resultLabel === 'number' && !isNaN(resultLabel)
|
202
|
+
? i18nString(UIStrings.lineS, {PH1: resultLabel}) : resultLabel}>
|
203
|
+
${resultLabel}
|
204
|
+
</span>
|
205
|
+
<span class="search-match-content" aria-label="${lineContent} line">
|
206
|
+
${lineContent}
|
207
|
+
</span>
|
208
|
+
</button>`,
|
209
|
+
searchMatchElement.listItemElement);
|
210
|
+
// clang-format on
|
211
|
+
const contentSpan = searchMatchElement.listItemElement.querySelector('.search-match-content') as HTMLElement;
|
212
|
+
UI.UIUtils.highlightRangesWithStyleClass(contentSpan, matchRanges, 'highlighted-search-result');
|
218
213
|
searchMatchElement.listItemElement.className = 'search-match';
|
219
|
-
searchMatchElement.listItemElement.appendChild(anchor);
|
220
214
|
searchMatchElement.listItemElement.addEventListener('keydown', event => {
|
221
215
|
if (event.key === 'Enter') {
|
222
216
|
event.consume(true);
|
@@ -237,15 +231,6 @@ export class SearchResultsTreeElement extends UI.TreeOutline.TreeElement {
|
|
237
231
|
this.showMoreMatchesElementSelected.bind(this, showMoreMatchesTreeElement, startMatchIndex);
|
238
232
|
}
|
239
233
|
|
240
|
-
private createContentSpan(lineContent: string, matchRanges: TextUtils.TextRange.SourceRange[]): Element {
|
241
|
-
const contentSpan = document.createElement('span');
|
242
|
-
contentSpan.className = 'search-match-content';
|
243
|
-
contentSpan.textContent = lineContent;
|
244
|
-
UI.ARIAUtils.setLabel(contentSpan, `${lineContent} line`);
|
245
|
-
UI.UIUtils.highlightRangesWithStyleClass(contentSpan, matchRanges, 'highlighted-search-result');
|
246
|
-
return contentSpan;
|
247
|
-
}
|
248
|
-
|
249
234
|
private regexMatchRanges(lineContent: string, regex: RegExp): TextUtils.TextRange.SourceRange[] {
|
250
235
|
regex.lastIndex = 0;
|
251
236
|
let match;
|