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.
@@ -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, type TemplateResult} from '../../ui/lit/lit.js';
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
- export interface TabbedEditorViewInput {
90
- openTabs: Array<{
91
- tabId: string,
92
- title: string,
93
- tooltip: string,
94
- uiSourceCode: Workspace.UISourceCode.UISourceCode,
95
- isCloseable: boolean,
96
- widget?: UI.Widget.Widget,
97
- icon?: TemplateResult|HTMLElement,
98
- suffix?: HTMLElement,
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 TabbedEditorViewOutput {
104
- onClose(e: Event): void;
105
- onTabOrderChanged(e: Event): void;
106
- onSelect(e: Event): void;
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: TabbedEditorViewOutput, target: HTMLElement) => void;
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, output, target) => {
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
- @close=${output.onClose}
162
- @taborderchanged=${output.onTabOrderChanged}
163
- @select=${output.onSelect}
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.icon ? html`<span slot="icon">${tab.icon}</span>` : nothing}
172
- ${tab.suffix ? html`<span slot="suffix">${tab.suffix}</span>` : nothing}
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.#renderView();
290
+ this.performUpdate();
194
291
  }
195
292
 
196
- #renderView(): void {
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
- ([tabId, uiSourceCode]) => ({
200
- tabId,
201
- title: this.titleForFile(uiSourceCode),
202
- tooltip: this.tooltipForFile(uiSourceCode),
203
- uiSourceCode,
204
- isCloseable: true,
205
- icon: this.#tabIcons.get(uiSourceCode),
206
- suffix: this.#tabSuffixes.get(uiSourceCode),
207
- widget: (this.#currentFile === uiSourceCode) ? this.getOrCreateSourceView(uiSourceCode) :
208
- this.getCreatedSourceView(uiSourceCode),
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
- const output: TabbedEditorViewOutput = {
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
- DEFAULT_VIEW(input, output, this.contentElement);
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
- private readonly tabbedPane: UI.TabbedPane.TabbedPaneElement;
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 #tabIcons = new Map<Workspace.UISourceCode.UISourceCode, TemplateResult|HTMLElement>();
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.#renderView();
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.tabbedPane;
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.tabbedPane.tabs;
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.#tabIcons.set(uiSourceCode, icon);
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
- const tabId = this.tabIds.get(uiSourceCode);
874
- if (tabId) {
875
- this.#scheduleUpdate();
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: 672d59d92e5d37d1bce1be75e0c852c7d728b0e6
4
+ Revision: d45ba94c77a2835c4097934e46ff30d9b7615fc0
5
5
  Update Mechanism: Manual (https://crbug.com/428069060)
6
6
  License: BSD-3-Clause
7
7
  License File: LICENSE