Implement fs module (#3)
* Implements the fs module * Add stats object * Add not implemented to createWriteStream * Update mkdtemp to use tmp dir * Unexport Stats * Add client web socket for commands and restructure
This commit is contained in:
@ -1,14 +1,15 @@
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { field, logger, time, Time } from "@coder/logger";
|
||||
import { escapePath } from "@coder/node-browser";
|
||||
import { escapePath } from "@coder/server";
|
||||
import { retry } from "./retry";
|
||||
|
||||
export interface IClientOptions {
|
||||
mkDirs?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Client represents a general abstraction of an IDE client.
|
||||
* A general abstraction of an IDE client.
|
||||
*
|
||||
* Everything the client provides is asynchronous so you can wait on what
|
||||
* you need from it without blocking anything else.
|
||||
@ -36,6 +37,11 @@ export class Client {
|
||||
await promisify(exec)(`mkdir -p ${options.mkDirs.map(escapePath).join(" ")}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Prevent Firefox from trying to reconnect when the page unloads.
|
||||
window.addEventListener("unload", () => {
|
||||
retry.block();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
4
packages/ide/src/fill/child_process.ts
Normal file
4
packages/ide/src/fill/child_process.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { CP } from "@coder/server";
|
||||
import { client } from "./client";
|
||||
|
||||
export = new CP(client);
|
157
packages/ide/src/fill/client.ts
Normal file
157
packages/ide/src/fill/client.ts
Normal file
@ -0,0 +1,157 @@
|
||||
import { Emitter } from "@coder/events";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { Client, ReadWriteConnection } from "@coder/server";
|
||||
import { retry } from "../retry";
|
||||
|
||||
/**
|
||||
* A connection based on a web socket. Automatically reconnects and buffers
|
||||
* messages during connection.
|
||||
*/
|
||||
class Connection implements ReadWriteConnection {
|
||||
|
||||
private activeSocket: WebSocket | undefined;
|
||||
private readonly messageEmitter: Emitter<Uint8Array>;
|
||||
private readonly closeEmitter: Emitter<void>;
|
||||
private readonly upEmitter: Emitter<void>;
|
||||
private readonly downEmitter: Emitter<void>;
|
||||
private readonly messageBuffer: Uint8Array[];
|
||||
private socketTimeoutDelay = 60 * 1000;
|
||||
private retryName = "Web socket";
|
||||
private isUp: boolean | undefined;
|
||||
private closed: boolean | undefined;
|
||||
|
||||
public constructor() {
|
||||
this.messageEmitter = new Emitter();
|
||||
this.closeEmitter = new Emitter();
|
||||
this.upEmitter = new Emitter();
|
||||
this.downEmitter = new Emitter();
|
||||
this.messageBuffer = [];
|
||||
retry.register(this.retryName, () => this.connect());
|
||||
this.connect().catch(() => {
|
||||
retry.block(this.retryName);
|
||||
retry.run(this.retryName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when the connection goes up.
|
||||
*/
|
||||
public onUp(cb: () => void): void {
|
||||
this.upEmitter.event(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when the connection goes down.
|
||||
*/
|
||||
public onDown(cb: () => void): void {
|
||||
this.downEmitter.event(cb);
|
||||
}
|
||||
|
||||
public send(data: Buffer | Uint8Array): void {
|
||||
if (this.closed) {
|
||||
throw new Error("web socket is closed");
|
||||
}
|
||||
if (!this.activeSocket || this.activeSocket.readyState !== this.activeSocket.OPEN) {
|
||||
this.messageBuffer.push(data);
|
||||
} else {
|
||||
this.activeSocket.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
public onMessage(cb: (data: Uint8Array | Buffer) => void): void {
|
||||
this.messageEmitter.event(cb);
|
||||
}
|
||||
|
||||
public onClose(cb: () => void): void {
|
||||
this.closeEmitter.event(cb);
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.closed = true;
|
||||
this.dispose();
|
||||
this.closeEmitter.emit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the server.
|
||||
*/
|
||||
private async connect(): Promise<void> {
|
||||
const socket = await this.openSocket();
|
||||
|
||||
socket.addEventListener("message", (event: MessageEvent) => {
|
||||
this.messageEmitter.emit(event.data);
|
||||
});
|
||||
|
||||
socket.addEventListener("close", (event) => {
|
||||
if (this.isUp) {
|
||||
this.isUp = false;
|
||||
this.downEmitter.emit(undefined);
|
||||
}
|
||||
logger.warn(
|
||||
"Web socket closed",
|
||||
field("code", event.code),
|
||||
field("reason", event.reason),
|
||||
field("wasClean", event.wasClean),
|
||||
);
|
||||
if (!this.closed) {
|
||||
retry.block(this.retryName);
|
||||
retry.run(this.retryName);
|
||||
}
|
||||
});
|
||||
|
||||
// Send any messages that were queued while we were waiting to connect.
|
||||
while (this.messageBuffer.length > 0) {
|
||||
socket.send(this.messageBuffer.shift()!);
|
||||
}
|
||||
|
||||
if (!this.isUp) {
|
||||
this.isUp = true;
|
||||
this.upEmitter.emit(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a web socket, disposing the previous connection if any.
|
||||
*/
|
||||
private async openSocket(): Promise<WebSocket> {
|
||||
this.dispose();
|
||||
const socket = new WebSocket("websocket");
|
||||
socket.binaryType = "arraybuffer";
|
||||
this.activeSocket = socket;
|
||||
|
||||
const socketWaitTimeout = window.setTimeout(() => {
|
||||
socket.close();
|
||||
}, this.socketTimeoutDelay);
|
||||
|
||||
await new Promise((resolve, reject): void => {
|
||||
const onClose = (): void => {
|
||||
clearTimeout(socketWaitTimeout);
|
||||
socket.removeEventListener("close", onClose);
|
||||
reject();
|
||||
};
|
||||
socket.addEventListener("close", onClose);
|
||||
|
||||
socket.addEventListener("open", async () => {
|
||||
clearTimeout(socketWaitTimeout);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the current connection.
|
||||
*/
|
||||
private dispose(): void {
|
||||
if (this.activeSocket) {
|
||||
this.activeSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A client for proxying Node APIs based on web sockets.
|
||||
*/
|
||||
export const client = new Client(new Connection());
|
83
packages/ide/src/fill/dialog.scss
Normal file
83
packages/ide/src/fill/dialog.scss
Normal file
@ -0,0 +1,83 @@
|
||||
.msgbox {
|
||||
padding-top: 25px;
|
||||
padding-left: 40px;
|
||||
padding-right: 40px;
|
||||
padding-bottom: 25px;
|
||||
background: #242424;
|
||||
-webkit-box-shadow: 0px 0px 10px -3px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: 0px 0px 10px -3px rgba(0,0,0,0.75);
|
||||
box-shadow: 0px 0px 10px -3px rgba(0,0,0,0.75);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.msgbox.input {
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.msgbox > .input {
|
||||
background: #141414;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 25px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.msgbox > .msg {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.msgbox > .detail {
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.msgbox > .errors {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.msgbox > .errors {
|
||||
color: #f44747;
|
||||
}
|
||||
|
||||
.msgbox > .button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.msgbox > .button-wrapper > button {
|
||||
flex: 1;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
background: #3d3d3d;
|
||||
border: 0px;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.msgbox > .button-wrapper > button:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.msgbox > .button-wrapper > button:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.msgbox-overlay {
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition: 300ms opacity ease;
|
||||
z-index: 15;
|
||||
}
|
193
packages/ide/src/fill/dialog.ts
Normal file
193
packages/ide/src/fill/dialog.ts
Normal file
@ -0,0 +1,193 @@
|
||||
import { IDisposable } from "@coder/disposable";
|
||||
import { Emitter } from "@coder/events";
|
||||
|
||||
import "./dialog.scss";
|
||||
|
||||
/**
|
||||
* Dialog options.
|
||||
*/
|
||||
export interface IDialogOptions {
|
||||
message?: string;
|
||||
detail?: string;
|
||||
buttons?: string[];
|
||||
input?: {
|
||||
value: string;
|
||||
selection?: {
|
||||
start: number;
|
||||
end: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface IDialogAction {
|
||||
buttonIndex?: number;
|
||||
key?: IKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pressed keys.
|
||||
*/
|
||||
export enum IKey {
|
||||
Enter = "Enter",
|
||||
Escape = "Escape",
|
||||
}
|
||||
|
||||
export class Dialog {
|
||||
|
||||
private options: IDialogOptions;
|
||||
private overlay: HTMLElement;
|
||||
private cachedActiveElement: HTMLElement | undefined;
|
||||
private input: HTMLInputElement | undefined;
|
||||
private actionEmitter: Emitter<IDialogAction>;
|
||||
private errors: HTMLElement;
|
||||
private buttons: HTMLElement[] | undefined;
|
||||
|
||||
public constructor(options: IDialogOptions) {
|
||||
this.options = options;
|
||||
|
||||
this.actionEmitter = new Emitter();
|
||||
|
||||
const msgBox = document.createElement("div");
|
||||
msgBox.classList.add("msgbox");
|
||||
|
||||
if (this.options.message) {
|
||||
const messageDiv = document.createElement("div");
|
||||
messageDiv.classList.add("msg");
|
||||
messageDiv.innerText = this.options.message;
|
||||
msgBox.appendChild(messageDiv);
|
||||
}
|
||||
|
||||
if (this.options.detail) {
|
||||
const detailDiv = document.createElement("div");
|
||||
detailDiv.classList.add("detail");
|
||||
detailDiv.innerText = this.options.detail;
|
||||
msgBox.appendChild(detailDiv);
|
||||
}
|
||||
|
||||
if (this.options.input) {
|
||||
msgBox.classList.add("input");
|
||||
this.input = document.createElement("input");
|
||||
this.input.classList.add("input");
|
||||
this.input.value = this.options.input.value;
|
||||
this.input.addEventListener("keydown", (event) => {
|
||||
if (event.key === IKey.Enter) {
|
||||
event.preventDefault();
|
||||
this.actionEmitter.emit({
|
||||
buttonIndex: undefined,
|
||||
key: IKey.Enter,
|
||||
});
|
||||
}
|
||||
});
|
||||
msgBox.appendChild(this.input);
|
||||
}
|
||||
|
||||
this.errors = document.createElement("div");
|
||||
this.errors.classList.add("errors");
|
||||
msgBox.appendChild(this.errors);
|
||||
|
||||
if (this.options.buttons && this.options.buttons.length > 0) {
|
||||
this.buttons = this.options.buttons.map((buttonText, buttonIndex) => {
|
||||
const button = document.createElement("button");
|
||||
button.innerText = buttonText;
|
||||
button.addEventListener("click", () => {
|
||||
this.actionEmitter.emit({
|
||||
buttonIndex,
|
||||
key: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
return button;
|
||||
});
|
||||
|
||||
const buttonWrapper = document.createElement("div");
|
||||
buttonWrapper.classList.add("button-wrapper");
|
||||
this.buttons.forEach((b) => buttonWrapper.appendChild(b));
|
||||
msgBox.appendChild(buttonWrapper);
|
||||
}
|
||||
|
||||
this.overlay = document.createElement("div");
|
||||
this.overlay.className = "msgbox-overlay";
|
||||
this.overlay.appendChild(msgBox);
|
||||
|
||||
setTimeout(() => {
|
||||
this.overlay.style.opacity = "1";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a function to be called when the user performs an action.
|
||||
*/
|
||||
public onAction(callback: (action: IDialogAction) => void): IDisposable {
|
||||
return this.actionEmitter.event(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Input value if this dialog has an input.
|
||||
*/
|
||||
public get inputValue(): string | undefined {
|
||||
return this.input ? this.input.value : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display or remove an error.
|
||||
*/
|
||||
public set error(error: string) {
|
||||
while (this.errors.lastChild) {
|
||||
this.errors.removeChild(this.errors.lastChild);
|
||||
}
|
||||
if (error) {
|
||||
const errorDiv = document.createElement("error");
|
||||
errorDiv.innerText = error;
|
||||
this.errors.appendChild(errorDiv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the dialog.
|
||||
*/
|
||||
public show(): void {
|
||||
if (!this.cachedActiveElement) {
|
||||
this.cachedActiveElement = document.activeElement as HTMLElement;
|
||||
document.body.appendChild(this.overlay);
|
||||
document.addEventListener("keydown", this.onKeydown);
|
||||
if (this.input) {
|
||||
this.input.focus();
|
||||
if (this.options.input && this.options.input.selection) {
|
||||
this.input.setSelectionRange(
|
||||
this.options.input.selection.start,
|
||||
this.options.input.selection.end,
|
||||
);
|
||||
}
|
||||
} else if (this.buttons) {
|
||||
this.buttons[0].focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the dialog and clean up.
|
||||
*/
|
||||
public hide(): void {
|
||||
if (this.cachedActiveElement) {
|
||||
this.overlay.remove();
|
||||
document.removeEventListener("keydown", this.onKeydown);
|
||||
this.cachedActiveElement.focus();
|
||||
this.cachedActiveElement = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture escape.
|
||||
*/
|
||||
private onKeydown = (event: KeyboardEvent): void => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.actionEmitter.emit({
|
||||
buttonIndex: undefined,
|
||||
key: IKey.Escape,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
352
packages/ide/src/fill/electron.ts
Normal file
352
packages/ide/src/fill/electron.ts
Normal file
@ -0,0 +1,352 @@
|
||||
// import * as electron from "electron";
|
||||
// import { EventEmitter } from "events";
|
||||
// import * as fs from "fs";
|
||||
// import { getFetchUrl } from "../src/coder/api";
|
||||
// import { escapePath } from "../src/coder/common";
|
||||
// import { wush } from "../src/coder/server";
|
||||
// import { IKey, Dialog } from "./dialog";
|
||||
|
||||
// (global as any).getOpenUrls = () => {
|
||||
// return [];
|
||||
// };
|
||||
|
||||
// const oldCreateElement = document.createElement;
|
||||
|
||||
// document.createElement = (tagName: string) => {
|
||||
// const createElement = (tagName: string) => {
|
||||
// return oldCreateElement.call(document, tagName);
|
||||
// };
|
||||
|
||||
// if (tagName === "webview") {
|
||||
// const view = createElement("iframe") as HTMLIFrameElement;
|
||||
// view.style.border = "0px";
|
||||
// const frameID = Math.random().toString();
|
||||
// view.addEventListener("error", (event) => {
|
||||
// console.log("Got iframe error", event.error, event.message);
|
||||
// });
|
||||
// window.addEventListener("message", (event) => {
|
||||
// if (!event.data || !event.data.id) {
|
||||
// return;
|
||||
// }
|
||||
// if (event.data.id !== frameID) {
|
||||
// return;
|
||||
// }
|
||||
// const e = new CustomEvent("ipc-message");
|
||||
// (e as any).channel = event.data.channel;
|
||||
// (e as any).args = event.data.data;
|
||||
// view.dispatchEvent(e);
|
||||
// });
|
||||
// view.sandbox.add("allow-same-origin", "allow-scripts", "allow-popups", "allow-forms");
|
||||
// Object.defineProperty(view, "preload", {
|
||||
// set: (url: string) => {
|
||||
// view.onload = () => {
|
||||
// view.contentDocument.body.id = frameID;
|
||||
// view.contentDocument.body.parentElement.style.overflow = "hidden";
|
||||
// const script = document.createElement("script");
|
||||
// script.src = url;
|
||||
// view.contentDocument.head.appendChild(script);
|
||||
// };
|
||||
// },
|
||||
// });
|
||||
// (view as any).getWebContents = () => undefined;
|
||||
// (view as any).send = (channel: string, ...args) => {
|
||||
// if (args[0] && typeof args[0] === "object" && args[0].contents) {
|
||||
// args[0].contents = (args[0].contents as string).replace(/"(file:\/\/[^"]*)"/g, (m) => `"${getFetchUrl(m)}"`);
|
||||
// args[0].contents = (args[0].contents as string).replace(/"vscode-resource:([^"]*)"/g, (m) => `"${getFetchUrl(m)}"`);
|
||||
// }
|
||||
// view.contentWindow.postMessage({
|
||||
// channel,
|
||||
// data: args,
|
||||
// id: frameID,
|
||||
// }, "*");
|
||||
// };
|
||||
// return view;
|
||||
// }
|
||||
|
||||
// return createElement(tagName);
|
||||
// };
|
||||
|
||||
// const rendererToMainEmitter = new EventEmitter();
|
||||
// const mainToRendererEmitter = new EventEmitter();
|
||||
|
||||
// module.exports = {
|
||||
// clipboard: {
|
||||
// has: () => {
|
||||
// return false;
|
||||
// },
|
||||
// writeText: (value: string) => {
|
||||
// // Taken from https://hackernoon.com/copying-text-to-clipboard-with-javascript-df4d4988697f
|
||||
// const active = document.activeElement as HTMLElement;
|
||||
// const el = document.createElement('textarea'); // Create a <textarea> element
|
||||
// el.value = value; // Set its value to the string that you want copied
|
||||
// el.setAttribute('readonly', ''); // Make it readonly to be tamper-proof
|
||||
// el.style.position = 'absolute';
|
||||
// el.style.left = '-9999px'; // Move outside the screen to make it invisible
|
||||
// document.body.appendChild(el); // Append the <textarea> element to the HTML document
|
||||
// const selected =
|
||||
// document.getSelection().rangeCount > 0 // Check if there is any content selected previously
|
||||
// ? document.getSelection().getRangeAt(0) // Store selection if found
|
||||
// : false; // Mark as false to know no selection existed before
|
||||
// el.select(); // Select the <textarea> content
|
||||
// document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events)
|
||||
// document.body.removeChild(el); // Remove the <textarea> element
|
||||
// if (selected) { // If a selection existed before copying
|
||||
// document.getSelection().removeAllRanges(); // Unselect everything on the HTML document
|
||||
// document.getSelection().addRange(selected); // Restore the original selection
|
||||
// }
|
||||
// active.focus();
|
||||
// },
|
||||
// },
|
||||
// dialog: {
|
||||
// showSaveDialog: (_: void, options: Electron.SaveDialogOptions, callback: (filename: string) => void): void => {
|
||||
// const defaultPath = options.defaultPath || "/untitled";
|
||||
// const fileIndex = defaultPath.lastIndexOf("/");
|
||||
// const extensionIndex = defaultPath.lastIndexOf(".");
|
||||
// const saveDialogOptions = {
|
||||
// buttons: ["Cancel", "Save"],
|
||||
// detail: "Enter a path for this file",
|
||||
// input: {
|
||||
// value: defaultPath,
|
||||
// selection: {
|
||||
// start: fileIndex === -1 ? 0 : fileIndex + 1,
|
||||
// end: extensionIndex === -1 ? defaultPath.length : extensionIndex,
|
||||
// },
|
||||
// },
|
||||
// message: "Save file",
|
||||
// };
|
||||
|
||||
// const dialog = new Dialog(saveDialogOptions);
|
||||
// dialog.onAction((action) => {
|
||||
// if (action.key !== IKey.Enter && action.buttonIndex !== 1) {
|
||||
// dialog.hide();
|
||||
// return callback(undefined);
|
||||
// }
|
||||
|
||||
// const filePath = dialog.inputValue.replace(/\/+$/, "");
|
||||
// const split = filePath.split("/");
|
||||
// const fileName = split.pop();
|
||||
// const parentName = split.pop() || "/";
|
||||
// if (fileName === "") {
|
||||
// dialog.error = "You must enter a file name.";
|
||||
// return;
|
||||
// }
|
||||
|
||||
// fs.stat(filePath, (error, stats) => {
|
||||
// if (error && error.code === "ENOENT") {
|
||||
// dialog.hide();
|
||||
// callback(filePath);
|
||||
// } else if (error) {
|
||||
// dialog.error = error.message;
|
||||
// } else if (stats.isDirectory()) {
|
||||
// dialog.error = `A directory named "${fileName}" already exists.`;
|
||||
// } else {
|
||||
// dialog.error = undefined;
|
||||
|
||||
// const confirmDialog = new Dialog({
|
||||
// message: `A file named "${fileName}" already exists. Do you want to replace it?`,
|
||||
// detail: `The file already exists in "${parentName}". Replacing it will overwrite its contents.`,
|
||||
// buttons: ["Cancel", "Replace"],
|
||||
// });
|
||||
|
||||
// confirmDialog.onAction((action) => {
|
||||
// if (action.buttonIndex === 1) {
|
||||
// confirmDialog.hide();
|
||||
// return callback(filePath);
|
||||
// }
|
||||
|
||||
// confirmDialog.hide();
|
||||
// dialog.show();
|
||||
// });
|
||||
|
||||
// dialog.hide();
|
||||
// confirmDialog.show();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// dialog.show();
|
||||
// },
|
||||
// showOpenDialog: () => {
|
||||
// console.log("Trying to show the open dialog");
|
||||
// },
|
||||
// showMessageBox: (_: void, options: Electron.MessageBoxOptions, callback: (button: number, checked: boolean) => void): void => {
|
||||
// const dialog = new Dialog(options);
|
||||
// dialog.onAction((action) => {
|
||||
// dialog.hide();
|
||||
// callback(action.buttonIndex, false);
|
||||
// });
|
||||
// dialog.show();
|
||||
// },
|
||||
// },
|
||||
// remote: {
|
||||
// dialog: {
|
||||
// showOpenDialog: () => {
|
||||
// console.log("Trying to remotely open");
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// webFrame: {
|
||||
// getZoomFactor: () => {
|
||||
// return 1;
|
||||
// },
|
||||
// getZoomLevel: () => {
|
||||
// return 1;
|
||||
// },
|
||||
// setZoomLevel: () => {
|
||||
// return;
|
||||
// },
|
||||
// },
|
||||
// screen: {
|
||||
// getAllDisplays: () => {
|
||||
// return [{
|
||||
// bounds: {
|
||||
// x: 1000,
|
||||
// y: 1000,
|
||||
// },
|
||||
// }];
|
||||
// },
|
||||
// },
|
||||
// app: {
|
||||
// isAccessibilitySupportEnabled: () => {
|
||||
// return false;
|
||||
// },
|
||||
// setAsDefaultProtocolClient: () => {
|
||||
|
||||
// },
|
||||
// send: (str) => {
|
||||
// console.log("APP Trying to send", str);
|
||||
// //
|
||||
// },
|
||||
// on: () => {
|
||||
// //
|
||||
// },
|
||||
// once: () => {
|
||||
// //
|
||||
// },
|
||||
// },
|
||||
// // ipcRenderer communicates with ipcMain
|
||||
// ipcRenderer: {
|
||||
// send: (str, ...args) => {
|
||||
// rendererToMainEmitter.emit(str, {
|
||||
// sender: module.exports.ipcMain,
|
||||
// }, ...args);
|
||||
// },
|
||||
// on: (str, listener) => {
|
||||
// mainToRendererEmitter.on(str, listener);
|
||||
// },
|
||||
// once: (str, listener) => {
|
||||
// mainToRendererEmitter.once(str, listener);
|
||||
// },
|
||||
// removeListener: (str, listener) => {
|
||||
// mainToRendererEmitter.removeListener(str, listener);
|
||||
// },
|
||||
// },
|
||||
// ipcMain: {
|
||||
// send: (str, ...args) => {
|
||||
// mainToRendererEmitter.emit(str, {
|
||||
// sender: module.exports.ipcRenderer,
|
||||
// }, ...args);
|
||||
// },
|
||||
// on: (str, listener) => {
|
||||
// rendererToMainEmitter.on(str, listener);
|
||||
// },
|
||||
// once: (str, listener) => {
|
||||
// rendererToMainEmitter.once(str, listener);
|
||||
// },
|
||||
// },
|
||||
// shell: {
|
||||
// moveItemToTrash: async (path) => {
|
||||
// const response = await wush.execute({
|
||||
// command: `trash-put --trash-dir ${escapePath("~/.Trash")} ${escapePath(path)}`,
|
||||
// }).done();
|
||||
// return response.wasSuccessful();
|
||||
// },
|
||||
// },
|
||||
// BrowserWindow: class {
|
||||
|
||||
// public webContents = {
|
||||
// on: () => {
|
||||
|
||||
// },
|
||||
// session: {
|
||||
// webRequest: {
|
||||
// onBeforeRequest: () => {
|
||||
|
||||
// },
|
||||
|
||||
// onBeforeSendHeaders: () => {
|
||||
|
||||
// },
|
||||
|
||||
// onHeadersReceived: () => {
|
||||
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
// removeAllListeners: () => {
|
||||
|
||||
// },
|
||||
// }
|
||||
|
||||
// public static getFocusedWindow() {
|
||||
// return undefined;
|
||||
// }
|
||||
|
||||
// public isMaximized() {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// public isFullScreen() {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// public setMenuBarVisibility(visibility) {
|
||||
// console.log("We are setting the menu bar to ", visibility);
|
||||
// }
|
||||
|
||||
// public setAutoHideMenuBar() {
|
||||
|
||||
// }
|
||||
|
||||
// public on() {
|
||||
|
||||
// }
|
||||
|
||||
// public setTitle(value: string): void {
|
||||
// document.title = value;
|
||||
// }
|
||||
// },
|
||||
// toggleFullScreen: () => {
|
||||
// const doc = document as any;
|
||||
// const isInFullScreen = doc.fullscreenElement
|
||||
// || doc.webkitFullscreenElement
|
||||
// || doc.mozFullScreenElement
|
||||
// || doc.msFullscreenElement;
|
||||
|
||||
// const body = doc.body;
|
||||
// if (!isInFullScreen) {
|
||||
// if (body.requestFullscreen) {
|
||||
// body.requestFullscreen();
|
||||
// } else if (body.mozRequestFullScreen) {
|
||||
// body.mozRequestFullScreen();
|
||||
// } else if (body.webkitRequestFullScreen) {
|
||||
// body.webkitRequestFullScreen();
|
||||
// } else if (body.msRequestFullscreen) {
|
||||
// body.msRequestFullscreen();
|
||||
// }
|
||||
// } else {
|
||||
// if (doc.exitFullscreen) {
|
||||
// doc.exitFullscreen();
|
||||
// } else if (doc.webkitExitFullscreen) {
|
||||
// doc.webkitExitFullscreen();
|
||||
// } else if (doc.mozCancelFullScreen) {
|
||||
// doc.mozCancelFullScreen();
|
||||
// } else if (doc.msExitFullscreen) {
|
||||
// doc.msExitFullscreen();
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// focusWindow: () => {
|
||||
// console.log("focusing window");
|
||||
// window.focus();
|
||||
// },
|
||||
// };
|
1
packages/ide/src/fill/empty.ts
Normal file
1
packages/ide/src/fill/empty.ts
Normal file
@ -0,0 +1 @@
|
||||
module.exports = {};
|
4
packages/ide/src/fill/fs.ts
Normal file
4
packages/ide/src/fill/fs.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { FS } from "@coder/server";
|
||||
import { client } from "./client";
|
||||
|
||||
export = new FS(client);
|
3
packages/ide/src/fill/net.ts
Normal file
3
packages/ide/src/fill/net.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { Net } from "@coder/server";
|
||||
|
||||
export = new Net();
|
5
packages/ide/src/fill/util.ts
Normal file
5
packages/ide/src/fill/util.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { implementation as promisify } from "util.promisify";
|
||||
|
||||
export {
|
||||
promisify,
|
||||
}
|
@ -2,7 +2,7 @@ import { exec } from "child_process";
|
||||
import { appendFile } from "fs";
|
||||
import { promisify } from "util";
|
||||
import { logger, Logger } from "@coder/logger";
|
||||
import { escapePath } from "@coder/node-browser";
|
||||
import { escapePath } from "@coder/server";
|
||||
import { IURI } from "./uri";
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user