chrome-devtools-frontend 1.0.1646286 → 1.0.1649421
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/SECURITY.md +1 -1
- package/extension-api/ExtensionAPI.d.ts +26 -0
- package/front_end/core/common/Debouncer.ts +9 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +3 -1
- package/front_end/core/sdk/CSSMetadata.ts +1 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +65 -0
- package/front_end/generated/InspectorBackendCommands.ts +2 -1
- package/front_end/generated/SupportedCSSProperties.js +739 -21
- package/front_end/generated/protocol.ts +26 -0
- package/front_end/models/ai_assistance/AiAgent2.ts +14 -9
- package/front_end/models/ai_assistance/agents/AiAgent.ts +10 -3
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +7 -0
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +45 -0
- package/front_end/models/ai_assistance/ai_assistance.ts +4 -0
- package/front_end/models/ai_assistance/skills/Skill.ts +1 -1
- package/front_end/models/ai_assistance/skills/SkillRegistry.ts +2 -0
- package/front_end/models/ai_assistance/skills/network.md +16 -0
- package/front_end/models/ai_assistance/skills/styling.md +1 -1
- package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +118 -0
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +124 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +2 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -0
- package/front_end/models/emulation/EmulatedDevices.ts +430 -12
- package/front_end/models/extensions/ExtensionAPI.ts +47 -21
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +40 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +228 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +39 -2
- package/front_end/panels/application/ApplicationPanelSidebar.ts +4 -0
- package/front_end/panels/application/CookieItemsView.ts +55 -6
- package/front_end/panels/application/DOMStorageItemsView.ts +43 -8
- package/front_end/panels/application/KeyValueStorageItemsView.ts +17 -9
- package/front_end/panels/application/WebMCPTreeElement.ts +8 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +73 -16
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +9 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +22 -8
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +309 -182
- package/front_end/panels/layer_viewer/paintProfiler.css +27 -23
- package/front_end/panels/layers/LayerPaintProfilerView.ts +86 -29
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +210 -356
- package/front_end/panels/sources/watchExpressionsSidebarPane.css +7 -0
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -5
- package/front_end/panels/timeline/components/NetworkTrackWidget.ts +122 -0
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/networkTrackWidget.css +31 -0
- package/front_end/panels/timeline/timeline.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/buttons/FloatingButton.ts +17 -2
- package/front_end/ui/components/buttons/floatingButton.css +2 -2
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +1 -1
- package/front_end/ui/legacy/TextPrompt.ts +24 -18
- package/front_end/ui/legacy/Treeoutline.ts +31 -4
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +50 -24
- package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +13 -0
- package/package.json +1 -1
|
@@ -42,6 +42,11 @@
|
|
|
42
42
|
display: flex;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
li.watch-expression-tree-item > span.tree-element-title {
|
|
46
|
+
display: block;
|
|
47
|
+
width: 100%;
|
|
48
|
+
}
|
|
49
|
+
|
|
45
50
|
.watch-expression-title:hover,
|
|
46
51
|
.watch-expression-title:focus-within {
|
|
47
52
|
padding-right: 26px;
|
|
@@ -55,6 +60,8 @@
|
|
|
55
60
|
position: relative;
|
|
56
61
|
flex: auto;
|
|
57
62
|
min-height: 20px;
|
|
63
|
+
display: block;
|
|
64
|
+
width: 100%;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
.watch-expression .name {
|
|
@@ -41,8 +41,8 @@ export class TimelinePaintProfilerView extends UI.SplitWidget.SplitWidget {
|
|
|
41
41
|
this.imageView = new TimelinePaintImageView();
|
|
42
42
|
this.logAndImageSplitWidget.setMainWidget(this.imageView);
|
|
43
43
|
|
|
44
|
-
this.paintProfilerView =
|
|
45
|
-
|
|
44
|
+
this.paintProfilerView = new LayerViewer.PaintProfilerView.PaintProfilerView();
|
|
45
|
+
this.paintProfilerView.showImageCallback = this.imageView.showImage.bind(this.imageView);
|
|
46
46
|
this.paintProfilerView.addEventListener(
|
|
47
47
|
LayerViewer.PaintProfilerView.Events.WINDOW_CHANGED, this.onWindowChanged, this);
|
|
48
48
|
this.setSidebarWidget(this.paintProfilerView);
|
|
@@ -142,7 +142,7 @@ export class TimelinePaintProfilerView extends UI.SplitWidget.SplitWidget {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
update(): void {
|
|
145
|
-
this.logTreeView.
|
|
145
|
+
this.logTreeView.commandLog = [];
|
|
146
146
|
void this.paintProfilerView.setSnapshotAndLog(null, [], null);
|
|
147
147
|
|
|
148
148
|
let snapshotPromise: Promise<{
|
|
@@ -188,7 +188,7 @@ export class TimelinePaintProfilerView extends UI.SplitWidget.SplitWidget {
|
|
|
188
188
|
function onCommandLogDone(
|
|
189
189
|
this: TimelinePaintProfilerView, snapshot: SDK.PaintProfiler.PaintProfilerSnapshot,
|
|
190
190
|
clipRect: Protocol.DOM.Rect|null, log?: SDK.PaintProfiler.PaintProfilerLogItem[]): void {
|
|
191
|
-
this.logTreeView.
|
|
191
|
+
this.logTreeView.commandLog = log || [];
|
|
192
192
|
void this.paintProfilerView.setSnapshotAndLog(snapshot, log || [], clipRect);
|
|
193
193
|
}
|
|
194
194
|
}
|
|
@@ -202,7 +202,7 @@ export class TimelinePaintProfilerView extends UI.SplitWidget.SplitWidget {
|
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
private onWindowChanged(): void {
|
|
205
|
-
this.logTreeView.
|
|
205
|
+
this.logTreeView.selectionWindow = this.paintProfilerView.selectionWindow();
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import * as Trace from '../../../models/trace/trace.js';
|
|
6
|
+
import * as PerfUI from '../../../ui/legacy/components/perf_ui/perf_ui.js';
|
|
7
|
+
import * as Lit from '../../../ui/lit/lit.js';
|
|
8
|
+
|
|
9
|
+
import networkTrackWidgetStyles from './networkTrackWidget.css.js';
|
|
10
|
+
|
|
11
|
+
const {html} = Lit;
|
|
12
|
+
|
|
13
|
+
// TODO: Do not redeclare the DataProvider here.
|
|
14
|
+
// To prevent circular dependencies with the parent timeline module (since timeline.ts
|
|
15
|
+
// imports and re-exports components/components.js, which in turn imports this file),
|
|
16
|
+
// we must not import any classes from '../../timeline/timeline.js' here.
|
|
17
|
+
// Instead, we declare a decoupled local `NetworkDataProvider` interface that extends
|
|
18
|
+
// the legacy PerfUI FlameChart data provider, and have the widget accept it.
|
|
19
|
+
export interface NetworkDataProvider extends PerfUI.FlameChart.FlameChartDataProvider {
|
|
20
|
+
setModel(parsedTrace: Trace.TraceModel.ParsedTrace, entityMapper: Trace.EntityMapper.EntityMapper): void;
|
|
21
|
+
setWindowTimes(min: number, max: number): void;
|
|
22
|
+
timelineData(): PerfUI.FlameChart.FlameChartTimelineData;
|
|
23
|
+
preparePopoverElement(index: number): Element|null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface NetworkTrackWidgetData {
|
|
27
|
+
parsedTrace: Trace.TraceModel.ParsedTrace|null;
|
|
28
|
+
bounds: Trace.Types.Timing.TraceWindowMicro;
|
|
29
|
+
dataProvider: NetworkDataProvider;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class NetworkTrackWidget extends HTMLElement implements PerfUI.FlameChart.FlameChartDelegate {
|
|
33
|
+
readonly #shadow = this.attachShadow({mode: 'open'});
|
|
34
|
+
|
|
35
|
+
#flameChartContainer = document.createElement('div');
|
|
36
|
+
#flameChart: PerfUI.FlameChart.FlameChart|null = null;
|
|
37
|
+
#dataProvider: NetworkDataProvider|null = null;
|
|
38
|
+
#parsedTrace: Trace.TraceModel.ParsedTrace|null = null;
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
super();
|
|
42
|
+
this.#flameChartContainer.classList.add('container');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
set data(data: NetworkTrackWidgetData) {
|
|
46
|
+
const parsedTrace = data.parsedTrace;
|
|
47
|
+
const dataProvider = data.dataProvider;
|
|
48
|
+
if (!parsedTrace || !dataProvider) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const isDataProviderChanged = dataProvider !== this.#dataProvider;
|
|
53
|
+
this.#dataProvider = dataProvider;
|
|
54
|
+
this.#parsedTrace = parsedTrace;
|
|
55
|
+
|
|
56
|
+
// 1. Render first to mount the container in the DOM so show() succeeds
|
|
57
|
+
this.#render();
|
|
58
|
+
|
|
59
|
+
// 2. Instantiate and show the flame chart in the mounted container
|
|
60
|
+
if (isDataProviderChanged || !this.#flameChart) {
|
|
61
|
+
this.#flameChartContainer.innerHTML = '';
|
|
62
|
+
this.#flameChart = new PerfUI.FlameChart.FlameChart(this.#dataProvider, this);
|
|
63
|
+
this.#flameChart.show(this.#flameChartContainer, undefined, true);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const entityMapper = Trace.EntityMapper.EntityMapper.getOrCreate(parsedTrace);
|
|
67
|
+
// Override preparePopoverElement to always return null, preventing any hover tooltips
|
|
68
|
+
// from being generated or rendered when the user hovers over the network request blocks.
|
|
69
|
+
this.#dataProvider.preparePopoverElement = () => null;
|
|
70
|
+
this.#dataProvider.setModel(parsedTrace, entityMapper);
|
|
71
|
+
const timelineData = this.#dataProvider.timelineData();
|
|
72
|
+
// Clearing the groups array hides the collapsible "Network" track header on the left.
|
|
73
|
+
// When the groups array is empty, the FlameChart component directly renders the track
|
|
74
|
+
// contents (the network requests) starting right from the top of the canvas.
|
|
75
|
+
timelineData.groups = [];
|
|
76
|
+
|
|
77
|
+
const bounds = Trace.Helpers.Timing.traceWindowMicroSecondsToMilliSeconds({
|
|
78
|
+
min: Trace.Types.Timing.Micro(data.bounds.min),
|
|
79
|
+
max: Trace.Types.Timing.Micro(data.bounds.max),
|
|
80
|
+
range: Trace.Types.Timing.Micro(data.bounds.range),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.#dataProvider.setWindowTimes(bounds.min, bounds.max);
|
|
84
|
+
this.#flameChart.setWindowTimes(bounds.min, bounds.max);
|
|
85
|
+
this.#render();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#render(): void {
|
|
89
|
+
if (!this.#parsedTrace) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const output = html`
|
|
93
|
+
<style>${networkTrackWidgetStyles}</style>
|
|
94
|
+
${this.#flameChartContainer}
|
|
95
|
+
`;
|
|
96
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
97
|
+
Lit.render(output, this.#shadow, {host: this});
|
|
98
|
+
|
|
99
|
+
if (this.#flameChart) {
|
|
100
|
+
this.#flameChart.update();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
windowChanged(_windowStartTime: number, _windowEndTime: number, _animate: boolean): void {
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
updateRangeSelection(_startTime: number, _endTime: number): void {
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
updateSelectedGroup(_flameChart: PerfUI.FlameChart.FlameChart, _group: PerfUI.FlameChart.Group|null): void {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!customElements.get('devtools-performance-agent-network-track')) {
|
|
115
|
+
customElements.define('devtools-performance-agent-network-track', NetworkTrackWidget);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare global {
|
|
119
|
+
interface HTMLElementTagNameMap {
|
|
120
|
+
'devtools-performance-agent-network-track': NetworkTrackWidget;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -17,6 +17,7 @@ import * as MetricCard from './MetricCard.js';
|
|
|
17
17
|
import * as NetworkRequestDetails from './NetworkRequestDetails.js';
|
|
18
18
|
import * as NetworkRequestTooltip from './NetworkRequestTooltip.js';
|
|
19
19
|
import * as NetworkThrottlingSelector from './NetworkThrottlingSelector.js';
|
|
20
|
+
import * as NetworkTrackWidget from './NetworkTrackWidget.js';
|
|
20
21
|
import * as OriginMap from './OriginMap.js';
|
|
21
22
|
import * as RelatedInsightChips from './RelatedInsightChips.js';
|
|
22
23
|
import * as Sidebar from './Sidebar.js';
|
|
@@ -43,6 +44,7 @@ export {
|
|
|
43
44
|
NetworkRequestDetails,
|
|
44
45
|
NetworkRequestTooltip,
|
|
45
46
|
NetworkThrottlingSelector,
|
|
47
|
+
NetworkTrackWidget,
|
|
46
48
|
OriginMap,
|
|
47
49
|
RelatedInsightChips,
|
|
48
50
|
Sidebar,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* Copyright 2026 The Chromium Authors
|
|
2
|
+
* Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
* found in the LICENSE file. */
|
|
4
|
+
|
|
5
|
+
:host {
|
|
6
|
+
display: flex;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.container {
|
|
10
|
+
display: flex;
|
|
11
|
+
width: 100%;
|
|
12
|
+
height: 150px;
|
|
13
|
+
background-color: var(--sys-color-cdt-base-container);
|
|
14
|
+
border-radius: 8px;
|
|
15
|
+
border: 1px solid var(--sys-color-divider);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.container canvas {
|
|
19
|
+
/* stylelint-disable-next-line declaration-no-important */
|
|
20
|
+
pointer-events: none !important;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.flex-auto {
|
|
24
|
+
flex: auto;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.vbox {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
position: relative;
|
|
31
|
+
}
|
|
@@ -7,6 +7,7 @@ import * as AnnotationHelpers from './AnnotationHelpers.js';
|
|
|
7
7
|
import * as AppenderUtils from './AppenderUtils.js';
|
|
8
8
|
import * as BenchmarkEvents from './BenchmarkEvents.js';
|
|
9
9
|
import * as CompatibilityTracksAppender from './CompatibilityTracksAppender.js';
|
|
10
|
+
import * as NetworkTrackWidget from './components/NetworkTrackWidget.js';
|
|
10
11
|
import * as CountersGraph from './CountersGraph.js';
|
|
11
12
|
import * as EntriesFilter from './EntriesFilter.js';
|
|
12
13
|
import * as EventsTimelineTreeView from './EventsTimelineTreeView.js';
|
|
@@ -61,6 +62,7 @@ export {
|
|
|
61
62
|
LayoutShiftsTrackAppender,
|
|
62
63
|
ModificationsManager,
|
|
63
64
|
NetworkTrackAppender,
|
|
65
|
+
NetworkTrackWidget,
|
|
64
66
|
RecordingMetadata,
|
|
65
67
|
SaveFileFormatter,
|
|
66
68
|
TargetForEvent,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Name: Dependencies sourced from the upstream `chromium` repository
|
|
2
2
|
URL: Internal
|
|
3
3
|
Version: N/A
|
|
4
|
-
Revision:
|
|
4
|
+
Revision: 619f853fcd9eb9a1f7b73de8a235a34248fd4a88
|
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License File: LICENSE
|
|
@@ -38,7 +38,7 @@ const {html, Directives: {classMap}} = Lit;
|
|
|
38
38
|
* provided context.
|
|
39
39
|
*/
|
|
40
40
|
export class FloatingButton extends HTMLElement {
|
|
41
|
-
static readonly observedAttributes = ['icon-name', 'jslogcontext'];
|
|
41
|
+
static readonly observedAttributes = ['icon-name', 'jslogcontext', 'disabled'];
|
|
42
42
|
|
|
43
43
|
readonly #shadow = this.attachShadow({mode: 'open'});
|
|
44
44
|
|
|
@@ -83,6 +83,18 @@ export class FloatingButton extends HTMLElement {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
get disabled(): boolean {
|
|
87
|
+
return this.hasAttribute('disabled');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
set disabled(disabled: boolean) {
|
|
91
|
+
if (disabled) {
|
|
92
|
+
this.setAttribute('disabled', '');
|
|
93
|
+
} else {
|
|
94
|
+
this.removeAttribute('disabled');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
86
98
|
attributeChangedCallback(name: string, oldValue: string|null, newValue: string|null): void {
|
|
87
99
|
if (oldValue === newValue) {
|
|
88
100
|
return;
|
|
@@ -93,6 +105,9 @@ export class FloatingButton extends HTMLElement {
|
|
|
93
105
|
if (name === 'jslogcontext') {
|
|
94
106
|
this.#updateJslog();
|
|
95
107
|
}
|
|
108
|
+
if (name === 'disabled') {
|
|
109
|
+
this.#render();
|
|
110
|
+
}
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
#render(): void {
|
|
@@ -103,7 +118,7 @@ export class FloatingButton extends HTMLElement {
|
|
|
103
118
|
// clang-format off
|
|
104
119
|
Lit.render(html`
|
|
105
120
|
<style>${floatingButtonStyles}</style>
|
|
106
|
-
<button class=${classes}><devtools-icon .name=${this.iconName}></devtools-icon></button>`,
|
|
121
|
+
<button class=${classes} ?disabled=${this.disabled}><devtools-icon .name=${this.iconName}></devtools-icon></button>`,
|
|
107
122
|
this.#shadow, {host: this});
|
|
108
123
|
// clang-format on
|
|
109
124
|
}
|
|
@@ -36,11 +36,11 @@ button {
|
|
|
36
36
|
height: var(--sys-size-7);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
:host-context(:not(.theme-with-dark-background)) & > devtools-icon {
|
|
39
|
+
&:not(:disabled):host-context(:not(.theme-with-dark-background)) & > devtools-icon {
|
|
40
40
|
color: var(--sys-color-on-tonal-container);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
:host-context(.theme-with-dark-background) & > devtools-icon {
|
|
43
|
+
&:not(:disabled):host-context(.theme-with-dark-background) & > devtools-icon {
|
|
44
44
|
color: var(--sys-color-on-primary);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -61,7 +61,7 @@ export interface AiCodeCompletionConfig {
|
|
|
61
61
|
|
|
62
62
|
export const DELAY_BEFORE_SHOWING_RESPONSE_MS = 500;
|
|
63
63
|
export const AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS = 200;
|
|
64
|
-
const MAX_PREFIX_SUFFIX_LENGTH = 20_000;
|
|
64
|
+
export const MAX_PREFIX_SUFFIX_LENGTH = 20_000;
|
|
65
65
|
|
|
66
66
|
export class AiCodeCompletionProvider {
|
|
67
67
|
#aidaClient: Host.AidaClient.AidaClient = new Host.AidaClient.AidaClient();
|
|
@@ -65,6 +65,7 @@ import {cloneCustomElement, ElementFocusRestorer} from './UIUtils.js';
|
|
|
65
65
|
* @attribute completions Sets the `id` of the <datalist> containing the autocomplete options.
|
|
66
66
|
* @attribute placeholder Sets a placeholder that's shown in place of the text contents when editing if the text is too
|
|
67
67
|
* large.
|
|
68
|
+
* @attribute value Sets the initial text value that's edited when editing starts. If not provided, the slot's inner text is used.
|
|
68
69
|
*/
|
|
69
70
|
export class TextPromptElement extends HTMLElement {
|
|
70
71
|
static readonly observedAttributes = ['editing', 'completions', 'placeholder', 'cancel-on-blur'];
|
|
@@ -189,10 +190,11 @@ export class TextPromptElement extends HTMLElement {
|
|
|
189
190
|
#startEditing(): void {
|
|
190
191
|
const truncatedTextPlaceholder = this.getAttribute('placeholder');
|
|
191
192
|
const placeholder = this.#entrypoint.createChild('span');
|
|
193
|
+
const initialText = this.getAttribute('value') ?? this.#slot.deepInnerText();
|
|
192
194
|
if (truncatedTextPlaceholder === null) {
|
|
193
|
-
placeholder.textContent =
|
|
195
|
+
placeholder.textContent = initialText;
|
|
194
196
|
} else {
|
|
195
|
-
placeholder.setTextContentTruncatedIfNeeded(
|
|
197
|
+
placeholder.setTextContentTruncatedIfNeeded(initialText, truncatedTextPlaceholder);
|
|
196
198
|
}
|
|
197
199
|
this.#slot.remove();
|
|
198
200
|
|
|
@@ -302,7 +304,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
302
304
|
#title: string;
|
|
303
305
|
protected queryRange: TextUtils.TextRange.TextRange|null;
|
|
304
306
|
private previousText: string;
|
|
305
|
-
|
|
307
|
+
#currentSuggestion: Suggestion|null;
|
|
306
308
|
private completionRequestId: number;
|
|
307
309
|
private ghostTextElement: HTMLSpanElement;
|
|
308
310
|
private leftParenthesesIndices: number[];
|
|
@@ -337,7 +339,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
337
339
|
this.#title = '';
|
|
338
340
|
this.queryRange = null;
|
|
339
341
|
this.previousText = '';
|
|
340
|
-
this
|
|
342
|
+
this.#currentSuggestion = null;
|
|
341
343
|
this.completionRequestId = 0;
|
|
342
344
|
this.ghostTextElement = document.createElement('span');
|
|
343
345
|
this.ghostTextElement.classList.add('auto-complete-text');
|
|
@@ -458,10 +460,10 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
458
460
|
|
|
459
461
|
textWithCurrentSuggestion(): string {
|
|
460
462
|
const text = this.text();
|
|
461
|
-
if (!this.queryRange || !this
|
|
463
|
+
if (!this.queryRange || !this.#currentSuggestion) {
|
|
462
464
|
return text;
|
|
463
465
|
}
|
|
464
|
-
const suggestion = this
|
|
466
|
+
const suggestion = this.#currentSuggestion.text;
|
|
465
467
|
return text.substring(0, this.queryRange.startColumn) + suggestion + text.substring(this.queryRange.endColumn);
|
|
466
468
|
}
|
|
467
469
|
|
|
@@ -623,7 +625,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
623
625
|
}
|
|
624
626
|
break;
|
|
625
627
|
case 'Escape':
|
|
626
|
-
if (this.isSuggestBoxVisible() || this
|
|
628
|
+
if (this.isSuggestBoxVisible() || this.#currentSuggestion) {
|
|
627
629
|
this.clearAutocomplete();
|
|
628
630
|
handled = true;
|
|
629
631
|
}
|
|
@@ -646,14 +648,14 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
646
648
|
}
|
|
647
649
|
|
|
648
650
|
private acceptSuggestionOnStopCharacters(key: string): boolean {
|
|
649
|
-
if (!this
|
|
651
|
+
if (!this.#currentSuggestion || !this.queryRange || key.length !== 1 ||
|
|
650
652
|
!this.completionStopCharacters?.includes(key) ||
|
|
651
|
-
this
|
|
653
|
+
this.#currentSuggestion.disableAcceptSuggestionOnStopCharacters) {
|
|
652
654
|
return false;
|
|
653
655
|
}
|
|
654
656
|
|
|
655
657
|
const query = this.text().substring(this.queryRange.startColumn, this.queryRange.endColumn);
|
|
656
|
-
if (query && this
|
|
658
|
+
if (query && this.#currentSuggestion.text.startsWith(query + key)) {
|
|
657
659
|
this.queryRange.endColumn += 1;
|
|
658
660
|
return this.acceptAutoComplete();
|
|
659
661
|
}
|
|
@@ -728,7 +730,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
728
730
|
if (beforeText !== this.textWithCurrentSuggestion()) {
|
|
729
731
|
this.dispatchEventToListeners(Events.TEXT_CHANGED);
|
|
730
732
|
}
|
|
731
|
-
this
|
|
733
|
+
this.#currentSuggestion = null;
|
|
732
734
|
}
|
|
733
735
|
|
|
734
736
|
private onBlur(): void {
|
|
@@ -736,14 +738,14 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
736
738
|
}
|
|
737
739
|
|
|
738
740
|
private refreshGhostText(): void {
|
|
739
|
-
if (this
|
|
741
|
+
if (this.#currentSuggestion?.hideGhostText) {
|
|
740
742
|
this.ghostTextElement.remove();
|
|
741
743
|
return;
|
|
742
744
|
}
|
|
743
|
-
if (this.queryRange && this
|
|
744
|
-
this
|
|
745
|
+
if (this.queryRange && this.#currentSuggestion && this.isCaretAtEndOfPrompt() &&
|
|
746
|
+
this.#currentSuggestion.text.startsWith(this.text().substring(this.queryRange.startColumn))) {
|
|
745
747
|
this.ghostTextElement.textContent =
|
|
746
|
-
this
|
|
748
|
+
this.#currentSuggestion.text.substring(this.queryRange.endColumn - this.queryRange.startColumn);
|
|
747
749
|
this.element().appendChild(this.ghostTextElement);
|
|
748
750
|
} else {
|
|
749
751
|
this.ghostTextElement.remove();
|
|
@@ -875,7 +877,7 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
875
877
|
}
|
|
876
878
|
|
|
877
879
|
applySuggestion(suggestion: Suggestion|null, isIntermediateSuggestion?: boolean): void {
|
|
878
|
-
this
|
|
880
|
+
this.#currentSuggestion = suggestion;
|
|
879
881
|
this.refreshGhostText();
|
|
880
882
|
if (isIntermediateSuggestion) {
|
|
881
883
|
this.dispatchEventToListeners(Events.TEXT_CHANGED);
|
|
@@ -891,8 +893,8 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
891
893
|
return false;
|
|
892
894
|
}
|
|
893
895
|
|
|
894
|
-
const suggestionLength = this
|
|
895
|
-
const selectionRange = this
|
|
896
|
+
const suggestionLength = this.#currentSuggestion ? this.#currentSuggestion.text.length : 0;
|
|
897
|
+
const selectionRange = this.#currentSuggestion ? this.#currentSuggestion.selectionRange : null;
|
|
896
898
|
const endColumn = selectionRange ? selectionRange.endColumn : suggestionLength;
|
|
897
899
|
const startColumn = selectionRange ? selectionRange.startColumn : suggestionLength;
|
|
898
900
|
this.element().textContent = this.textWithCurrentSuggestion();
|
|
@@ -1043,6 +1045,10 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
|
|
|
1043
1045
|
suggestBoxForTest(): SuggestBox|undefined {
|
|
1044
1046
|
return this.suggestBox;
|
|
1045
1047
|
}
|
|
1048
|
+
|
|
1049
|
+
currentSuggestion(): Suggestion|null {
|
|
1050
|
+
return this.#currentSuggestion;
|
|
1051
|
+
}
|
|
1046
1052
|
}
|
|
1047
1053
|
|
|
1048
1054
|
const DefaultAutocompletionTimeout = 250;
|
|
@@ -409,11 +409,11 @@ export class TreeOutlineInShadow extends TreeOutline {
|
|
|
409
409
|
shadowRoot: ShadowRoot;
|
|
410
410
|
private readonly disclosureElement: Element;
|
|
411
411
|
override renderSelection: boolean;
|
|
412
|
-
constructor(variant: TreeVariant = TreeVariant.OTHER, element?: HTMLElement) {
|
|
412
|
+
constructor(variant: TreeVariant = TreeVariant.OTHER, element?: HTMLElement, delegatesFocus?: boolean) {
|
|
413
413
|
super();
|
|
414
414
|
this.contentElement.classList.add('tree-outline');
|
|
415
415
|
this.element = element ?? document.createElement('div');
|
|
416
|
-
this.shadowRoot = createShadowRootWithCoreStyles(this.element, {cssFile: treeoutlineStyles});
|
|
416
|
+
this.shadowRoot = createShadowRootWithCoreStyles(this.element, {cssFile: treeoutlineStyles, delegatesFocus});
|
|
417
417
|
this.disclosureElement = this.shadowRoot.createChild('div', 'tree-outline-disclosure');
|
|
418
418
|
this.disclosureElement.appendChild(this.contentElement);
|
|
419
419
|
this.renderSelection = true;
|
|
@@ -1542,6 +1542,7 @@ class TreeViewTreeElement extends TreeElement {
|
|
|
1542
1542
|
#userExpanded = false;
|
|
1543
1543
|
#isProcessingAttribute = false;
|
|
1544
1544
|
#previousOpenAttributeValue?: string|null;
|
|
1545
|
+
#refreshScheduled = false;
|
|
1545
1546
|
|
|
1546
1547
|
static #elementToTreeElement = new WeakMap<Node, TreeViewTreeElement>();
|
|
1547
1548
|
readonly configElement: HTMLLIElement;
|
|
@@ -1589,6 +1590,17 @@ class TreeViewTreeElement extends TreeElement {
|
|
|
1589
1590
|
}
|
|
1590
1591
|
}
|
|
1591
1592
|
|
|
1593
|
+
refreshSoon(): void {
|
|
1594
|
+
if (this.#refreshScheduled) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
this.#refreshScheduled = true;
|
|
1598
|
+
queueMicrotask(() => {
|
|
1599
|
+
this.#refreshScheduled = false;
|
|
1600
|
+
this.refresh();
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1592
1604
|
refresh(): void {
|
|
1593
1605
|
const expandable = Boolean(this.configElement.querySelector('ul[role="group"]'));
|
|
1594
1606
|
this.setExpandable(expandable);
|
|
@@ -1754,10 +1766,17 @@ function removeNode(node: TreeElement, preserveParentExpandable = false): void {
|
|
|
1754
1766
|
export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
|
|
1755
1767
|
static readonly observedAttributes =
|
|
1756
1768
|
['navigation-variant', 'hide-overflow', 'dense', 'show-selection-on-keyboard-focus'];
|
|
1757
|
-
readonly #treeOutline = new TreeOutlineInShadow(undefined, this);
|
|
1769
|
+
readonly #treeOutline = new TreeOutlineInShadow(undefined, this, true);
|
|
1758
1770
|
|
|
1759
1771
|
constructor() {
|
|
1760
1772
|
super();
|
|
1773
|
+
this.addEventListener('focusin', (event: Event) => {
|
|
1774
|
+
const actualTarget = event.composedPath()[0];
|
|
1775
|
+
if (actualTarget === this.#treeOutline.contentElement && !this.#treeOutline.selectedTreeElement &&
|
|
1776
|
+
this.#treeOutline.firstChild()) {
|
|
1777
|
+
this.#treeOutline.firstChild()?.select(/* omitFocus */ true, /* selectedByUser */ false);
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1761
1780
|
this.#treeOutline.addEventListener(Events.ElementSelected, event => {
|
|
1762
1781
|
if (event.data instanceof TreeViewTreeElement) {
|
|
1763
1782
|
event.data.listItemElement.dispatchEvent(new TreeViewElement.SelectEvent());
|
|
@@ -1784,6 +1803,14 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
|
|
|
1784
1803
|
return this.#treeOutline;
|
|
1785
1804
|
}
|
|
1786
1805
|
|
|
1806
|
+
override focus(): void {
|
|
1807
|
+
if (!this.#treeOutline.selectedTreeElement && this.#treeOutline.firstChild()) {
|
|
1808
|
+
this.#treeOutline.firstChild()?.select(/* omitFocus */ true, /* selectedByUser */ false);
|
|
1809
|
+
} else {
|
|
1810
|
+
this.#treeOutline.focus();
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1787
1814
|
#getParentTreeElement(element: HTMLLIElement|TreeElementWrapper):
|
|
1788
1815
|
{treeElement: TreeElement, expanded: boolean, classes: DOMTokenList}|null {
|
|
1789
1816
|
const subtreeRoot = element.parentElement;
|
|
@@ -1813,7 +1840,7 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
|
|
|
1813
1840
|
if (!treeElement) {
|
|
1814
1841
|
return;
|
|
1815
1842
|
}
|
|
1816
|
-
treeElement.
|
|
1843
|
+
treeElement.refreshSoon();
|
|
1817
1844
|
if (node === treeNode && attributeName === 'selected' && hasBooleanAttribute(treeNode, 'selected')) {
|
|
1818
1845
|
treeElement.revealAndSelect(true);
|
|
1819
1846
|
}
|