chrome-devtools-frontend 1.0.1702515 → 1.0.1703574
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front_end/core/common/ParsedURL.ts +5 -1
- package/front_end/core/text_utils/Markdown.ts +73 -0
- package/front_end/core/text_utils/text_utils.ts +2 -0
- package/front_end/generated/InspectorBackendCommands.ts +3 -1
- package/front_end/generated/protocol-mapping.d.ts +8 -0
- package/front_end/generated/protocol-proxy-api.d.ts +6 -0
- package/front_end/generated/protocol.ts +22 -0
- package/front_end/models/comment_manager/CD4ABridge.ts +5 -0
- package/front_end/models/issues_manager/MarkdownIssueDescription.ts +4 -47
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +1 -1
- package/front_end/panels/common/CommentsStatusBarPill.ts +40 -7
- package/front_end/panels/common/commentsStatusBarPill.css +13 -0
- package/front_end/panels/issues/IssueView.ts +5 -1
- package/front_end/panels/sources/SourcesView.ts +127 -147
- package/front_end/panels/sources/TabbedEditorContainer.ts +266 -207
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +7 -65
- package/front_end/ui/legacy/TabbedPane.ts +51 -40
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +342 -373
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +2 -1
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import '../../ui/components/tooltips/tooltips.js';
|
|
6
|
+
import '../../ui/kit/kit.js';
|
|
7
|
+
|
|
5
8
|
import * as Common from '../../core/common/common.js';
|
|
6
9
|
import * as Host from '../../core/host/host.js';
|
|
7
10
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
@@ -10,12 +13,10 @@ import * as TextUtils from '../../core/text_utils/text_utils.js';
|
|
|
10
13
|
import * as Persistence from '../../models/persistence/persistence.js';
|
|
11
14
|
import * as Workspace from '../../models/workspace/workspace.js';
|
|
12
15
|
import type * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
|
13
|
-
import * as Tooltips from '../../ui/components/tooltips/tooltips.js';
|
|
14
16
|
import * as uiI18n from '../../ui/i18n/i18n.js';
|
|
15
|
-
import {Icon, Link} from '../../ui/kit/kit.js';
|
|
16
17
|
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
|
|
17
18
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
18
|
-
import {Directives, html, nothing, render
|
|
19
|
+
import {Directives, html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js';
|
|
19
20
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
20
21
|
import * as PanelCommon from '../common/common.js';
|
|
21
22
|
import * as Snippets from '../snippets/snippets.js';
|
|
@@ -86,27 +87,38 @@ const enum SourceViewType {
|
|
|
86
87
|
}
|
|
87
88
|
const HEADER_OVERRIDES_FILENAME = '.headers';
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
activeTabId?: string;
|
|
90
|
+
interface TabInfo {
|
|
91
|
+
tabId: string;
|
|
92
|
+
title: string;
|
|
93
|
+
tooltip: string;
|
|
94
|
+
uiSourceCode: Workspace.UISourceCode.UISourceCode;
|
|
95
|
+
isCloseable: boolean;
|
|
96
|
+
widget?: UI.Widget.Widget;
|
|
97
|
+
hasLoadError: boolean;
|
|
98
|
+
hasUnsavedCommittedChanges: boolean;
|
|
99
|
+
disconnectedAutomaticFileSystemRoot?: string;
|
|
100
|
+
icon?: LitTemplate;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
export interface
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
export interface TabbedEditorViewInput {
|
|
104
|
+
openTabs: TabInfo[];
|
|
105
|
+
activeTabId?: string;
|
|
106
|
+
leftToolbarItems: Array<UI.Toolbar.ToolbarItem|LitTemplate>;
|
|
107
|
+
rightToolbarItems: Array<UI.Toolbar.ToolbarItem|LitTemplate>;
|
|
108
|
+
tabDelegate: UI.TabbedPane.TabbedPaneTabDelegate;
|
|
109
|
+
shortcuts: Array<{
|
|
110
|
+
description: Platform.UIString.LocalizedString,
|
|
111
|
+
onClick: () => void,
|
|
112
|
+
keys: string[],
|
|
113
|
+
}>;
|
|
114
|
+
onAddFileSystemClicked: () => void;
|
|
115
|
+
onConnectAutomaticFileSystem: (e: Event) => void;
|
|
116
|
+
onClose: (e: Event) => void;
|
|
117
|
+
onTabOrderChanged: (e: Event) => void;
|
|
118
|
+
onSelect: (e: Event) => void;
|
|
107
119
|
}
|
|
108
120
|
|
|
109
|
-
export type View = (input: TabbedEditorViewInput, output:
|
|
121
|
+
export type View = (input: TabbedEditorViewInput, output: undefined, target: HTMLElement) => void;
|
|
110
122
|
|
|
111
123
|
const UI_SOURCE_CODE_WIDGET_MAP = new WeakMap<Workspace.UISourceCode.UISourceCode, UI.Widget.Widget>();
|
|
112
124
|
|
|
@@ -152,24 +164,109 @@ function removeSourceViewCache(uiSourceCode: Workspace.UISourceCode.UISourceCode
|
|
|
152
164
|
}
|
|
153
165
|
return view;
|
|
154
166
|
}
|
|
167
|
+
function renderPlaceholder(input: TabbedEditorViewInput): LitTemplate {
|
|
168
|
+
// clang-format off
|
|
169
|
+
return html`
|
|
170
|
+
<div class="sources-placeholder">
|
|
171
|
+
<div class="tabbed-pane-placeholder-row workspace">
|
|
172
|
+
<span class="icon-container">
|
|
173
|
+
<devtools-icon name="sync" class="sync-icon"></devtools-icon>
|
|
174
|
+
</span>
|
|
175
|
+
<span>
|
|
176
|
+
${i18nString(UIStrings.workspaceDropInAFolderToSyncSources)}
|
|
177
|
+
<button @click=${input.onAddFileSystemClicked}>${i18nString(UIStrings.selectFolder)}</button>
|
|
178
|
+
</span>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="shortcuts-list tabbed-pane-placeholder-row" role="list"
|
|
181
|
+
aria-label=${i18nString(UIStrings.sourceViewActions)}>
|
|
182
|
+
${input.shortcuts.map(shortcut => !shortcut.keys.length
|
|
183
|
+
? html`<div class="shortcut-line" role="listitem"></div>`
|
|
184
|
+
: html`<div class="shortcut-line" role="listitem">
|
|
185
|
+
<button @click=${shortcut.onClick}>${shortcut.description}</button>
|
|
186
|
+
<span class="shortcuts">
|
|
187
|
+
${shortcut.keys.map(key => html`
|
|
188
|
+
<span class="keybinds-key"><span>${key}</span></span>
|
|
189
|
+
`)}
|
|
190
|
+
</span>
|
|
191
|
+
</div>`)}
|
|
192
|
+
</div>
|
|
193
|
+
</div>`;
|
|
194
|
+
// clang-format on
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function renderTabIcon(tab: TabInfo): LitTemplate {
|
|
198
|
+
if (tab.hasLoadError) {
|
|
199
|
+
// clang-format off
|
|
200
|
+
return html`
|
|
201
|
+
<span slot="icon">
|
|
202
|
+
<devtools-icon class="small" name="cross-circle-filled"
|
|
203
|
+
title=${i18nString(UIStrings.unableToLoadThisContent)}>
|
|
204
|
+
</devtools-icon>
|
|
205
|
+
</span>`;
|
|
206
|
+
// clang-format on
|
|
207
|
+
}
|
|
208
|
+
if (tab.icon) {
|
|
209
|
+
return html`<span slot="icon">${tab.icon}</span>`;
|
|
210
|
+
}
|
|
211
|
+
return nothing;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function renderTabSuffix(tab: TabInfo, input: TabbedEditorViewInput): LitTemplate {
|
|
215
|
+
if (!tab.hasUnsavedCommittedChanges) {
|
|
216
|
+
return nothing;
|
|
217
|
+
}
|
|
218
|
+
const tooltipId = `tab-tooltip-${tab.tabId}`;
|
|
219
|
+
// clang-format off
|
|
220
|
+
return html`
|
|
221
|
+
<span slot="suffix">
|
|
222
|
+
<div>
|
|
223
|
+
<devtools-icon name="warning-filled" class="small" aria-describedby=${tooltipId}></devtools-icon>
|
|
224
|
+
<devtools-tooltip id=${tooltipId} variant="rich">
|
|
225
|
+
${tab.disconnectedAutomaticFileSystemRoot !== undefined
|
|
226
|
+
? uiI18n.getFormatLocalizedStringTemplate(
|
|
227
|
+
str_, UIStrings.changesWereNotSavedToFileSystemToSaveAddFolderToWorkspace, {
|
|
228
|
+
PH1: html`<devtools-link class="devtools-link" @click=${input.onConnectAutomaticFileSystem}>${
|
|
229
|
+
tab.disconnectedAutomaticFileSystemRoot}</devtools-link>`,
|
|
230
|
+
})
|
|
231
|
+
: uiI18n.getFormatLocalizedStringTemplate(
|
|
232
|
+
str_, UIStrings.changesWereNotSavedToFileSystemToSaveSetUpYourWorkspace, {
|
|
233
|
+
PH1: html`<devtools-link href="https://developer.chrome.com/docs/devtools/workspaces/">Workspace</devtools-link>`,
|
|
234
|
+
})}
|
|
235
|
+
</devtools-tooltip>
|
|
236
|
+
</div>
|
|
237
|
+
</span>`;
|
|
238
|
+
// clang-format on
|
|
239
|
+
}
|
|
155
240
|
|
|
156
|
-
export const DEFAULT_VIEW: View = (input,
|
|
241
|
+
export const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
157
242
|
// clang-format off
|
|
158
243
|
render(html`
|
|
159
244
|
<devtools-tabbed-pane
|
|
160
245
|
class="flex-auto vbox"
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
246
|
+
.closeableTabs=${true}
|
|
247
|
+
.allowTabReorder=${true}
|
|
248
|
+
.automaticReorder=${true}
|
|
249
|
+
.tabDelegate=${input.tabDelegate}
|
|
250
|
+
.headerJslog=${`${VisualLogging.toolbar('top').track({keydown: 'ArrowUp|ArrowLeft|ArrowDown|ArrowRight|Enter|Space'})}`}
|
|
251
|
+
.placeholder=${renderPlaceholder(input)}
|
|
252
|
+
@close=${input.onClose}
|
|
253
|
+
@taborderchanged=${input.onTabOrderChanged}
|
|
254
|
+
@select=${input.onSelect}
|
|
164
255
|
>
|
|
256
|
+
<devtools-toolbar class="tabbed-pane-left-toolbar" slot="left">
|
|
257
|
+
${input.leftToolbarItems.map(item => item instanceof UI.Toolbar.ToolbarItem ? item.element : item)}
|
|
258
|
+
</devtools-toolbar>
|
|
259
|
+
<devtools-toolbar class="tabbed-pane-right-toolbar" slot="right">
|
|
260
|
+
${input.rightToolbarItems.map(item => item instanceof UI.Toolbar.ToolbarItem ? item.element : item)}
|
|
261
|
+
</devtools-toolbar>
|
|
165
262
|
${repeat(input.openTabs, tab => tab.tabId, tab => html`
|
|
166
263
|
<div id=${tab.tabId}
|
|
167
264
|
title=${tab.title}
|
|
168
265
|
?closeable=${tab.isCloseable}
|
|
169
266
|
?selected=${input.activeTabId === tab.tabId}
|
|
170
267
|
style="display: flex; flex: auto;">
|
|
171
|
-
${tab
|
|
172
|
-
${tab
|
|
268
|
+
${renderTabIcon(tab)}
|
|
269
|
+
${renderTabSuffix(tab, input)}
|
|
173
270
|
${tab.widget ? html`${widget(UI.Widget.WrapperWidget, {widget: tab.widget})}` : nothing}
|
|
174
271
|
</div>`)}
|
|
175
272
|
</devtools-tabbed-pane>`, target);
|
|
@@ -190,26 +287,81 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
190
287
|
}
|
|
191
288
|
|
|
192
289
|
#scheduleUpdate(): void {
|
|
193
|
-
this
|
|
290
|
+
this.performUpdate();
|
|
194
291
|
}
|
|
195
292
|
|
|
196
|
-
|
|
293
|
+
override performUpdate(): void {
|
|
294
|
+
if (false) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const shortcuts = [
|
|
298
|
+
{actionId: 'quick-open.show', description: i18nString(UIStrings.openFile)},
|
|
299
|
+
{actionId: 'quick-open.show-command-menu', description: i18nString(UIStrings.runCommand)},
|
|
300
|
+
];
|
|
301
|
+
const separator = Host.Platform.isMac() ? '\u2004' : ' + ';
|
|
302
|
+
const shortcutElements = shortcuts.map(shortcut => {
|
|
303
|
+
const shortcutKeys = UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutsForAction(shortcut.actionId);
|
|
304
|
+
if (!shortcutKeys?.[0]) {
|
|
305
|
+
return {
|
|
306
|
+
description: shortcut.description,
|
|
307
|
+
onClick: () => {},
|
|
308
|
+
keys: [],
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const action = UI.ActionRegistry.ActionRegistry.instance().getAction(shortcut.actionId);
|
|
312
|
+
const keys = shortcutKeys[0].descriptors.flatMap(descriptor => descriptor.name.split(separator));
|
|
313
|
+
return {
|
|
314
|
+
description: shortcut.description,
|
|
315
|
+
onClick: () => {
|
|
316
|
+
void action.execute();
|
|
317
|
+
},
|
|
318
|
+
keys,
|
|
319
|
+
};
|
|
320
|
+
});
|
|
321
|
+
|
|
197
322
|
const input: TabbedEditorViewInput = {
|
|
198
|
-
openTabs: [...this.files.entries()].map(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
323
|
+
openTabs: [...this.files.entries()].map(([tabId, uiSourceCode]) => {
|
|
324
|
+
const hasLoadError = Boolean(uiSourceCode.loadError()) || this.#loadErrorFiles.has(uiSourceCode);
|
|
325
|
+
const hasUnsavedCommittedChanges = !hasLoadError &&
|
|
326
|
+
Persistence.Persistence.PersistenceImpl.instance().hasUnsavedCommittedChanges(uiSourceCode);
|
|
327
|
+
let disconnectedAutomaticFileSystemRoot: string|undefined;
|
|
328
|
+
if (hasUnsavedCommittedChanges) {
|
|
329
|
+
const {automaticFileSystem} = Persistence.AutomaticFileSystemManager.AutomaticFileSystemManager.instance();
|
|
330
|
+
if (automaticFileSystem?.state === 'disconnected') {
|
|
331
|
+
disconnectedAutomaticFileSystemRoot = Common.ParsedURL.ParsedURL.extractName(automaticFileSystem.root);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const icon = !hasLoadError ?
|
|
335
|
+
(PanelCommon.PersistenceUtils.PersistenceUtils.iconForUISourceCode(uiSourceCode) ?? undefined) :
|
|
336
|
+
undefined;
|
|
337
|
+
return {
|
|
338
|
+
tabId,
|
|
339
|
+
title: this.titleForFile(uiSourceCode),
|
|
340
|
+
tooltip: this.tooltipForFile(uiSourceCode),
|
|
341
|
+
uiSourceCode,
|
|
342
|
+
isCloseable: true,
|
|
343
|
+
hasLoadError,
|
|
344
|
+
hasUnsavedCommittedChanges,
|
|
345
|
+
disconnectedAutomaticFileSystemRoot,
|
|
346
|
+
icon,
|
|
347
|
+
widget: (this.#currentFile === uiSourceCode) ? this.getOrCreateSourceView(uiSourceCode) :
|
|
348
|
+
this.getCreatedSourceView(uiSourceCode),
|
|
349
|
+
};
|
|
350
|
+
}),
|
|
210
351
|
activeTabId: this.#currentFile ? this.tabIds.get(this.#currentFile) : undefined,
|
|
211
|
-
|
|
212
|
-
|
|
352
|
+
leftToolbarItems: this.#leftToolbarItems,
|
|
353
|
+
rightToolbarItems: this.#rightToolbarItems,
|
|
354
|
+
tabDelegate: this.#tabDelegate,
|
|
355
|
+
shortcuts: shortcutElements,
|
|
356
|
+
onAddFileSystemClicked: () => {
|
|
357
|
+
void this.#addFileSystemClicked();
|
|
358
|
+
},
|
|
359
|
+
onConnectAutomaticFileSystem: async (event: Event) => {
|
|
360
|
+
event.consume();
|
|
361
|
+
await UI.ViewManager.ViewManager.instance().showView('navigator-files');
|
|
362
|
+
await Persistence.AutomaticFileSystemManager.AutomaticFileSystemManager.instance().connectAutomaticFileSystem(
|
|
363
|
+
/* addIfMissing= */ true);
|
|
364
|
+
},
|
|
213
365
|
onClose: (e: Event) => {
|
|
214
366
|
this.tabClosed((e as CustomEvent<{tabId: string}>).detail.tabId,
|
|
215
367
|
(e as CustomEvent<{isUserGesture?: boolean}>).detail.isUserGesture);
|
|
@@ -235,21 +387,67 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
235
387
|
this.#scheduleUpdate();
|
|
236
388
|
},
|
|
237
389
|
};
|
|
238
|
-
|
|
390
|
+
this.#view(input, undefined, this.contentElement);
|
|
239
391
|
}
|
|
240
392
|
#historyManager!: EditingLocationHistoryManager;
|
|
241
393
|
set historyManager(historyManager: EditingLocationHistoryManager) {
|
|
242
394
|
this.#historyManager = historyManager;
|
|
243
395
|
}
|
|
244
396
|
|
|
245
|
-
|
|
397
|
+
#leftToolbarItems: Array<UI.Toolbar.ToolbarItem|LitTemplate> = [];
|
|
398
|
+
set leftToolbarItems(items: Array<UI.Toolbar.ToolbarItem|LitTemplate>) {
|
|
399
|
+
if (this.#leftToolbarItems === items) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
this.#leftToolbarItems = items;
|
|
403
|
+
this.#scheduleUpdate();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
#rightToolbarItems: Array<UI.Toolbar.ToolbarItem|LitTemplate> = [];
|
|
407
|
+
set rightToolbarItems(items: Array<UI.Toolbar.ToolbarItem|LitTemplate>) {
|
|
408
|
+
if (this.#rightToolbarItems === items) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
this.#rightToolbarItems = items;
|
|
412
|
+
this.#scheduleUpdate();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
readonly #syncedUISourceCodes = new Set<Workspace.UISourceCode.UISourceCode>();
|
|
416
|
+
set uiSourceCodes(uiSourceCodes: ReadonlySet<Workspace.UISourceCode.UISourceCode>) {
|
|
417
|
+
const removed: Workspace.UISourceCode.UISourceCode[] = [];
|
|
418
|
+
for (const existing of this.#syncedUISourceCodes) {
|
|
419
|
+
if (!uiSourceCodes.has(existing)) {
|
|
420
|
+
removed.push(existing);
|
|
421
|
+
this.#syncedUISourceCodes.delete(existing);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (removed.length > 0) {
|
|
425
|
+
this.removeUISourceCodes(removed);
|
|
426
|
+
}
|
|
427
|
+
UI.UIUtils.startBatchUpdate();
|
|
428
|
+
for (const uiSourceCode of uiSourceCodes) {
|
|
429
|
+
if (!this.#syncedUISourceCodes.has(uiSourceCode)) {
|
|
430
|
+
this.#syncedUISourceCodes.add(uiSourceCode);
|
|
431
|
+
this.addUISourceCode(uiSourceCode);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
UI.UIUtils.endBatchUpdate();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
onEditorSelected?: (event: EditorSelectedEvent) => void;
|
|
438
|
+
onEditorClosed?: (uiSourceCode: Workspace.UISourceCode.UISourceCode) => void;
|
|
439
|
+
|
|
440
|
+
readonly #view: View;
|
|
441
|
+
readonly #tabDelegate: EditorContainerTabDelegate;
|
|
246
442
|
private tabIds: Map<Workspace.UISourceCode.UISourceCode, string>;
|
|
247
443
|
private files: Map<string, Workspace.UISourceCode.UISourceCode>;
|
|
248
|
-
readonly #
|
|
249
|
-
readonly #tabSuffixes = new Map<Workspace.UISourceCode.UISourceCode, HTMLElement>();
|
|
444
|
+
readonly #loadErrorFiles = new Set<Workspace.UISourceCode.UISourceCode>();
|
|
250
445
|
#previouslyViewedFilesSetting!: Common.Settings.Setting<SerializedHistoryItem[]>;
|
|
251
446
|
history!: History;
|
|
252
447
|
set previouslyViewedFilesSetting(setting: Common.Settings.Setting<SerializedHistoryItem[]>) {
|
|
448
|
+
if (this.#previouslyViewedFilesSetting === setting) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
253
451
|
this.#previouslyViewedFilesSetting = setting;
|
|
254
452
|
this.history = History.fromObject(this.#previouslyViewedFilesSetting.get());
|
|
255
453
|
}
|
|
@@ -262,8 +460,10 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
262
460
|
private currentView!: UI.Widget.Widget|null;
|
|
263
461
|
private scrollTimer?: number;
|
|
264
462
|
private reentrantShow: boolean;
|
|
265
|
-
constructor(element?: HTMLElement) {
|
|
463
|
+
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
266
464
|
super(element);
|
|
465
|
+
this.#view = view;
|
|
466
|
+
this.#tabDelegate = new EditorContainerTabDelegate(this);
|
|
267
467
|
|
|
268
468
|
this.tabIds = new Map();
|
|
269
469
|
this.files = new Map();
|
|
@@ -271,25 +471,7 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
271
471
|
this.idToUISourceCode = new Map();
|
|
272
472
|
this.reentrantShow = false;
|
|
273
473
|
|
|
274
|
-
this
|
|
275
|
-
|
|
276
|
-
this.tabbedPane = this.contentElement.querySelector('devtools-tabbed-pane') as UI.TabbedPane.TabbedPaneElement;
|
|
277
|
-
|
|
278
|
-
// eslint-disable-next-line @devtools/no-imperative-dom-api
|
|
279
|
-
const placeholderElement = document.createElement('div');
|
|
280
|
-
placeholderElement.classList.add('sources-placeholder');
|
|
281
|
-
this.tabbedPane.placeholder = placeholderElement;
|
|
282
|
-
this.#renderPlaceholder(placeholderElement as HTMLElement);
|
|
283
|
-
this.tabbedPane.tabDelegate = new EditorContainerTabDelegate(this);
|
|
284
|
-
|
|
285
|
-
this.tabbedPane.closeableTabs = true;
|
|
286
|
-
this.tabbedPane.allowTabReorder = true;
|
|
287
|
-
this.tabbedPane.automaticReorder = true;
|
|
288
|
-
|
|
289
|
-
const widget = UI.Widget.Widget.getOrCreateWidget(this.tabbedPane) as UI.TabbedPane.TabbedPane;
|
|
290
|
-
widget.headerElement().setAttribute(
|
|
291
|
-
'jslog',
|
|
292
|
-
`${VisualLogging.toolbar('top').track({keydown: 'ArrowUp|ArrowLeft|ArrowDown|ArrowRight|Enter|Space'})}`);
|
|
474
|
+
this.performUpdate();
|
|
293
475
|
|
|
294
476
|
Persistence.Persistence.PersistenceImpl.instance().addEventListener(Persistence.Persistence.Events.BindingCreated,
|
|
295
477
|
this.onBindingCreated, this);
|
|
@@ -304,8 +486,16 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
304
486
|
this.#tabsHistory.unshift(tabId);
|
|
305
487
|
}
|
|
306
488
|
|
|
489
|
+
get #tabbedPane(): UI.TabbedPane.TabbedPaneElement|null {
|
|
490
|
+
return this.contentElement.querySelector('devtools-tabbed-pane');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
get tabbedPane(): UI.TabbedPane.TabbedPaneElement {
|
|
494
|
+
return this.#tabbedPane as UI.TabbedPane.TabbedPaneElement;
|
|
495
|
+
}
|
|
496
|
+
|
|
307
497
|
get tabbedPaneForTesting(): UI.TabbedPane.TabbedPaneElement {
|
|
308
|
-
return this.
|
|
498
|
+
return this.#tabbedPane as UI.TabbedPane.TabbedPaneElement;
|
|
309
499
|
}
|
|
310
500
|
|
|
311
501
|
private onBindingCreated(event: Common.EventTarget.EventTargetEvent<Persistence.Persistence.PersistenceBinding>):
|
|
@@ -369,30 +559,6 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
369
559
|
return Array.from(this.files.values()).map(getViewByUISourceCode).filter(Boolean) as UI.Widget.Widget[];
|
|
370
560
|
}
|
|
371
561
|
|
|
372
|
-
#leftToolbar?: UI.Toolbar.Toolbar;
|
|
373
|
-
leftToolbar(): UI.Toolbar.Toolbar {
|
|
374
|
-
if (!this.#leftToolbar) {
|
|
375
|
-
this.#leftToolbar =
|
|
376
|
-
document.createElement('devtools-toolbar') /* eslint-disable-line @devtools/no-imperative-dom-api */;
|
|
377
|
-
this.#leftToolbar.classList.add('tabbed-pane-left-toolbar');
|
|
378
|
-
this.#leftToolbar.slot = 'left';
|
|
379
|
-
this.tabbedPane.appendChild(this.#leftToolbar);
|
|
380
|
-
}
|
|
381
|
-
return this.#leftToolbar;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
#rightToolbar?: UI.Toolbar.Toolbar;
|
|
385
|
-
rightToolbar(): UI.Toolbar.Toolbar {
|
|
386
|
-
if (!this.#rightToolbar) {
|
|
387
|
-
this.#rightToolbar =
|
|
388
|
-
document.createElement('devtools-toolbar') /* eslint-disable-line @devtools/no-imperative-dom-api */;
|
|
389
|
-
this.#rightToolbar.classList.add('tabbed-pane-right-toolbar');
|
|
390
|
-
this.#rightToolbar.slot = 'right';
|
|
391
|
-
this.tabbedPane.appendChild(this.#rightToolbar);
|
|
392
|
-
}
|
|
393
|
-
return this.#rightToolbar;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
562
|
showFile(uiSourceCode: Workspace.UISourceCode.UISourceCode): void {
|
|
397
563
|
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
|
|
398
564
|
uiSourceCode = binding ? binding.fileSystem : uiSourceCode;
|
|
@@ -544,7 +710,7 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
544
710
|
|
|
545
711
|
this.#scheduleUpdate();
|
|
546
712
|
// Force TabbedPaneElement to sync its tabs synchronously to avoid layout races in E2E tests.
|
|
547
|
-
this
|
|
713
|
+
this.#tabbedPane?.tabs;
|
|
548
714
|
} finally {
|
|
549
715
|
this.reentrantShow = false;
|
|
550
716
|
}
|
|
@@ -578,6 +744,7 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
578
744
|
userGesture,
|
|
579
745
|
};
|
|
580
746
|
this.dispatchEventToListeners(Events.EDITOR_SELECTED, eventData);
|
|
747
|
+
this.onEditorSelected?.(eventData);
|
|
581
748
|
}
|
|
582
749
|
|
|
583
750
|
private titleForFile(uiSourceCode: Workspace.UISourceCode.UISourceCode): string {
|
|
@@ -796,14 +963,9 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
796
963
|
}
|
|
797
964
|
|
|
798
965
|
private addLoadErrorIcon(tabId: string): void {
|
|
799
|
-
// clang-format off
|
|
800
|
-
const icon = html`<devtools-icon class="small" name="cross-circle-filled"
|
|
801
|
-
title=${i18nString(UIStrings.unableToLoadThisContent)}>
|
|
802
|
-
</devtools-icon>`;
|
|
803
|
-
// clang-format on
|
|
804
966
|
const uiSourceCode = this.files.get(tabId);
|
|
805
967
|
if (uiSourceCode) {
|
|
806
|
-
this.#
|
|
968
|
+
this.#loadErrorFiles.add(uiSourceCode);
|
|
807
969
|
this.#scheduleUpdate();
|
|
808
970
|
}
|
|
809
971
|
}
|
|
@@ -831,6 +993,7 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
831
993
|
}
|
|
832
994
|
if (uiSourceCode) {
|
|
833
995
|
this.tabIds.delete(uiSourceCode);
|
|
996
|
+
this.#loadErrorFiles.delete(uiSourceCode);
|
|
834
997
|
}
|
|
835
998
|
this.files.delete(tabId);
|
|
836
999
|
|
|
@@ -839,6 +1002,7 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
839
1002
|
this.removeSourceFrame(uiSourceCode);
|
|
840
1003
|
|
|
841
1004
|
this.dispatchEventToListeners(Events.EDITOR_CLOSED, uiSourceCode);
|
|
1005
|
+
this.onEditorClosed?.(uiSourceCode);
|
|
842
1006
|
|
|
843
1007
|
if (isUserGesture) {
|
|
844
1008
|
this.editorClosedByUserAction(uiSourceCode);
|
|
@@ -870,60 +1034,11 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
870
1034
|
}
|
|
871
1035
|
|
|
872
1036
|
private updateFileTitle(uiSourceCode: Workspace.UISourceCode.UISourceCode): void {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
if (uiSourceCode.loadError()) {
|
|
877
|
-
// clang-format off
|
|
878
|
-
const icon = html`<devtools-icon class="small" name="cross-circle-filled"
|
|
879
|
-
title=${i18nString(UIStrings.unableToLoadThisContent)}>
|
|
880
|
-
</devtools-icon>`;
|
|
881
|
-
// clang-format on
|
|
882
|
-
this.#tabIcons.set(uiSourceCode, icon);
|
|
883
|
-
this.#scheduleUpdate();
|
|
884
|
-
} else if (Persistence.Persistence.PersistenceImpl.instance().hasUnsavedCommittedChanges(uiSourceCode)) {
|
|
885
|
-
/* eslint-disable @devtools/no-imperative-dom-api --
|
|
886
|
-
* This is a temporary solution using the <devtools-tooltip>
|
|
887
|
-
* and we will use a toast instead once available.
|
|
888
|
-
**/
|
|
889
|
-
const suffixElement = document.createElement('div');
|
|
890
|
-
const icon = new Icon();
|
|
891
|
-
icon.name = 'warning-filled';
|
|
892
|
-
icon.classList.add('small');
|
|
893
|
-
const id = `tab-tooltip-${nextTooltipId++}`;
|
|
894
|
-
icon.setAttribute('aria-describedby', id);
|
|
895
|
-
const tooltip = new Tooltips.Tooltip.Tooltip({id, anchor: icon, variant: 'rich'});
|
|
896
|
-
const automaticFileSystemManager = Persistence.AutomaticFileSystemManager.AutomaticFileSystemManager.instance();
|
|
897
|
-
const {automaticFileSystem} = automaticFileSystemManager;
|
|
898
|
-
if (automaticFileSystem?.state === 'disconnected') {
|
|
899
|
-
const link = document.createElement('a');
|
|
900
|
-
link.className = 'devtools-link';
|
|
901
|
-
link.textContent = Common.ParsedURL.ParsedURL.extractName(automaticFileSystem.root);
|
|
902
|
-
link.addEventListener('click', async event => {
|
|
903
|
-
event.consume();
|
|
904
|
-
await UI.ViewManager.ViewManager.instance().showView('navigator-files');
|
|
905
|
-
await automaticFileSystemManager.connectAutomaticFileSystem(/* addIfMissing= */ true);
|
|
906
|
-
});
|
|
907
|
-
tooltip.append(uiI18n.getFormatLocalizedString(
|
|
908
|
-
str_, UIStrings.changesWereNotSavedToFileSystemToSaveAddFolderToWorkspace, {PH1: link}));
|
|
909
|
-
} else {
|
|
910
|
-
const link = Link.create('https://developer.chrome.com/docs/devtools/workspaces/', 'Workspace');
|
|
911
|
-
tooltip.append(uiI18n.getFormatLocalizedString(
|
|
912
|
-
str_, UIStrings.changesWereNotSavedToFileSystemToSaveSetUpYourWorkspace, {PH1: link}));
|
|
913
|
-
}
|
|
914
|
-
suffixElement.append(icon, tooltip);
|
|
915
|
-
/* eslint-enable @devtools/no-imperative-dom-api */
|
|
916
|
-
this.#tabSuffixes.set(uiSourceCode, suffixElement);
|
|
917
|
-
this.#scheduleUpdate();
|
|
918
|
-
} else {
|
|
919
|
-
const icon = PanelCommon.PersistenceUtils.PersistenceUtils.iconForUISourceCode(uiSourceCode);
|
|
920
|
-
if (icon) {
|
|
921
|
-
this.#tabIcons.set(uiSourceCode, icon as TemplateResult);
|
|
922
|
-
} else {
|
|
923
|
-
this.#tabIcons.delete(uiSourceCode);
|
|
924
|
-
}
|
|
925
|
-
this.#scheduleUpdate();
|
|
1037
|
+
if (this.tabIds.has(uiSourceCode)) {
|
|
1038
|
+
if (!uiSourceCode.loadError()) {
|
|
1039
|
+
this.#loadErrorFiles.delete(uiSourceCode);
|
|
926
1040
|
}
|
|
1041
|
+
this.#scheduleUpdate();
|
|
927
1042
|
}
|
|
928
1043
|
}
|
|
929
1044
|
|
|
@@ -977,60 +1092,6 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
977
1092
|
return 'tab-' + (tabId++) as Lowercase<string>;
|
|
978
1093
|
}
|
|
979
1094
|
|
|
980
|
-
#renderPlaceholder(placeholderElement: HTMLElement): void {
|
|
981
|
-
const shortcuts = [
|
|
982
|
-
{actionId: 'quick-open.show', description: i18nString(UIStrings.openFile)},
|
|
983
|
-
{actionId: 'quick-open.show-command-menu', description: i18nString(UIStrings.runCommand)},
|
|
984
|
-
];
|
|
985
|
-
const separator = Host.Platform.isMac() ? '\u2004' : ' + ';
|
|
986
|
-
const shortcutElements = shortcuts.map(shortcut => {
|
|
987
|
-
const shortcutKeys = UI.ShortcutRegistry.ShortcutRegistry.instance().shortcutsForAction(shortcut.actionId);
|
|
988
|
-
if (!shortcutKeys?.[0]) {
|
|
989
|
-
return {
|
|
990
|
-
description: shortcut.description,
|
|
991
|
-
onClick: () => {},
|
|
992
|
-
keys: [],
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
const action = UI.ActionRegistry.ActionRegistry.instance().getAction(shortcut.actionId);
|
|
996
|
-
const keys = shortcutKeys[0].descriptors.flatMap(descriptor => descriptor.name.split(separator));
|
|
997
|
-
return {
|
|
998
|
-
description: shortcut.description,
|
|
999
|
-
onClick: () => {
|
|
1000
|
-
void action.execute();
|
|
1001
|
-
},
|
|
1002
|
-
keys,
|
|
1003
|
-
};
|
|
1004
|
-
});
|
|
1005
|
-
|
|
1006
|
-
// clang-format off
|
|
1007
|
-
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
1008
|
-
render(html`
|
|
1009
|
-
<div class="tabbed-pane-placeholder-row workspace">
|
|
1010
|
-
<span class="icon-container">
|
|
1011
|
-
<devtools-icon name="sync" class="sync-icon"></devtools-icon>
|
|
1012
|
-
</span>
|
|
1013
|
-
<span>
|
|
1014
|
-
${i18nString(UIStrings.workspaceDropInAFolderToSyncSources)}
|
|
1015
|
-
<button @click=${this.#addFileSystemClicked.bind(this)}>${i18nString(UIStrings.selectFolder)}</button>
|
|
1016
|
-
</span>
|
|
1017
|
-
</div>
|
|
1018
|
-
<div class="shortcuts-list tabbed-pane-placeholder-row" role="list"
|
|
1019
|
-
aria-label=${i18nString(UIStrings.sourceViewActions)}>
|
|
1020
|
-
${shortcutElements.map(shortcut => !shortcut.keys.length
|
|
1021
|
-
? html`<div class="shortcut-line" role="listitem"></div>`
|
|
1022
|
-
: html`<div class="shortcut-line" role="listitem">
|
|
1023
|
-
<button @click=${shortcut.onClick}>${shortcut.description}</button>
|
|
1024
|
-
<span class="shortcuts">
|
|
1025
|
-
${shortcut.keys.map(key => html`
|
|
1026
|
-
<span class="keybinds-key"><span>${key}</span></span>
|
|
1027
|
-
`)}
|
|
1028
|
-
</span>
|
|
1029
|
-
</div>`)}
|
|
1030
|
-
</div>`, placeholderElement);
|
|
1031
|
-
// clang-format on
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
1095
|
async #addFileSystemClicked(): Promise<void> {
|
|
1035
1096
|
const result = await Persistence.IsolatedFileSystemManager.IsolatedFileSystemManager.instance().addFileSystem();
|
|
1036
1097
|
if (!result) {
|
|
@@ -1111,8 +1172,6 @@ export class TabbedEditorContainer extends TabbedEditorContainerBase {
|
|
|
1111
1172
|
}
|
|
1112
1173
|
}
|
|
1113
1174
|
|
|
1114
|
-
let nextTooltipId = 1;
|
|
1115
|
-
|
|
1116
1175
|
export const enum Events {
|
|
1117
1176
|
EDITOR_SELECTED = 'EditorSelected',
|
|
1118
1177
|
EDITOR_CLOSED = 'EditorClosed',
|
|
@@ -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: d45ba94c77a2835c4097934e46ff30d9b7615fc0
|
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License File: LICENSE
|