Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new window watermark redesign #87

Merged
merged 1 commit into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 130 additions & 16 deletions src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ import { isMacintosh, isWeb, OS } from 'vs/base/common/platform';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { append, clearNode, $, h } from 'vs/base/browser/dom';
import { append, clearNode, $, h, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { defaultKeybindingLabelStyles } from 'vs/platform/theme/browser/defaultStyles';
import { editorForeground, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { isRecentFolder, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { IWindowOpenable } from 'vs/platform/window/common/window';
import { ILabelService, Verbosity } from 'vs/platform/label/common/label';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { splitRecentLabel } from 'vs/base/common/labels';

registerColor('editorWatermark.foreground', { dark: transparent(editorForeground, 0.6), light: transparent(editorForeground, 0.68), hcDark: editorForeground, hcLight: editorForeground }, localize('editorLineHighlight', 'Foreground color for the labels in the editor watermark.'));

Expand All @@ -26,7 +31,7 @@ interface WatermarkEntry {
readonly when?: ContextKeyExpression;
}

const openPearAIChat: WatermarkEntry = { text: localize('watermark.openPearAIChat', "Open Chat"), id: 'pearai.focusContinueInput', when: ContextKeyExpr.has('pearAIExtensionLoaded') };
const openPearAIChat: WatermarkEntry = { text: localize('watermark.openPearAIChat', "Open Chat"), id: 'pearai.focusContinueInput', when: ContextKeyExpr.has('pearAIExtensionLoaded') };
const bigChat: WatermarkEntry = { text: localize('watermark.pearAIBigChat', "Big Chat"), id: 'pearai.resizeAuxiliaryBarWidth', when: ContextKeyExpr.has('pearAIExtensionLoaded') };
const prevChat: WatermarkEntry = { text: localize('watermark.pearAIPrevChat', "Previous Chat"), id: 'pearai.loadRecentChat', when: ContextKeyExpr.has('pearAIExtensionLoaded') };
const showCommands: WatermarkEntry = { text: localize('watermark.showCommands', "Show All Commands"), id: 'workbench.action.showCommands' };
Expand Down Expand Up @@ -68,7 +73,7 @@ const folderEntries = [
];

export class EditorGroupWatermark extends Disposable {
private readonly shortcuts: HTMLElement;
private readonly watermark: HTMLElement;
private readonly transientDisposables = this._register(new DisposableStore());
private enabled: boolean = false;
private workbenchState: WorkbenchState;
Expand All @@ -80,21 +85,21 @@ export class EditorGroupWatermark extends Disposable {
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionService private readonly extensionService: IExtensionService
@IExtensionService private readonly extensionService: IExtensionService,
@IWorkspacesService private readonly workspacesService: IWorkspacesService,
@ILabelService private readonly labelService: ILabelService,
@IHostService private readonly hostService: IHostService,
@ICommandService private readonly commandService: ICommandService
) {
super();
this.workbenchState = contextService.getWorkbenchState();
const hasWorkspace = this.workbenchState !== WorkbenchState.EMPTY;

const elements = h('.editor-group-watermark', [
h('.letterpress'),
h('.shortcuts@shortcuts'),
]);
const elements = h('.editor-group-watermark-' + (hasWorkspace ? 'workspace' : 'no-workspace'));

append(container, elements.root);
this.shortcuts = elements.shortcuts;

this.watermark = elements.root;
this.registerListeners();

this.workbenchState = contextService.getWorkbenchState();
this.render();
}

Expand Down Expand Up @@ -127,7 +132,7 @@ export class EditorGroupWatermark extends Disposable {
private async render(): Promise<void> {
// Wait for the all extensions to be activated
await this.extensionService.activateByEvent('onStartupFinished');
// TODO: @Himanshu-Singh-Chauhan - this should be set from inside the extension, test it later, if it works, remove this
// TODO: @Himanshu-Singh-Chauhan - this should be set from inside the extension, test it later, if it works, remove this
this.contextKeyService.createKey('pearAIExtensionLoaded', true); // Set a context key when the PearAI extension is loaded

const enabled = this.configurationService.getValue<boolean>('workbench.tips.enabled');
Expand All @@ -143,7 +148,16 @@ export class EditorGroupWatermark extends Disposable {
return;
}

const box = append(this.shortcuts, $('.watermark-box'));
const hasWorkspace = this.workbenchState !== WorkbenchState.EMPTY;

if (!hasWorkspace) {
this.renderNoWorkspaceWatermark();
return;
}

append(this.watermark, $('.letterpress'));
const shortcuts = append(this.watermark, $('.shortcuts'));
const box = append(shortcuts, $('.watermark-box'));
const folder = this.workbenchState !== WorkbenchState.EMPTY;
const selected = (folder ? folderEntries : noFolderEntries)
.filter(entry => !('when' in entry) || this.contextKeyService.contextMatchesRules(entry.when))
Expand Down Expand Up @@ -175,8 +189,108 @@ export class EditorGroupWatermark extends Disposable {
this.transientDisposables.add(this.keybindingService.onDidUpdateKeybindings(update));
}

private async renderNoWorkspaceWatermark(): Promise<void> {
const container = append(this.watermark, $('.editor-group-watermark-no-workspace'));

// button container
const buttonContainer = append(container, $('.button-container'));
const openFolderButton = append(buttonContainer, $('button.open-folder-button'));
// folder icon and text in separate spans
append(openFolderButton, $('span.codicon.codicon-folder-opened'));
append(openFolderButton, $('span.text', {}, localize('watermark.openFolder', "Open Folder")));
// click handler for Open Folder button
this._register(addDisposableListener(openFolderButton, EventType.CLICK, (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
this.commandService.executeCommand('workbench.action.files.openFolder');
}));

// New File text button
const newFileItem = append(container, $('.new-file-item'));
newFileItem.textContent = localize('watermark.newFile', "New File");
newFileItem.style.cursor = 'pointer';

const newFileKeybinding = this.keybindingService.lookupKeybinding('workbench.action.files.newUntitledFile')?.getLabel();
newFileItem.title = newFileKeybinding ?
localize('watermark.newFileWithKeybinding', "New File ({0})", newFileKeybinding) :
localize('watermark.newFile', "New File");

this._register(addDisposableListener(newFileItem, EventType.CLICK, (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
this.commandService.executeCommand('workbench.action.files.newUntitledFile');
}));

// recent folders and workspaces list
const recentList = append(container, $('.recent-list'));

const recentlyOpened = await this.workspacesService.getRecentlyOpened();
const recents = recentlyOpened.workspaces
.filter(recent => !this.contextService.isCurrentWorkspace(
isRecentFolder(recent) ? recent.folderUri : recent.workspace.configPath
))
.slice(0, 6);

if (recents.length === 0) {
const noRecentsElement = append(recentList, $('.recent-item'));
noRecentsElement.textContent = localize('watermark.noRecents', "No Recent Folders");
return;
}

recents.forEach(recent => {
const itemElement = append(recentList, $('.recent-item'));

let fullPath: string;
let windowOpenable: IWindowOpenable;

if (isRecentFolder(recent)) {
windowOpenable = { folderUri: recent.folderUri };
fullPath = recent.label || this.labelService.getWorkspaceLabel(recent.folderUri, { verbose: Verbosity.LONG });
} else {
fullPath = recent.label || this.labelService.getWorkspaceLabel(recent.workspace, { verbose: Verbosity.LONG });
windowOpenable = { workspaceUri: recent.workspace.configPath };
}

const { name, parentPath } = splitRecentLabel(fullPath);

itemElement.textContent = name;
if (parentPath) {
append(itemElement, $('span.spacer'));
const pathSpan = append(itemElement, $('span.path'));
pathSpan.textContent = parentPath;
}

itemElement.title = fullPath;
itemElement.style.cursor = 'pointer';

this._register(addDisposableListener(itemElement, EventType.CLICK, async (e: MouseEvent) => {
try {
e.preventDefault();
e.stopPropagation();
await this.hostService.openWindow([windowOpenable], {
forceNewWindow: e.ctrlKey || e.metaKey,
remoteAuthority: recent.remoteAuthority ?? null
});
} catch (error) {
console.error('Failed to open recent item:', error);
}
}));
});

// "More..." item
const moreItem = append(recentList, $('.more-item'));
moreItem.textContent = localize('watermark.more', "More...");
moreItem.title = localize('watermark.showMoreRecents', "Show All Recent Folders");
moreItem.style.cursor = 'pointer';
this._register(addDisposableListener(moreItem, EventType.CLICK, (e: MouseEvent) => {
e.preventDefault();
e.stopPropagation();
this.commandService.executeCommand('workbench.action.openRecent');
}));
}

private clear(): void {
clearNode(this.shortcuts);
clearNode(this.watermark);
this.transientDisposables.clear();
}

Expand Down
Loading
Loading