mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Delete unused files
This commit is contained in:
@@ -1,70 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { mainWindow } from 'vs/base/browser/window';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class BroadcastDataChannel<T> extends Disposable {
|
||||
|
||||
private broadcastChannel: BroadcastChannel | undefined;
|
||||
|
||||
private readonly _onDidReceiveData = this._register(new Emitter<T>());
|
||||
readonly onDidReceiveData = this._onDidReceiveData.event;
|
||||
|
||||
constructor(private readonly channelName: string) {
|
||||
super();
|
||||
|
||||
// Use BroadcastChannel
|
||||
if ('BroadcastChannel' in mainWindow) {
|
||||
try {
|
||||
this.broadcastChannel = new BroadcastChannel(channelName);
|
||||
const listener = (event: MessageEvent) => {
|
||||
this._onDidReceiveData.fire(event.data);
|
||||
};
|
||||
this.broadcastChannel.addEventListener('message', listener);
|
||||
this._register(toDisposable(() => {
|
||||
if (this.broadcastChannel) {
|
||||
this.broadcastChannel.removeEventListener('message', listener);
|
||||
this.broadcastChannel.close();
|
||||
}
|
||||
}));
|
||||
} catch (error) {
|
||||
console.warn('Error while creating broadcast channel. Falling back to localStorage.', getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
|
||||
// BroadcastChannel is not supported. Use storage.
|
||||
if (!this.broadcastChannel) {
|
||||
this.channelName = `BroadcastDataChannel.${channelName}`;
|
||||
this.createBroadcastChannel();
|
||||
}
|
||||
}
|
||||
|
||||
private createBroadcastChannel(): void {
|
||||
const listener = (event: StorageEvent) => {
|
||||
if (event.key === this.channelName && event.newValue) {
|
||||
this._onDidReceiveData.fire(JSON.parse(event.newValue));
|
||||
}
|
||||
};
|
||||
mainWindow.addEventListener('storage', listener);
|
||||
this._register(toDisposable(() => mainWindow.removeEventListener('storage', listener)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to other BroadcastChannel objects set up for this channel. Data can be structured objects, e.g. nested objects and arrays.
|
||||
* @param data data to broadcast
|
||||
*/
|
||||
postData(data: T): void {
|
||||
if (this.broadcastChannel) {
|
||||
this.broadcastChannel.postMessage(data);
|
||||
} else {
|
||||
// remove previous changes so that event is triggered even if new changes are same as old changes
|
||||
localStorage.removeItem(this.channelName);
|
||||
localStorage.setItem(this.channelName, JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { COI } from 'vs/base/common/network';
|
||||
import { IWorker, IWorkerCallback, IWorkerFactory, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
const ttPolicy = createTrustedTypesPolicy('defaultWorkerFactory', { createScriptURL: value => value });
|
||||
|
||||
export function createBlobWorker(blobUrl: string, options?: WorkerOptions): Worker {
|
||||
if (!blobUrl.startsWith('blob:')) {
|
||||
throw new URIError('Not a blob-url: ' + blobUrl);
|
||||
}
|
||||
return new Worker(ttPolicy ? ttPolicy.createScriptURL(blobUrl) as unknown as string : blobUrl, options);
|
||||
}
|
||||
|
||||
function getWorker(label: string): Worker | Promise<Worker> {
|
||||
// Option for hosts to overwrite the worker script (used in the standalone editor)
|
||||
interface IMonacoEnvironment {
|
||||
getWorker?(moduleId: string, label: string): Worker | Promise<Worker>;
|
||||
getWorkerUrl?(moduleId: string, label: string): string;
|
||||
}
|
||||
const monacoEnvironment: IMonacoEnvironment | undefined = (globalThis as any).MonacoEnvironment;
|
||||
if (monacoEnvironment) {
|
||||
if (typeof monacoEnvironment.getWorker === 'function') {
|
||||
return monacoEnvironment.getWorker('workerMain.js', label);
|
||||
}
|
||||
if (typeof monacoEnvironment.getWorkerUrl === 'function') {
|
||||
const workerUrl = monacoEnvironment.getWorkerUrl('workerMain.js', label);
|
||||
return new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) as unknown as string : workerUrl, { name: label });
|
||||
}
|
||||
}
|
||||
// ESM-comment-begin
|
||||
if (typeof require === 'function') {
|
||||
// check if the JS lives on a different origin
|
||||
const workerMain = require.toUrl('vs/base/worker/workerMain.js'); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
|
||||
const workerUrl = getWorkerBootstrapUrl(workerMain, label);
|
||||
return new Worker(ttPolicy ? ttPolicy.createScriptURL(workerUrl) as unknown as string : workerUrl, { name: label });
|
||||
}
|
||||
// ESM-comment-end
|
||||
throw new Error(`You must define a function MonacoEnvironment.getWorkerUrl or MonacoEnvironment.getWorker`);
|
||||
}
|
||||
|
||||
// ESM-comment-begin
|
||||
export function getWorkerBootstrapUrl(scriptPath: string, label: string): string {
|
||||
if (/^((http:)|(https:)|(file:))/.test(scriptPath) && scriptPath.substring(0, globalThis.origin.length) !== globalThis.origin) {
|
||||
// this is the cross-origin case
|
||||
// i.e. the webpage is running at a different origin than where the scripts are loaded from
|
||||
} else {
|
||||
const start = scriptPath.lastIndexOf('?');
|
||||
const end = scriptPath.lastIndexOf('#', start);
|
||||
const params = start > 0
|
||||
? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined))
|
||||
: new URLSearchParams();
|
||||
|
||||
COI.addSearchParam(params, true, true);
|
||||
const search = params.toString();
|
||||
if (!search) {
|
||||
scriptPath = `${scriptPath}#${label}`;
|
||||
} else {
|
||||
scriptPath = `${scriptPath}?${params.toString()}#${label}`;
|
||||
}
|
||||
}
|
||||
|
||||
const factoryModuleId = 'vs/base/worker/defaultWorkerFactory.js';
|
||||
const workerBaseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321
|
||||
const blob = new Blob([[
|
||||
`/*${label}*/`,
|
||||
`globalThis.MonacoEnvironment = { baseUrl: '${workerBaseUrl}' };`,
|
||||
// VSCODE_GLOBALS: NLS
|
||||
`globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(globalThis._VSCODE_NLS_MESSAGES)};`,
|
||||
`globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(globalThis._VSCODE_NLS_LANGUAGE)};`,
|
||||
`const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });`,
|
||||
`importScripts(ttPolicy?.createScriptURL('${scriptPath}') ?? '${scriptPath}');`,
|
||||
`/*${label}*/`
|
||||
].join('')], { type: 'application/javascript' });
|
||||
return URL.createObjectURL(blob);
|
||||
}
|
||||
// ESM-comment-end
|
||||
|
||||
function isPromiseLike<T>(obj: any): obj is PromiseLike<T> {
|
||||
if (typeof obj.then === 'function') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A worker that uses HTML5 web workers so that is has
|
||||
* its own global scope and its own thread.
|
||||
*/
|
||||
class WebWorker extends Disposable implements IWorker {
|
||||
|
||||
private readonly id: number;
|
||||
private readonly label: string;
|
||||
private worker: Promise<Worker> | null;
|
||||
|
||||
constructor(moduleId: string, id: number, label: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void) {
|
||||
super();
|
||||
this.id = id;
|
||||
this.label = label;
|
||||
const workerOrPromise = getWorker(label);
|
||||
if (isPromiseLike(workerOrPromise)) {
|
||||
this.worker = workerOrPromise;
|
||||
} else {
|
||||
this.worker = Promise.resolve(workerOrPromise);
|
||||
}
|
||||
this.postMessage(moduleId, []);
|
||||
this.worker.then((w) => {
|
||||
w.onmessage = function (ev) {
|
||||
onMessageCallback(ev.data);
|
||||
};
|
||||
w.onmessageerror = onErrorCallback;
|
||||
if (typeof w.addEventListener === 'function') {
|
||||
w.addEventListener('error', onErrorCallback);
|
||||
}
|
||||
});
|
||||
this._register(toDisposable(() => {
|
||||
this.worker?.then(w => {
|
||||
w.onmessage = null;
|
||||
w.onmessageerror = null;
|
||||
w.removeEventListener('error', onErrorCallback);
|
||||
w.terminate();
|
||||
});
|
||||
this.worker = null;
|
||||
}));
|
||||
}
|
||||
|
||||
public getId(): number {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public postMessage(message: any, transfer: Transferable[]): void {
|
||||
this.worker?.then(w => {
|
||||
try {
|
||||
w.postMessage(message, transfer);
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
onUnexpectedError(new Error(`FAILED to post message to '${this.label}'-worker`, { cause: err }));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DefaultWorkerFactory implements IWorkerFactory {
|
||||
|
||||
private static LAST_WORKER_ID = 0;
|
||||
|
||||
private _label: string | undefined;
|
||||
private _webWorkerFailedBeforeError: any;
|
||||
|
||||
constructor(label: string | undefined) {
|
||||
this._label = label;
|
||||
this._webWorkerFailedBeforeError = false;
|
||||
}
|
||||
|
||||
public create(moduleId: string, onMessageCallback: IWorkerCallback, onErrorCallback: (err: any) => void): IWorker {
|
||||
const workerId = (++DefaultWorkerFactory.LAST_WORKER_ID);
|
||||
|
||||
if (this._webWorkerFailedBeforeError) {
|
||||
throw this._webWorkerFailedBeforeError;
|
||||
}
|
||||
|
||||
return new WebWorker(moduleId, workerId, this._label || 'anonymous' + workerId, onMessageCallback, (err) => {
|
||||
logOnceWebWorkerWarning(err);
|
||||
this._webWorkerFailedBeforeError = err;
|
||||
onErrorCallback(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// https://wicg.github.io/webusb/
|
||||
|
||||
export interface UsbDeviceData {
|
||||
readonly deviceClass: number;
|
||||
readonly deviceProtocol: number;
|
||||
readonly deviceSubclass: number;
|
||||
readonly deviceVersionMajor: number;
|
||||
readonly deviceVersionMinor: number;
|
||||
readonly deviceVersionSubminor: number;
|
||||
readonly manufacturerName?: string;
|
||||
readonly productId: number;
|
||||
readonly productName?: string;
|
||||
readonly serialNumber?: string;
|
||||
readonly usbVersionMajor: number;
|
||||
readonly usbVersionMinor: number;
|
||||
readonly usbVersionSubminor: number;
|
||||
readonly vendorId: number;
|
||||
}
|
||||
|
||||
export async function requestUsbDevice(options?: { filters?: unknown[] }): Promise<UsbDeviceData | undefined> {
|
||||
const usb = (navigator as any).usb;
|
||||
if (!usb) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const device = await usb.requestDevice({ filters: options?.filters ?? [] });
|
||||
if (!device) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
deviceClass: device.deviceClass,
|
||||
deviceProtocol: device.deviceProtocol,
|
||||
deviceSubclass: device.deviceSubclass,
|
||||
deviceVersionMajor: device.deviceVersionMajor,
|
||||
deviceVersionMinor: device.deviceVersionMinor,
|
||||
deviceVersionSubminor: device.deviceVersionSubminor,
|
||||
manufacturerName: device.manufacturerName,
|
||||
productId: device.productId,
|
||||
productName: device.productName,
|
||||
serialNumber: device.serialNumber,
|
||||
usbVersionMajor: device.usbVersionMajor,
|
||||
usbVersionMinor: device.usbVersionMinor,
|
||||
usbVersionSubminor: device.usbVersionSubminor,
|
||||
vendorId: device.vendorId,
|
||||
};
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/
|
||||
|
||||
export interface SerialPortData {
|
||||
readonly usbVendorId?: number | undefined;
|
||||
readonly usbProductId?: number | undefined;
|
||||
}
|
||||
|
||||
export async function requestSerialPort(options?: { filters?: unknown[] }): Promise<SerialPortData | undefined> {
|
||||
const serial = (navigator as any).serial;
|
||||
if (!serial) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const port = await serial.requestPort({ filters: options?.filters ?? [] });
|
||||
if (!port) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const info = port.getInfo();
|
||||
return {
|
||||
usbVendorId: info.usbVendorId,
|
||||
usbProductId: info.usbProductId
|
||||
};
|
||||
}
|
||||
|
||||
// https://wicg.github.io/webhid/
|
||||
|
||||
export interface HidDeviceData {
|
||||
readonly opened: boolean;
|
||||
readonly vendorId: number;
|
||||
readonly productId: number;
|
||||
readonly productName: string;
|
||||
readonly collections: [];
|
||||
}
|
||||
|
||||
export async function requestHidDevice(options?: { filters?: unknown[] }): Promise<HidDeviceData | undefined> {
|
||||
const hid = (navigator as any).hid;
|
||||
if (!hid) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const devices = await hid.requestDevice({ filters: options?.filters ?? [] });
|
||||
if (!devices.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const device = devices[0];
|
||||
return {
|
||||
opened: device.opened,
|
||||
vendorId: device.vendorId,
|
||||
productId: device.productId,
|
||||
productName: device.productName,
|
||||
collections: device.collections
|
||||
};
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { addDisposableListener, getWindow } from 'vs/base/browser/dom';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Mimes } from 'vs/base/common/mime';
|
||||
|
||||
/**
|
||||
* A helper that will execute a provided function when the provided HTMLElement receives
|
||||
* dragover event for 800ms. If the drag is aborted before, the callback will not be triggered.
|
||||
*/
|
||||
export class DelayedDragHandler extends Disposable {
|
||||
private timeout: any;
|
||||
|
||||
constructor(container: HTMLElement, callback: () => void) {
|
||||
super();
|
||||
|
||||
this._register(addDisposableListener(container, 'dragover', e => {
|
||||
e.preventDefault(); // needed so that the drop event fires (https://stackoverflow.com/questions/21339924/drop-event-not-firing-in-chrome)
|
||||
|
||||
if (!this.timeout) {
|
||||
this.timeout = setTimeout(() => {
|
||||
callback();
|
||||
|
||||
this.timeout = null;
|
||||
}, 800);
|
||||
}
|
||||
}));
|
||||
|
||||
['dragleave', 'drop', 'dragend'].forEach(type => {
|
||||
this._register(addDisposableListener(container, type, () => {
|
||||
this.clearDragTimeout();
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private clearDragTimeout(): void {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
override dispose(): void {
|
||||
super.dispose();
|
||||
|
||||
this.clearDragTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
// Common data transfers
|
||||
export const DataTransfers = {
|
||||
|
||||
/**
|
||||
* Application specific resource transfer type
|
||||
*/
|
||||
RESOURCES: 'ResourceURLs',
|
||||
|
||||
/**
|
||||
* Browser specific transfer type to download
|
||||
*/
|
||||
DOWNLOAD_URL: 'DownloadURL',
|
||||
|
||||
/**
|
||||
* Browser specific transfer type for files
|
||||
*/
|
||||
FILES: 'Files',
|
||||
|
||||
/**
|
||||
* Typically transfer type for copy/paste transfers.
|
||||
*/
|
||||
TEXT: Mimes.text,
|
||||
|
||||
/**
|
||||
* Internal type used to pass around text/uri-list data.
|
||||
*
|
||||
* This is needed to work around https://bugs.chromium.org/p/chromium/issues/detail?id=239745.
|
||||
*/
|
||||
INTERNAL_URI_LIST: 'application/vnd.code.uri-list',
|
||||
};
|
||||
|
||||
export function applyDragImage(event: DragEvent, label: string | null, clazz: string, backgroundColor?: string | null, foregroundColor?: string | null): void {
|
||||
const dragImage = document.createElement('div');
|
||||
dragImage.className = clazz;
|
||||
dragImage.textContent = label;
|
||||
|
||||
if (foregroundColor) {
|
||||
dragImage.style.color = foregroundColor;
|
||||
}
|
||||
|
||||
if (backgroundColor) {
|
||||
dragImage.style.background = backgroundColor;
|
||||
}
|
||||
|
||||
if (event.dataTransfer) {
|
||||
const ownerDocument = getWindow(event).document;
|
||||
ownerDocument.body.appendChild(dragImage);
|
||||
event.dataTransfer.setDragImage(dragImage, -10, -10);
|
||||
|
||||
// Removes the element when the DND operation is done
|
||||
setTimeout(() => dragImage.remove(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDragAndDropData {
|
||||
update(dataTransfer: DataTransfer): void;
|
||||
getData(): unknown;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createStyleSheet2 } from 'vs/base/browser/dom';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { autorun, IObservable } from 'vs/base/common/observable';
|
||||
|
||||
export function createStyleSheetFromObservable(css: IObservable<string>): IDisposable {
|
||||
const store = new DisposableStore();
|
||||
const w = store.add(createStyleSheet2());
|
||||
store.add(autorun(reader => {
|
||||
w.setStyle(css.read(reader));
|
||||
}));
|
||||
return store;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { GestureEvent } from 'vs/base/browser/touch';
|
||||
import { Emitter, Event as BaseEvent } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export type EventHandler = HTMLElement | HTMLDocument | Window;
|
||||
|
||||
export interface IDomEvent {
|
||||
<K extends keyof HTMLElementEventMap>(element: EventHandler, type: K, useCapture?: boolean): BaseEvent<HTMLElementEventMap[K]>;
|
||||
(element: EventHandler, type: string, useCapture?: boolean): BaseEvent<unknown>;
|
||||
}
|
||||
|
||||
export interface DOMEventMap extends HTMLElementEventMap, DocumentEventMap, WindowEventMap {
|
||||
'-monaco-gesturetap': GestureEvent;
|
||||
'-monaco-gesturechange': GestureEvent;
|
||||
'-monaco-gesturestart': GestureEvent;
|
||||
'-monaco-gesturesend': GestureEvent;
|
||||
'-monaco-gesturecontextmenu': GestureEvent;
|
||||
'compositionstart': CompositionEvent;
|
||||
'compositionupdate': CompositionEvent;
|
||||
'compositionend': CompositionEvent;
|
||||
}
|
||||
|
||||
export class DomEmitter<K extends keyof DOMEventMap> implements IDisposable {
|
||||
|
||||
private emitter: Emitter<DOMEventMap[K]>;
|
||||
|
||||
get event(): BaseEvent<DOMEventMap[K]> {
|
||||
return this.emitter.event;
|
||||
}
|
||||
|
||||
constructor(element: Window & typeof globalThis, type: WindowEventMap, useCapture?: boolean);
|
||||
constructor(element: Document, type: DocumentEventMap, useCapture?: boolean);
|
||||
constructor(element: EventHandler, type: K, useCapture?: boolean);
|
||||
constructor(element: EventHandler, type: K, useCapture?: boolean) {
|
||||
const fn = (e: Event) => this.emitter.fire(e as DOMEventMap[K]);
|
||||
this.emitter = new Emitter({
|
||||
onWillAddFirstListener: () => element.addEventListener(type, fn, useCapture),
|
||||
onDidRemoveLastListener: () => element.removeEventListener(type, fn, useCapture)
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.emitter.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
|
||||
/**
|
||||
* The best font-family to be used in CSS based on the platform:
|
||||
* - Windows: Segoe preferred, fallback to sans-serif
|
||||
* - macOS: standard system font, fallback to sans-serif
|
||||
* - Linux: standard system font preferred, fallback to Ubuntu fonts
|
||||
*
|
||||
* Note: this currently does not adjust for different locales.
|
||||
*/
|
||||
export const DEFAULT_FONT_FAMILY = isWindows ? '"Segoe WPC", "Segoe UI", sans-serif' : isMacintosh ? '-apple-system, BlinkMacSystemFont, sans-serif' : 'system-ui, "Ubuntu", "Droid Sans", sans-serif';
|
||||
@@ -1,226 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IContentActionHandler {
|
||||
callback: (content: string, event: IMouseEvent | IKeyboardEvent) => void;
|
||||
readonly disposables: DisposableStore;
|
||||
}
|
||||
|
||||
export interface FormattedTextRenderOptions {
|
||||
readonly className?: string;
|
||||
readonly inline?: boolean;
|
||||
readonly actionHandler?: IContentActionHandler;
|
||||
readonly renderCodeSegments?: boolean;
|
||||
}
|
||||
|
||||
export function renderText(text: string, options: FormattedTextRenderOptions = {}): HTMLElement {
|
||||
const element = createElement(options);
|
||||
element.textContent = text;
|
||||
return element;
|
||||
}
|
||||
|
||||
export function renderFormattedText(formattedText: string, options: FormattedTextRenderOptions = {}): HTMLElement {
|
||||
const element = createElement(options);
|
||||
_renderFormattedText(element, parseFormattedText(formattedText, !!options.renderCodeSegments), options.actionHandler, options.renderCodeSegments);
|
||||
return element;
|
||||
}
|
||||
|
||||
export function createElement(options: FormattedTextRenderOptions): HTMLElement {
|
||||
const tagName = options.inline ? 'span' : 'div';
|
||||
const element = document.createElement(tagName);
|
||||
if (options.className) {
|
||||
element.className = options.className;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
class StringStream {
|
||||
private source: string;
|
||||
private index: number;
|
||||
|
||||
constructor(source: string) {
|
||||
this.source = source;
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
public eos(): boolean {
|
||||
return this.index >= this.source.length;
|
||||
}
|
||||
|
||||
public next(): string {
|
||||
const next = this.peek();
|
||||
this.advance();
|
||||
return next;
|
||||
}
|
||||
|
||||
public peek(): string {
|
||||
return this.source[this.index];
|
||||
}
|
||||
|
||||
public advance(): void {
|
||||
this.index++;
|
||||
}
|
||||
}
|
||||
|
||||
const enum FormatType {
|
||||
Invalid,
|
||||
Root,
|
||||
Text,
|
||||
Bold,
|
||||
Italics,
|
||||
Action,
|
||||
ActionClose,
|
||||
Code,
|
||||
NewLine
|
||||
}
|
||||
|
||||
interface IFormatParseTree {
|
||||
type: FormatType;
|
||||
content?: string;
|
||||
index?: number;
|
||||
children?: IFormatParseTree[];
|
||||
}
|
||||
|
||||
function _renderFormattedText(element: Node, treeNode: IFormatParseTree, actionHandler?: IContentActionHandler, renderCodeSegments?: boolean) {
|
||||
let child: Node | undefined;
|
||||
|
||||
if (treeNode.type === FormatType.Text) {
|
||||
child = document.createTextNode(treeNode.content || '');
|
||||
} else if (treeNode.type === FormatType.Bold) {
|
||||
child = document.createElement('b');
|
||||
} else if (treeNode.type === FormatType.Italics) {
|
||||
child = document.createElement('i');
|
||||
} else if (treeNode.type === FormatType.Code && renderCodeSegments) {
|
||||
child = document.createElement('code');
|
||||
} else if (treeNode.type === FormatType.Action && actionHandler) {
|
||||
const a = document.createElement('a');
|
||||
actionHandler.disposables.add(DOM.addStandardDisposableListener(a, 'click', (event) => {
|
||||
actionHandler.callback(String(treeNode.index), event);
|
||||
}));
|
||||
|
||||
child = a;
|
||||
} else if (treeNode.type === FormatType.NewLine) {
|
||||
child = document.createElement('br');
|
||||
} else if (treeNode.type === FormatType.Root) {
|
||||
child = element;
|
||||
}
|
||||
|
||||
if (child && element !== child) {
|
||||
element.appendChild(child);
|
||||
}
|
||||
|
||||
if (child && Array.isArray(treeNode.children)) {
|
||||
treeNode.children.forEach((nodeChild) => {
|
||||
_renderFormattedText(child, nodeChild, actionHandler, renderCodeSegments);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function parseFormattedText(content: string, parseCodeSegments: boolean): IFormatParseTree {
|
||||
|
||||
const root: IFormatParseTree = {
|
||||
type: FormatType.Root,
|
||||
children: []
|
||||
};
|
||||
|
||||
let actionViewItemIndex = 0;
|
||||
let current = root;
|
||||
const stack: IFormatParseTree[] = [];
|
||||
const stream = new StringStream(content);
|
||||
|
||||
while (!stream.eos()) {
|
||||
let next = stream.next();
|
||||
|
||||
const isEscapedFormatType = (next === '\\' && formatTagType(stream.peek(), parseCodeSegments) !== FormatType.Invalid);
|
||||
if (isEscapedFormatType) {
|
||||
next = stream.next(); // unread the backslash if it escapes a format tag type
|
||||
}
|
||||
|
||||
if (!isEscapedFormatType && isFormatTag(next, parseCodeSegments) && next === stream.peek()) {
|
||||
stream.advance();
|
||||
|
||||
if (current.type === FormatType.Text) {
|
||||
current = stack.pop()!;
|
||||
}
|
||||
|
||||
const type = formatTagType(next, parseCodeSegments);
|
||||
if (current.type === type || (current.type === FormatType.Action && type === FormatType.ActionClose)) {
|
||||
current = stack.pop()!;
|
||||
} else {
|
||||
const newCurrent: IFormatParseTree = {
|
||||
type: type,
|
||||
children: []
|
||||
};
|
||||
|
||||
if (type === FormatType.Action) {
|
||||
newCurrent.index = actionViewItemIndex;
|
||||
actionViewItemIndex++;
|
||||
}
|
||||
|
||||
current.children!.push(newCurrent);
|
||||
stack.push(current);
|
||||
current = newCurrent;
|
||||
}
|
||||
} else if (next === '\n') {
|
||||
if (current.type === FormatType.Text) {
|
||||
current = stack.pop()!;
|
||||
}
|
||||
|
||||
current.children!.push({
|
||||
type: FormatType.NewLine
|
||||
});
|
||||
|
||||
} else {
|
||||
if (current.type !== FormatType.Text) {
|
||||
const textCurrent: IFormatParseTree = {
|
||||
type: FormatType.Text,
|
||||
content: next
|
||||
};
|
||||
current.children!.push(textCurrent);
|
||||
stack.push(current);
|
||||
current = textCurrent;
|
||||
|
||||
} else {
|
||||
current.content += next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current.type === FormatType.Text) {
|
||||
current = stack.pop()!;
|
||||
}
|
||||
|
||||
if (stack.length) {
|
||||
// incorrectly formatted string literal
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function isFormatTag(char: string, supportCodeSegments: boolean): boolean {
|
||||
return formatTagType(char, supportCodeSegments) !== FormatType.Invalid;
|
||||
}
|
||||
|
||||
function formatTagType(char: string, supportCodeSegments: boolean): FormatType {
|
||||
switch (char) {
|
||||
case '*':
|
||||
return FormatType.Bold;
|
||||
case '_':
|
||||
return FormatType.Italics;
|
||||
case '[':
|
||||
return FormatType.Action;
|
||||
case ']':
|
||||
return FormatType.ActionClose;
|
||||
case '`':
|
||||
return supportCodeSegments ? FormatType.Code : FormatType.Invalid;
|
||||
default:
|
||||
return FormatType.Invalid;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { StringSHA1, toHexString } from 'vs/base/common/hash';
|
||||
|
||||
export async function sha1Hex(str: string): Promise<string> {
|
||||
|
||||
// Prefer to use browser's crypto module
|
||||
if (globalThis?.crypto?.subtle) {
|
||||
|
||||
// Careful to use `dontUseNodeBuffer` when passing the
|
||||
// buffer to the browser `crypto` API. Users reported
|
||||
// native crashes in certain cases that we could trace
|
||||
// back to passing node.js `Buffer` around
|
||||
// (https://github.com/microsoft/vscode/issues/114227)
|
||||
const buffer = VSBuffer.fromString(str, { dontUseNodeBuffer: true }).buffer;
|
||||
const hash = await globalThis.crypto.subtle.digest({ name: 'sha-1' }, buffer);
|
||||
|
||||
return toHexString(hash);
|
||||
}
|
||||
|
||||
// Otherwise fallback to `StringSHA1`
|
||||
else {
|
||||
const computer = new StringSHA1();
|
||||
computer.update(str);
|
||||
|
||||
return computer.digest();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export interface IHistoryNavigationWidget {
|
||||
|
||||
readonly element: HTMLElement;
|
||||
|
||||
showPreviousValue(): void;
|
||||
|
||||
showNextValue(): void;
|
||||
|
||||
onDidFocus: Event<void>;
|
||||
|
||||
onDidBlur: Event<void>;
|
||||
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { ErrorNoTelemetry, getErrorMessage } from 'vs/base/common/errors';
|
||||
import { mark } from 'vs/base/common/performance';
|
||||
|
||||
class MissingStoresError extends Error {
|
||||
constructor(readonly db: IDBDatabase) {
|
||||
super('Missing stores');
|
||||
}
|
||||
}
|
||||
|
||||
export class DBClosedError extends Error {
|
||||
readonly code = 'DBClosed';
|
||||
constructor(dbName: string) {
|
||||
super(`IndexedDB database '${dbName}' is closed.`);
|
||||
}
|
||||
}
|
||||
|
||||
export class IndexedDB {
|
||||
|
||||
static async create(name: string, version: number | undefined, stores: string[]): Promise<IndexedDB> {
|
||||
const database = await IndexedDB.openDatabase(name, version, stores);
|
||||
return new IndexedDB(database, name);
|
||||
}
|
||||
|
||||
private static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise<IDBDatabase> {
|
||||
mark(`code/willOpenDatabase/${name}`);
|
||||
try {
|
||||
return await IndexedDB.doOpenDatabase(name, version, stores);
|
||||
} catch (err) {
|
||||
if (err instanceof MissingStoresError) {
|
||||
console.info(`Attempting to recreate the IndexedDB once.`, name);
|
||||
|
||||
try {
|
||||
// Try to delete the db
|
||||
await IndexedDB.deleteDatabase(err.db);
|
||||
} catch (error) {
|
||||
console.error(`Error while deleting the IndexedDB`, getErrorMessage(error));
|
||||
throw error;
|
||||
}
|
||||
|
||||
return await IndexedDB.doOpenDatabase(name, version, stores);
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
mark(`code/didOpenDatabase/${name}`);
|
||||
}
|
||||
}
|
||||
|
||||
private static doOpenDatabase(name: string, version: number | undefined, stores: string[]): Promise<IDBDatabase> {
|
||||
return new Promise((c, e) => {
|
||||
const request = indexedDB.open(name, version);
|
||||
request.onerror = () => e(request.error);
|
||||
request.onsuccess = () => {
|
||||
const db = request.result;
|
||||
for (const store of stores) {
|
||||
if (!db.objectStoreNames.contains(store)) {
|
||||
console.error(`Error while opening IndexedDB. Could not find '${store}'' object store`);
|
||||
e(new MissingStoresError(db));
|
||||
return;
|
||||
}
|
||||
}
|
||||
c(db);
|
||||
};
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result;
|
||||
for (const store of stores) {
|
||||
if (!db.objectStoreNames.contains(store)) {
|
||||
db.createObjectStore(store);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private static deleteDatabase(database: IDBDatabase): Promise<void> {
|
||||
return new Promise((c, e) => {
|
||||
// Close any opened connections
|
||||
database.close();
|
||||
|
||||
// Delete the db
|
||||
const deleteRequest = indexedDB.deleteDatabase(database.name);
|
||||
deleteRequest.onerror = (err) => e(deleteRequest.error);
|
||||
deleteRequest.onsuccess = () => c();
|
||||
});
|
||||
}
|
||||
|
||||
private database: IDBDatabase | null = null;
|
||||
private readonly pendingTransactions: IDBTransaction[] = [];
|
||||
|
||||
constructor(database: IDBDatabase, private readonly name: string) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
hasPendingTransactions(): boolean {
|
||||
return this.pendingTransactions.length > 0;
|
||||
}
|
||||
|
||||
close(): void {
|
||||
if (this.pendingTransactions.length) {
|
||||
this.pendingTransactions.splice(0, this.pendingTransactions.length).forEach(transaction => transaction.abort());
|
||||
}
|
||||
this.database?.close();
|
||||
this.database = null;
|
||||
}
|
||||
|
||||
runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T>[]): Promise<T[]>;
|
||||
runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T>): Promise<T>;
|
||||
async runInTransaction<T>(store: string, transactionMode: IDBTransactionMode, dbRequestFn: (store: IDBObjectStore) => IDBRequest<T> | IDBRequest<T>[]): Promise<T | T[]> {
|
||||
if (!this.database) {
|
||||
throw new DBClosedError(this.name);
|
||||
}
|
||||
const transaction = this.database.transaction(store, transactionMode);
|
||||
this.pendingTransactions.push(transaction);
|
||||
return new Promise<T | T[]>((c, e) => {
|
||||
transaction.oncomplete = () => {
|
||||
if (Array.isArray(request)) {
|
||||
c(request.map(r => r.result));
|
||||
} else {
|
||||
c(request.result);
|
||||
}
|
||||
};
|
||||
transaction.onerror = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error'));
|
||||
transaction.onabort = () => e(transaction.error ? ErrorNoTelemetry.fromError(transaction.error) : new ErrorNoTelemetry('unknown error'));
|
||||
const request = dbRequestFn(transaction.objectStore(store));
|
||||
}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));
|
||||
}
|
||||
|
||||
async getKeyValues<V>(store: string, isValid: (value: unknown) => value is V): Promise<Map<string, V>> {
|
||||
if (!this.database) {
|
||||
throw new DBClosedError(this.name);
|
||||
}
|
||||
const transaction = this.database.transaction(store, 'readonly');
|
||||
this.pendingTransactions.push(transaction);
|
||||
return new Promise<Map<string, V>>(resolve => {
|
||||
const items = new Map<string, V>();
|
||||
|
||||
const objectStore = transaction.objectStore(store);
|
||||
|
||||
// Open a IndexedDB Cursor to iterate over key/values
|
||||
const cursor = objectStore.openCursor();
|
||||
if (!cursor) {
|
||||
return resolve(items); // this means the `ItemTable` was empty
|
||||
}
|
||||
|
||||
// Iterate over rows of `ItemTable` until the end
|
||||
cursor.onsuccess = () => {
|
||||
if (cursor.result) {
|
||||
|
||||
// Keep cursor key/value in our map
|
||||
if (isValid(cursor.result.value)) {
|
||||
items.set(cursor.result.key.toString(), cursor.result.value);
|
||||
}
|
||||
|
||||
// Advance cursor to next row
|
||||
cursor.result.continue();
|
||||
} else {
|
||||
resolve(items); // reached end of table
|
||||
}
|
||||
};
|
||||
|
||||
// Error handlers
|
||||
const onError = (error: Error | null) => {
|
||||
console.error(`IndexedDB getKeyValues(): ${toErrorMessage(error, true)}`);
|
||||
|
||||
resolve(items);
|
||||
};
|
||||
cursor.onerror = () => onError(cursor.error);
|
||||
transaction.onerror = () => onError(transaction.error);
|
||||
}).finally(() => this.pendingTransactions.splice(this.pendingTransactions.indexOf(transaction), 1));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-aria-container {
|
||||
position: absolute; /* try to hide from window but not from screen readers */
|
||||
left:-999em;
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
// import 'vs/css!./aria';
|
||||
|
||||
// Use a max length since we are inserting the whole msg in the DOM and that can cause browsers to freeze for long messages #94233
|
||||
const MAX_MESSAGE_LENGTH = 20000;
|
||||
let ariaContainer: HTMLElement;
|
||||
let alertContainer: HTMLElement;
|
||||
let alertContainer2: HTMLElement;
|
||||
let statusContainer: HTMLElement;
|
||||
let statusContainer2: HTMLElement;
|
||||
export function setARIAContainer(parent: HTMLElement) {
|
||||
ariaContainer = document.createElement('div');
|
||||
ariaContainer.className = 'monaco-aria-container';
|
||||
|
||||
const createAlertContainer = () => {
|
||||
const element = document.createElement('div');
|
||||
element.className = 'monaco-alert';
|
||||
element.setAttribute('role', 'alert');
|
||||
element.setAttribute('aria-atomic', 'true');
|
||||
ariaContainer.appendChild(element);
|
||||
return element;
|
||||
};
|
||||
alertContainer = createAlertContainer();
|
||||
alertContainer2 = createAlertContainer();
|
||||
|
||||
const createStatusContainer = () => {
|
||||
const element = document.createElement('div');
|
||||
element.className = 'monaco-status';
|
||||
element.setAttribute('aria-live', 'polite');
|
||||
element.setAttribute('aria-atomic', 'true');
|
||||
ariaContainer.appendChild(element);
|
||||
return element;
|
||||
};
|
||||
statusContainer = createStatusContainer();
|
||||
statusContainer2 = createStatusContainer();
|
||||
|
||||
parent.appendChild(ariaContainer);
|
||||
}
|
||||
/**
|
||||
* Given the provided message, will make sure that it is read as alert to screen readers.
|
||||
*/
|
||||
export function alert(msg: string): void {
|
||||
if (!ariaContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use alternate containers such that duplicated messages get read out by screen readers #99466
|
||||
if (alertContainer.textContent !== msg) {
|
||||
dom.clearNode(alertContainer2);
|
||||
insertMessage(alertContainer, msg);
|
||||
} else {
|
||||
dom.clearNode(alertContainer);
|
||||
insertMessage(alertContainer2, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the provided message, will make sure that it is read as status to screen readers.
|
||||
*/
|
||||
export function status(msg: string): void {
|
||||
if (!ariaContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (statusContainer.textContent !== msg) {
|
||||
dom.clearNode(statusContainer2);
|
||||
insertMessage(statusContainer, msg);
|
||||
} else {
|
||||
dom.clearNode(statusContainer);
|
||||
insertMessage(statusContainer2, msg);
|
||||
}
|
||||
}
|
||||
|
||||
function insertMessage(target: HTMLElement, msg: string): void {
|
||||
dom.clearNode(target);
|
||||
if (msg.length > MAX_MESSAGE_LENGTH) {
|
||||
msg = msg.substr(0, MAX_MESSAGE_LENGTH);
|
||||
}
|
||||
target.textContent = msg;
|
||||
|
||||
// See https://www.paciellogroup.com/blog/2012/06/html5-accessibility-chops-aria-rolealert-browser-support/
|
||||
target.style.visibility = 'hidden';
|
||||
target.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
// Copied from @types/react which original came from https://www.w3.org/TR/wai-aria-1.1/#role_definitions
|
||||
export type AriaRole =
|
||||
| 'alert'
|
||||
| 'alertdialog'
|
||||
| 'application'
|
||||
| 'article'
|
||||
| 'banner'
|
||||
| 'button'
|
||||
| 'cell'
|
||||
| 'checkbox'
|
||||
| 'columnheader'
|
||||
| 'combobox'
|
||||
| 'complementary'
|
||||
| 'contentinfo'
|
||||
| 'definition'
|
||||
| 'dialog'
|
||||
| 'directory'
|
||||
| 'document'
|
||||
| 'feed'
|
||||
| 'figure'
|
||||
| 'form'
|
||||
| 'grid'
|
||||
| 'gridcell'
|
||||
| 'group'
|
||||
| 'heading'
|
||||
| 'img'
|
||||
| 'link'
|
||||
| 'list'
|
||||
| 'listbox'
|
||||
| 'listitem'
|
||||
| 'log'
|
||||
| 'main'
|
||||
| 'marquee'
|
||||
| 'math'
|
||||
| 'menu'
|
||||
| 'menubar'
|
||||
| 'menuitem'
|
||||
| 'menuitemcheckbox'
|
||||
| 'menuitemradio'
|
||||
| 'navigation'
|
||||
| 'none'
|
||||
| 'note'
|
||||
| 'option'
|
||||
| 'presentation'
|
||||
| 'progressbar'
|
||||
| 'radio'
|
||||
| 'radiogroup'
|
||||
| 'region'
|
||||
| 'row'
|
||||
| 'rowgroup'
|
||||
| 'rowheader'
|
||||
| 'scrollbar'
|
||||
| 'search'
|
||||
| 'searchbox'
|
||||
| 'separator'
|
||||
| 'slider'
|
||||
| 'spinbutton'
|
||||
| 'status'
|
||||
| 'switch'
|
||||
| 'tab'
|
||||
| 'table'
|
||||
| 'tablist'
|
||||
| 'tabpanel'
|
||||
| 'term'
|
||||
| 'textbox'
|
||||
| 'timer'
|
||||
| 'toolbar'
|
||||
| 'tooltip'
|
||||
| 'tree'
|
||||
| 'treegrid'
|
||||
| 'treeitem'
|
||||
| (string & {}) // Prevent type collapsing to `string`
|
||||
;
|
||||
@@ -1,36 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-breadcrumbs {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
outline-style: none;
|
||||
}
|
||||
|
||||
.monaco-breadcrumbs .monaco-breadcrumb-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 0 1 auto;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
align-self: center;
|
||||
height: 100%;
|
||||
outline: none;
|
||||
}
|
||||
.monaco-breadcrumbs.disabled .monaco-breadcrumb-item {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.monaco-breadcrumbs .monaco-breadcrumb-item .codicon-breadcrumb-separator {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.monaco-breadcrumbs .monaco-breadcrumb-item:first-of-type::before {
|
||||
content: ' ';
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
|
||||
import { commonPrefixLength } from 'vs/base/common/arrays';
|
||||
import { ThemeIcon } from 'vs/base/common/themables';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
// import 'vs/css!./breadcrumbsWidget';
|
||||
|
||||
export abstract class BreadcrumbsItem {
|
||||
abstract dispose(): void;
|
||||
abstract equals(other: BreadcrumbsItem): boolean;
|
||||
abstract render(container: HTMLElement): void;
|
||||
}
|
||||
|
||||
export interface IBreadcrumbsWidgetStyles {
|
||||
readonly breadcrumbsBackground: string | undefined;
|
||||
readonly breadcrumbsForeground: string | undefined;
|
||||
readonly breadcrumbsHoverForeground: string | undefined;
|
||||
readonly breadcrumbsFocusForeground: string | undefined;
|
||||
readonly breadcrumbsFocusAndSelectionForeground: string | undefined;
|
||||
}
|
||||
|
||||
export interface IBreadcrumbsItemEvent {
|
||||
type: 'select' | 'focus';
|
||||
item: BreadcrumbsItem;
|
||||
node: HTMLElement;
|
||||
payload: any;
|
||||
}
|
||||
|
||||
export class BreadcrumbsWidget {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
private readonly _domNode: HTMLDivElement;
|
||||
private readonly _scrollable: DomScrollableElement;
|
||||
|
||||
private readonly _onDidSelectItem = new Emitter<IBreadcrumbsItemEvent>();
|
||||
private readonly _onDidFocusItem = new Emitter<IBreadcrumbsItemEvent>();
|
||||
private readonly _onDidChangeFocus = new Emitter<boolean>();
|
||||
|
||||
readonly onDidSelectItem: Event<IBreadcrumbsItemEvent> = this._onDidSelectItem.event;
|
||||
readonly onDidFocusItem: Event<IBreadcrumbsItemEvent> = this._onDidFocusItem.event;
|
||||
readonly onDidChangeFocus: Event<boolean> = this._onDidChangeFocus.event;
|
||||
|
||||
private readonly _items = new Array<BreadcrumbsItem>();
|
||||
private readonly _nodes = new Array<HTMLDivElement>();
|
||||
private readonly _freeNodes = new Array<HTMLDivElement>();
|
||||
private readonly _separatorIcon: ThemeIcon;
|
||||
|
||||
private _enabled: boolean = true;
|
||||
private _focusedItemIdx: number = -1;
|
||||
private _selectedItemIdx: number = -1;
|
||||
|
||||
private _pendingDimLayout: IDisposable | undefined;
|
||||
private _pendingLayout: IDisposable | undefined;
|
||||
private _dimension: dom.Dimension | undefined;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
horizontalScrollbarSize: number,
|
||||
separatorIcon: ThemeIcon,
|
||||
styles: IBreadcrumbsWidgetStyles
|
||||
) {
|
||||
this._domNode = document.createElement('div');
|
||||
this._domNode.className = 'monaco-breadcrumbs';
|
||||
this._domNode.tabIndex = 0;
|
||||
this._domNode.setAttribute('role', 'list');
|
||||
this._scrollable = new DomScrollableElement(this._domNode, {
|
||||
vertical: ScrollbarVisibility.Hidden,
|
||||
horizontal: ScrollbarVisibility.Auto,
|
||||
horizontalScrollbarSize,
|
||||
useShadows: false,
|
||||
scrollYToX: true
|
||||
});
|
||||
this._separatorIcon = separatorIcon;
|
||||
this._disposables.add(this._scrollable);
|
||||
this._disposables.add(dom.addStandardDisposableListener(this._domNode, 'click', e => this._onClick(e)));
|
||||
container.appendChild(this._scrollable.getDomNode());
|
||||
|
||||
const styleElement = dom.createStyleSheet(this._domNode);
|
||||
this._style(styleElement, styles);
|
||||
|
||||
const focusTracker = dom.trackFocus(this._domNode);
|
||||
this._disposables.add(focusTracker);
|
||||
this._disposables.add(focusTracker.onDidBlur(_ => this._onDidChangeFocus.fire(false)));
|
||||
this._disposables.add(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true)));
|
||||
}
|
||||
|
||||
setHorizontalScrollbarSize(size: number) {
|
||||
this._scrollable.updateOptions({
|
||||
horizontalScrollbarSize: size
|
||||
});
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._disposables.dispose();
|
||||
this._pendingLayout?.dispose();
|
||||
this._pendingDimLayout?.dispose();
|
||||
this._onDidSelectItem.dispose();
|
||||
this._onDidFocusItem.dispose();
|
||||
this._onDidChangeFocus.dispose();
|
||||
this._domNode.remove();
|
||||
this._nodes.length = 0;
|
||||
this._freeNodes.length = 0;
|
||||
}
|
||||
|
||||
layout(dim: dom.Dimension | undefined): void {
|
||||
if (dim && dom.Dimension.equals(dim, this._dimension)) {
|
||||
return;
|
||||
}
|
||||
if (dim) {
|
||||
// only measure
|
||||
this._pendingDimLayout?.dispose();
|
||||
this._pendingDimLayout = this._updateDimensions(dim);
|
||||
} else {
|
||||
this._pendingLayout?.dispose();
|
||||
this._pendingLayout = this._updateScrollbar();
|
||||
}
|
||||
}
|
||||
|
||||
private _updateDimensions(dim: dom.Dimension): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
disposables.add(dom.modify(dom.getWindow(this._domNode), () => {
|
||||
this._dimension = dim;
|
||||
this._domNode.style.width = `${dim.width}px`;
|
||||
this._domNode.style.height = `${dim.height}px`;
|
||||
disposables.add(this._updateScrollbar());
|
||||
}));
|
||||
return disposables;
|
||||
}
|
||||
|
||||
private _updateScrollbar(): IDisposable {
|
||||
return dom.measure(dom.getWindow(this._domNode), () => {
|
||||
dom.measure(dom.getWindow(this._domNode), () => { // double RAF
|
||||
this._scrollable.setRevealOnScroll(false);
|
||||
this._scrollable.scanDomNode();
|
||||
this._scrollable.setRevealOnScroll(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private _style(styleElement: HTMLStyleElement, style: IBreadcrumbsWidgetStyles): void {
|
||||
let content = '';
|
||||
if (style.breadcrumbsBackground) {
|
||||
content += `.monaco-breadcrumbs { background-color: ${style.breadcrumbsBackground}}`;
|
||||
}
|
||||
if (style.breadcrumbsForeground) {
|
||||
content += `.monaco-breadcrumbs .monaco-breadcrumb-item { color: ${style.breadcrumbsForeground}}\n`;
|
||||
}
|
||||
if (style.breadcrumbsFocusForeground) {
|
||||
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused { color: ${style.breadcrumbsFocusForeground}}\n`;
|
||||
}
|
||||
if (style.breadcrumbsFocusAndSelectionForeground) {
|
||||
content += `.monaco-breadcrumbs .monaco-breadcrumb-item.focused.selected { color: ${style.breadcrumbsFocusAndSelectionForeground}}\n`;
|
||||
}
|
||||
if (style.breadcrumbsHoverForeground) {
|
||||
content += `.monaco-breadcrumbs:not(.disabled ) .monaco-breadcrumb-item:hover:not(.focused):not(.selected) { color: ${style.breadcrumbsHoverForeground}}\n`;
|
||||
}
|
||||
styleElement.innerText = content;
|
||||
}
|
||||
|
||||
setEnabled(value: boolean) {
|
||||
this._enabled = value;
|
||||
this._domNode.classList.toggle('disabled', !this._enabled);
|
||||
}
|
||||
|
||||
domFocus(): void {
|
||||
const idx = this._focusedItemIdx >= 0 ? this._focusedItemIdx : this._items.length - 1;
|
||||
if (idx >= 0 && idx < this._items.length) {
|
||||
this._focus(idx, undefined);
|
||||
} else {
|
||||
this._domNode.focus();
|
||||
}
|
||||
}
|
||||
|
||||
isDOMFocused(): boolean {
|
||||
return dom.isAncestorOfActiveElement(this._domNode);
|
||||
}
|
||||
|
||||
getFocused(): BreadcrumbsItem {
|
||||
return this._items[this._focusedItemIdx];
|
||||
}
|
||||
|
||||
setFocused(item: BreadcrumbsItem | undefined, payload?: any): void {
|
||||
this._focus(this._items.indexOf(item!), payload);
|
||||
}
|
||||
|
||||
focusPrev(payload?: any): any {
|
||||
if (this._focusedItemIdx > 0) {
|
||||
this._focus(this._focusedItemIdx - 1, payload);
|
||||
}
|
||||
}
|
||||
|
||||
focusNext(payload?: any): any {
|
||||
if (this._focusedItemIdx + 1 < this._nodes.length) {
|
||||
this._focus(this._focusedItemIdx + 1, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private _focus(nth: number, payload: any): void {
|
||||
this._focusedItemIdx = -1;
|
||||
for (let i = 0; i < this._nodes.length; i++) {
|
||||
const node = this._nodes[i];
|
||||
if (i !== nth) {
|
||||
node.classList.remove('focused');
|
||||
} else {
|
||||
this._focusedItemIdx = i;
|
||||
node.classList.add('focused');
|
||||
node.focus();
|
||||
}
|
||||
}
|
||||
this._reveal(this._focusedItemIdx, true);
|
||||
this._onDidFocusItem.fire({ type: 'focus', item: this._items[this._focusedItemIdx], node: this._nodes[this._focusedItemIdx], payload });
|
||||
}
|
||||
|
||||
reveal(item: BreadcrumbsItem): void {
|
||||
const idx = this._items.indexOf(item);
|
||||
if (idx >= 0) {
|
||||
this._reveal(idx, false);
|
||||
}
|
||||
}
|
||||
|
||||
revealLast(): void {
|
||||
this._reveal(this._items.length - 1, false);
|
||||
}
|
||||
|
||||
private _reveal(nth: number, minimal: boolean): void {
|
||||
if (nth < 0 || nth >= this._nodes.length) {
|
||||
return;
|
||||
}
|
||||
const node = this._nodes[nth];
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const { width } = this._scrollable.getScrollDimensions();
|
||||
const { scrollLeft } = this._scrollable.getScrollPosition();
|
||||
if (!minimal || node.offsetLeft > scrollLeft + width || node.offsetLeft < scrollLeft) {
|
||||
this._scrollable.setRevealOnScroll(false);
|
||||
this._scrollable.setScrollPosition({ scrollLeft: node.offsetLeft });
|
||||
this._scrollable.setRevealOnScroll(true);
|
||||
}
|
||||
}
|
||||
|
||||
getSelection(): BreadcrumbsItem {
|
||||
return this._items[this._selectedItemIdx];
|
||||
}
|
||||
|
||||
setSelection(item: BreadcrumbsItem | undefined, payload?: any): void {
|
||||
this._select(this._items.indexOf(item!), payload);
|
||||
}
|
||||
|
||||
private _select(nth: number, payload: any): void {
|
||||
this._selectedItemIdx = -1;
|
||||
for (let i = 0; i < this._nodes.length; i++) {
|
||||
const node = this._nodes[i];
|
||||
if (i !== nth) {
|
||||
node.classList.remove('selected');
|
||||
} else {
|
||||
this._selectedItemIdx = i;
|
||||
node.classList.add('selected');
|
||||
}
|
||||
}
|
||||
this._onDidSelectItem.fire({ type: 'select', item: this._items[this._selectedItemIdx], node: this._nodes[this._selectedItemIdx], payload });
|
||||
}
|
||||
|
||||
getItems(): readonly BreadcrumbsItem[] {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
setItems(items: BreadcrumbsItem[]): void {
|
||||
let prefix: number | undefined;
|
||||
let removed: BreadcrumbsItem[] = [];
|
||||
try {
|
||||
prefix = commonPrefixLength(this._items, items, (a, b) => a.equals(b));
|
||||
removed = this._items.splice(prefix, this._items.length - prefix, ...items.slice(prefix));
|
||||
this._render(prefix);
|
||||
dispose(removed);
|
||||
this._focus(-1, undefined);
|
||||
} catch (e) {
|
||||
const newError = new Error(`BreadcrumbsItem#setItems: newItems: ${items.length}, prefix: ${prefix}, removed: ${removed.length}`);
|
||||
newError.name = e.name;
|
||||
newError.stack = e.stack;
|
||||
throw newError;
|
||||
}
|
||||
}
|
||||
|
||||
private _render(start: number): void {
|
||||
let didChange = false;
|
||||
for (; start < this._items.length && start < this._nodes.length; start++) {
|
||||
const item = this._items[start];
|
||||
const node = this._nodes[start];
|
||||
this._renderItem(item, node);
|
||||
didChange = true;
|
||||
}
|
||||
// case a: more nodes -> remove them
|
||||
while (start < this._nodes.length) {
|
||||
const free = this._nodes.pop();
|
||||
if (free) {
|
||||
this._freeNodes.push(free);
|
||||
free.remove();
|
||||
didChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
// case b: more items -> render them
|
||||
for (; start < this._items.length; start++) {
|
||||
const item = this._items[start];
|
||||
const node = this._freeNodes.length > 0 ? this._freeNodes.pop() : document.createElement('div');
|
||||
if (node) {
|
||||
this._renderItem(item, node);
|
||||
this._domNode.appendChild(node);
|
||||
this._nodes.push(node);
|
||||
didChange = true;
|
||||
}
|
||||
}
|
||||
if (didChange) {
|
||||
this.layout(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private _renderItem(item: BreadcrumbsItem, container: HTMLDivElement): void {
|
||||
dom.clearNode(container);
|
||||
container.className = '';
|
||||
try {
|
||||
item.render(container);
|
||||
} catch (err) {
|
||||
container.innerText = '<<RENDER ERROR>>';
|
||||
console.error(err);
|
||||
}
|
||||
container.tabIndex = -1;
|
||||
container.setAttribute('role', 'listitem');
|
||||
container.classList.add('monaco-breadcrumb-item');
|
||||
const iconContainer = dom.$(ThemeIcon.asCSSSelector(this._separatorIcon));
|
||||
container.appendChild(iconContainer);
|
||||
}
|
||||
|
||||
private _onClick(event: IMouseEvent): void {
|
||||
if (!this._enabled) {
|
||||
return;
|
||||
}
|
||||
for (let el: HTMLElement | null = event.target; el; el = el.parentElement) {
|
||||
const idx = this._nodes.indexOf(el as HTMLDivElement);
|
||||
if (idx >= 0) {
|
||||
this._focus(idx, event);
|
||||
this._select(idx, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { $, IDomNodePagePosition } from 'vs/base/browser/dom';
|
||||
import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid';
|
||||
import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash';
|
||||
import { DistributeSizing, ISplitViewStyles, IView as ISplitViewView, Orientation, SplitView } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface CenteredViewState {
|
||||
// width of the fixed centered layout
|
||||
targetWidth: number;
|
||||
// proportional size of left margin
|
||||
leftMarginRatio: number;
|
||||
// proportional size of right margin
|
||||
rightMarginRatio: number;
|
||||
}
|
||||
|
||||
const defaultState: CenteredViewState = {
|
||||
targetWidth: 900,
|
||||
leftMarginRatio: 0.1909,
|
||||
rightMarginRatio: 0.1909,
|
||||
};
|
||||
|
||||
const distributeSizing: DistributeSizing = { type: 'distribute' };
|
||||
|
||||
function createEmptyView(background: Color | undefined): ISplitViewView<{ top: number; left: number }> {
|
||||
const element = $('.centered-layout-margin');
|
||||
element.style.height = '100%';
|
||||
if (background) {
|
||||
element.style.backgroundColor = background.toString();
|
||||
}
|
||||
|
||||
return {
|
||||
element,
|
||||
layout: () => undefined,
|
||||
minimumSize: 60,
|
||||
maximumSize: Number.POSITIVE_INFINITY,
|
||||
onDidChange: Event.None
|
||||
};
|
||||
}
|
||||
|
||||
function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView<{ top: number; left: number }> {
|
||||
return {
|
||||
element: view.element,
|
||||
get maximumSize() { return view.maximumWidth; },
|
||||
get minimumSize() { return view.minimumWidth; },
|
||||
onDidChange: Event.map(view.onDidChange, e => e && e.width),
|
||||
layout: (size, offset, ctx) => view.layout(size, getHeight(), ctx?.top ?? 0, (ctx?.left ?? 0) + offset)
|
||||
};
|
||||
}
|
||||
|
||||
export interface ICenteredViewStyles extends ISplitViewStyles {
|
||||
background: Color;
|
||||
}
|
||||
|
||||
export class CenteredViewLayout implements IDisposable {
|
||||
|
||||
private splitView?: SplitView<{ top: number; left: number }>;
|
||||
private lastLayoutPosition: IDomNodePagePosition = { width: 0, height: 0, left: 0, top: 0 };
|
||||
private style!: ICenteredViewStyles;
|
||||
private didLayout = false;
|
||||
private emptyViews: ISplitViewView<{ top: number; left: number }>[] | undefined;
|
||||
private readonly splitViewDisposables = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
private container: HTMLElement,
|
||||
private view: IView,
|
||||
public state: CenteredViewState = { ...defaultState },
|
||||
private centeredLayoutFixedWidth: boolean = false
|
||||
) {
|
||||
this.container.appendChild(this.view.element);
|
||||
// Make sure to hide the split view overflow like sashes #52892
|
||||
this.container.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
get minimumWidth(): number { return this.splitView ? this.splitView.minimumSize : this.view.minimumWidth; }
|
||||
get maximumWidth(): number { return this.splitView ? this.splitView.maximumSize : this.view.maximumWidth; }
|
||||
get minimumHeight(): number { return this.view.minimumHeight; }
|
||||
get maximumHeight(): number { return this.view.maximumHeight; }
|
||||
get onDidChange(): Event<IViewSize | undefined> { return this.view.onDidChange; }
|
||||
|
||||
private _boundarySashes: IBoundarySashes = {};
|
||||
get boundarySashes(): IBoundarySashes { return this._boundarySashes; }
|
||||
set boundarySashes(boundarySashes: IBoundarySashes) {
|
||||
this._boundarySashes = boundarySashes;
|
||||
|
||||
if (!this.splitView) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.splitView.orthogonalStartSash = boundarySashes.top;
|
||||
this.splitView.orthogonalEndSash = boundarySashes.bottom;
|
||||
}
|
||||
|
||||
layout(width: number, height: number, top: number, left: number): void {
|
||||
this.lastLayoutPosition = { width, height, top, left };
|
||||
if (this.splitView) {
|
||||
this.splitView.layout(width, this.lastLayoutPosition);
|
||||
if (!this.didLayout || this.centeredLayoutFixedWidth) {
|
||||
this.resizeSplitViews();
|
||||
}
|
||||
} else {
|
||||
this.view.layout(width, height, top, left);
|
||||
}
|
||||
|
||||
this.didLayout = true;
|
||||
}
|
||||
|
||||
private resizeSplitViews(): void {
|
||||
if (!this.splitView) {
|
||||
return;
|
||||
}
|
||||
if (this.centeredLayoutFixedWidth) {
|
||||
const centerViewWidth = Math.min(this.lastLayoutPosition.width, this.state.targetWidth);
|
||||
const marginWidthFloat = (this.lastLayoutPosition.width - centerViewWidth) / 2;
|
||||
this.splitView.resizeView(0, Math.floor(marginWidthFloat));
|
||||
this.splitView.resizeView(1, centerViewWidth);
|
||||
this.splitView.resizeView(2, Math.ceil(marginWidthFloat));
|
||||
} else {
|
||||
const leftMargin = this.state.leftMarginRatio * this.lastLayoutPosition.width;
|
||||
const rightMargin = this.state.rightMarginRatio * this.lastLayoutPosition.width;
|
||||
const center = this.lastLayoutPosition.width - leftMargin - rightMargin;
|
||||
this.splitView.resizeView(0, leftMargin);
|
||||
this.splitView.resizeView(1, center);
|
||||
this.splitView.resizeView(2, rightMargin);
|
||||
}
|
||||
}
|
||||
|
||||
setFixedWidth(option: boolean) {
|
||||
this.centeredLayoutFixedWidth = option;
|
||||
if (!!this.splitView) {
|
||||
this.updateState();
|
||||
this.resizeSplitViews();
|
||||
}
|
||||
}
|
||||
|
||||
private updateState() {
|
||||
if (!!this.splitView) {
|
||||
this.state.targetWidth = this.splitView.getViewSize(1);
|
||||
this.state.leftMarginRatio = this.splitView.getViewSize(0) / this.lastLayoutPosition.width;
|
||||
this.state.rightMarginRatio = this.splitView.getViewSize(2) / this.lastLayoutPosition.width;
|
||||
}
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return !!this.splitView;
|
||||
}
|
||||
|
||||
styles(style: ICenteredViewStyles): void {
|
||||
this.style = style;
|
||||
if (this.splitView && this.emptyViews) {
|
||||
this.splitView.style(this.style);
|
||||
this.emptyViews[0].element.style.backgroundColor = this.style.background.toString();
|
||||
this.emptyViews[1].element.style.backgroundColor = this.style.background.toString();
|
||||
}
|
||||
}
|
||||
|
||||
activate(active: boolean): void {
|
||||
if (active === this.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
this.view.element.remove();
|
||||
this.splitView = new SplitView(this.container, {
|
||||
inverseAltBehavior: true,
|
||||
orientation: Orientation.HORIZONTAL,
|
||||
styles: this.style
|
||||
});
|
||||
this.splitView.orthogonalStartSash = this.boundarySashes.top;
|
||||
this.splitView.orthogonalEndSash = this.boundarySashes.bottom;
|
||||
|
||||
this.splitViewDisposables.add(this.splitView.onDidSashChange(() => {
|
||||
if (!!this.splitView) {
|
||||
this.updateState();
|
||||
}
|
||||
}));
|
||||
this.splitViewDisposables.add(this.splitView.onDidSashReset(() => {
|
||||
this.state = { ...defaultState };
|
||||
this.resizeSplitViews();
|
||||
}));
|
||||
|
||||
this.splitView.layout(this.lastLayoutPosition.width, this.lastLayoutPosition);
|
||||
const backgroundColor = this.style ? this.style.background : undefined;
|
||||
this.emptyViews = [createEmptyView(backgroundColor), createEmptyView(backgroundColor)];
|
||||
|
||||
this.splitView.addView(this.emptyViews[0], distributeSizing, 0);
|
||||
this.splitView.addView(toSplitViewView(this.view, () => this.lastLayoutPosition.height), distributeSizing, 1);
|
||||
this.splitView.addView(this.emptyViews[1], distributeSizing, 2);
|
||||
|
||||
this.resizeSplitViews();
|
||||
} else {
|
||||
this.splitView?.el.remove();
|
||||
this.splitViewDisposables.clear();
|
||||
this.splitView?.dispose();
|
||||
this.splitView = undefined;
|
||||
this.emptyViews = undefined;
|
||||
this.container.appendChild(this.view.element);
|
||||
this.view.layout(this.lastLayoutPosition.width, this.lastLayoutPosition.height, this.lastLayoutPosition.top, this.lastLayoutPosition.left);
|
||||
}
|
||||
}
|
||||
|
||||
isDefault(state: CenteredViewState): boolean {
|
||||
if (this.centeredLayoutFixedWidth) {
|
||||
return state.targetWidth === defaultState.targetWidth;
|
||||
} else {
|
||||
return state.leftMarginRatio === defaultState.leftMarginRatio
|
||||
&& state.rightMarginRatio === defaultState.rightMarginRatio;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.splitViewDisposables.dispose();
|
||||
|
||||
if (this.splitView) {
|
||||
this.splitView.dispose();
|
||||
this.splitView = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.codicon-wrench-subaction {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@keyframes codicon-spin {
|
||||
100% {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.codicon-sync.codicon-modifier-spin,
|
||||
.codicon-loading.codicon-modifier-spin,
|
||||
.codicon-gear.codicon-modifier-spin,
|
||||
.codicon-notebook-state-executing.codicon-modifier-spin {
|
||||
/* Use steps to throttle FPS to reduce CPU usage */
|
||||
animation: codicon-spin 1.5s steps(30) infinite;
|
||||
}
|
||||
|
||||
.codicon-modifier-disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
/* custom speed & easing for loading icon */
|
||||
.codicon-loading,
|
||||
.codicon-tree-item-loading::before {
|
||||
animation-duration: 1s !important;
|
||||
animation-timing-function: cubic-bezier(0.53, 0.21, 0.29, 0.67) !important;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
font-display: block;
|
||||
src: url("./codicon.ttf?5d4d76ab2ce5108968ad644d591a16a6") format("truetype");
|
||||
}
|
||||
|
||||
.codicon[class*='codicon-'] {
|
||||
font: normal normal normal 16px/1 codicon;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
text-align: center;
|
||||
text-transform: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
/* icon rules are dynamically created by the platform theme service (see iconsStyleSheet.ts) */
|
||||
@@ -1,7 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// import 'vs/css!./codicon/codicon';
|
||||
// import 'vs/css!./codicon/codicon-modifiers';
|
||||
@@ -1,24 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-count-badge {
|
||||
padding: 3px 6px;
|
||||
border-radius: 11px;
|
||||
font-size: 11px;
|
||||
min-width: 18px;
|
||||
min-height: 18px;
|
||||
line-height: 11px;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monaco-count-badge.long {
|
||||
padding: 2px 3px;
|
||||
border-radius: 2px;
|
||||
min-height: auto;
|
||||
line-height: normal;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user