chrome-devtools-frontend 1.0.1698733 → 1.0.1700561
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/.agents/skills/devtools-verification/SKILL.md +19 -0
- package/.agents/skills/{repro-flaky-tests → fix-tests}/SKILL.md +2 -2
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -2
- package/front_end/core/sdk/DOMModel.ts +3 -5
- package/front_end/core/sdk/NetworkManager.ts +2 -2
- package/front_end/core/sdk/OverlayModel.ts +11 -12
- package/front_end/core/sdk/ServiceWorkerCacheModel.ts +3 -0
- package/front_end/core/sdk/SourceMapScopeChainEntry.ts +84 -32
- package/front_end/core/sdk/StorageBucketsModel.ts +2 -0
- package/front_end/entrypoints/main/GlobalAiButton.ts +2 -6
- package/front_end/entrypoints/main/MainImpl.ts +8 -1
- package/front_end/generated/InspectorBackendCommands.ts +12 -2
- package/front_end/generated/SupportedCSSProperties.ts +14 -0
- package/front_end/generated/protocol-mapping.d.ts +48 -1
- package/front_end/generated/protocol-proxy-api.d.ts +37 -1
- package/front_end/generated/protocol.ts +131 -0
- package/front_end/models/bindings/NetworkProject.ts +30 -0
- package/front_end/models/bindings/ResourceScriptMapping.ts +62 -0
- package/front_end/models/change_tracker/ChangeTracker.ts +1 -1
- package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
- package/front_end/models/comment_manager/CommentManager.ts +18 -0
- package/front_end/models/emulation/DeviceModeModel.ts +1 -1
- package/front_end/models/extensions/ExtensionAPI.ts +4 -2
- package/front_end/models/issues_manager/CookieIssue.ts +0 -15
- package/front_end/models/persistence/NetworkPersistenceManager.ts +29 -4
- package/front_end/models/source_map_scopes/NamesResolver.ts +34 -4
- package/front_end/models/source_map_scopes/ScopeChainModel.ts +31 -7
- package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +281 -3
- package/front_end/panels/accessibility/accessibilityAnnouncementRecordingView.css +85 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -2
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +5 -8
- package/front_end/panels/animation/AnimationTimeline.ts +1 -1
- package/front_end/panels/application/AppManifestView.ts +128 -134
- package/front_end/panels/application/CrashReportContextView.ts +6 -6
- package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +17 -17
- package/front_end/panels/application/DeviceBoundSessionsView.ts +136 -137
- package/front_end/panels/application/IndexedDBModel.ts +3 -0
- package/front_end/panels/application/IndexedDBViews.ts +1 -1
- package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
- package/front_end/panels/application/OriginTrialTreeView.ts +18 -23
- package/front_end/panels/application/ReportingApiTreeElement.ts +3 -3
- package/front_end/panels/application/ReportingApiView.ts +6 -14
- package/front_end/panels/application/application-meta.ts +7 -7
- package/front_end/panels/application/components/AdsView.ts +63 -1
- package/front_end/panels/application/components/adsView.css +11 -1
- package/front_end/panels/common/CommentThreadWidget.ts +20 -22
- package/front_end/panels/common/CommentsOverlayWidget.ts +115 -9
- package/front_end/panels/common/CommentsStatusBarPill.ts +13 -1
- package/front_end/panels/common/ExtensionServer.ts +43 -34
- package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
- package/front_end/panels/common/commentThreadWidget.css +6 -0
- package/front_end/panels/common/common-meta.ts +4 -1
- package/front_end/panels/elements/CSSRuleValidator.ts +1 -1
- package/front_end/panels/elements/ElementsPanel.ts +4 -4
- package/front_end/panels/elements/ElementsTreeElement.ts +9 -2
- package/front_end/panels/elements/ElementsTreeOutline.ts +25 -33
- package/front_end/panels/elements/elementsTreeOutline.css +8 -4
- package/front_end/panels/event_listeners/EventListenersView.ts +9 -4
- package/front_end/panels/issues/IssuesPane.ts +34 -22
- package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
- package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +1 -1
- package/front_end/panels/network/RequestPayloadView.ts +17 -15
- package/front_end/panels/recorder/RecordingView.ts +32 -53
- package/front_end/panels/recorder/StepView.ts +19 -4
- package/front_end/panels/recorder/TimelineSection.ts +1 -1
- package/front_end/panels/recorder/controlButton.css +6 -0
- package/front_end/panels/recorder/recordingView.css +11 -2
- package/front_end/panels/settings/BackendLinkingSettingsTab.ts +2 -1
- package/front_end/panels/settings/EditFileSystemView.ts +1 -1
- package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
- package/front_end/panels/sources/DebuggerPlugin.ts +137 -56
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +17 -15
- package/front_end/panels/timeline/components/DetailsView.ts +3 -3
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +0 -2
- package/front_end/panels/timeline/components/LiveMetricsView.ts +59 -64
- package/front_end/panels/timeline/components/OriginMap.ts +1 -1
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -3
- package/front_end/panels/timeline/components/liveMetricsView.css +1 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lighthouse/README.chromium +2 -2
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1580 -1524
- package/front_end/third_party/lighthouse/locales/ar-XB.json +111 -72
- package/front_end/third_party/lighthouse/locales/ar.json +113 -74
- package/front_end/third_party/lighthouse/locales/bg.json +112 -73
- package/front_end/third_party/lighthouse/locales/ca.json +112 -73
- package/front_end/third_party/lighthouse/locales/cs.json +112 -73
- package/front_end/third_party/lighthouse/locales/da.json +112 -73
- package/front_end/third_party/lighthouse/locales/de.json +112 -73
- package/front_end/third_party/lighthouse/locales/el.json +112 -73
- package/front_end/third_party/lighthouse/locales/en-GB.json +111 -72
- package/front_end/third_party/lighthouse/locales/en-US.json +102 -66
- package/front_end/third_party/lighthouse/locales/en-XA.json +111 -72
- package/front_end/third_party/lighthouse/locales/en-XL.json +102 -66
- package/front_end/third_party/lighthouse/locales/es-419.json +112 -73
- package/front_end/third_party/lighthouse/locales/es.json +112 -73
- package/front_end/third_party/lighthouse/locales/fi.json +112 -73
- package/front_end/third_party/lighthouse/locales/fil.json +112 -73
- package/front_end/third_party/lighthouse/locales/fr.json +112 -73
- package/front_end/third_party/lighthouse/locales/he.json +112 -73
- package/front_end/third_party/lighthouse/locales/hi.json +112 -73
- package/front_end/third_party/lighthouse/locales/hr.json +112 -73
- package/front_end/third_party/lighthouse/locales/hu.json +112 -73
- package/front_end/third_party/lighthouse/locales/id.json +112 -73
- package/front_end/third_party/lighthouse/locales/it.json +113 -74
- package/front_end/third_party/lighthouse/locales/ja.json +112 -73
- package/front_end/third_party/lighthouse/locales/ko.json +112 -73
- package/front_end/third_party/lighthouse/locales/lt.json +112 -73
- package/front_end/third_party/lighthouse/locales/lv.json +112 -73
- package/front_end/third_party/lighthouse/locales/nl.json +112 -73
- package/front_end/third_party/lighthouse/locales/no.json +112 -73
- package/front_end/third_party/lighthouse/locales/pl.json +112 -73
- package/front_end/third_party/lighthouse/locales/pt-PT.json +112 -73
- package/front_end/third_party/lighthouse/locales/pt.json +112 -73
- package/front_end/third_party/lighthouse/locales/ro.json +112 -73
- package/front_end/third_party/lighthouse/locales/ru.json +112 -73
- package/front_end/third_party/lighthouse/locales/sk.json +112 -73
- package/front_end/third_party/lighthouse/locales/sl.json +112 -73
- package/front_end/third_party/lighthouse/locales/sr-Latn.json +112 -73
- package/front_end/third_party/lighthouse/locales/sr.json +112 -73
- package/front_end/third_party/lighthouse/locales/sv.json +124 -85
- package/front_end/third_party/lighthouse/locales/ta.json +112 -73
- package/front_end/third_party/lighthouse/locales/te.json +112 -73
- package/front_end/third_party/lighthouse/locales/th.json +112 -73
- package/front_end/third_party/lighthouse/locales/tr.json +113 -74
- package/front_end/third_party/lighthouse/locales/uk.json +112 -73
- package/front_end/third_party/lighthouse/locales/vi.json +112 -73
- package/front_end/third_party/lighthouse/locales/zh-HK.json +112 -73
- package/front_end/third_party/lighthouse/locales/zh-TW.json +113 -74
- package/front_end/third_party/lighthouse/locales/zh.json +113 -74
- package/front_end/third_party/lighthouse/report/bundle.d.ts +4 -4
- package/front_end/third_party/lighthouse/report/bundle.js +10 -3
- package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
- package/front_end/third_party/lit/lit.ts +3 -0
- package/front_end/third_party/lit/package.json +3 -0
- package/front_end/third_party/lit/rebuild.sh +3 -2
- package/front_end/ui/comments/CommentAnchorResolver.ts +8 -2
- package/front_end/ui/comments/CommentOverlayManager.ts +2 -1
- package/front_end/ui/components/buttons/Button.ts +19 -1
- package/front_end/ui/legacy/ContextMenu.ts +16 -1
- package/front_end/ui/legacy/InspectorView.ts +4 -3
- package/front_end/ui/legacy/PlusButton.ts +5 -6
- package/front_end/ui/legacy/TabbedPane.ts +121 -80
- package/front_end/ui/legacy/Treeoutline.ts +37 -6
- package/front_end/ui/legacy/UIUtils.ts +7 -0
- package/front_end/ui/legacy/Widget.ts +33 -0
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +3 -0
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +14 -4
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +157 -171
- package/front_end/ui/lit/lit.ts +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
- package/inspector_overlay/highlight_anchor.ts +197 -0
- package/inspector_overlay/tool_highlight.ts +8 -0
- package/package.json +1 -1
- package/.agents/skills/devtools-ci/SKILL.md +0 -65
- package/.agents/skills/evaluate-ai-css-completion/SKILL.md +0 -143
- package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +0 -104
- package/.agents/skills/fixing-skipped-tests/SKILL.md +0 -32
- package/.agents/skills/gerrit-cli/SKILL.md +0 -74
- package/.agents/skills/gerrit-cli/references/commands.md +0 -169
- package/.agents/skills/gerrit-cli/references/workflows.md +0 -63
- package/.agents/skills/gerrit-cli/scripts/import_upstream.py +0 -85
- package/front_end/models/issues_manager/descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +0 -4
|
@@ -9,4 +9,7 @@ import * as Directive from './lib/directive.js';
|
|
|
9
9
|
import * as Decorators from './lib/decorators.js';
|
|
10
10
|
import * as StaticHtml from './lib/static-html.js';
|
|
11
11
|
|
|
12
|
+
// Required for lit-analyzer
|
|
13
|
+
export type {DirectiveResult} from './lib/directive.js';
|
|
14
|
+
|
|
12
15
|
export {AsyncDirective, Directives, Directive, Decorators, StaticHtml};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
set -e
|
|
1
2
|
npm install
|
|
2
|
-
../../../
|
|
3
|
-
|
|
3
|
+
vpython3 ../../../third_party/typescript/typescript.py --ignoreConfig --skipLibCheck -d -t esnext -m esnext --moduleResolution bundler src/*
|
|
4
|
+
node_modules/rollup/dist/bin/rollup -c
|
|
4
5
|
rm -rf node_modules src/*.js src/*.d.ts
|
|
5
6
|
# Because there's a bug in clang causing it to reformat import lists even where formatting is disabled, run it right away
|
|
6
7
|
git cl format --js
|
|
@@ -29,6 +29,12 @@ const DISALLOWED_COMMENT_TARGETS = new Set<number>([
|
|
|
29
29
|
VisualLogging.VisualElements.Toolbar,
|
|
30
30
|
]);
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* The comment thread UI itself is never a valid comment target: anything inside it (including the
|
|
34
|
+
* DOM node link in its header) must stay inert while comment mode is on.
|
|
35
|
+
*/
|
|
36
|
+
export const COMMENT_THREAD_UI_SELECTOR = '.comment-thread-widget';
|
|
37
|
+
|
|
32
38
|
/**
|
|
33
39
|
* Finds the closest ancestor (or the element itself) matching a CSS selector,
|
|
34
40
|
* traversing across Shadow DOM boundaries (shadow root boundaries to shadow hosts).
|
|
@@ -268,7 +274,7 @@ function resolveCodeMirrorLineInfo(element: Element): CodeMirrorLineInfo|null {
|
|
|
268
274
|
* Resolves an arbitrary clicked or targeted DOM element to its appropriate semantic comment anchor element.
|
|
269
275
|
*
|
|
270
276
|
* Traversal hierarchy:
|
|
271
|
-
* 1. Checks if the element is part of a tab title (returns null if so).
|
|
277
|
+
* 1. Checks if the element is part of a tab title or of the comment thread UI (returns null if so).
|
|
272
278
|
* 2. Escalates CodeMirror line/gutter elements to .cm-editor (only if the clicked line is non-empty).
|
|
273
279
|
* 3. Checks for domain IDs (`data-network-request-id` or `data-backend-node-id`) across shadow boundaries,
|
|
274
280
|
* returning the owning domain element.
|
|
@@ -281,7 +287,7 @@ function resolveCodeMirrorLineInfo(element: Element): CodeMirrorLineInfo|null {
|
|
|
281
287
|
*/
|
|
282
288
|
export function resolveCommentAnchorElement(
|
|
283
289
|
element: Element, options?: {clientX: number, clientY: number, forHover?: boolean}): Element|null {
|
|
284
|
-
if (isTabTitle(element)) {
|
|
290
|
+
if (isTabTitle(element) || closestAcrossShadow(element, COMMENT_THREAD_UI_SELECTOR)) {
|
|
285
291
|
return null;
|
|
286
292
|
}
|
|
287
293
|
const customResolver = getCustomAnchorResolverForElement(element);
|
|
@@ -7,6 +7,7 @@ import * as CommentManager from '../../models/comment_manager/comment_manager.js
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
closestAcrossShadow,
|
|
10
|
+
COMMENT_THREAD_UI_SELECTOR,
|
|
10
11
|
type CommentAnchorSignature,
|
|
11
12
|
type CommentThread,
|
|
12
13
|
computeVisibleRect,
|
|
@@ -238,7 +239,7 @@ export class CommentOverlayManager extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
|
238
239
|
}
|
|
239
240
|
|
|
240
241
|
handleElementClick(element: Element, options?: {clientX: number, clientY: number}): boolean {
|
|
241
|
-
if (!this.isCommentMode() || closestAcrossShadow(element,
|
|
242
|
+
if (!this.isCommentMode() || closestAcrossShadow(element, COMMENT_THREAD_UI_SELECTOR)) {
|
|
242
243
|
return false;
|
|
243
244
|
}
|
|
244
245
|
this.clearDraftThreads();
|
|
@@ -48,6 +48,7 @@ interface ButtonState {
|
|
|
48
48
|
variant: Variant;
|
|
49
49
|
size?: Size;
|
|
50
50
|
reducedFocusRing?: boolean;
|
|
51
|
+
buttonTitle?: string;
|
|
51
52
|
disabled: boolean;
|
|
52
53
|
toggled?: boolean;
|
|
53
54
|
toggleOnClick?: boolean;
|
|
@@ -80,7 +81,14 @@ interface CommonButtonData {
|
|
|
80
81
|
spinner?: boolean;
|
|
81
82
|
type?: ButtonType;
|
|
82
83
|
value?: string;
|
|
84
|
+
/**
|
|
85
|
+
* Sets title on the host and, unless buttonTitle is provided, the internal <button> element.
|
|
86
|
+
*/
|
|
83
87
|
title?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Sets title on the internal <button> element, taking precedence over title.
|
|
90
|
+
*/
|
|
91
|
+
buttonTitle?: string;
|
|
84
92
|
jslogContext?: string;
|
|
85
93
|
longClickable?: boolean;
|
|
86
94
|
inverseColorTheme?: boolean;
|
|
@@ -166,6 +174,7 @@ export class Button extends HTMLElement {
|
|
|
166
174
|
if (data.title) {
|
|
167
175
|
this.title = data.title;
|
|
168
176
|
}
|
|
177
|
+
this.#props.buttonTitle = data.buttonTitle;
|
|
169
178
|
|
|
170
179
|
if (data.accessibleLabel) {
|
|
171
180
|
this.accessibleLabel = data.accessibleLabel;
|
|
@@ -245,6 +254,15 @@ export class Button extends HTMLElement {
|
|
|
245
254
|
this.#render();
|
|
246
255
|
}
|
|
247
256
|
|
|
257
|
+
get buttonTitle(): string|undefined {
|
|
258
|
+
return this.#props.buttonTitle;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
set buttonTitle(buttonTitle: string|undefined) {
|
|
262
|
+
this.#props.buttonTitle = buttonTitle;
|
|
263
|
+
this.#render();
|
|
264
|
+
}
|
|
265
|
+
|
|
248
266
|
get disabled(): boolean {
|
|
249
267
|
return this.#props.disabled;
|
|
250
268
|
}
|
|
@@ -396,7 +414,7 @@ export class Button extends HTMLElement {
|
|
|
396
414
|
Lit.render(
|
|
397
415
|
html`
|
|
398
416
|
<style>${buttonStyles}</style>
|
|
399
|
-
<button title=${ifDefined(this.title || undefined)}
|
|
417
|
+
<button title=${ifDefined(this.buttonTitle || this.title || undefined)}
|
|
400
418
|
?disabled=${this.#props.disabled}
|
|
401
419
|
class=${classMap(classes)}
|
|
402
420
|
aria-pressed=${ifDefined(this.#props.toggled)}
|
|
@@ -989,6 +989,7 @@ export class ContextMenu extends SubMenu {
|
|
|
989
989
|
* @property keepOpen -Reflects the `"keep-open"` attribute.
|
|
990
990
|
* @property iconName - Reflects the `"icon-name"` attribute.
|
|
991
991
|
* @property disabled - Reflects the `"disabled"` attribute.
|
|
992
|
+
* @property accessibleLabel - Sets the accessible name and tooltip on the internal button.
|
|
992
993
|
* @attribute soft-menu - Whether to use the soft menu implementation.
|
|
993
994
|
* @attribute keep-open - Whether the menu should stay open after an item is clicked.
|
|
994
995
|
* @attribute icon-name - Name of the icon to display on the button.
|
|
@@ -1001,6 +1002,7 @@ export class MenuButton extends HTMLElement {
|
|
|
1001
1002
|
readonly #shadow = this.attachShadow({mode: 'open'});
|
|
1002
1003
|
#triggerTimeoutId?: number;
|
|
1003
1004
|
#populateMenuCall?: (arg0: ContextMenu) => void;
|
|
1005
|
+
#accessibleLabel?: string;
|
|
1004
1006
|
|
|
1005
1007
|
/**
|
|
1006
1008
|
* Sets the callback function used to populate the context menu when the button is clicked.
|
|
@@ -1010,6 +1012,17 @@ export class MenuButton extends HTMLElement {
|
|
|
1010
1012
|
this.#populateMenuCall = populateCall;
|
|
1011
1013
|
}
|
|
1012
1014
|
|
|
1015
|
+
set accessibleLabel(accessibleLabel: string|undefined) {
|
|
1016
|
+
this.#accessibleLabel = accessibleLabel;
|
|
1017
|
+
if (this.iconName) {
|
|
1018
|
+
this.#render();
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
get accessibleLabel(): string|undefined {
|
|
1023
|
+
return this.#accessibleLabel;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1013
1026
|
/**
|
|
1014
1027
|
* Reflects the `soft-menu` attribute. If true, uses the `SoftContextMenu` implementation.
|
|
1015
1028
|
* @default false
|
|
@@ -1120,13 +1133,15 @@ export class MenuButton extends HTMLElement {
|
|
|
1120
1133
|
if (!this.iconName) {
|
|
1121
1134
|
throw new Error('<devtools-menu-button> expects an icon.');
|
|
1122
1135
|
}
|
|
1136
|
+
const accessibleLabel = this.accessibleLabel ?? this.title;
|
|
1123
1137
|
|
|
1124
1138
|
// clang-format off
|
|
1125
1139
|
render(html`
|
|
1126
1140
|
<devtools-button .disabled=${this.disabled}
|
|
1127
1141
|
.iconName=${this.iconName}
|
|
1128
1142
|
.variant=${Buttons.Button.Variant.ICON}
|
|
1129
|
-
.
|
|
1143
|
+
.accessibleLabel=${accessibleLabel}
|
|
1144
|
+
.buttonTitle=${accessibleLabel}
|
|
1130
1145
|
aria-haspopup='menu'
|
|
1131
1146
|
@click=${this.#triggerContextMenu}>
|
|
1132
1147
|
</devtools-button>`,
|
|
@@ -853,15 +853,16 @@ export class InspectorView extends VBox implements ViewLocationResolver {
|
|
|
853
853
|
}
|
|
854
854
|
}
|
|
855
855
|
|
|
856
|
-
renderStatusBar(): void {
|
|
856
|
+
renderStatusBar(visible = true): void {
|
|
857
857
|
if (!this.#statusBarContainer) {
|
|
858
858
|
this.#statusBarContainer = document.createElement('div');
|
|
859
859
|
this.#statusBarContainer.style.display = 'contents';
|
|
860
860
|
this.element.appendChild(this.#statusBarContainer);
|
|
861
861
|
}
|
|
862
862
|
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
863
|
-
Lit.render(
|
|
864
|
-
|
|
863
|
+
Lit.render(
|
|
864
|
+
visible ? html`<devtools-widget class="flex-none" ${widget(StatusBarWidget)}></devtools-widget>` : Lit.nothing,
|
|
865
|
+
this.#statusBarContainer);
|
|
865
866
|
}
|
|
866
867
|
|
|
867
868
|
private createInfoBarDiv(): void {
|
|
@@ -233,19 +233,18 @@ interface PlusButtonViewInput {
|
|
|
233
233
|
*/
|
|
234
234
|
export const PLUS_BUTTON_VIEW =
|
|
235
235
|
(input: PlusButtonViewInput, output: {button?: MenuButton}, target: HTMLElement): void => {
|
|
236
|
-
render(
|
|
237
|
-
html`
|
|
236
|
+
render(html`
|
|
238
237
|
<devtools-menu-button
|
|
239
238
|
${Directives.ref(el => {
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
output.button = el as MenuButton | undefined;
|
|
240
|
+
})}
|
|
242
241
|
slot="trailing-button"
|
|
243
242
|
.iconName=${'plus'}
|
|
244
|
-
.
|
|
243
|
+
.accessibleLabel=${input.title}
|
|
245
244
|
.jslogContext=${input.jslogContext}
|
|
246
245
|
.populateMenuCall=${input.populateMenuCall}>
|
|
247
246
|
</devtools-menu-button>`,
|
|
248
|
-
|
|
247
|
+
target);
|
|
249
248
|
};
|
|
250
249
|
|
|
251
250
|
/**
|
|
@@ -77,6 +77,8 @@ export interface TabInfo {
|
|
|
77
77
|
jslogContext?: string;
|
|
78
78
|
enabled?: boolean;
|
|
79
79
|
selected?: boolean;
|
|
80
|
+
icon?: Element|null;
|
|
81
|
+
suffix?: Element|null;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
const TabbedPaneBase: Common.ObjectWrapper.EventMixin<EventTypes, typeof VBox> = Common.ObjectWrapper.eventMixin(
|
|
@@ -120,6 +122,8 @@ export class TabbedPane extends TabbedPaneBase {
|
|
|
120
122
|
this.registerRequiredCSS(tabbedPaneStyles);
|
|
121
123
|
this.element.classList.add('tabbed-pane');
|
|
122
124
|
this.contentElement.classList.add('tabbed-pane-shadow');
|
|
125
|
+
this.element.classList.add('flex-auto', 'vbox');
|
|
126
|
+
this.contentElement.classList.add('flex-auto', 'vbox');
|
|
123
127
|
this.contentElement.tabIndex = -1;
|
|
124
128
|
this.setDefaultFocusedElement(this.contentElement);
|
|
125
129
|
this.#headerElement = this.contentElement.createChild('div', 'tabbed-pane-header');
|
|
@@ -565,47 +569,6 @@ export class TabbedPane extends TabbedPaneBase {
|
|
|
565
569
|
}));
|
|
566
570
|
}
|
|
567
571
|
|
|
568
|
-
set tabs(tabs: TabInfo[]) {
|
|
569
|
-
const newIds = new Set(tabs.map(tab => tab.id));
|
|
570
|
-
for (const id of this.tabsById.keys()) {
|
|
571
|
-
if (!newIds.has(id)) {
|
|
572
|
-
this.#closeTab(id);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
let index = 0;
|
|
576
|
-
for (const tab of tabs) {
|
|
577
|
-
const existingTab = this.tabsById.get(tab.id);
|
|
578
|
-
if (existingTab) {
|
|
579
|
-
this.changeTabView(tab.id, tab.view);
|
|
580
|
-
this.changeTabTitle(tab.id, tab.title, tab.tabTooltip);
|
|
581
|
-
if (tab.jslogContext !== undefined) {
|
|
582
|
-
existingTab.jslogContext = tab.jslogContext;
|
|
583
|
-
}
|
|
584
|
-
if (tab.isCloseable !== undefined) {
|
|
585
|
-
existingTab.closeable = tab.isCloseable;
|
|
586
|
-
}
|
|
587
|
-
if (tab.previewFeature !== undefined) {
|
|
588
|
-
existingTab.previewFeature = tab.previewFeature;
|
|
589
|
-
}
|
|
590
|
-
const currentIndex = this.#tabs.indexOf(existingTab);
|
|
591
|
-
if (currentIndex !== index) {
|
|
592
|
-
this.insertBefore(existingTab, index);
|
|
593
|
-
}
|
|
594
|
-
} else {
|
|
595
|
-
this.appendTab(
|
|
596
|
-
tab.id, tab.title, tab.view, tab.tabTooltip, /* userGesture=*/ false, tab.isCloseable, tab.previewFeature,
|
|
597
|
-
index, tab.jslogContext);
|
|
598
|
-
}
|
|
599
|
-
if (tab.enabled !== undefined) {
|
|
600
|
-
this.setTabEnabled(tab.id, tab.enabled);
|
|
601
|
-
}
|
|
602
|
-
if (tab.selected) {
|
|
603
|
-
this.selectTab(tab.id);
|
|
604
|
-
}
|
|
605
|
-
++index;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
572
|
override onResize(): void {
|
|
610
573
|
if (this.currentDevicePixelRatio !== window.devicePixelRatio) {
|
|
611
574
|
// Force recalculation of all tab widths on a DPI change
|
|
@@ -1694,42 +1657,72 @@ export class TabbedPaneElement extends WidgetElement<TabbedPane> {
|
|
|
1694
1657
|
this.#automaticReorder = automatic;
|
|
1695
1658
|
this.getWidget()?.setAllowTabReorder(this.#allowTabReorder, this.#automaticReorder);
|
|
1696
1659
|
}
|
|
1660
|
+
|
|
1661
|
+
get tabs(): TabInfo[] {
|
|
1662
|
+
const widget = Widget.getOrCreateWidget(this) as TabbedPane;
|
|
1663
|
+
if (widget) {
|
|
1664
|
+
this.#updateTabs(widget);
|
|
1665
|
+
return widget.tabs;
|
|
1666
|
+
}
|
|
1667
|
+
return [];
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
#delegate?: TabbedPaneTabDelegate;
|
|
1671
|
+
set tabDelegate(delegate: TabbedPaneTabDelegate) {
|
|
1672
|
+
this.#delegate = delegate;
|
|
1673
|
+
this.getWidget()?.setTabDelegate(delegate);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
#placeholderElement?: Element;
|
|
1677
|
+
#managedTabIds = new Set<string>();
|
|
1678
|
+
set placeholder(element: Element) {
|
|
1679
|
+
this.#placeholderElement = element;
|
|
1680
|
+
this.getWidget()?.setPlaceholderElement(element);
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1697
1683
|
readonly #tabObserver = new MutationObserver(() => this.#updateTabs());
|
|
1698
1684
|
|
|
1699
1685
|
constructor() {
|
|
1700
1686
|
super();
|
|
1701
1687
|
|
|
1702
|
-
registerWidgetConfig(
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1688
|
+
registerWidgetConfig(
|
|
1689
|
+
this, widgetConfig(element => {
|
|
1690
|
+
const widget = new TabbedPane(element as TabbedPaneElement);
|
|
1691
|
+
widget.setCloseableTabs(this.#closeableTabs);
|
|
1692
|
+
widget.setAllowTabReorder(this.#allowTabReorder, this.#automaticReorder);
|
|
1693
|
+
if (this.#delegate) {
|
|
1694
|
+
widget.setTabDelegate(this.#delegate);
|
|
1695
|
+
}
|
|
1696
|
+
const slot = widget.contentElement.querySelector('slot:not([name])');
|
|
1697
|
+
if (slot) {
|
|
1698
|
+
slot.addEventListener('slotchange', () => this.#syncTabs());
|
|
1699
|
+
}
|
|
1700
|
+
widget.addEventListener(Events.TabSelected, event => {
|
|
1701
|
+
const nodes = this.#getTabNodes(widget);
|
|
1702
|
+
for (const child of nodes) {
|
|
1703
|
+
if (child.id === widget.selectedTabId) {
|
|
1704
|
+
child.setAttribute('selected', '');
|
|
1705
|
+
} else {
|
|
1706
|
+
child.removeAttribute('selected');
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
this.dispatchEvent(new CustomEvent(
|
|
1710
|
+
'select', {detail: {tabId: widget.selectedTabId, isUserGesture: event.data?.isUserGesture}}));
|
|
1711
|
+
});
|
|
1712
|
+
widget.addEventListener(Events.TabClosed, event => {
|
|
1713
|
+
this.dispatchEvent(
|
|
1714
|
+
new CustomEvent('close', {detail: {tabId: event.data.tabId, isUserGesture: event.data.isUserGesture}}));
|
|
1715
|
+
});
|
|
1716
|
+
widget.addEventListener(Events.TabOrderChanged, event => {
|
|
1717
|
+
this.dispatchEvent(
|
|
1718
|
+
new CustomEvent('taborderchanged', {detail: {tabId: event.data.tabId, tabIds: widget.tabIds()}}));
|
|
1719
|
+
});
|
|
1720
|
+
if (this.#placeholderElement) {
|
|
1721
|
+
widget.setPlaceholderElement(this.#placeholderElement);
|
|
1722
|
+
}
|
|
1723
|
+
this.#syncTabs(widget);
|
|
1724
|
+
return widget;
|
|
1725
|
+
}));
|
|
1733
1726
|
}
|
|
1734
1727
|
|
|
1735
1728
|
override disconnectedCallback(): void {
|
|
@@ -1747,8 +1740,7 @@ export class TabbedPaneElement extends WidgetElement<TabbedPane> {
|
|
|
1747
1740
|
|
|
1748
1741
|
#updateObserver(widget: TabbedPane): void {
|
|
1749
1742
|
this.#tabObserver.disconnect();
|
|
1750
|
-
const
|
|
1751
|
-
const nodes = slot ? slot.assignedElements() : [];
|
|
1743
|
+
const nodes = this.#getTabNodes(widget);
|
|
1752
1744
|
for (const child of nodes) {
|
|
1753
1745
|
this.#tabObserver.observe(child, {
|
|
1754
1746
|
attributes: true,
|
|
@@ -1757,13 +1749,18 @@ export class TabbedPaneElement extends WidgetElement<TabbedPane> {
|
|
|
1757
1749
|
}
|
|
1758
1750
|
}
|
|
1759
1751
|
|
|
1752
|
+
#getTabNodes(widget: TabbedPane): Element[] {
|
|
1753
|
+
return Array.from(widget.element.children)
|
|
1754
|
+
.filter(c => c.id !== '' && !c.hasAttribute('slot') && !c.classList.contains('tabbed-pane-header') &&
|
|
1755
|
+
!c.classList.contains('tabbed-pane-content'));
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1760
1758
|
#updateTabs(widget = this.getWidget()): void {
|
|
1761
1759
|
if (!widget) {
|
|
1762
1760
|
return;
|
|
1763
1761
|
}
|
|
1764
1762
|
const tabs: TabInfo[] = [];
|
|
1765
|
-
const
|
|
1766
|
-
const nodes = slot ? slot.assignedElements() : [];
|
|
1763
|
+
const nodes = this.#getTabNodes(widget);
|
|
1767
1764
|
for (const child of nodes) {
|
|
1768
1765
|
const id = child.id;
|
|
1769
1766
|
const title = child.getAttribute('title') || '';
|
|
@@ -1772,13 +1769,12 @@ export class TabbedPaneElement extends WidgetElement<TabbedPane> {
|
|
|
1772
1769
|
const enabled = !child.hasAttribute('disabled');
|
|
1773
1770
|
const isCloseable =
|
|
1774
1771
|
child.hasAttribute('closeable') ? true : (child.hasAttribute('uncloseable') ? false : undefined);
|
|
1772
|
+
const icon = child.querySelector('[slot="icon"]') ?? undefined;
|
|
1773
|
+
const suffix = child.querySelector('[slot="suffix"]') ?? undefined;
|
|
1775
1774
|
const view = Widget.getOrCreateWidget(child as HTMLElement);
|
|
1776
1775
|
view.setHideOnDetach();
|
|
1777
1776
|
if (widget.selectedTabId !== id) {
|
|
1778
|
-
view.hideWidget();
|
|
1779
1777
|
child.classList.add('hidden');
|
|
1780
|
-
} else {
|
|
1781
|
-
view.showWidget();
|
|
1782
1778
|
}
|
|
1783
1779
|
tabs.push({
|
|
1784
1780
|
id,
|
|
@@ -1788,10 +1784,55 @@ export class TabbedPaneElement extends WidgetElement<TabbedPane> {
|
|
|
1788
1784
|
selected,
|
|
1789
1785
|
enabled,
|
|
1790
1786
|
isCloseable,
|
|
1787
|
+
icon,
|
|
1788
|
+
suffix,
|
|
1791
1789
|
});
|
|
1792
1790
|
}
|
|
1793
1791
|
|
|
1794
|
-
|
|
1792
|
+
const newIds = new Set(tabs.map(tab => tab.id));
|
|
1793
|
+
for (const id of this.#managedTabIds) {
|
|
1794
|
+
if (!newIds.has(id)) {
|
|
1795
|
+
widget.closeTab(id);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
this.#managedTabIds = newIds;
|
|
1799
|
+
let index = 0;
|
|
1800
|
+
for (const tab of tabs) {
|
|
1801
|
+
const existingTab = widget.tabsById.get(tab.id);
|
|
1802
|
+
if (existingTab) {
|
|
1803
|
+
widget.changeTabView(tab.id, tab.view);
|
|
1804
|
+
widget.changeTabTitle(tab.id, tab.title, tab.tabTooltip);
|
|
1805
|
+
if (tab.jslogContext !== undefined) {
|
|
1806
|
+
existingTab.jslogContext = tab.jslogContext;
|
|
1807
|
+
}
|
|
1808
|
+
if (tab.isCloseable !== undefined) {
|
|
1809
|
+
existingTab.closeable = tab.isCloseable;
|
|
1810
|
+
}
|
|
1811
|
+
if (tab.previewFeature !== undefined) {
|
|
1812
|
+
existingTab.previewFeature = tab.previewFeature;
|
|
1813
|
+
}
|
|
1814
|
+
const currentIndex = widget.tabIndex(tab.id);
|
|
1815
|
+
if (currentIndex !== index) {
|
|
1816
|
+
widget.insertBefore(existingTab, index);
|
|
1817
|
+
}
|
|
1818
|
+
} else {
|
|
1819
|
+
widget.appendTab(tab.id, tab.title, tab.view, tab.tabTooltip, /* userGesture=*/ false, tab.isCloseable,
|
|
1820
|
+
tab.previewFeature, index, tab.jslogContext);
|
|
1821
|
+
}
|
|
1822
|
+
if (tab.icon !== undefined) {
|
|
1823
|
+
widget.setTabIcon(tab.id, tab.icon as unknown as Icon);
|
|
1824
|
+
}
|
|
1825
|
+
if (tab.suffix !== undefined) {
|
|
1826
|
+
widget.setSuffixElement(tab.id, tab.suffix as unknown as HTMLElement);
|
|
1827
|
+
}
|
|
1828
|
+
if (tab.enabled !== undefined) {
|
|
1829
|
+
widget.setTabEnabled(tab.id, tab.enabled);
|
|
1830
|
+
}
|
|
1831
|
+
if (tab.selected) {
|
|
1832
|
+
widget.selectTab(tab.id);
|
|
1833
|
+
}
|
|
1834
|
+
++index;
|
|
1835
|
+
}
|
|
1795
1836
|
}
|
|
1796
1837
|
}
|
|
1797
1838
|
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
HTMLElementWithLightDOMTemplate,
|
|
61
61
|
isEditing,
|
|
62
62
|
} from './UIUtils.js';
|
|
63
|
+
import {Widget} from './Widget.js';
|
|
63
64
|
|
|
64
65
|
const UIStrings = {
|
|
65
66
|
/**
|
|
@@ -1304,9 +1305,9 @@ export class TreeElement {
|
|
|
1304
1305
|
}
|
|
1305
1306
|
}
|
|
1306
1307
|
|
|
1307
|
-
revealAndSelect(omitFocus?: boolean): void {
|
|
1308
|
+
revealAndSelect(omitFocus?: boolean, selectedByUser?: boolean): void {
|
|
1308
1309
|
this.reveal(true);
|
|
1309
|
-
this.select(omitFocus);
|
|
1310
|
+
this.select(omitFocus, selectedByUser ?? false);
|
|
1310
1311
|
}
|
|
1311
1312
|
|
|
1312
1313
|
deselect(): void {
|
|
@@ -1653,11 +1654,18 @@ class TreeViewTreeElement extends TreeElement {
|
|
|
1653
1654
|
}
|
|
1654
1655
|
this.#refreshScheduled = true;
|
|
1655
1656
|
queueMicrotask(() => {
|
|
1656
|
-
this.#refreshScheduled
|
|
1657
|
-
|
|
1657
|
+
if (this.#refreshScheduled) {
|
|
1658
|
+
this.refresh();
|
|
1659
|
+
}
|
|
1658
1660
|
});
|
|
1659
1661
|
}
|
|
1660
1662
|
|
|
1663
|
+
flushPendingRefreshForTesting(): void {
|
|
1664
|
+
if (this.#refreshScheduled) {
|
|
1665
|
+
this.refresh();
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1661
1669
|
updateAttributes(): void {
|
|
1662
1670
|
const expandable = Boolean(this.configElement.querySelector(':scope > ul[role="group"]'));
|
|
1663
1671
|
this.setExpandable(expandable);
|
|
@@ -1678,10 +1686,13 @@ class TreeViewTreeElement extends TreeElement {
|
|
|
1678
1686
|
this.#clonedClasses.add(className);
|
|
1679
1687
|
}
|
|
1680
1688
|
this.hidden = hasBooleanAttribute(this.configElement, 'hidden');
|
|
1689
|
+
this.selectable =
|
|
1690
|
+
!this.configElement.hasAttribute('selectable') || hasBooleanAttribute(this.configElement, 'selectable');
|
|
1681
1691
|
this.updateExpansionFromAttribute();
|
|
1682
1692
|
}
|
|
1683
1693
|
|
|
1684
1694
|
refresh(): void {
|
|
1695
|
+
this.#refreshScheduled = false;
|
|
1685
1696
|
const hadFocus = this.listItemElement.hasFocus();
|
|
1686
1697
|
this.titleElement.textContent = '';
|
|
1687
1698
|
this.updateAttributes();
|
|
@@ -1724,7 +1735,7 @@ class TreeViewTreeElement extends TreeElement {
|
|
|
1724
1735
|
return super.onenter();
|
|
1725
1736
|
}
|
|
1726
1737
|
|
|
1727
|
-
|
|
1738
|
+
removeFromTree(): void {
|
|
1728
1739
|
removeNode(this,
|
|
1729
1740
|
Boolean(this.parent &&
|
|
1730
1741
|
(this.parent as TreeViewTreeElement).configElement?.querySelector(':scope > ul[role="group"]')));
|
|
@@ -1890,6 +1901,26 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
|
|
|
1890
1901
|
return this.#treeOutline;
|
|
1891
1902
|
}
|
|
1892
1903
|
|
|
1904
|
+
flushPendingUpdatesForTesting(): void {
|
|
1905
|
+
this.flushPendingMutationsForTesting();
|
|
1906
|
+
const stack: TreeElement[] = [...this.#treeOutline.rootElement().children()];
|
|
1907
|
+
while (stack.length > 0) {
|
|
1908
|
+
const item = stack.pop();
|
|
1909
|
+
if (!item) {
|
|
1910
|
+
continue;
|
|
1911
|
+
}
|
|
1912
|
+
if (item instanceof TreeViewTreeElement) {
|
|
1913
|
+
item.flushPendingRefreshForTesting();
|
|
1914
|
+
}
|
|
1915
|
+
if (item.children()) {
|
|
1916
|
+
stack.push(...item.children());
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
for (const widgetEl of this.#treeOutline.shadowRoot.querySelectorAll('devtools-widget')) {
|
|
1920
|
+
void Widget.get(widgetEl)?.performUpdate();
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1893
1924
|
override focus(): void {
|
|
1894
1925
|
if (!this.#treeOutline.selectedTreeElement && this.#treeOutline.firstChild()) {
|
|
1895
1926
|
this.#treeOutline.firstChild()?.select(/* omitFocus */ true, /* selectedByUser */ false);
|
|
@@ -2028,7 +2059,7 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
|
|
|
2028
2059
|
protected override removeNodes(nodes: NodeList|Node[]): void {
|
|
2029
2060
|
for (const node of getTreeNodes(nodes)) {
|
|
2030
2061
|
if (node instanceof HTMLLIElement) {
|
|
2031
|
-
TreeViewTreeElement.get(node)?.
|
|
2062
|
+
TreeViewTreeElement.get(node)?.removeFromTree();
|
|
2032
2063
|
} else if (node.treeElement) {
|
|
2033
2064
|
removeNode(node.treeElement,
|
|
2034
2065
|
Boolean(node.treeElement.parent &&
|
|
@@ -2196,6 +2196,13 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
|
|
|
2196
2196
|
render(template, this.#contentTemplate.content);
|
|
2197
2197
|
}
|
|
2198
2198
|
|
|
2199
|
+
flushPendingMutationsForTesting(): void {
|
|
2200
|
+
const records = this.#mutationObserver.takeRecords();
|
|
2201
|
+
if (records.length > 0) {
|
|
2202
|
+
this.#onChange(records);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2199
2206
|
#onChange(mutationList: MutationRecord[]): void {
|
|
2200
2207
|
this.onChange(mutationList);
|
|
2201
2208
|
const addedNodes = new Set<Node>();
|
|
@@ -1454,3 +1454,36 @@ Node.prototype.removeChildren = function(): void {
|
|
|
1454
1454
|
}
|
|
1455
1455
|
return originalRemoveChildren.call(this);
|
|
1456
1456
|
};
|
|
1457
|
+
|
|
1458
|
+
export interface WrapperWidgetParams {
|
|
1459
|
+
widget: Widget;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
export class WrapperWidget extends Widget {
|
|
1463
|
+
#widget: Widget|null = null;
|
|
1464
|
+
constructor(element: HTMLElement, _deps: never[], params?: WrapperWidgetParams) {
|
|
1465
|
+
super(element);
|
|
1466
|
+
this.element.style.setProperty('display', 'contents');
|
|
1467
|
+
if (params?.widget) {
|
|
1468
|
+
this.widget = params.widget;
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
set widget(widget: Widget|null) {
|
|
1473
|
+
if (this.#widget === widget) {
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (this.#widget) {
|
|
1477
|
+
this.#widget.detach();
|
|
1478
|
+
}
|
|
1479
|
+
this.#widget = widget;
|
|
1480
|
+
if (this.#widget) {
|
|
1481
|
+
this.#widget.show(this.element, undefined, /* suppressOrphanWidgetError */ true);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
override focus(): void {
|
|
1485
|
+
if (this.#widget) {
|
|
1486
|
+
this.#widget.focus();
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|