mirror of
https://github.com/coder/code-server.git
synced 2024-12-28 20:25:54 +03:00
Merge branch 'master' of github.com:codercom/code-server
This commit is contained in:
commit
da420cdda9
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,4 +4,5 @@ dist
|
||||
out
|
||||
.DS_Store
|
||||
release
|
||||
.cache
|
||||
.vscode
|
||||
.cache
|
||||
|
@ -2,7 +2,7 @@ language: node_js
|
||||
node_js:
|
||||
- 8.15.0
|
||||
env:
|
||||
- VSCODE_VERSION="1.33.0" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION"
|
||||
- VSCODE_VERSION="1.33.1" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION"
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
|
@ -24,7 +24,10 @@ RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
locales \
|
||||
sudo \
|
||||
dumb-init
|
||||
dumb-init \
|
||||
vim \
|
||||
curl \
|
||||
wget
|
||||
|
||||
RUN locale-gen en_US.UTF-8
|
||||
# We unfortunately cannot use update-locale because docker will not use the env variables
|
||||
|
@ -23,10 +23,6 @@ docker run -it -p 127.0.0.1:8443:8443 -v "${PWD}:/home/coder/project" codercom/c
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Hosted
|
||||
|
||||
[Try `code-server` now](https://coder.com/signup) for free at coder.com.
|
||||
|
||||
### Docker
|
||||
|
||||
See docker oneliner mentioned above. Dockerfile is at [/Dockerfile](/Dockerfile).
|
||||
|
@ -12,7 +12,7 @@ const libPath = path.join(__dirname, "../lib");
|
||||
const vscodePath = path.join(libPath, "vscode");
|
||||
const defaultExtensionsPath = path.join(libPath, "extensions");
|
||||
const pkgsPath = path.join(__dirname, "../packages");
|
||||
const vscodeVersion = process.env.VSCODE_VERSION || "1.33.0";
|
||||
const vscodeVersion = process.env.VSCODE_VERSION || "1.33.1";
|
||||
const vsSourceUrl = `https://codesrv-ci.cdr.sh/vstar-${vscodeVersion}.tar.gz`;
|
||||
|
||||
const buildServerBinary = register("build:server:binary", async (runner) => {
|
||||
|
@ -39,7 +39,7 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../../security/ssl.md)
|
||||
- Finally start the code-server
|
||||
```
|
||||
sudo ./code-server-linux -p 80
|
||||
sudo ./code-server -p 80
|
||||
```
|
||||
> For instructions on how to keep the server running after you end your SSH session please checkout [how to use systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) to start linux based services if they are killed
|
||||
- When you visit the public IP for your Digital Ocean instance, you will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../../assets/chrome_warning.png">
|
||||
|
@ -62,7 +62,7 @@ OPTIONS
|
||||
> Example: `code-server -h 127.0.0.1`
|
||||
|
||||
### Open
|
||||
You can have the server automatically open the VS Code in your browser on startup by using the `code server -o` or `code-server --open` flags
|
||||
You can have the server automatically open the VS Code in your browser on startup by using the `code-server -o` or `code-server --open` flags
|
||||
|
||||
### Port
|
||||
By default, code-server will use `8443` as its port. This can be changed by using `code-server -p` or `code-server --port=` followed by the port you want to use.
|
||||
|
@ -7,22 +7,24 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="login">
|
||||
<div class="back"> <- Back </div>
|
||||
<h4 class="title">code-server</h4>
|
||||
<h2 class="subtitle">
|
||||
Enter server password
|
||||
</h2>
|
||||
<div class="mdc-text-field">
|
||||
<input type="password" id="password" class="mdc-text-field__input" required>
|
||||
<label class="mdc-floating-label" for="password">Password</label>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div>
|
||||
<button id="submit" class="mdc-button mdc-button--unelevated">
|
||||
<span class="mdc-button__label">Enter IDE</span>
|
||||
</button>
|
||||
<div id="error-display"></div>
|
||||
</div>
|
||||
<form id="login-form">
|
||||
<div class="login">
|
||||
<div class="back">
|
||||
<- Back </div> <h4 class="title">code-server</h4>
|
||||
<h2 class="subtitle">
|
||||
Enter server password
|
||||
</h2>
|
||||
<div class="mdc-text-field">
|
||||
<input type="password" id="password" class="mdc-text-field__input" required>
|
||||
<label class="mdc-floating-label" for="password">Password</label>
|
||||
<div class="mdc-line-ripple"></div>
|
||||
</div>
|
||||
<button id="submit" class="mdc-button mdc-button--unelevated">
|
||||
<span class="mdc-button__label">Enter IDE</span>
|
||||
</button>
|
||||
<div id="error-display"></div>
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -20,11 +20,14 @@ window.addEventListener("message", (event) => {
|
||||
});
|
||||
|
||||
const password = document.getElementById("password") as HTMLInputElement;
|
||||
const submit = document.getElementById("submit") as HTMLButtonElement;
|
||||
if (!submit) {
|
||||
throw new Error("No submit button found");
|
||||
const form = document.getElementById("login-form") as HTMLFormElement;
|
||||
|
||||
if (!form) {
|
||||
throw new Error("No password form found");
|
||||
}
|
||||
submit.addEventListener("click", () => {
|
||||
|
||||
form.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
document.cookie = `password=${password.value}`;
|
||||
location.reload();
|
||||
});
|
||||
@ -38,4 +41,4 @@ const errorDisplay = document.getElementById("error-display") as HTMLDivElement;
|
||||
|
||||
if (document.referrer === document.location.href && matches) {
|
||||
errorDisplay.innerText = "Password is incorrect!";
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,8 @@ const newCreateElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HT
|
||||
},
|
||||
set: (value: string): void => {
|
||||
if (value) {
|
||||
value = value.replace(/file:\/\//g, "/resource");
|
||||
const resourceBaseUrl = location.pathname.replace(/\/$/, "") + "/resource";
|
||||
value = value.replace(/file:\/\//g, resourceBaseUrl);
|
||||
}
|
||||
oldSrc!.set!.call(img, value);
|
||||
},
|
||||
@ -66,7 +67,8 @@ const newCreateElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HT
|
||||
},
|
||||
set: (value: string): void => {
|
||||
if (value) {
|
||||
value = value.replace(/file:\/\//g, "/resource");
|
||||
const resourceBaseUrl = location.pathname.replace(/\/$/, "") + "/resource";
|
||||
value = value.replace(/file:\/\//g, resourceBaseUrl);
|
||||
}
|
||||
oldInnerHtml!.set!.call(style, value);
|
||||
},
|
||||
@ -80,7 +82,8 @@ const newCreateElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HT
|
||||
if (sheet && !overridden) {
|
||||
const oldInsertRule = sheet.insertRule;
|
||||
sheet.insertRule = (rule: string, index?: number): void => {
|
||||
rule = rule.replace(/file:\/\//g, "/resource");
|
||||
const resourceBaseUrl = location.pathname.replace(/\/$/, "") + "/resource";
|
||||
rule = rule.replace(/file:\/\//g, resourceBaseUrl);
|
||||
oldInsertRule.call(sheet, rule, index);
|
||||
};
|
||||
overridden = true;
|
||||
@ -145,8 +148,9 @@ const newCreateElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HT
|
||||
(view as any).send = (channel: string, ...args: any[]): void => { // tslint:disable-line no-any
|
||||
if (args[0] && typeof args[0] === "object" && args[0].contents) {
|
||||
// TODO
|
||||
args[0].contents = (args[0].contents as string).replace(/"(file:\/\/[^"]*)"/g, (m1) => `"/resource${m1}"`);
|
||||
args[0].contents = (args[0].contents as string).replace(/"vscode-resource:([^"]*)"/g, (m, m1) => `"/resource${m1}"`);
|
||||
const resourceBaseUrl = location.pathname.replace(/\/$/, "") + "/resource";
|
||||
args[0].contents = (args[0].contents as string).replace(/"(file:\/\/[^"]*)"/g, (m1) => `"${resourceBaseUrl}${m1}"`);
|
||||
args[0].contents = (args[0].contents as string).replace(/"vscode-resource:([^"]*)"/g, (m, m1) => `"${resourceBaseUrl}${m1}"`);
|
||||
args[0].contents = (args[0].contents as string).replace(/style-src vscode-core-resource:/g, "style-src 'self'");
|
||||
}
|
||||
if (view.contentWindow) {
|
||||
|
@ -75,7 +75,7 @@ export class Retry {
|
||||
|
||||
// Times are in seconds.
|
||||
private readonly retryMinDelay = 1;
|
||||
private readonly retryMaxDelay = 10;
|
||||
private readonly retryMaxDelay = 3;
|
||||
private readonly maxImmediateRetries = 5;
|
||||
private readonly retryExponent = 1.5;
|
||||
private blocked: string | boolean | undefined;
|
||||
|
@ -200,7 +200,13 @@ const bold = (text: string | number): string | number => {
|
||||
const webpackConfig = require(path.resolve(__dirname, "..", "..", "web", "webpack.config.js"));
|
||||
const compiler = require("webpack")(webpackConfig);
|
||||
app.use(require("webpack-dev-middleware")(compiler, {
|
||||
logger,
|
||||
logger: {
|
||||
trace: (m: string): void => logger.trace("webpack", field("message", m)),
|
||||
debug: (m: string): void => logger.debug("webpack", field("message", m)),
|
||||
info: (m: string): void => logger.info("webpack", field("message", m)),
|
||||
warn: (m: string): void => logger.warn("webpack", field("message", m)),
|
||||
error: (m: string): void => logger.error("webpack", field("message", m)),
|
||||
},
|
||||
publicPath: webpackConfig.output.publicPath,
|
||||
stats: webpackConfig.stats,
|
||||
}));
|
||||
|
@ -2,6 +2,7 @@
|
||||
--primary: #2A2E37;
|
||||
--border: black;
|
||||
--faded: #a0a1a5;
|
||||
--disabled: #888;
|
||||
--header-background: #161616;
|
||||
--header-foreground: white;
|
||||
--list-active-selection-background: rgb(0, 120, 160);
|
||||
@ -101,6 +102,12 @@
|
||||
background-color: var(--list-active-selection-background);
|
||||
color: var(--list-active-selection-foreground);
|
||||
}
|
||||
|
||||
&.disabled, &.disabled:hover {
|
||||
background-color: var(--primary);
|
||||
color: var(--disabled);
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,6 +141,11 @@
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
button[disabled], button[disabled]:hover {
|
||||
color: var(--disabled);
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,9 @@ import { IThemeService } from "vs/platform/theme/common/themeService";
|
||||
import { workbench } from "./workbench";
|
||||
import "./dialog.scss";
|
||||
|
||||
/**
|
||||
* Describes the type of dialog to show.
|
||||
*/
|
||||
export enum DialogType {
|
||||
NewFolder,
|
||||
Save,
|
||||
@ -68,8 +71,12 @@ interface DialogEntry {
|
||||
readonly isDirectory: boolean;
|
||||
readonly size: number;
|
||||
readonly lastModified: string;
|
||||
readonly isDisabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open and save dialogs.
|
||||
*/
|
||||
class Dialog {
|
||||
private _path: string | undefined;
|
||||
|
||||
@ -265,8 +272,7 @@ class Dialog {
|
||||
}
|
||||
if (element.isDirectory) {
|
||||
this.path = element.fullPath;
|
||||
} else {
|
||||
// Open
|
||||
} else if (!(this.options as OpenDialogOptions).properties.openDirectory) {
|
||||
this.selectEmitter.emit(element.fullPath);
|
||||
}
|
||||
});
|
||||
@ -282,12 +288,18 @@ class Dialog {
|
||||
});
|
||||
buttonsNode.appendChild(cancelBtn);
|
||||
const confirmBtn = document.createElement("button");
|
||||
confirmBtn.innerText = "Confirm";
|
||||
const openFile = (this.options as OpenDialogOptions).properties.openFile;
|
||||
confirmBtn.innerText = openFile ? "Open" : "Confirm";
|
||||
confirmBtn.addEventListener("click", () => {
|
||||
if (this._path) {
|
||||
if (this._path && !openFile) {
|
||||
this.selectEmitter.emit(this._path);
|
||||
}
|
||||
});
|
||||
// Since a single click opens a file, the only time this button can be
|
||||
// used is on a directory, which is invalid for opening files.
|
||||
if (openFile) {
|
||||
confirmBtn.disabled = true;
|
||||
}
|
||||
buttonsNode.appendChild(confirmBtn);
|
||||
this.root.appendChild(buttonsNode);
|
||||
this.entryList.layout();
|
||||
@ -303,6 +315,9 @@ class Dialog {
|
||||
return this.errorEmitter.event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the dialog.
|
||||
*/
|
||||
public dispose(): void {
|
||||
this.selectEmitter.dispose();
|
||||
this.errorEmitter.dispose();
|
||||
@ -310,6 +325,9 @@ class Dialog {
|
||||
this.background.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and insert the path shown at the top of the dialog.
|
||||
*/
|
||||
private buildPath(): void {
|
||||
while (this.pathNode.lastChild) {
|
||||
this.pathNode.removeChild(this.pathNode.lastChild);
|
||||
@ -376,6 +394,9 @@ class Dialog {
|
||||
return (<any>this.entryList).typeFilterController.filter._pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* List the files and return dialog entries.
|
||||
*/
|
||||
private async list(directory: string): Promise<ReadonlyArray<DialogEntry>> {
|
||||
const paths = (await util.promisify(fs.readdir)(directory)).sort();
|
||||
const stats = await Promise.all(paths.map(p => util.promisify(fs.stat)(path.join(directory, p))));
|
||||
@ -386,6 +407,8 @@ class Dialog {
|
||||
isDirectory: stat.isDirectory(),
|
||||
lastModified: stat.mtime.toDateString(),
|
||||
size: stat.size,
|
||||
// If we are opening a directory, show files as disabled.
|
||||
isDisabled: !stat.isDirectory() && (this.options as OpenDialogOptions).properties.openDirectory,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -397,11 +420,17 @@ interface DialogEntryData {
|
||||
label: HighlightedLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendering for the different parts of a dialog entry.
|
||||
*/
|
||||
class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEntryData> {
|
||||
public get templateId(): string {
|
||||
return "dialog-entry";
|
||||
}
|
||||
|
||||
/**
|
||||
* Append and return containers for each part of the dialog entry.
|
||||
*/
|
||||
public renderTemplate(container: HTMLElement): DialogEntryData {
|
||||
addClass(container, "dialog-entry");
|
||||
addClass(container, "dialog-grid");
|
||||
@ -422,6 +451,9 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a dialog entry.
|
||||
*/
|
||||
public renderElement(node: ITreeNode<DialogEntry, string>, _index: number, templateData: DialogEntryData): void {
|
||||
templateData.icon.className = "dialog-entry-icon monaco-icon-label";
|
||||
const classes = getIconClasses(
|
||||
@ -444,8 +476,19 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
|
||||
}] : []);
|
||||
templateData.size.innerText = node.element.size.toString();
|
||||
templateData.lastModified.innerText = node.element.lastModified;
|
||||
|
||||
// We know this exists because we created the template.
|
||||
const entryContainer = templateData.label.element.parentElement!.parentElement!.parentElement!;
|
||||
if (node.element.isDisabled) {
|
||||
entryContainer.classList.add("disabled");
|
||||
} else {
|
||||
entryContainer.classList.remove("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing (not implemented).
|
||||
*/
|
||||
public disposeTemplate(_templateData: DialogEntryData): void {
|
||||
// throw new Error("Method not implemented.");
|
||||
}
|
||||
|
@ -28,7 +28,8 @@ class StorageDatabase implements workspaceStorage.IStorageDatabase {
|
||||
}
|
||||
|
||||
this.triggerFlush(WillSaveStateReason.SHUTDOWN);
|
||||
navigator.sendBeacon(`/resource${this.path}`, this.content);
|
||||
const resourceBaseUrl = location.pathname.replace(/\/$/, "") + "/resource";
|
||||
navigator.sendBeacon(`${resourceBaseUrl}/${this.path}`, this.content);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -47,9 +47,9 @@ export class WindowsService implements IWindowsService {
|
||||
private readonly window = new electron.BrowserWindow();
|
||||
|
||||
// Dialogs
|
||||
public async pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
public async pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
showOpenDialog({
|
||||
...(_options.dialogOptions || {}),
|
||||
...(options.dialogOptions || {}),
|
||||
properties: {
|
||||
openFile: true,
|
||||
openDirectory: true,
|
||||
@ -66,9 +66,9 @@ export class WindowsService implements IWindowsService {
|
||||
});
|
||||
}
|
||||
|
||||
public async pickFileAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
public async pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
showOpenDialog({
|
||||
...(_options.dialogOptions || {}),
|
||||
...(options.dialogOptions || {}),
|
||||
properties: {
|
||||
openFile: true,
|
||||
},
|
||||
@ -84,9 +84,15 @@ export class WindowsService implements IWindowsService {
|
||||
});
|
||||
}
|
||||
|
||||
public async pickFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
public async pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
if (!options.dialogOptions) {
|
||||
options.dialogOptions = {};
|
||||
}
|
||||
if (!options.dialogOptions.title) {
|
||||
options.dialogOptions.title = "Open Folder";
|
||||
}
|
||||
showOpenDialog({
|
||||
...(_options.dialogOptions || {}),
|
||||
...(options.dialogOptions || {}),
|
||||
properties: {
|
||||
openDirectory: true,
|
||||
},
|
||||
@ -97,9 +103,9 @@ export class WindowsService implements IWindowsService {
|
||||
});
|
||||
}
|
||||
|
||||
public async pickWorkspaceAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
||||
public async pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
showOpenDialog({
|
||||
...(_options.dialogOptions || {}),
|
||||
...(options.dialogOptions || {}),
|
||||
properties: {
|
||||
openDirectory: true,
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ set -euxo pipefail
|
||||
# Builds a tarfile containing vscode sourcefiles neccessary for CI.
|
||||
# Done outside the CI and uploaded to object storage to reduce CI time.
|
||||
|
||||
branch=1.33.0
|
||||
branch=1.33.1
|
||||
dir=/tmp/vstar
|
||||
outfile=/tmp/vstar-$branch.tar.gz
|
||||
rm -rf $dir
|
||||
|
@ -21,7 +21,7 @@ module.exports = (options = {}) => ({
|
||||
// they are parsed as URIs and will throw errors if not fully formed.
|
||||
// The !! prefix causes it to ignore other loaders (doesn't work).
|
||||
search: "require\\.toUrl\\(",
|
||||
replace: "location.protocol + '//' + location.host + '/' + require('!!file-loader?name=[path][name].[ext]!' + ",
|
||||
replace: "location.protocol + '//' + location.host + location.pathname.replace(/\\/$/,'') + '/' + require('!!file-loader?name=[path][name].[ext]!' + ",
|
||||
flags: "g",
|
||||
}, {
|
||||
search: "require\\.__\\$__nodeRequire",
|
||||
|
Loading…
Reference in New Issue
Block a user