chrome-devtools-frontend 1.0.1593959 → 1.0.1595925
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/docs/contributing/settings-experiments-features.md +25 -5
- package/front_end/core/common/README.md +126 -0
- package/front_end/core/sdk/CSSModel.ts +22 -0
- package/front_end/core/sdk/CSSNavigation.ts +33 -0
- package/front_end/core/sdk/CSSRule.ts +12 -2
- package/front_end/core/sdk/sdk-meta.ts +22 -18
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/design_system_tokens.css +538 -259
- package/front_end/entrypoints/main/main-meta.ts +2 -2
- package/front_end/generated/InspectorBackendCommands.ts +3 -5
- package/front_end/generated/SupportedCSSProperties.js +12 -0
- package/front_end/generated/protocol-mapping.d.ts +0 -8
- package/front_end/generated/protocol-proxy-api.d.ts +0 -6
- package/front_end/generated/protocol.ts +1 -19
- package/front_end/models/issues_manager/Issue.ts +1 -0
- package/front_end/models/issues_manager/IssueAggregator.ts +9 -0
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/SelectivePermissionsInterventionIssue.ts +65 -0
- package/front_end/models/issues_manager/descriptions/selectivePermissionsIntervention.md +7 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +49 -10
- package/front_end/models/persistence/persistence-meta.ts +4 -4
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +37 -30
- package/front_end/panels/ai_assistance/components/ChatView.ts +4 -2
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +10 -7
- package/front_end/panels/ai_assistance/components/chatInput.css +26 -1
- package/front_end/panels/ai_assistance/components/walkthroughView.css +15 -0
- package/front_end/panels/application/StorageView.ts +8 -2
- package/front_end/panels/application/preloading/PreloadingView.ts +105 -7
- package/front_end/panels/application/preloading/preloadingView.css +4 -0
- package/front_end/panels/console/ConsoleView.ts +2 -2
- package/front_end/panels/console/console-meta.ts +18 -14
- package/front_end/panels/elements/ComputedStyleWidget.ts +12 -7
- package/front_end/panels/elements/ElementsPanel.ts +1 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +4 -6
- package/front_end/panels/elements/ImagePreviewPopover.ts +6 -9
- package/front_end/panels/elements/StylePropertiesSection.ts +30 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +13 -7
- package/front_end/panels/elements/elements-meta.ts +12 -8
- package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +120 -0
- package/front_end/panels/issues/IssueView.ts +2 -0
- package/front_end/panels/issues/IssuesPane.ts +6 -0
- package/front_end/panels/issues/issues.ts +2 -0
- package/front_end/panels/lighthouse/LighthouseStartView.ts +99 -38
- package/front_end/panels/lighthouse/LighthouseTimespanView.ts +53 -14
- package/front_end/panels/lighthouse/RadioSetting.ts +33 -19
- package/front_end/panels/lighthouse/lighthouse.ts +2 -0
- package/front_end/panels/media/PlayerMessagesView.ts +62 -49
- package/front_end/panels/media/media.ts +2 -0
- package/front_end/panels/media/playerMessagesView.css +1 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +49 -24
- package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +2 -2
- package/front_end/panels/sources/sources-meta.ts +8 -4
- package/front_end/panels/utils/utils.ts +11 -5
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lit/lib/async-directive.d.ts +465 -0
- package/front_end/third_party/lit/lib/async-directive.js +23 -0
- package/front_end/third_party/lit/lib/async-directive.js.map +1 -0
- package/front_end/third_party/lit/lib/decorators.d.ts +7 -1
- package/front_end/third_party/lit/lib/decorators.js +2 -2
- package/front_end/third_party/lit/lib/decorators.js.map +1 -1
- package/front_end/third_party/lit/lib/directive.js.map +1 -1
- package/front_end/third_party/lit/lib/directives.d.ts +27 -8
- package/front_end/third_party/lit/lib/directives.js +8 -8
- package/front_end/third_party/lit/lib/directives.js.map +1 -1
- package/front_end/third_party/lit/lib/lit.d.ts +15 -5
- package/front_end/third_party/lit/lib/lit.js +4 -4
- package/front_end/third_party/lit/lib/lit.js.map +1 -1
- package/front_end/third_party/lit/lib/static-html.js +2 -2
- package/front_end/third_party/lit/lib/static-html.js.map +1 -1
- package/front_end/third_party/lit/lit.ts +2 -1
- package/front_end/third_party/lit/rollup.config.mjs +1 -1
- package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +5 -0
- package/front_end/ui/legacy/components/utils/ImagePreview.ts +27 -23
- package/front_end/ui/lit/lit.ts +1 -0
- package/front_end/ui/visual_logging/Debugging.ts +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
- package/package.json +1 -1
|
@@ -693,7 +693,15 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
693
693
|
blockedByCrossOrigin: this.#conversation.isBlockedByOrigin,
|
|
694
694
|
isLoading: this.#isLoading,
|
|
695
695
|
messages: this.#messages,
|
|
696
|
-
|
|
696
|
+
/**
|
|
697
|
+
* We pass either the selected context with isContextSelected=true
|
|
698
|
+
* to make sure the pill is show with normal styling and a remove button.
|
|
699
|
+
* Or we pass the panels default context with isContextSelected=false
|
|
700
|
+
* to display a placeholder pill with neutral styling and an add button.
|
|
701
|
+
*/
|
|
702
|
+
context:
|
|
703
|
+
this.#conversation.selectedContext ?? this.#getConversationContext(this.#getDefaultConversationType()),
|
|
704
|
+
isContextSelected: Boolean(this.#conversation.selectedContext),
|
|
697
705
|
conversationType: this.#conversation.type,
|
|
698
706
|
isReadOnly: this.#conversation.isReadOnly ?? false,
|
|
699
707
|
changeSummary: this.#getChangeSummary(),
|
|
@@ -857,17 +865,20 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
857
865
|
#selectDefaultAgentIfNeeded(): void {
|
|
858
866
|
// We don't change the current agent when there is a message in flight.
|
|
859
867
|
if (this.#isLoading) {
|
|
868
|
+
this.requestUpdate();
|
|
860
869
|
return;
|
|
861
870
|
}
|
|
862
871
|
|
|
863
872
|
// If there already is an agent and if it is not empty,
|
|
864
873
|
// we don't automatically change the agent.
|
|
865
874
|
if (this.#conversation && !this.#conversation.isEmpty) {
|
|
875
|
+
this.requestUpdate();
|
|
866
876
|
return;
|
|
867
877
|
}
|
|
868
878
|
|
|
869
879
|
const targetConversationType = this.#getDefaultConversationType();
|
|
870
880
|
if (this.#conversation?.type === targetConversationType) {
|
|
881
|
+
this.requestUpdate();
|
|
871
882
|
// The above if makes sure even if we have an active agent it's empty
|
|
872
883
|
// So we can just reuse it
|
|
873
884
|
return;
|
|
@@ -134,7 +134,8 @@ export interface ViewInput {
|
|
|
134
134
|
blockedByCrossOrigin: boolean;
|
|
135
135
|
isTextInputDisabled: boolean;
|
|
136
136
|
inputPlaceholder: Platform.UIString.LocalizedString;
|
|
137
|
-
|
|
137
|
+
context: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null;
|
|
138
|
+
isContextSelected: boolean;
|
|
138
139
|
inspectElementToggled: boolean;
|
|
139
140
|
disclaimerText: string;
|
|
140
141
|
conversationType: AiAssistanceModel.AiHistoryStorage.ConversationType;
|
|
@@ -183,7 +184,7 @@ function getContextRemoveLabel(context: AiAssistanceModel.AiAgent.ConversationCo
|
|
|
183
184
|
export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTMLElement): void => {
|
|
184
185
|
const chatInputContainerCls = Lit.Directives.classMap({
|
|
185
186
|
'chat-input-container': true,
|
|
186
|
-
'single-line-layout': !input.
|
|
187
|
+
'single-line-layout': !input.context,
|
|
187
188
|
disabled: input.isTextInputDisabled,
|
|
188
189
|
});
|
|
189
190
|
|
|
@@ -300,7 +301,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
300
301
|
></textarea>
|
|
301
302
|
<div class="chat-input-actions">
|
|
302
303
|
<div class="chat-input-actions-left">
|
|
303
|
-
${input.
|
|
304
|
+
${input.context ?
|
|
304
305
|
html`
|
|
305
306
|
<div class="select-element">
|
|
306
307
|
${input.conversationType === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING ?
|
|
@@ -321,29 +322,33 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
321
322
|
></devtools-button>`
|
|
322
323
|
: Lit.nothing}
|
|
323
324
|
<div
|
|
324
|
-
class
|
|
325
|
+
class=${Lit.Directives.classMap({
|
|
326
|
+
'resource-link': true,
|
|
327
|
+
disabled: !input.isContextSelected,
|
|
328
|
+
})}
|
|
325
329
|
>
|
|
326
330
|
${
|
|
327
|
-
input.
|
|
331
|
+
input.context instanceof AiAssistanceModel.StylingAgent.NodeContext ?
|
|
328
332
|
html`
|
|
329
333
|
<devtools-widget
|
|
330
334
|
class="title"
|
|
331
335
|
.widgetConfig=${UI.Widget.widgetConfig(PanelsCommon.DOMLinkifier.DOMNodeLink, {
|
|
332
|
-
node: input.
|
|
336
|
+
node: input.context.getItem(),
|
|
333
337
|
options: {
|
|
334
|
-
|
|
338
|
+
disabled: !input.isContextSelected,
|
|
339
|
+
hiddenClassList: input.context.getItem().classNames().filter(
|
|
335
340
|
className => className.startsWith(AiAssistanceModel.Injected.AI_ASSISTANCE_CSS_CLASS_NAME)),
|
|
336
341
|
ariaDescription: i18nString(UIStrings.revealContextDescription),
|
|
337
342
|
},
|
|
338
343
|
})}
|
|
339
344
|
></devtools-widget>` :
|
|
340
345
|
html`
|
|
341
|
-
${input.
|
|
342
|
-
PanelUtils.PanelUtils.getIconForNetworkRequest(input.
|
|
343
|
-
input.
|
|
344
|
-
PanelUtils.PanelUtils.getIconForSourceFile(input.
|
|
345
|
-
input.
|
|
346
|
-
html`<devtools-icon name="performance" title="Performance"></devtools-icon>` :
|
|
346
|
+
${input.context instanceof AiAssistanceModel.NetworkAgent.RequestContext ?
|
|
347
|
+
PanelUtils.PanelUtils.getIconForNetworkRequest(input.context.getItem()) :
|
|
348
|
+
input.context instanceof AiAssistanceModel.FileAgent.FileContext ?
|
|
349
|
+
PanelUtils.PanelUtils.getIconForSourceFile(input.context.getItem()) :
|
|
350
|
+
input.context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext ?
|
|
351
|
+
html`<devtools-icon class="icon" name="performance" title="Performance"></devtools-icon>` :
|
|
347
352
|
Lit.nothing}
|
|
348
353
|
<span
|
|
349
354
|
role="button"
|
|
@@ -356,31 +361,31 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
356
361
|
}
|
|
357
362
|
}}
|
|
358
363
|
aria-description=${i18nString(UIStrings.revealContextDescription)}
|
|
359
|
-
>${input.
|
|
364
|
+
>${input.context.getTitle()}</span>`
|
|
360
365
|
}
|
|
361
|
-
${input.onContextRemoved ? html`
|
|
366
|
+
${input.isContextSelected && input.onContextRemoved ? html`
|
|
362
367
|
<devtools-button
|
|
363
|
-
title=${getContextRemoveLabel(input.
|
|
364
|
-
aria-label=${getContextRemoveLabel(input.
|
|
368
|
+
title=${getContextRemoveLabel(input.context)}
|
|
369
|
+
aria-label=${getContextRemoveLabel(input.context)}
|
|
365
370
|
class="remove-context"
|
|
366
371
|
.iconName=${'cross'}
|
|
367
372
|
.size=${Buttons.Button.Size.MICRO}
|
|
368
373
|
.jslogContext=${'context-removed'}
|
|
369
374
|
.variant=${Buttons.Button.Variant.ICON}
|
|
370
375
|
@click=${input.onContextRemoved}></devtools-button>` : Lit.nothing}
|
|
376
|
+
${!input.isContextSelected && input.onContextAdd ? html`
|
|
377
|
+
<devtools-button
|
|
378
|
+
title=${lockedString(UIStringsNotTranslate.addContext)}
|
|
379
|
+
aria-label=${lockedString(UIStringsNotTranslate.addContext)}
|
|
380
|
+
class="add-context"
|
|
381
|
+
.iconName=${'plus'}
|
|
382
|
+
.size=${Buttons.Button.Size.MICRO}
|
|
383
|
+
.jslogContext=${'context-added'}
|
|
384
|
+
.variant=${Buttons.Button.Variant.ICON}
|
|
385
|
+
@click=${input.onContextAdd}></devtools-button>` : Lit.nothing}
|
|
371
386
|
</div>
|
|
372
387
|
</div>`
|
|
373
|
-
:
|
|
374
|
-
input.onContextAdd ? html`
|
|
375
|
-
<devtools-button
|
|
376
|
-
title=${lockedString(UIStringsNotTranslate.addContext)}
|
|
377
|
-
aria-label=${lockedString(UIStringsNotTranslate.addContext)}
|
|
378
|
-
class="add-context"
|
|
379
|
-
.iconName=${'plus'}
|
|
380
|
-
.size=${Buttons.Button.Size.SMALL}
|
|
381
|
-
.jslogContext=${'context-added'}
|
|
382
|
-
.variant=${Buttons.Button.Variant.ICON}
|
|
383
|
-
@click=${input.onContextAdd}></devtools-button>` : Lit.nothing}
|
|
388
|
+
: Lit.nothing}
|
|
384
389
|
</div>
|
|
385
390
|
<div class="chat-input-actions-right">
|
|
386
391
|
<div class="chat-input-disclaimer-container">
|
|
@@ -488,7 +493,8 @@ export class ChatInput extends UI.Widget.Widget implements SDK.TargetManager.Obs
|
|
|
488
493
|
blockedByCrossOrigin = false;
|
|
489
494
|
isTextInputDisabled = false;
|
|
490
495
|
inputPlaceholder = '' as Platform.UIString.LocalizedString;
|
|
491
|
-
|
|
496
|
+
context: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null = null;
|
|
497
|
+
isContextSelected = false;
|
|
492
498
|
inspectElementToggled = false;
|
|
493
499
|
disclaimerText = '';
|
|
494
500
|
conversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING;
|
|
@@ -675,7 +681,8 @@ export class ChatInput extends UI.Widget.Widget implements SDK.TargetManager.Obs
|
|
|
675
681
|
isLoading: this.isLoading,
|
|
676
682
|
blockedByCrossOrigin: this.blockedByCrossOrigin,
|
|
677
683
|
isTextInputDisabled: this.isTextInputDisabled,
|
|
678
|
-
|
|
684
|
+
context: this.context,
|
|
685
|
+
isContextSelected: this.isContextSelected,
|
|
679
686
|
inspectElementToggled: this.inspectElementToggled,
|
|
680
687
|
isTextInputEmpty: this.#isTextInputEmpty(),
|
|
681
688
|
disclaimerText: this.disclaimerText,
|
|
@@ -65,7 +65,8 @@ export interface Props {
|
|
|
65
65
|
changeManager: AiAssistanceModel.ChangeManager.ChangeManager;
|
|
66
66
|
inspectElementToggled: boolean;
|
|
67
67
|
messages: Message[];
|
|
68
|
-
|
|
68
|
+
context: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null;
|
|
69
|
+
isContextSelected: boolean;
|
|
69
70
|
isLoading: boolean;
|
|
70
71
|
canShowFeedbackForm: boolean;
|
|
71
72
|
userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountGivenName'>;
|
|
@@ -178,7 +179,8 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
178
179
|
isTextInputDisabled: input.isTextInputDisabled,
|
|
179
180
|
inputPlaceholder: input.inputPlaceholder,
|
|
180
181
|
disclaimerText: input.disclaimerText,
|
|
181
|
-
|
|
182
|
+
context: input.context,
|
|
183
|
+
isContextSelected: input.isContextSelected,
|
|
182
184
|
inspectElementToggled: input.inspectElementToggled,
|
|
183
185
|
multimodalInputEnabled: input.multimodalInputEnabled ?? false,
|
|
184
186
|
conversationType: input.conversationType,
|
|
@@ -117,13 +117,16 @@ export const DEFAULT_VIEW = (
|
|
|
117
117
|
const stepsOutput = steps.length > 0 ? html`
|
|
118
118
|
<div class="steps-container">
|
|
119
119
|
${steps.map((step, index) => html`
|
|
120
|
-
<div class="step
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
<div class="walkthrough-step">
|
|
121
|
+
<span class="step-number">${index+1}</span>
|
|
122
|
+
<div class="step-wrapper">
|
|
123
|
+
${renderStep({
|
|
124
|
+
step,
|
|
125
|
+
isLoading: input.isLoading,
|
|
126
|
+
markdownRenderer: input.markdownRenderer,
|
|
127
|
+
isLast: index === steps.length - 1
|
|
128
|
+
})}
|
|
129
|
+
</div>
|
|
127
130
|
</div>
|
|
128
131
|
`)}
|
|
129
132
|
</div>
|
|
@@ -239,12 +239,15 @@
|
|
|
239
239
|
|
|
240
240
|
& .title {
|
|
241
241
|
vertical-align: middle;
|
|
242
|
+
/* Fixed italic text getting cut off */
|
|
243
|
+
padding-right: var(--sys-size-2);
|
|
242
244
|
font: var(--sys-typescale-body5-regular);
|
|
243
245
|
overflow: hidden;
|
|
244
246
|
text-overflow: ellipsis;
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
& .remove-context
|
|
249
|
+
& .remove-context,
|
|
250
|
+
& .add-context {
|
|
248
251
|
vertical-align: middle;
|
|
249
252
|
}
|
|
250
253
|
|
|
@@ -260,6 +263,28 @@
|
|
|
260
263
|
min-height: var(--sys-size-7);
|
|
261
264
|
}
|
|
262
265
|
|
|
266
|
+
&.disabled {
|
|
267
|
+
border-style: dashed;
|
|
268
|
+
border-color: var(--sys-color-neutral-outline);
|
|
269
|
+
color: var(--sys-color-on-surface-light);
|
|
270
|
+
|
|
271
|
+
devtools-icon,
|
|
272
|
+
devtools-file-source-icon {
|
|
273
|
+
/* Override devtools-file-source-icon */
|
|
274
|
+
--override-file-source-icon-color: var(
|
|
275
|
+
--sys-color-on-surface-light-graphics
|
|
276
|
+
);
|
|
277
|
+
/* Some icons set their style attribute and we need to override it */
|
|
278
|
+
/* stylelint-disable-next-line declaration-no-important */
|
|
279
|
+
color: var(--sys-color-on-surface-light-graphics) !important;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.title {
|
|
283
|
+
color: var(--sys-color-on-surface-light);
|
|
284
|
+
font-style: italic;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
263
288
|
/*
|
|
264
289
|
CSS styling for `network-override-marker` is similar to
|
|
265
290
|
https://source.chromium.org/chromium/chromium/src/+/main:third_party/devtools-frontend/src/front_end/panels/network/networkLogView.css;l=379.
|
|
@@ -38,6 +38,21 @@
|
|
|
38
38
|
gap: var(--sys-size-6);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
.walkthrough-step {
|
|
42
|
+
display: flex;
|
|
43
|
+
gap: var(--sys-size-6);
|
|
44
|
+
align-items: flex-start;
|
|
45
|
+
justify-content: flex-start;
|
|
46
|
+
|
|
47
|
+
.step-number {
|
|
48
|
+
font: var(--sys-typescale-body4-regular);
|
|
49
|
+
color: var(--sys-color-on-surface-subtle);
|
|
50
|
+
padding-top:var(--sys-size-4);
|
|
51
|
+
flex-grow: 0;
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
41
56
|
.step-wrapper {
|
|
42
57
|
display: flex;
|
|
43
58
|
flex-direction: column;
|
|
@@ -510,8 +510,14 @@ export class StorageView extends UI.Widget.VBox {
|
|
|
510
510
|
const quotaAsString = i18n.ByteUtilities.bytesToString(response.quota);
|
|
511
511
|
const usageAsString = i18n.ByteUtilities.bytesToString(response.usage);
|
|
512
512
|
const formattedQuotaAsString = i18nString(UIStrings.storageWithCustomMarker, {PH1: quotaAsString});
|
|
513
|
-
|
|
514
|
-
|
|
513
|
+
|
|
514
|
+
let quota: string|HTMLElement = quotaAsString;
|
|
515
|
+
if (quotaOverridden) {
|
|
516
|
+
const element = document.createElement('b');
|
|
517
|
+
element.textContent = formattedQuotaAsString;
|
|
518
|
+
quota = element;
|
|
519
|
+
}
|
|
520
|
+
|
|
515
521
|
const element = uiI18n.getFormatLocalizedString(str_, UIStrings.storageQuotaUsed, {PH1: usageAsString, PH2: quota});
|
|
516
522
|
this.quotaRow.appendChild(element);
|
|
517
523
|
UI.Tooltip.Tooltip.install(
|
|
@@ -13,6 +13,7 @@ import * as Platform from '../../../core/platform/platform.js';
|
|
|
13
13
|
import {assertNotNullOrUndefined} from '../../../core/platform/platform.js';
|
|
14
14
|
import * as SDK from '../../../core/sdk/sdk.js';
|
|
15
15
|
import * as Protocol from '../../../generated/protocol.js';
|
|
16
|
+
import * as TextUtils from '../../../models/text_utils/text_utils.js';
|
|
16
17
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
17
18
|
// eslint-disable-next-line @devtools/es-modules-import
|
|
18
19
|
import emptyWidgetStyles from '../../../ui/legacy/emptyWidget.css.js';
|
|
@@ -21,7 +22,7 @@ import {Directives, html, render} from '../../../ui/lit/lit.js';
|
|
|
21
22
|
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
|
22
23
|
|
|
23
24
|
import * as PreloadingComponents from './components/components.js';
|
|
24
|
-
import {ruleSetTagOrLocationShort} from './components/PreloadingString.js';
|
|
25
|
+
import {capitalizedAction, ruleSetTagOrLocationShort} from './components/PreloadingString.js';
|
|
25
26
|
import * as PreloadingHelper from './helper/helper.js';
|
|
26
27
|
import preloadingViewStyles from './preloadingView.css.js';
|
|
27
28
|
import preloadingViewDropDownStyles from './preloadingViewDropDown.css.js';
|
|
@@ -362,6 +363,78 @@ export class PreloadingRuleSetView extends UI.Widget.VBox {
|
|
|
362
363
|
}
|
|
363
364
|
}
|
|
364
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Pure filtering function for preloading grid rows.
|
|
368
|
+
* Exported for testability.
|
|
369
|
+
*/
|
|
370
|
+
export function applyFilterText(filterText: string, rows: PreloadingComponents.PreloadingGrid.PreloadingGridRow[]):
|
|
371
|
+
PreloadingComponents.PreloadingGrid.PreloadingGridRow[] {
|
|
372
|
+
const trimmedFilter = filterText.trim();
|
|
373
|
+
if (trimmedFilter === '') {
|
|
374
|
+
return rows;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const FILTER_KEYS = ['url', 'action', 'status'] as const;
|
|
378
|
+
const parser = new TextUtils.TextUtils.FilterParser([...FILTER_KEYS]);
|
|
379
|
+
// The match is case-insensitive. We handle the matching with everything lower cased,
|
|
380
|
+
// both keywords and values.
|
|
381
|
+
const query = parser.parse(filterText.toLowerCase());
|
|
382
|
+
|
|
383
|
+
// Drop the last term if it is an incomplete filter key (e.g., "action:" with no value).
|
|
384
|
+
// FilterParser parses "action:" as plain text since KEY_VALUE_FILTER_REGEXP requires a value.
|
|
385
|
+
// This lets users type "url:foo action:" and still see results for "url:foo".
|
|
386
|
+
const lastTerm = query.at(-1);
|
|
387
|
+
|
|
388
|
+
// If the parse result is empty, the query only contains spaces.
|
|
389
|
+
if (!lastTerm) {
|
|
390
|
+
return rows;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const isKeyWithNoValue =
|
|
394
|
+
(lastTerm.key === undefined || lastTerm.key === null) && FILTER_KEYS.some(key => lastTerm.text === `${key}:`);
|
|
395
|
+
if (isKeyWithNoValue) {
|
|
396
|
+
query.pop();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (query.length === 0) {
|
|
400
|
+
return rows;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return rows.filter(row => {
|
|
404
|
+
const attempt = row.pipeline.getOriginallyTriggered();
|
|
405
|
+
const url = attempt.key.url.toLowerCase();
|
|
406
|
+
const action = capitalizedAction(attempt.action).toLowerCase();
|
|
407
|
+
const status = PreloadingUIUtils.status(attempt.status).toLowerCase();
|
|
408
|
+
|
|
409
|
+
// Each term must match (AND logic between terms)
|
|
410
|
+
return query.every(term => {
|
|
411
|
+
if (term.text === undefined || term.text === null || term.text === '') {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
const searchText = term.text.toLowerCase();
|
|
415
|
+
|
|
416
|
+
// The query is lowercased before parsing, so keys are already normalized.
|
|
417
|
+
const key = term.key;
|
|
418
|
+
switch (key) {
|
|
419
|
+
case 'url':
|
|
420
|
+
return url.includes(searchText);
|
|
421
|
+
case 'action':
|
|
422
|
+
return action.includes(searchText);
|
|
423
|
+
case 'status': {
|
|
424
|
+
// Support multiple status values separated by comma (e.g., "status:ready,success")
|
|
425
|
+
const statusValues = searchText.split(',');
|
|
426
|
+
return statusValues.some(v => status.includes(v));
|
|
427
|
+
}
|
|
428
|
+
case undefined:
|
|
429
|
+
// No key specified: search across all columns (URL, action, status)
|
|
430
|
+
return url.includes(searchText) || action.includes(searchText) || status.includes(searchText);
|
|
431
|
+
default:
|
|
432
|
+
return false;
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
365
438
|
export class PreloadingAttemptView extends UI.Widget.VBox {
|
|
366
439
|
private model: SDK.PreloadingModel.PreloadingModel;
|
|
367
440
|
// Note that we use id of (representative) preloading attempt while we show pipelines in grid.
|
|
@@ -374,6 +447,7 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
|
|
|
374
447
|
private readonly preloadingDetails =
|
|
375
448
|
new PreloadingComponents.PreloadingDetailsReportView.PreloadingDetailsReportView();
|
|
376
449
|
private readonly ruleSetSelector: PreloadingRuleSetSelector;
|
|
450
|
+
private readonly textFilterUI: UI.Toolbar.ToolbarFilter;
|
|
377
451
|
private clearButton: UI.Toolbar.ToolbarButton;
|
|
378
452
|
|
|
379
453
|
constructor(model: SDK.PreloadingModel.PreloadingModel) {
|
|
@@ -417,7 +491,19 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
|
|
|
417
491
|
const toolbar = vbox.contentElement.createChild('devtools-toolbar', 'preloading-toolbar');
|
|
418
492
|
toolbar.setAttribute('jslog', `${VisualLogging.toolbar()}`);
|
|
419
493
|
|
|
420
|
-
//
|
|
494
|
+
// Rule set dropdown first
|
|
495
|
+
this.ruleSetSelector = new PreloadingRuleSetSelector(() => this.render());
|
|
496
|
+
toolbar.appendToolbarItem(this.ruleSetSelector.item());
|
|
497
|
+
|
|
498
|
+
// Text filter second
|
|
499
|
+
this.textFilterUI = new UI.Toolbar.ToolbarFilter(undefined, 1, 1);
|
|
500
|
+
this.textFilterUI.addEventListener(UI.Toolbar.ToolbarInput.Event.TEXT_CHANGED, this.onTextFilterChanged, this);
|
|
501
|
+
toolbar.appendToolbarItem(this.textFilterUI);
|
|
502
|
+
|
|
503
|
+
// Separator between text filter and clear button
|
|
504
|
+
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSeparator());
|
|
505
|
+
|
|
506
|
+
// Clear button last (rightmost)
|
|
421
507
|
this.clearButton =
|
|
422
508
|
new UI.Toolbar.ToolbarButton('Clear speculative loads', 'clear', undefined, 'clear-speculative-loads');
|
|
423
509
|
this.clearButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, () => {
|
|
@@ -428,14 +514,13 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
|
|
|
428
514
|
}
|
|
429
515
|
|
|
430
516
|
model.reset();
|
|
517
|
+
// Reset UI state
|
|
518
|
+
this.textFilterUI.setValue('');
|
|
431
519
|
this.ruleSetSelector.select(null);
|
|
520
|
+
this.render();
|
|
432
521
|
});
|
|
433
522
|
toolbar.appendToolbarItem(this.clearButton);
|
|
434
523
|
|
|
435
|
-
// Rule set dropdown
|
|
436
|
-
this.ruleSetSelector = new PreloadingRuleSetSelector(() => this.render());
|
|
437
|
-
toolbar.appendToolbarItem(this.ruleSetSelector.item());
|
|
438
|
-
|
|
439
524
|
this.preloadingGrid.onSelect = this.onPreloadingGridCellFocused.bind(this);
|
|
440
525
|
|
|
441
526
|
const preloadingGridContainer = document.createElement('div');
|
|
@@ -491,6 +576,14 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
|
|
|
491
576
|
}
|
|
492
577
|
|
|
493
578
|
this.ruleSetSelector.select(id);
|
|
579
|
+
|
|
580
|
+
// Reset text filter when navigating from Rules view to clear any existing filter
|
|
581
|
+
this.textFilterUI.setValue('');
|
|
582
|
+
this.render();
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private onTextFilterChanged(): void {
|
|
586
|
+
this.render();
|
|
494
587
|
}
|
|
495
588
|
|
|
496
589
|
private updatePreloadingDetails(): void {
|
|
@@ -532,8 +625,13 @@ export class PreloadingAttemptView extends UI.Widget.VBox {
|
|
|
532
625
|
statusCode,
|
|
533
626
|
};
|
|
534
627
|
});
|
|
535
|
-
|
|
628
|
+
|
|
629
|
+
// Apply text filter
|
|
630
|
+
const filteredRows = applyFilterText(this.textFilterUI.valueWithoutSuggestion(), rows);
|
|
631
|
+
|
|
632
|
+
this.preloadingGrid.rows = filteredRows;
|
|
536
633
|
this.preloadingGrid.pageURL = pageURL();
|
|
634
|
+
// Only show empty state when there are truly no speculations (not when filter has no matches)
|
|
537
635
|
this.contentElement.classList.toggle('empty', rows.length === 0);
|
|
538
636
|
|
|
539
637
|
this.updatePreloadingDetails();
|
|
@@ -107,11 +107,11 @@ const UIStrings = {
|
|
|
107
107
|
/**
|
|
108
108
|
* @description Title of a setting under the Console category that can be invoked through the Command Menu
|
|
109
109
|
*/
|
|
110
|
-
groupSimilarMessagesInConsole: 'Group similar messages
|
|
110
|
+
groupSimilarMessagesInConsole: 'Group similar messages',
|
|
111
111
|
/**
|
|
112
112
|
* @description Title of a setting under the Console category that can be invoked through the Command Menu
|
|
113
113
|
*/
|
|
114
|
-
showCorsErrorsInConsole: '
|
|
114
|
+
showCorsErrorsInConsole: 'CORS errors in console',
|
|
115
115
|
/**
|
|
116
116
|
* @description Tooltip for the the console sidebar toggle in the Console panel. Command to
|
|
117
117
|
* open/show the sidebar.
|
|
@@ -62,11 +62,11 @@ const UIStrings = {
|
|
|
62
62
|
*/
|
|
63
63
|
timestamps: 'Timestamps',
|
|
64
64
|
/**
|
|
65
|
-
* @description Title of
|
|
65
|
+
* @description Title of an option under the Console category that can be invoked through the Command Menu
|
|
66
66
|
*/
|
|
67
67
|
showTimestamps: 'Show timestamps',
|
|
68
68
|
/**
|
|
69
|
-
* @description Title of
|
|
69
|
+
* @description Title of an option under the Console category that can be invoked through the Command Menu
|
|
70
70
|
*/
|
|
71
71
|
hideTimestamps: 'Hide timestamps',
|
|
72
72
|
/**
|
|
@@ -88,19 +88,23 @@ const UIStrings = {
|
|
|
88
88
|
/**
|
|
89
89
|
* @description Title of a setting under the Console category that can be invoked through the Command Menu
|
|
90
90
|
*/
|
|
91
|
-
|
|
91
|
+
groupSimilarMessages: 'Group similar messages',
|
|
92
92
|
/**
|
|
93
93
|
* @description Title of a setting under the Console category that can be invoked through the Command Menu
|
|
94
94
|
*/
|
|
95
|
-
|
|
95
|
+
doNotGroupSimilarMessages: 'Don\'t group similar messages',
|
|
96
96
|
/**
|
|
97
|
-
* @description Title of a setting under the Console category
|
|
97
|
+
* @description Title of a setting under the Console category in Settings
|
|
98
98
|
*/
|
|
99
|
-
|
|
99
|
+
corsErrorsInConsole: 'CORS errors in console',
|
|
100
100
|
/**
|
|
101
|
-
* @description Title of
|
|
101
|
+
* @description Title of an option under the Console category that can be invoked through the Command Menu
|
|
102
|
+
*/
|
|
103
|
+
showCorsErrorsInConsole: 'Show CORS errors in console',
|
|
104
|
+
/**
|
|
105
|
+
* @description Title of an option under the Console category that can be invoked through the Command Menu
|
|
102
106
|
*/
|
|
103
|
-
doNotShowCorsErrorsIn: '
|
|
107
|
+
doNotShowCorsErrorsIn: 'Don\'t show CORS errors in console',
|
|
104
108
|
/**
|
|
105
109
|
* @description Title of a setting under the Console category in Settings
|
|
106
110
|
*/
|
|
@@ -137,7 +141,7 @@ const UIStrings = {
|
|
|
137
141
|
* @description Title of a setting under the Console category in Settings that controls whether AI summaries should
|
|
138
142
|
* be shown for console warnings/errors.
|
|
139
143
|
*/
|
|
140
|
-
|
|
144
|
+
consoleInsightTeasers: 'AI summaries for console messages',
|
|
141
145
|
} as const;
|
|
142
146
|
const str_ = i18n.i18n.registerUIStrings('panels/console/console-meta.ts', UIStrings);
|
|
143
147
|
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
@@ -342,25 +346,25 @@ Common.Settings.registerSettingExtension({
|
|
|
342
346
|
Common.Settings.registerSettingExtension({
|
|
343
347
|
category: Common.Settings.SettingCategory.CONSOLE,
|
|
344
348
|
storageType: Common.Settings.SettingStorageType.SYNCED,
|
|
345
|
-
title: i18nLazyString(UIStrings.
|
|
349
|
+
title: i18nLazyString(UIStrings.groupSimilarMessages),
|
|
346
350
|
settingName: 'console-group-similar',
|
|
347
351
|
settingType: Common.Settings.SettingType.BOOLEAN,
|
|
348
352
|
defaultValue: true,
|
|
349
353
|
options: [
|
|
350
354
|
{
|
|
351
355
|
value: true,
|
|
352
|
-
title: i18nLazyString(UIStrings.
|
|
356
|
+
title: i18nLazyString(UIStrings.groupSimilarMessages),
|
|
353
357
|
},
|
|
354
358
|
{
|
|
355
359
|
value: false,
|
|
356
|
-
title: i18nLazyString(UIStrings.
|
|
360
|
+
title: i18nLazyString(UIStrings.doNotGroupSimilarMessages),
|
|
357
361
|
},
|
|
358
362
|
],
|
|
359
363
|
});
|
|
360
364
|
|
|
361
365
|
Common.Settings.registerSettingExtension({
|
|
362
366
|
category: Common.Settings.SettingCategory.CONSOLE,
|
|
363
|
-
title: i18nLazyString(UIStrings.
|
|
367
|
+
title: i18nLazyString(UIStrings.corsErrorsInConsole),
|
|
364
368
|
settingName: 'console-shows-cors-errors',
|
|
365
369
|
settingType: Common.Settings.SettingType.BOOLEAN,
|
|
366
370
|
defaultValue: true,
|
|
@@ -436,7 +440,7 @@ Common.Settings.registerSettingExtension({
|
|
|
436
440
|
Common.Settings.registerSettingExtension({
|
|
437
441
|
category: Common.Settings.SettingCategory.CONSOLE,
|
|
438
442
|
storageType: Common.Settings.SettingStorageType.SYNCED,
|
|
439
|
-
title: i18nLazyString(UIStrings.
|
|
443
|
+
title: i18nLazyString(UIStrings.consoleInsightTeasers),
|
|
440
444
|
settingName: 'console-insight-teasers-enabled',
|
|
441
445
|
settingType: Common.Settings.SettingType.BOOLEAN,
|
|
442
446
|
defaultValue: true,
|
|
@@ -353,13 +353,18 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
353
353
|
|
|
354
354
|
this.filterRegex = null;
|
|
355
355
|
this.linkifier = new Components.Linkifier.Linkifier(maxLinkLength);
|
|
356
|
-
this.imagePreviewPopover = new ImagePreviewPopover(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
356
|
+
this.imagePreviewPopover = new ImagePreviewPopover(
|
|
357
|
+
this.contentElement,
|
|
358
|
+
event => {
|
|
359
|
+
const link = event.composedPath()[0];
|
|
360
|
+
if (link instanceof Element) {
|
|
361
|
+
return link;
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
},
|
|
365
|
+
async () => {
|
|
366
|
+
return await Components.ImagePreview.loadPrecomputedFeatures(this.#computedStyleModel?.node);
|
|
367
|
+
});
|
|
363
368
|
|
|
364
369
|
this.#updateView({hasMatches: true});
|
|
365
370
|
}
|
|
@@ -1556,7 +1556,7 @@ export class DOMNodeRevealer implements
|
|
|
1556
1556
|
}
|
|
1557
1557
|
|
|
1558
1558
|
if (resolvedNode) {
|
|
1559
|
-
const opts: RevealAndSelectNodeOpts = {showPanel: true, focusNode:
|
|
1559
|
+
const opts: RevealAndSelectNodeOpts = omitFocus ? {showPanel: false} : {showPanel: true, focusNode: true};
|
|
1560
1560
|
const promise = resolvedNode instanceof SDK.DOMModel.AdoptedStyleSheet ?
|
|
1561
1561
|
panel.revealAndSelectAdoptedStyleSheet(resolvedNode, opts) :
|
|
1562
1562
|
panel.revealAndSelectNode(resolvedNode, opts);
|