Handle when VS Code fails to load

This is mostly for development where VS Code might not have finished
compiling yet.
This commit is contained in:
Asher 2020-02-05 14:21:59 -06:00
parent 7c6fe56043
commit 6e809b6a31
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A
9 changed files with 205 additions and 183 deletions

View File

@ -246,18 +246,11 @@ class Builder {
await this.task("copying vs code into build directory", async () => { await this.task("copying vs code into build directory", async () => {
await fs.mkdirp(vscodeBuildPath) await fs.mkdirp(vscodeBuildPath)
await Promise.all([ await Promise.all([
(async (): Promise<void> => { fs.move(
await fs.move(
path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`), path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`),
path.join(vscodeBuildPath, "out") path.join(vscodeBuildPath, "out")
) ),
await fs.remove(path.join(vscodeBuildPath, "out/vs/server/browser/workbench.html")) fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")),
await fs.move(
path.join(vscodeBuildPath, "out/vs/server/browser/workbench-build.html"),
path.join(vscodeBuildPath, "out/vs/server/browser/workbench.html")
)
})(),
await fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")),
]) ])
}) })

View File

@ -418,7 +418,7 @@ index d0f6e6b18a..1966fd297d 100644
- -
diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts
new file mode 100644 new file mode 100644
index 0000000000..8e7c5af184 index 0000000000..3a62205b38
--- /dev/null --- /dev/null
+++ b/src/vs/server/browser/client.ts +++ b/src/vs/server/browser/client.ts
@@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
@ -532,12 +532,12 @@ index 0000000000..8e7c5af184
+ if (!window.isSecureContext) { + if (!window.isSecureContext) {
+ (services.get(INotificationService) as INotificationService).notify({ + (services.get(INotificationService) as INotificationService).notify({
+ severity: Severity.Warning, + severity: Severity.Warning,
+ message: "code-server is being accessed over an insecure domain. Some functionality may not work as expected.", + message: 'code-server is being accessed over an insecure domain. Some functionality may not work as expected.',
+ actions: { + actions: {
+ primary: [{ + primary: [{
+ id: "understand", + id: 'understand',
+ label: "I understand", + label: 'I understand',
+ tooltip: "", + tooltip: '',
+ class: undefined, + class: undefined,
+ enabled: true, + enabled: true,
+ checked: true, + checked: true,
@ -679,165 +679,6 @@ index 0000000000..0d2e93edae
+ this.disposed = true; + this.disposed = true;
+ } + }
+} +}
diff --git a/src/vs/server/browser/workbench-build.html b/src/vs/server/browser/workbench-build.html
new file mode 100644
index 0000000000..50f48cd74c
--- /dev/null
+++ b/src/vs/server/browser/workbench-build.html
@@ -0,0 +1,92 @@
+<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+
+ <!-- Disable pinch zooming -->
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
+
+ <!-- Workbench Configuration -->
+ <meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}">
+
+ <!-- Workarounds/Hacks (remote user data uri) -->
+ <meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
+ <!-- NOTE@coder: Added the commit for use in caching, the product for the
+ extensions gallery URL, and nls for language support. -->
+ <meta id="vscode-remote-commit" data-settings="{{COMMIT}}">
+ <meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}">
+ <meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
+
+ <!-- Workbench Icon/Manifest/CSS -->
+ <link rel="icon" href="../static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
+ <link rel="manifest" href="../static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
+ <link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
+ <link rel="apple-touch-icon" href="./static-{{COMMIT}}/src/browser/media/code-server.png" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+
+ <!-- Prefetch to avoid waterfall -->
+ <link rel="prefetch" href="./static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
+ </head>
+
+ <body aria-label="">
+ </body>
+
+ <!-- Startup (do not modify order of script tags!) -->
+ <!-- NOTE:coder: Modified to work against the current path and use the commit for caching. -->
+ <script>
+ // NOTE: Changes to inline scripts require update of content security policy
+ const basePath = window.location.pathname.replace(/\/+$/, '');
+ const base = window.location.origin + basePath;
+ const el = document.getElementById('vscode-remote-commit');
+ const commit = el ? el.getAttribute('data-settings') : "";
+ const staticBase = base + '/static-' + commit;
+ let nlsConfig;
+ try {
+ nlsConfig = JSON.parse(document.getElementById('vscode-remote-nls-configuration').getAttribute('data-settings'));
+ if (nlsConfig._resolvedLanguagePackCoreLocation) {
+ const bundles = Object.create(null);
+ nlsConfig.loadBundle = (bundle, language, cb) => {
+ let result = bundles[bundle];
+ if (result) {
+ return cb(undefined, result);
+ }
+ // FIXME: Only works if path separators are /.
+ const path = nlsConfig._resolvedLanguagePackCoreLocation
+ + '/' + bundle.replace(/\//g, '!') + '.nls.json';
+ fetch(`${base}/resource/?path=${encodeURIComponent(path)}`)
+ .then((response) => response.json())
+ .then((json) => {
+ bundles[bundle] = json;
+ cb(undefined, json);
+ })
+ .catch(cb);
+ };
+ }
+ } catch (error) { /* Probably fine. */ }
+ self.require = {
+ baseUrl: `${staticBase}/out`,
+ paths: {
+ 'vscode-textmate': `${staticBase}/node_modules/vscode-textmate/release/main`,
+ 'onigasm-umd': `${staticBase}/node_modules/onigasm-umd/release/main`,
+ 'xterm': `${staticBase}/node_modules/xterm/lib/xterm.js`,
+ 'xterm-addon-search': `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
+ 'xterm-addon-web-links': `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
+ 'xterm-addon-webgl': `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
+ 'semver-umd': `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
+ },
+ 'vs/nls': nlsConfig,
+ };
+ </script>
+ <script src="./static-{{COMMIT}}/out/vs/loader.js"></script>
+ <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
+ <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
+ <!-- TODO@coder: This errors with multiple anonymous define calls (one is
+ workbench.js and one is semver-umd.js). For now use the same method found in
+ workbench-dev.html. Appears related to the timing of the script load events. -->
+ <!-- <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
+ <script>
+ // NOTE: Changes to inline scripts require update of content security policy
+ require(['vs/code/browser/workbench/workbench'], function() {});
+ </script>
+</html>
diff --git a/src/vs/server/browser/workbench.html b/src/vs/server/browser/workbench.html
new file mode 100644
index 0000000000..47d76f388b
--- /dev/null
+++ b/src/vs/server/browser/workbench.html
@@ -0,0 +1,55 @@
+<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+
+ <!-- Disable pinch zooming -->
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
+
+ <!-- Workbench Configuration -->
+ <meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}">
+
+ <!-- Workarounds/Hacks (remote user data uri) -->
+ <meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
+ <!-- NOTE@coder: Added the commit for use in caching, the product for the
+ extensions gallery URL, and nls for language support. -->
+ <meta id="vscode-remote-commit" data-settings="{{COMMIT}}">
+ <meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}">
+ <meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
+
+ <!-- Workbench Icon/Manifest/CSS -->
+ <link rel="icon" href="../static/src/browser/media/favicon.ico" type="image/x-icon" />
+ <link rel="manifest" href="../static/src/browser/media/manifest.json" crossorigin="use-credentials">
+ <link rel="apple-touch-icon" href="./static/src/browser/media/code-server.png" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ </head>
+
+ <body aria-label="">
+ </body>
+
+ <!-- Startup (do not modify order of script tags!) -->
+ <script>
+ const basePath = window.location.pathname.replace(/\/+$/, '');
+ const base = window.location.origin + basePath;
+ const el = document.getElementById('vscode-remote-commit');
+ const commit = el ? el.getAttribute('data-settings') : "";
+ const staticBase = base + '/static-' + commit;
+ self.require = {
+ baseUrl: `${staticBase}/out`,
+ paths: {
+ 'vscode-textmate': `${staticBase}/node_modules/vscode-textmate/release/main`,
+ 'onigasm-umd': `${staticBase}/node_modules/onigasm-umd/release/main`,
+ 'xterm': `${staticBase}/node_modules/xterm/lib/xterm.js`,
+ 'xterm-addon-search': `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
+ 'xterm-addon-web-links': `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
+ 'xterm-addon-webgl': `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
+ 'semver-umd': `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
+ },
+ };
+ </script>
+ <script src="./static/out/vs/loader.js"></script>
+ <script>
+ require(['vs/code/browser/workbench/workbench'], function() {});
+ </script>
+</html>
diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts
new file mode 100644 new file mode 100644
index 0000000000..0ba93cc070 index 0000000000..0ba93cc070

View File

@ -19,7 +19,7 @@ export class MainHttpProvider extends HttpProvider {
): Promise<HttpResponse | undefined> { ): Promise<HttpResponse | undefined> {
if (base === "/static") { if (base === "/static") {
const response = await this.getResource(this.rootPath, requestPath) const response = await this.getResource(this.rootPath, requestPath)
if (this.options.commit && this.options.commit !== "development") { if (!this.isDev) {
response.cache = true response.cache = true
} }
return response return response
@ -41,7 +41,7 @@ export class MainHttpProvider extends HttpProvider {
const response = await this.getUtf8Resource(this.rootPath, "src/browser/index.html") const response = await this.getUtf8Resource(this.rootPath, "src/browser/index.html")
response.content = response.content response.content = response.content
.replace(/{{COMMIT}}/g, this.options.commit || "development") .replace(/{{COMMIT}}/g, this.options.commit)
.replace(/"{{OPTIONS}}"/g, `'${JSON.stringify(options)}'`) .replace(/"{{OPTIONS}}"/g, `'${JSON.stringify(options)}'`)
.replace(/{{COMPONENT}}/g, ReactDOMServer.renderToString(<App options={options} />)) .replace(/{{COMPONENT}}/g, ReactDOMServer.renderToString(<App options={options} />))
return response return response

View File

@ -23,7 +23,7 @@ const main = async (args: Args = {}): Promise<void> => {
const auth = args.auth || AuthType.Password const auth = args.auth || AuthType.Password
const originalPassword = auth === AuthType.Password && (process.env.PASSWORD || (await generatePassword())) const originalPassword = auth === AuthType.Password && (process.env.PASSWORD || (await generatePassword()))
let commit = "development" let commit: string | undefined
try { try {
commit = require("../../package.json").commit commit = require("../../package.json").commit
} catch (error) { } catch (error) {
@ -36,7 +36,7 @@ const main = async (args: Args = {}): Promise<void> => {
basePath: args["base-path"], basePath: args["base-path"],
cert: args.cert, cert: args.cert,
certKey: args["cert-key"], certKey: args["cert-key"],
commit, commit: commit || "development",
host: args.host || (args.auth === AuthType.Password && typeof args.cert !== "undefined" ? "0.0.0.0" : "localhost"), host: args.host || (args.auth === AuthType.Password && typeof args.cert !== "undefined" ? "0.0.0.0" : "localhost"),
password: originalPassword ? hash(originalPassword) : undefined, password: originalPassword ? hash(originalPassword) : undefined,
port: typeof args.port !== "undefined" ? parseInt(args.port, 10) : 8080, port: typeof args.port !== "undefined" ? parseInt(args.port, 10) : 8080,

View File

@ -112,7 +112,7 @@ export interface HttpProviderOptions {
readonly base: string readonly base: string
readonly auth: AuthType readonly auth: AuthType
readonly password?: string readonly password?: string
readonly commit?: string readonly commit: string
} }
/** /**
@ -150,6 +150,10 @@ export abstract class HttpProvider {
request: http.IncomingMessage request: http.IncomingMessage
): Promise<HttpResponse | undefined> ): Promise<HttpResponse | undefined>
protected get isDev(): boolean {
return this.options.commit === "development"
}
/** /**
* Return the specified path with the base path prepended. * Return the specified path with the base path prepended.
*/ */

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- <meta http-equiv="Content-Security-Policy" content="font-src 'self'; connect-src 'self'; default-src ws: wss:; style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:;"> -->
<title>code-server</title>
<link rel="icon" href="./static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="./static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
<link rel="apple-touch-icon" href="./static-{{COMMIT}}/src/browser/media/code-server.png" />
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap" rel="stylesheet" />
<meta id="coder-options" data-settings="{{OPTIONS}}">
</head>
<body>
<div id="root" style="color:#f4f4f4;padding:20px;max-width:700px;">
{{ERROR}}
</div>
<script>
if (parent) {
parent.postMessage({ event: "loaded" }, window.location.origin);
}
</script>
</body>
</html>

View File

@ -128,7 +128,11 @@ export class VscodeHttpProvider extends HttpProvider {
if (!this.authenticated(request)) { if (!this.authenticated(request)) {
return { redirect: "/login" } return { redirect: "/login" }
} }
return this.getRoot(request, query) try {
return await this.getRoot(request, query)
} catch (error) {
return this.getErrorRoot(error)
}
case "/static": { case "/static": {
switch (requestPath) { switch (requestPath) {
case "/out/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js": { case "/out/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js": {
@ -171,7 +175,7 @@ export class VscodeHttpProvider extends HttpProvider {
private async getRoot(request: http.IncomingMessage, query: querystring.ParsedUrlQuery): Promise<HttpResponse> { private async getRoot(request: http.IncomingMessage, query: querystring.ParsedUrlQuery): Promise<HttpResponse> {
const settings = await this.settings.read() const settings = await this.settings.read()
const [response, options] = await Promise.all([ const [response, options] = await Promise.all([
this.getUtf8Resource(this.serverRootPath, "browser/workbench.html"), await this.getUtf8Resource(this.rootPath, `src/node/vscode/workbench${!this.isDev ? "-build" : ""}.html`),
this.initialize({ this.initialize({
args: this.args, args: this.args,
query, query,
@ -201,4 +205,13 @@ export class VscodeHttpProvider extends HttpProvider {
.replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`), .replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`),
} }
} }
private async getErrorRoot(error: Error): Promise<HttpResponse> {
const response = await this.getUtf8Resource(this.rootPath, "src/node/vscode/error.html")
const message = `VS Code failed to load. ${
this.isDev ? "It might not have finished compiling (check for 'Finished compilation' in the output)." : ""
} <br><br>${error}`
response.content = response.content.replace(/{{COMMIT}}/g, this.options.commit).replace(/{{ERROR}}/g, message)
return response
}
} }

View File

@ -0,0 +1,92 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<!-- Disable pinch zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- Workbench Configuration -->
<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}">
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
<!-- NOTE@coder: Added the commit for use in caching, the product for the
extensions gallery URL, and nls for language support. -->
<meta id="vscode-remote-commit" data-settings="{{COMMIT}}">
<meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}">
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="./static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="./static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
<link rel="apple-touch-icon" href="./static-{{COMMIT}}/src/browser/media/code-server.png" />
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- Prefetch to avoid waterfall -->
<link rel="prefetch" href="./static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
</head>
<body aria-label="">
</body>
<!-- Startup (do not modify order of script tags!) -->
<!-- NOTE:coder: Modified to work against the current path and use the commit for caching. -->
<script>
// NOTE: Changes to inline scripts require update of content security policy
const basePath = window.location.pathname.replace(/\/+$/, '');
const base = window.location.origin + basePath;
const el = document.getElementById('vscode-remote-commit');
const commit = el ? el.getAttribute('data-settings') : "";
const staticBase = base + '/static-' + commit;
let nlsConfig;
try {
nlsConfig = JSON.parse(document.getElementById('vscode-remote-nls-configuration').getAttribute('data-settings'));
if (nlsConfig._resolvedLanguagePackCoreLocation) {
const bundles = Object.create(null);
nlsConfig.loadBundle = (bundle, language, cb) => {
let result = bundles[bundle];
if (result) {
return cb(undefined, result);
}
// FIXME: Only works if path separators are /.
const path = nlsConfig._resolvedLanguagePackCoreLocation
+ '/' + bundle.replace(/\//g, '!') + '.nls.json';
fetch(`${base}/resource/?path=${encodeURIComponent(path)}`)
.then((response) => response.json())
.then((json) => {
bundles[bundle] = json;
cb(undefined, json);
})
.catch(cb);
};
}
} catch (error) { /* Probably fine. */ }
self.require = {
baseUrl: `${staticBase}/out`,
paths: {
'vscode-textmate': `${staticBase}/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${staticBase}/node_modules/onigasm-umd/release/main`,
'xterm': `${staticBase}/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'xterm-addon-webgl': `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
},
'vs/nls': nlsConfig,
};
</script>
<script src="./static-{{COMMIT}}/out/vs/loader.js"></script>
<script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
<script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
<!-- TODO@coder: This errors with multiple anonymous define calls (one is
workbench.js and one is semver-umd.js). For now use the same method found in
workbench-dev.html. Appears related to the timing of the script load events. -->
<!-- <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
<script>
// NOTE: Changes to inline scripts require update of content security policy
require(['vs/code/browser/workbench/workbench'], function() {});
</script>
</html>

View File

@ -0,0 +1,55 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<!-- Disable pinch zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- Workbench Configuration -->
<meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}">
<!-- Workarounds/Hacks (remote user data uri) -->
<meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
<!-- NOTE@coder: Added the commit for use in caching, the product for the
extensions gallery URL, and nls for language support. -->
<meta id="vscode-remote-commit" data-settings="{{COMMIT}}">
<meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}">
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
<!-- Workbench Icon/Manifest/CSS -->
<link rel="icon" href="./static/src/browser/media/favicon.ico" type="image/x-icon" />
<link rel="manifest" href="./static/src/browser/media/manifest.json" crossorigin="use-credentials">
<link rel="apple-touch-icon" href="./static/src/browser/media/code-server.png" />
<meta name="apple-mobile-web-app-capable" content="yes">
</head>
<body aria-label="">
</body>
<!-- Startup (do not modify order of script tags!) -->
<script>
const basePath = window.location.pathname.replace(/\/+$/, '');
const base = window.location.origin + basePath;
const el = document.getElementById('vscode-remote-commit');
const commit = el ? el.getAttribute('data-settings') : "";
const staticBase = base + '/static-' + commit;
self.require = {
baseUrl: `${staticBase}/out`,
paths: {
'vscode-textmate': `${staticBase}/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${staticBase}/node_modules/onigasm-umd/release/main`,
'xterm': `${staticBase}/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'xterm-addon-webgl': `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
'semver-umd': `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
},
};
</script>
<script src="./static/out/vs/loader.js"></script>
<script>
require(['vs/code/browser/workbench/workbench'], function() {});
</script>
</html>