Hello from PearAI!
+This is a test webview content.
+diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 8660a9a084393..bd4eec5706288 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -31,6 +31,7 @@ import { ICommandActionTitle } from 'vs/platform/action/common/action'; import { mainWindow } from 'vs/base/browser/window'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { TitlebarStyle } from 'vs/platform/window/common/window'; +import { TogglePearOverlayAction } from 'vs/workbench/browser/parts/pearai/pearOverlayActions'; // Register Icons const menubarIcon = registerIcon('menuBar', Codicon.layoutMenubar, localize('menuBarIcon', "Represents the menu bar")); @@ -1341,6 +1342,7 @@ ToggleVisibilityActions.push(...[ CreateToggleLayoutItem(ToggleAuxiliaryBarAction.ID, AuxiliaryBarVisibleContext, localize('secondarySideBar', "Secondary Side Bar"), { whenA: ContextKeyExpr.equals('config.workbench.sideBar.location', 'left'), iconA: panelRightIcon, iconB: panelLeftIcon }), CreateToggleLayoutItem(TogglePanelAction.ID, PanelVisibleContext, localize('panel', "Panel"), panelIcon), CreateToggleLayoutItem(ToggleStatusbarVisibilityAction.ID, ContextKeyExpr.equals('config.workbench.statusBar.visible', true), localize('statusBar', "Status Bar"), statusBarIcon), + CreateToggleLayoutItem(TogglePearOverlayAction.ID, PearAIVisibleContext, 'PearAI', pearaiIcon) ]); const MoveSideBarActions: CustomizeLayoutItem[] = [ diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 4e2809e149c2a..31d5e942b9984 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -264,6 +264,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private auxiliaryBarPartView!: ISerializableView; private editorPartView!: ISerializableView; private statusBarPartView!: ISerializableView; + private pearOverlayPartView!: ISerializableView; private environmentService!: IBrowserWorkbenchEnvironmentService; private extensionService!: IExtensionService; @@ -1483,6 +1484,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const auxiliaryBarPart = this.getPart(Parts.AUXILIARYBAR_PART); const sideBar = this.getPart(Parts.SIDEBAR_PART); const statusBar = this.getPart(Parts.STATUSBAR_PART); + const pearOverlayPart = this.getPart(Parts.PEAROVERLAY_PART); // View references for all parts this.titleBarPartView = titleBar; @@ -1493,6 +1495,21 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.panelPartView = panelPart; this.auxiliaryBarPartView = auxiliaryBarPart; this.statusBarPartView = statusBar; + this.pearOverlayPartView = pearOverlayPart; + + // Create a new container for PearOverlayPart + const pearOverlayPartContainer = document.createElement("div"); + pearOverlayPartContainer.style.position = "absolute"; + pearOverlayPartContainer.style.top = "0"; + pearOverlayPartContainer.style.left = "0"; + pearOverlayPartContainer.style.right = "0"; + pearOverlayPartContainer.style.bottom = "0"; + pearOverlayPartContainer.style.zIndex = "-10"; + pearOverlayPartContainer.style.display = "absolute"; + pearOverlayPartContainer.classList.add("pearoverlay-part-container"); + this.mainContainer.appendChild(pearOverlayPartContainer); + // Initialize PearOverlayPart in this new container + pearOverlayPart.create(pearOverlayPartContainer); const viewMap = { [Parts.ACTIVITYBAR_PART]: this.activityBarPartView, @@ -1502,7 +1519,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi [Parts.PANEL_PART]: this.panelPartView, [Parts.SIDEBAR_PART]: this.sideBarPartView, [Parts.STATUSBAR_PART]: this.statusBarPartView, - [Parts.AUXILIARYBAR_PART]: this.auxiliaryBarPartView + [Parts.AUXILIARYBAR_PART]: this.auxiliaryBarPartView, + [Parts.PEAROVERLAY_PART]: this.pearOverlayPartView, }; const fromJSON = ({ type }: { type: Parts }) => viewMap[type]; @@ -1571,6 +1589,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Layout the grid widget this.workbenchGrid.layout(this._mainContainerDimension.width, this._mainContainerDimension.height); + this.pearOverlayPartView.layout(this._mainContainerDimension.width, this._mainContainerDimension.height, 0, 0); this.initialized = true; // Emit as event diff --git a/src/vs/workbench/browser/parts/pearai/pearOverlayActions.ts b/src/vs/workbench/browser/parts/pearai/pearOverlayActions.ts new file mode 100644 index 0000000000000..13aaa3823311c --- /dev/null +++ b/src/vs/workbench/browser/parts/pearai/pearOverlayActions.ts @@ -0,0 +1,50 @@ +import { registerAction2, Action2 } from "vs/platform/actions/common/actions"; +import { ServicesAccessor } from "vs/platform/instantiation/common/instantiation"; +import { IPearOverlayService } from "./pearOverlayService"; +import { KeyCode, KeyMod } from "vs/base/common/keyCodes"; + +export class ClosePearOverlayAction extends Action2 { + static readonly ID = "workbench.action.closePearAI"; + + constructor() { + super({ + id: ClosePearOverlayAction.ID, + title: { value: "Close PearAI Popup", original: "Close PearAI Popup" }, + f1: true, + keybinding: { + weight: 200, + primary: KeyCode.Escape, + }, + }); + } + + run(accessor: ServicesAccessor): void { + const pearaiOverlayService = accessor.get(IPearOverlayService); + pearaiOverlayService.hide(); + } +} + +export class TogglePearOverlayAction extends Action2 { + static readonly ID = "workbench.action.togglePearAI"; + + constructor() { + super({ + id: TogglePearOverlayAction.ID, + title: { value: "Toggle PearAI Popup", original: "Toggle PearAI Popup" }, + f1: true, + keybinding: { + weight: 200, + primary: KeyMod.CtrlCmd | KeyCode.KeyE, + }, + }); + } + + run(accessor: ServicesAccessor): void { + const pearaiOverlayService = accessor.get(IPearOverlayService); + console.log("TOGGLED PEARAI SERVICE 2"); + pearaiOverlayService.toggle(); + } +} + +registerAction2(TogglePearOverlayAction); +registerAction2(ClosePearOverlayAction); diff --git a/src/vs/workbench/browser/parts/pearai/pearOverlayPart.ts b/src/vs/workbench/browser/parts/pearai/pearOverlayPart.ts new file mode 100644 index 0000000000000..500aa3a1b16c5 --- /dev/null +++ b/src/vs/workbench/browser/parts/pearai/pearOverlayPart.ts @@ -0,0 +1,314 @@ +import { Part } from "vs/workbench/browser/part"; +import { + IWorkbenchLayoutService, + Parts, +} from "vs/workbench/services/layout/browser/layoutService"; +import { IThemeService } from "vs/platform/theme/common/themeService"; +import { IStorageService } from "vs/platform/storage/common/storage"; +import { $, getActiveWindow } from "vs/base/browser/dom"; +// import { CancellationTokenSource } from "vs/base/common/cancellation"; +import { IInstantiationService } from "vs/platform/instantiation/common/instantiation"; + +// imports that violate vscode source organization +import { + // IWebviewViewService, + WebviewView, +} from "vs/workbench/contrib/webviewView/browser/webviewViewService"; +import { WebviewService } from "vs/workbench/contrib/webview/browser/webviewService"; + +export class PearOverlayPart extends Part { + static readonly ID = "workbench.parts.pearoverlay"; + + //#region IView + + readonly minimumWidth: number = 300; + readonly maximumWidth: number = 800; + readonly minimumHeight: number = 200; + readonly maximumHeight: number = 600; + + //#endregion + + private fullScreenOverlay: HTMLElement | undefined; + private popupAreaOverlay: HTMLElement | undefined; + private webviewView: WebviewView | undefined; + private _webviewService: WebviewService | undefined; + + private state: "loading" | "open" | "closed" = "loading"; + + constructor( + @IThemeService themeService: IThemeService, + @IStorageService storageService: IStorageService, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + // @IWebviewViewService + // private readonly _webviewViewService: IWebviewViewService, + @IInstantiationService + private readonly _instantiationService: IInstantiationService, + ) { + super( + PearOverlayPart.ID, + { hasTitle: false }, + themeService, + storageService, + layoutService, + ); + + this._webviewService = + this._instantiationService.createInstance(WebviewService); + + this.initialize(); + } + + private async initialize() { + // 1. create an IOverlayWebview + const webview = this._webviewService!.createWebviewOverlay({ + title: "PearAI", + options: { + enableFindWidget: false, + }, + contentOptions: { + allowScripts: true, + localResourceRoots: [ + // Uri.joinPath(this._pearaiUri, 'out'), + // URI.joinPath(pearaiUri, 'webview-ui/build'), + ], + }, + extension: undefined, + }); + + webview.claim(this, getActiveWindow(), undefined); + + webview.setHtml(this.getTestWebviewContent()); + + // 2. initialize this.webviewView by creating a WebviewView + this.webviewView = { + webview, + onDidChangeVisibility: () => { + return { dispose: () => {} }; + }, + onDispose: () => { + return { dispose: () => {} }; + }, + + get title(): string | undefined { + return "PearAI"; + }, + set title(value: string | undefined) {}, + + get description(): string | undefined { + return undefined; + }, + set description(value: string | undefined) {}, + + get badge() { + return undefined; + }, + set badge(badge) {}, + + dispose: () => { + // Only reset and clear the webview itself. Don't dispose of the view container + // this._activated = false; + // this._webview.clear(); + // // this._webviewDisposables.clear(); + }, + + show: (preserveFocus) => { + // this.viewService.openView(this.id, !preserveFocus); + }, + }; + + // this.webviewView = this.getTestWebviewContent(); + + // 3. ask the webviewViewService to connect our webviewView to the webviewViewProvider, i.e., HelloWorldPanel + // const source = new CancellationTokenSource(); // todo add to disposables + // await this._webviewViewService.resolve( + // "pearai.magicWebview", + // this.webviewView!, + // source.token, + // ); + + // if both content and webview are ready, end loading state and open + if (this.popupAreaOverlay && this.webviewView) { + this.webviewView?.webview.layoutWebviewOverElement(this.popupAreaOverlay); + this.open(); + } else { + // hide stuff while we load + this.webviewView!.webview.container.style.display = "none"; + } + } + + private getTestWebviewContent(): string { + return ` + + +
+ + +This is a test webview content.
+