mirror of
https://github.com/langgenius/dify.git
synced 2026-03-17 12:57:51 +08:00
refactor: use hoisted modern monaco (#33540)
This commit is contained in:
12
web/app/components/base/modern-monaco/hoisted-config.ts
Normal file
12
web/app/components/base/modern-monaco/hoisted-config.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// This file is generated by scripts/hoist-modern-monaco.ts.
|
||||
// Do not edit it manually.
|
||||
|
||||
export const HOIST_BASE_PATH = '/hoisted-modern-monaco' as const
|
||||
export const TM_THEMES_VERSION = '1.12.1' as const
|
||||
export const TM_GRAMMARS_VERSION = '1.31.2' as const
|
||||
export const HOIST_THEME_IDS = ['light-plus', 'dark-plus'] as const
|
||||
export const HOIST_LANGUAGE_IDS = ['javascript', 'json', 'python', 'html', 'css'] as const
|
||||
export const MODERN_MONACO_IMPORT_MAP = {
|
||||
'modern-monaco/editor-core': '/hoisted-modern-monaco/modern-monaco/editor-core.mjs',
|
||||
'modern-monaco/lsp': '/hoisted-modern-monaco/modern-monaco/lsp/index.mjs',
|
||||
} as const
|
||||
38
web/app/components/base/modern-monaco/import-map.tsx
Normal file
38
web/app/components/base/modern-monaco/import-map.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { headers } from 'next/headers'
|
||||
import { env } from '@/env'
|
||||
import { MODERN_MONACO_IMPORT_MAP } from './hoisted-config'
|
||||
|
||||
function withBasePath(pathname: string) {
|
||||
return `${env.NEXT_PUBLIC_BASE_PATH}${pathname}`
|
||||
}
|
||||
|
||||
function getRequestOrigin(requestHeaders: Headers) {
|
||||
const protocol = requestHeaders.get('x-forwarded-proto') ?? 'http'
|
||||
const host = requestHeaders.get('x-forwarded-host') ?? requestHeaders.get('host')
|
||||
if (!host)
|
||||
return null
|
||||
return `${protocol}://${host}`
|
||||
}
|
||||
|
||||
const MonacoImportMap = async () => {
|
||||
const requestHeaders = await headers()
|
||||
const nonce = process.env.NODE_ENV === 'production' ? requestHeaders.get('x-nonce') ?? '' : ''
|
||||
const requestOrigin = getRequestOrigin(requestHeaders)
|
||||
const importMap = JSON.stringify({
|
||||
imports: Object.fromEntries(
|
||||
Object.entries(MODERN_MONACO_IMPORT_MAP).map(([specifier, pathname]) => {
|
||||
const modulePath = withBasePath(pathname)
|
||||
const moduleUrl = requestOrigin ? new URL(modulePath, requestOrigin).toString() : modulePath
|
||||
return [specifier, moduleUrl]
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
return (
|
||||
<script nonce={nonce || undefined} type="importmap" data-modern-monaco-importmap="">
|
||||
{importMap}
|
||||
</script>
|
||||
)
|
||||
}
|
||||
|
||||
export default MonacoImportMap
|
||||
@ -1,14 +1,24 @@
|
||||
import type { InitOptions } from 'modern-monaco'
|
||||
import { basePath } from '@/utils/var'
|
||||
import {
|
||||
HOIST_BASE_PATH,
|
||||
HOIST_LANGUAGE_IDS,
|
||||
HOIST_THEME_IDS,
|
||||
TM_GRAMMARS_VERSION,
|
||||
TM_THEMES_VERSION,
|
||||
} from './hoisted-config'
|
||||
|
||||
export const LIGHT_THEME_ID = 'light-plus'
|
||||
export const DARK_THEME_ID = 'dark-plus'
|
||||
|
||||
const assetPath = (pathname: string) => `${basePath}${HOIST_BASE_PATH}${pathname}`
|
||||
const themeAssetPath = (themeId: string) => assetPath(`/tm-themes@${TM_THEMES_VERSION}/themes/${themeId}.json`)
|
||||
const grammarAssetPath = (languageId: string) => assetPath(`/tm-grammars@${TM_GRAMMARS_VERSION}/grammars/${languageId}.json`)
|
||||
|
||||
const DEFAULT_INIT_OPTIONS: InitOptions = {
|
||||
defaultTheme: DARK_THEME_ID,
|
||||
themes: [
|
||||
LIGHT_THEME_ID,
|
||||
DARK_THEME_ID,
|
||||
],
|
||||
defaultTheme: themeAssetPath(DARK_THEME_ID),
|
||||
themes: HOIST_THEME_IDS.map(themeAssetPath),
|
||||
langs: HOIST_LANGUAGE_IDS.map(grammarAssetPath),
|
||||
}
|
||||
|
||||
let monacoInitPromise: Promise<typeof import('modern-monaco/editor-core') | null> | null = null
|
||||
|
||||
@ -8,6 +8,7 @@ import GlobalPublicStoreProvider from '@/context/global-public-context'
|
||||
import { TanstackQueryInitializer } from '@/context/query-client'
|
||||
import { getDatasetMap } from '@/env'
|
||||
import { getLocaleOnServer } from '@/i18n-config/server'
|
||||
import MonacoImportMap from './components/base/modern-monaco/import-map'
|
||||
import { ToastProvider } from './components/base/toast'
|
||||
import { TooltipProvider } from './components/base/ui/tooltip'
|
||||
import BrowserInitializer from './components/browser-initializer'
|
||||
@ -37,6 +38,7 @@ const LocaleLayout = async ({
|
||||
return (
|
||||
<html lang={locale ?? 'en'} className="h-full" suppressHydrationWarning>
|
||||
<head>
|
||||
<MonacoImportMap />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#1C64F2" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
"dev:vinext": "vinext dev",
|
||||
"gen-doc-paths": "tsx ./scripts/gen-doc-paths.ts",
|
||||
"gen-icons": "node ./scripts/gen-icons.mjs && eslint --fix app/components/base/icons/src/",
|
||||
"hoist:modern-monaco": "tsx ./scripts/hoist-modern-monaco.ts",
|
||||
"i18n:check": "tsx ./scripts/check-i18n.js",
|
||||
"knip": "knip",
|
||||
"lint": "eslint --cache --concurrency=auto",
|
||||
|
||||
129
web/public/hoisted-modern-monaco/modern-monaco/cache.mjs
Normal file
129
web/public/hoisted-modern-monaco/modern-monaco/cache.mjs
Normal file
@ -0,0 +1,129 @@
|
||||
// src/cache.ts
|
||||
import { defineProperty, normalizeURL, openIDB, promisifyIDBRequest } from "./util.mjs";
|
||||
var IndexedDB = class {
|
||||
#db;
|
||||
constructor(name) {
|
||||
this.#db = this.#openDB(name);
|
||||
}
|
||||
#openDB(name) {
|
||||
return openIDB(name, 1, { name: "store", keyPath: "url" }).then((db) => {
|
||||
db.onclose = () => {
|
||||
this.#db = this.#openDB(name);
|
||||
};
|
||||
return this.#db = db;
|
||||
});
|
||||
}
|
||||
async get(url) {
|
||||
const db = await this.#db;
|
||||
const tx = db.transaction("store", "readonly").objectStore("store");
|
||||
return promisifyIDBRequest(tx.get(url));
|
||||
}
|
||||
async put(file) {
|
||||
const db = await this.#db;
|
||||
const tx = db.transaction("store", "readwrite").objectStore("store");
|
||||
await promisifyIDBRequest(tx.put(file));
|
||||
}
|
||||
async delete(url) {
|
||||
const db = await this.#db;
|
||||
const tx = db.transaction("store", "readwrite").objectStore("store");
|
||||
await promisifyIDBRequest(tx.delete(url));
|
||||
}
|
||||
};
|
||||
var MemoryCache = class {
|
||||
#cache = /* @__PURE__ */ new Map();
|
||||
async get(url) {
|
||||
return this.#cache.get(url) ?? null;
|
||||
}
|
||||
async put(file) {
|
||||
this.#cache.set(file.url, file);
|
||||
}
|
||||
async delete(url) {
|
||||
this.#cache.delete(url);
|
||||
}
|
||||
};
|
||||
var Cache = class {
|
||||
_db;
|
||||
constructor(name) {
|
||||
if (globalThis.indexedDB) {
|
||||
this._db = new IndexedDB(name);
|
||||
} else {
|
||||
this._db = new MemoryCache();
|
||||
}
|
||||
}
|
||||
async fetch(url) {
|
||||
const storedRes = await this.query(url);
|
||||
if (storedRes) {
|
||||
return storedRes;
|
||||
}
|
||||
const res = await fetch(url);
|
||||
if (!res.ok || !res.headers.has("cache-control")) {
|
||||
return res;
|
||||
}
|
||||
const cacheControl = res.headers.get("cache-control");
|
||||
const maxAgeStr = cacheControl.match(/max-age=(\d+)/)?.[1];
|
||||
if (!maxAgeStr) {
|
||||
return res;
|
||||
}
|
||||
const maxAge = parseInt(maxAgeStr);
|
||||
if (isNaN(maxAge) || maxAge <= 0) {
|
||||
return res;
|
||||
}
|
||||
const createdAt = Date.now();
|
||||
const expiresAt = createdAt + maxAge * 1e3;
|
||||
const file = {
|
||||
url: res.url,
|
||||
content: null,
|
||||
createdAt,
|
||||
expiresAt,
|
||||
headers: []
|
||||
};
|
||||
if (res.redirected) {
|
||||
await this._db.put({
|
||||
...file,
|
||||
url: url instanceof URL ? url.href : url,
|
||||
// raw url
|
||||
headers: [["location", res.url]]
|
||||
});
|
||||
}
|
||||
for (const header of ["content-type", "x-typescript-types"]) {
|
||||
if (res.headers.has(header)) {
|
||||
file.headers.push([header, res.headers.get(header)]);
|
||||
}
|
||||
}
|
||||
file.content = await res.arrayBuffer();
|
||||
await this._db.put(file);
|
||||
const resp = new Response(file.content, { headers: file.headers });
|
||||
defineProperty(resp, "url", res.url);
|
||||
defineProperty(resp, "redirected", res.redirected);
|
||||
return resp;
|
||||
}
|
||||
async query(key) {
|
||||
const url = normalizeURL(key).href;
|
||||
const file = await this._db.get(url);
|
||||
if (file) {
|
||||
if (file.expiresAt < Date.now()) {
|
||||
await this._db.delete(url);
|
||||
return null;
|
||||
}
|
||||
const headers = new Headers(file.headers);
|
||||
if (headers.has("location")) {
|
||||
const redirectedUrl = headers.get("location");
|
||||
const res2 = await this.query(redirectedUrl);
|
||||
if (res2) {
|
||||
defineProperty(res2, "redirected", true);
|
||||
}
|
||||
return res2;
|
||||
}
|
||||
const res = new Response(file.content, { headers });
|
||||
defineProperty(res, "url", url);
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
var cache = new Cache("modern-monaco-cache");
|
||||
var cache_default = cache;
|
||||
export {
|
||||
cache,
|
||||
cache_default as default
|
||||
};
|
||||
441
web/public/hoisted-modern-monaco/modern-monaco/core.mjs
Normal file
441
web/public/hoisted-modern-monaco/modern-monaco/core.mjs
Normal file
@ -0,0 +1,441 @@
|
||||
// package.json
|
||||
var version = "0.4.0";
|
||||
|
||||
// src/core.ts
|
||||
import { getExtnameFromLanguageId, getLanguageIdFromPath, grammars, initShiki, setDefaultWasmLoader, themes } from "./shiki.mjs";
|
||||
import { initShikiMonacoTokenizer, registerShikiMonacoTokenizer } from "./shiki.mjs";
|
||||
import { render } from "./shiki.mjs";
|
||||
import { getWasmInstance } from "./shiki-wasm.mjs";
|
||||
import { NotFoundError, Workspace } from "./workspace.mjs";
|
||||
import { debunce, decode, isDigital } from "./util.mjs";
|
||||
var editorProps = [
|
||||
"autoDetectHighContrast",
|
||||
"automaticLayout",
|
||||
"contextmenu",
|
||||
"cursorBlinking",
|
||||
"cursorSmoothCaretAnimation",
|
||||
"cursorStyle",
|
||||
"cursorWidth",
|
||||
"fontFamily",
|
||||
"fontLigatures",
|
||||
"fontSize",
|
||||
"fontVariations",
|
||||
"fontWeight",
|
||||
"letterSpacing",
|
||||
"lineHeight",
|
||||
"lineNumbers",
|
||||
"lineNumbersMinChars",
|
||||
"matchBrackets",
|
||||
"minimap",
|
||||
"mouseStyle",
|
||||
"multiCursorModifier",
|
||||
"padding",
|
||||
"readOnly",
|
||||
"readOnlyMessage",
|
||||
"rulers",
|
||||
"scrollbar",
|
||||
"stickyScroll",
|
||||
"tabSize",
|
||||
"theme",
|
||||
"wordWrap"
|
||||
];
|
||||
var errors = {
|
||||
NotFound: NotFoundError
|
||||
};
|
||||
var syntaxes = [];
|
||||
var lspProviders = {};
|
||||
var getAttr = (el, name) => el.getAttribute(name);
|
||||
var setStyle = (el, style) => Object.assign(el.style, style);
|
||||
async function init(options) {
|
||||
const langs = (options?.langs ?? []).concat(syntaxes);
|
||||
const shiki = await initShiki({ ...options, langs });
|
||||
return loadMonaco(shiki, options?.workspace, options?.lsp);
|
||||
}
|
||||
function lazy(options) {
|
||||
if (!customElements.get("monaco-editor")) {
|
||||
let monacoPromise = null;
|
||||
customElements.define(
|
||||
"monaco-editor",
|
||||
class extends HTMLElement {
|
||||
async connectedCallback() {
|
||||
const workspace = options?.workspace;
|
||||
const renderOptions = {};
|
||||
for (const attrName of this.getAttributeNames()) {
|
||||
const key = editorProps.find((k) => k.toLowerCase() === attrName);
|
||||
if (key) {
|
||||
let value = getAttr(this, attrName);
|
||||
if (value === "") {
|
||||
value = key === "minimap" || key === "stickyScroll" ? { enabled: true } : true;
|
||||
} else {
|
||||
value = value.trim();
|
||||
if (value === "true") {
|
||||
value = true;
|
||||
} else if (value === "false") {
|
||||
value = false;
|
||||
} else if (value === "null") {
|
||||
value = null;
|
||||
} else if (/^\d+$/.test(value)) {
|
||||
value = Number(value);
|
||||
} else if (/^\{.+\}$/.test(value)) {
|
||||
try {
|
||||
value = JSON.parse(value);
|
||||
} catch (error) {
|
||||
value = void 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key === "padding") {
|
||||
if (typeof value === "number") {
|
||||
value = { top: value, bottom: value };
|
||||
} else if (/^\d+\s+\d+$/.test(value)) {
|
||||
const [top, bottom] = value.split(/\s+/);
|
||||
if (top && bottom) {
|
||||
value = { top: Number(top), bottom: Number(bottom) };
|
||||
}
|
||||
} else {
|
||||
value = void 0;
|
||||
}
|
||||
}
|
||||
if (key === "wordWrap" && (value === "on" || value === true)) {
|
||||
value = "on";
|
||||
}
|
||||
if (value !== void 0) {
|
||||
renderOptions[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
let filename;
|
||||
let code;
|
||||
const firstEl = this.firstElementChild;
|
||||
if (firstEl && firstEl.tagName === "SCRIPT" && firstEl.className === "monaco-editor-options") {
|
||||
try {
|
||||
const v = JSON.parse(firstEl.textContent);
|
||||
if (Array.isArray(v) && v.length === 2) {
|
||||
const [input, opts] = v;
|
||||
Object.assign(renderOptions, opts);
|
||||
if (opts.fontDigitWidth) {
|
||||
Reflect.set(globalThis, "__monaco_maxDigitWidth", opts.fontDigitWidth);
|
||||
}
|
||||
if (typeof input === "string") {
|
||||
code = input;
|
||||
} else {
|
||||
filename = input.filename;
|
||||
code = input.code;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
firstEl.remove();
|
||||
}
|
||||
setStyle(this, { display: "block", position: "relative" });
|
||||
let widthAttr = getAttr(this, "width");
|
||||
let heightAttr = getAttr(this, "height");
|
||||
if (isDigital(widthAttr) && isDigital(heightAttr)) {
|
||||
const width = Number(widthAttr);
|
||||
const height = Number(heightAttr);
|
||||
setStyle(this, { width: width + "px", height: height + "px" });
|
||||
renderOptions.dimension = { width, height };
|
||||
} else {
|
||||
if (isDigital(widthAttr)) {
|
||||
widthAttr += "px";
|
||||
}
|
||||
if (isDigital(heightAttr)) {
|
||||
heightAttr += "px";
|
||||
}
|
||||
this.style.width ||= widthAttr ?? "100%";
|
||||
this.style.height ||= heightAttr ?? "100%";
|
||||
}
|
||||
const containerEl = document.createElement("div");
|
||||
containerEl.className = "monaco-editor-container";
|
||||
setStyle(containerEl, { width: "100%", height: "100%" });
|
||||
this.appendChild(containerEl);
|
||||
if (!filename && workspace) {
|
||||
if (workspace.history.state.current) {
|
||||
filename = workspace.history.state.current;
|
||||
} else if (workspace.entryFile) {
|
||||
filename = workspace.entryFile;
|
||||
workspace.history.replace(filename);
|
||||
} else {
|
||||
const rootFiles = (await workspace.fs.readDirectory("/")).filter(([name, type]) => type === 1).map(([name]) => name);
|
||||
filename = rootFiles.includes("index.html") ? "index.html" : rootFiles[0];
|
||||
if (filename) {
|
||||
workspace.history.replace(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
const langs = (options?.langs ?? []).concat(syntaxes);
|
||||
if (renderOptions.language || filename) {
|
||||
const lang = renderOptions.language ?? getLanguageIdFromPath(filename) ?? "plaintext";
|
||||
if (!syntaxes.find((s) => s.name === lang)) {
|
||||
langs.push(lang);
|
||||
}
|
||||
}
|
||||
let theme = options?.theme ?? renderOptions.theme;
|
||||
if (typeof theme === "string") {
|
||||
theme = theme.toLowerCase().replace(/ +/g, "-");
|
||||
}
|
||||
const highlighter = await initShiki({ ...options, theme, langs });
|
||||
renderOptions.theme = highlighter.getLoadedThemes()[0];
|
||||
let prerenderEl;
|
||||
for (const el of this.children) {
|
||||
if (el.className === "monaco-editor-prerender") {
|
||||
prerenderEl = el;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!prerenderEl && filename && workspace) {
|
||||
try {
|
||||
const code2 = await workspace.fs.readFile(filename);
|
||||
const language = getLanguageIdFromPath(filename);
|
||||
prerenderEl = containerEl.cloneNode(true);
|
||||
prerenderEl.className = "monaco-editor-prerender";
|
||||
prerenderEl.innerHTML = render(highlighter, decode(code2), { ...renderOptions, language });
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) {
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prerenderEl) {
|
||||
setStyle(prerenderEl, { position: "absolute", top: "0", left: "0" });
|
||||
this.appendChild(prerenderEl);
|
||||
if (filename && workspace) {
|
||||
const viewState = await workspace.viewState.get(filename);
|
||||
const scrollTop = viewState?.viewState.scrollTop ?? 0;
|
||||
if (scrollTop) {
|
||||
const mockEl = prerenderEl.querySelector(".mock-monaco-editor");
|
||||
if (mockEl) {
|
||||
mockEl.scrollTop = scrollTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
const monaco = await (monacoPromise ?? (monacoPromise = loadMonaco(highlighter, workspace, options?.lsp)));
|
||||
const editor = monaco.editor.create(containerEl, renderOptions);
|
||||
if (workspace) {
|
||||
const storeViewState = () => {
|
||||
const currentModel = editor.getModel();
|
||||
if (currentModel?.uri.scheme === "file") {
|
||||
const state = editor.saveViewState();
|
||||
if (state) {
|
||||
state.viewState.scrollTop ??= editor.getScrollTop();
|
||||
workspace.viewState.save(currentModel.uri.toString(), Object.freeze(state));
|
||||
}
|
||||
}
|
||||
};
|
||||
editor.onDidChangeCursorSelection(debunce(storeViewState, 500));
|
||||
editor.onDidScrollChange(debunce(storeViewState, 500));
|
||||
workspace.history.onChange((state) => {
|
||||
if (editor.getModel()?.uri.toString() !== state.current) {
|
||||
workspace._openTextDocument(monaco, editor, state.current);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (filename && workspace) {
|
||||
try {
|
||||
const model = await workspace._openTextDocument(monaco, editor, filename);
|
||||
if (code && code !== model.getValue()) {
|
||||
model.setValue(code);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) {
|
||||
if (code) {
|
||||
const dirname = filename.split("/").slice(0, -1).join("/");
|
||||
if (dirname) {
|
||||
await workspace.fs.createDirectory(dirname);
|
||||
}
|
||||
await workspace.fs.writeFile(filename, code);
|
||||
workspace._openTextDocument(monaco, editor, filename);
|
||||
} else {
|
||||
editor.setModel(monaco.editor.createModel(""));
|
||||
}
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} else if (code && (renderOptions.language || filename)) {
|
||||
const modelUri = filename ? monaco.Uri.file(filename) : void 0;
|
||||
let model = modelUri ? monaco.editor.getModel(modelUri) : null;
|
||||
if (!model) {
|
||||
model = monaco.editor.createModel(code, renderOptions.language, modelUri);
|
||||
} else if (code !== model.getValue()) {
|
||||
model.setValue(code);
|
||||
}
|
||||
editor.setModel(model);
|
||||
} else {
|
||||
editor.setModel(monaco.editor.createModel(""));
|
||||
}
|
||||
if (prerenderEl) {
|
||||
setTimeout(() => {
|
||||
const animate = prerenderEl.animate?.([{ opacity: 1 }, { opacity: 0 }], { duration: 200 });
|
||||
if (animate) {
|
||||
animate.finished.then(() => prerenderEl.remove());
|
||||
} else {
|
||||
setTimeout(() => prerenderEl.remove(), 200);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
function hydrate(options) {
|
||||
return lazy(options);
|
||||
}
|
||||
async function loadMonaco(highlighter, workspace, lsp) {
|
||||
let cdnUrl = `https://esm.sh/modern-monaco@${version}`;
|
||||
let editorCoreModuleUrl = `${cdnUrl}/es2022/editor-core.mjs`;
|
||||
let lspModuleUrl = `${cdnUrl}/es2022/lsp.mjs`;
|
||||
let importmapEl = null;
|
||||
if (importmapEl = document.querySelector("script[type='importmap']")) {
|
||||
try {
|
||||
const { imports = {} } = JSON.parse(importmapEl.textContent);
|
||||
if (imports["modern-monaco/editor-core"]) {
|
||||
editorCoreModuleUrl = imports["modern-monaco/editor-core"];
|
||||
}
|
||||
if (imports["modern-monaco/lsp"]) {
|
||||
lspModuleUrl = imports["modern-monaco/lsp"];
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
const useBuiltinLSP = globalThis.MonacoEnvironment?.useBuiltinLSP;
|
||||
const [monaco, { builtinLSPProviders }] = await Promise.all([
|
||||
import(
|
||||
/* webpackIgnore: true */
|
||||
editorCoreModuleUrl
|
||||
),
|
||||
useBuiltinLSP ? import(
|
||||
/* webpackIgnore: true */
|
||||
lspModuleUrl
|
||||
) : Promise.resolve({ builtinLSPProviders: {} })
|
||||
]);
|
||||
const allLspProviders = { ...builtinLSPProviders, ...lspProviders, ...lsp?.providers };
|
||||
workspace?.setupMonaco(monaco);
|
||||
if (!document.getElementById("monaco-editor-core-css")) {
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.id = "monaco-editor-core-css";
|
||||
styleEl.media = "screen";
|
||||
styleEl.textContent = monaco.cssBundle;
|
||||
document.head.appendChild(styleEl);
|
||||
}
|
||||
Reflect.set(globalThis, "MonacoEnvironment", {
|
||||
getWorker: async (_workerId, label) => {
|
||||
if (label === "editorWorkerService") {
|
||||
return monaco.createEditorWorkerMain();
|
||||
}
|
||||
},
|
||||
getLanguageIdFromUri: (uri) => getLanguageIdFromPath(uri.path),
|
||||
getExtnameFromLanguageId
|
||||
});
|
||||
monaco.editor.registerLinkOpener({
|
||||
async open(link) {
|
||||
if ((link.scheme === "https" || link.scheme === "http") && monaco.editor.getModel(link)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
monaco.editor.registerEditorOpener({
|
||||
openCodeEditor: async (editor, resource, selectionOrPosition) => {
|
||||
if (workspace && resource.scheme === "file") {
|
||||
try {
|
||||
await workspace._openTextDocument(monaco, editor, resource.toString(), selectionOrPosition);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err instanceof NotFoundError) {
|
||||
return false;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const model = monaco.editor.getModel(resource);
|
||||
if (model) {
|
||||
editor.setModel(model);
|
||||
if (selectionOrPosition) {
|
||||
if ("startLineNumber" in selectionOrPosition) {
|
||||
editor.setSelection(selectionOrPosition);
|
||||
} else {
|
||||
editor.setPosition(selectionOrPosition);
|
||||
}
|
||||
const pos = editor.getPosition();
|
||||
if (pos) {
|
||||
const svp = editor.getScrolledVisiblePosition(new monaco.Position(pos.lineNumber - 7, pos.column));
|
||||
if (svp) {
|
||||
editor.setScrollTop(svp.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
const isHttpUrl = resource.scheme === "https" || resource.scheme === "http";
|
||||
editor.updateOptions({ readOnly: isHttpUrl });
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (globalThis.navigator?.userAgent?.includes("Macintosh")) {
|
||||
monaco.editor.addKeybindingRule({
|
||||
keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK,
|
||||
command: "editor.action.quickCommand"
|
||||
});
|
||||
}
|
||||
const allLanguages = new Set(grammars.filter((g) => !g.injectTo).map((g) => g.name));
|
||||
allLanguages.forEach((id) => {
|
||||
const languages = monaco.languages;
|
||||
languages.register({ id, aliases: grammars.find((g) => g.name === id)?.aliases });
|
||||
languages.onLanguage(id, async () => {
|
||||
const config = monaco.languageConfigurations[monaco.languageConfigurationAliases[id] ?? id];
|
||||
const loadedGrammars = new Set(highlighter.getLoadedLanguages());
|
||||
const reqiredGrammars = [id].concat(grammars.find((g) => g.name === id)?.embedded ?? []).filter((id2) => !loadedGrammars.has(id2));
|
||||
if (config) {
|
||||
languages.setLanguageConfiguration(id, monaco.convertVscodeLanguageConfiguration(config));
|
||||
}
|
||||
if (reqiredGrammars.length > 0) {
|
||||
await highlighter.loadGrammarFromCDN(...reqiredGrammars);
|
||||
}
|
||||
registerShikiMonacoTokenizer(monaco, highlighter, id);
|
||||
let lspLabel = id;
|
||||
let lspProvider = allLspProviders[lspLabel];
|
||||
if (!lspProvider) {
|
||||
const alias = Object.entries(allLspProviders).find(([, lsp2]) => lsp2.aliases?.includes(id));
|
||||
if (alias) {
|
||||
[lspLabel, lspProvider] = alias;
|
||||
}
|
||||
}
|
||||
if (lspProvider) {
|
||||
lspProvider.import().then(({ setup }) => setup(monaco, id, lsp?.[lspLabel], lsp?.formatting, workspace));
|
||||
}
|
||||
});
|
||||
});
|
||||
initShikiMonacoTokenizer(monaco, highlighter);
|
||||
return monaco;
|
||||
}
|
||||
function registerSyntax(syntax) {
|
||||
syntaxes.push(syntax);
|
||||
}
|
||||
function registerTheme(theme) {
|
||||
if (theme.name) {
|
||||
themes.set(theme.name, theme);
|
||||
}
|
||||
}
|
||||
function registerLSPProvider(lang, provider) {
|
||||
lspProviders[lang] = provider;
|
||||
}
|
||||
setDefaultWasmLoader(getWasmInstance);
|
||||
export {
|
||||
Workspace,
|
||||
errors,
|
||||
hydrate,
|
||||
init,
|
||||
lazy,
|
||||
registerLSPProvider,
|
||||
registerSyntax,
|
||||
registerTheme
|
||||
};
|
||||
205645
web/public/hoisted-modern-monaco/modern-monaco/editor-core.mjs
Normal file
205645
web/public/hoisted-modern-monaco/modern-monaco/editor-core.mjs
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,5 @@
|
||||
// src/editor-worker-main.ts
|
||||
import { start } from "./editor-worker.mjs";
|
||||
self.onmessage = (e) => {
|
||||
start(() => ({}));
|
||||
};
|
||||
14466
web/public/hoisted-modern-monaco/modern-monaco/editor-worker.mjs
Normal file
14466
web/public/hoisted-modern-monaco/modern-monaco/editor-worker.mjs
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,23 @@
|
||||
{
|
||||
"generatedAt": "2026-03-17T02:53:27.971Z",
|
||||
"modernMonacoVersion": "0.4.0",
|
||||
"tmGrammarsVersion": "1.31.2",
|
||||
"tmThemesVersion": "1.12.1",
|
||||
"typescriptVersion": "5.9.3",
|
||||
"localTypeScriptPath": "/hoisted-modern-monaco/typescript@5.9.3/es2022/typescript.mjs",
|
||||
"themes": [
|
||||
"light-plus",
|
||||
"dark-plus"
|
||||
],
|
||||
"languages": [
|
||||
"javascript",
|
||||
"json",
|
||||
"python",
|
||||
"html",
|
||||
"css"
|
||||
],
|
||||
"importMap": {
|
||||
"modern-monaco/editor-core": "/hoisted-modern-monaco/modern-monaco/editor-core.mjs",
|
||||
"modern-monaco/lsp": "/hoisted-modern-monaco/modern-monaco/lsp/index.mjs"
|
||||
}
|
||||
}
|
||||
29339
web/public/hoisted-modern-monaco/modern-monaco/index.mjs
Normal file
29339
web/public/hoisted-modern-monaco/modern-monaco/index.mjs
Normal file
File diff suppressed because one or more lines are too long
2267
web/public/hoisted-modern-monaco/modern-monaco/lsp/client.mjs
Normal file
2267
web/public/hoisted-modern-monaco/modern-monaco/lsp/client.mjs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,58 @@
|
||||
// src/lsp/css/setup.ts
|
||||
import * as client from "../client.mjs";
|
||||
async function setup(monaco, languageId, languageSettings, formattingOptions, workspace) {
|
||||
const validProperties = languageSettings?.validProperties;
|
||||
const dataProviders = { ...languageSettings?.dataProviders };
|
||||
if (validProperties) {
|
||||
dataProviders["#valid-properties"] = {
|
||||
version: 1.1,
|
||||
properties: validProperties.map((property) => ({ name: property }))
|
||||
};
|
||||
}
|
||||
const { tabSize, insertSpaces, insertFinalNewline, trimFinalNewlines } = formattingOptions ?? {};
|
||||
const createData = {
|
||||
language: languageId,
|
||||
data: {
|
||||
useDefaultDataProvider: languageSettings?.useDefaultDataProvider ?? true,
|
||||
dataProviders
|
||||
},
|
||||
format: {
|
||||
tabSize,
|
||||
insertFinalNewline,
|
||||
insertSpaces,
|
||||
preserveNewLines: !trimFinalNewlines,
|
||||
newlineBetweenSelectors: true,
|
||||
newlineBetweenRules: true,
|
||||
spaceAroundSelectorSeparator: false,
|
||||
braceStyle: "collapse"
|
||||
},
|
||||
fs: workspace ? await client.walkFS(workspace.fs, "/") : void 0
|
||||
};
|
||||
const worker = monaco.editor.createWebWorker({
|
||||
worker: getWorker(createData),
|
||||
host: client.createHost(workspace)
|
||||
});
|
||||
client.init(monaco);
|
||||
client.registerBasicFeatures(languageId, worker, ["/", "-", ":", "("], workspace, languageSettings?.diagnosticsOptions);
|
||||
client.registerCodeAction(languageId, worker);
|
||||
client.registerColorPresentation(languageId, worker);
|
||||
client.registerDocumentLinks(languageId, worker);
|
||||
}
|
||||
function createWebWorker() {
|
||||
const workerUrl = new URL("./worker.mjs", import.meta.url);
|
||||
if (workerUrl.origin !== location.origin) {
|
||||
return new Worker(
|
||||
URL.createObjectURL(new Blob([`import "${workerUrl.href}"`], { type: "application/javascript" })),
|
||||
{ type: "module", name: "css-worker" }
|
||||
);
|
||||
}
|
||||
return new Worker(workerUrl, { type: "module", name: "css-worker" });
|
||||
}
|
||||
function getWorker(createData) {
|
||||
const worker = createWebWorker();
|
||||
worker.postMessage(createData);
|
||||
return worker;
|
||||
}
|
||||
export {
|
||||
setup
|
||||
};
|
||||
59415
web/public/hoisted-modern-monaco/modern-monaco/lsp/css/worker.mjs
Normal file
59415
web/public/hoisted-modern-monaco/modern-monaco/lsp/css/worker.mjs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,110 @@
|
||||
// src/lsp/html/setup.ts
|
||||
import * as client from "../client.mjs";
|
||||
async function setup(monaco, languageId, languageSettings, formattingOptions, workspace) {
|
||||
const { editor, languages } = monaco;
|
||||
const { tabSize, insertSpaces, insertFinalNewline, trimFinalNewlines } = formattingOptions ?? {};
|
||||
const dataProviders = { ...languageSettings?.dataProviders };
|
||||
if (languageSettings?.customTags) {
|
||||
dataProviders["#custom-tags"] = { version: 1.1, tags: languageSettings.customTags };
|
||||
}
|
||||
const createData = {
|
||||
suggest: {
|
||||
attributeDefaultValue: languageSettings?.attributeDefaultValue,
|
||||
hideAutoCompleteProposals: languageSettings?.hideAutoCompleteProposals,
|
||||
hideEndTagSuggestions: languageSettings?.hideEndTagSuggestions
|
||||
},
|
||||
format: {
|
||||
tabSize,
|
||||
insertSpaces,
|
||||
endWithNewline: insertFinalNewline,
|
||||
preserveNewLines: !trimFinalNewlines,
|
||||
maxPreserveNewLines: 1,
|
||||
indentInnerHtml: false,
|
||||
indentHandlebars: false,
|
||||
unformatted: 'default": "a, abbr, acronym, b, bdo, big, br, button, cite, code, dfn, em, i, img, input, kbd, label, map, object, q, samp, select, small, span, strong, sub, sup, textarea, tt, var',
|
||||
contentUnformatted: "pre",
|
||||
// extraLiners: "head, body, /html",
|
||||
extraLiners: "",
|
||||
wrapAttributes: "auto"
|
||||
},
|
||||
data: {
|
||||
useDefaultDataProvider: languageSettings?.useDefaultDataProvider ?? true,
|
||||
dataProviders
|
||||
},
|
||||
fs: workspace ? await client.walkFS(workspace.fs, "/") : void 0
|
||||
};
|
||||
const htmlWorker = editor.createWebWorker({
|
||||
worker: getWorker(createData),
|
||||
host: client.createHost(workspace)
|
||||
});
|
||||
const workerWithEmbeddedLanguages = client.createWorkerWithEmbeddedLanguages(htmlWorker);
|
||||
client.init(monaco);
|
||||
client.registerEmbedded(languageId, workerWithEmbeddedLanguages, ["css", "javascript", "importmap"]);
|
||||
client.registerBasicFeatures(
|
||||
languageId,
|
||||
workerWithEmbeddedLanguages,
|
||||
["<", "/", "=", '"'],
|
||||
workspace,
|
||||
languageSettings?.diagnosticsOptions
|
||||
);
|
||||
client.registerAutoComplete(languageId, workerWithEmbeddedLanguages, [">", "/", "="]);
|
||||
client.registerColorPresentation(languageId, workerWithEmbeddedLanguages);
|
||||
client.registerDocumentLinks(languageId, workerWithEmbeddedLanguages);
|
||||
if (languageSettings?.importMapCodeLens ?? true) {
|
||||
languages.registerCodeLensProvider(languageId, {
|
||||
provideCodeLenses: (model, _token) => {
|
||||
const m = model.findNextMatch(
|
||||
`<script\\s[^>]*?type=['"]importmap['"]`,
|
||||
{ lineNumber: 4, column: 1 },
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
false
|
||||
);
|
||||
if (m) {
|
||||
const m2 = model.findNextMatch(
|
||||
`"imports":\\s*\\{`,
|
||||
m.range.getEndPosition(),
|
||||
true,
|
||||
false,
|
||||
null,
|
||||
false
|
||||
);
|
||||
return {
|
||||
lenses: [
|
||||
{
|
||||
range: (m2 ?? m).range,
|
||||
command: {
|
||||
id: "importmap:add-import",
|
||||
title: "$(sparkle-filled) Add import from esm.sh",
|
||||
tooltip: "Add Import",
|
||||
arguments: [model]
|
||||
}
|
||||
}
|
||||
],
|
||||
dispose: () => {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function createWebWorker() {
|
||||
const workerUrl = new URL("./worker.mjs", import.meta.url);
|
||||
if (workerUrl.origin !== location.origin) {
|
||||
return new Worker(
|
||||
URL.createObjectURL(new Blob([`import "${workerUrl.href}"`], { type: "application/javascript" })),
|
||||
{ type: "module", name: "html-worker" }
|
||||
);
|
||||
}
|
||||
return new Worker(workerUrl, { type: "module", name: "html-worker" });
|
||||
}
|
||||
function getWorker(createData) {
|
||||
const worker = createWebWorker();
|
||||
worker.postMessage(createData);
|
||||
return worker;
|
||||
}
|
||||
export {
|
||||
setup
|
||||
};
|
||||
22525
web/public/hoisted-modern-monaco/modern-monaco/lsp/html/worker.mjs
Normal file
22525
web/public/hoisted-modern-monaco/modern-monaco/lsp/html/worker.mjs
Normal file
File diff suppressed because one or more lines are too long
20
web/public/hoisted-modern-monaco/modern-monaco/lsp/index.mjs
Normal file
20
web/public/hoisted-modern-monaco/modern-monaco/lsp/index.mjs
Normal file
@ -0,0 +1,20 @@
|
||||
// src/lsp/index.ts
|
||||
var builtinLSPProviders = {
|
||||
html: {
|
||||
import: () => import("./html/setup.mjs")
|
||||
},
|
||||
css: {
|
||||
aliases: ["less", "sass"],
|
||||
import: () => import("./css/setup.mjs")
|
||||
},
|
||||
json: {
|
||||
import: () => import("./json/setup.mjs")
|
||||
},
|
||||
typescript: {
|
||||
aliases: ["javascript", "jsx", "tsx"],
|
||||
import: () => import("./typescript/setup.mjs")
|
||||
}
|
||||
};
|
||||
export {
|
||||
builtinLSPProviders
|
||||
};
|
||||
2740
web/public/hoisted-modern-monaco/modern-monaco/lsp/json/setup.mjs
Normal file
2740
web/public/hoisted-modern-monaco/modern-monaco/lsp/json/setup.mjs
Normal file
File diff suppressed because it is too large
Load Diff
8652
web/public/hoisted-modern-monaco/modern-monaco/lsp/json/worker.mjs
Normal file
8652
web/public/hoisted-modern-monaco/modern-monaco/lsp/json/worker.mjs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
web/public/hoisted-modern-monaco/modern-monaco/onig.wasm
Normal file
BIN
web/public/hoisted-modern-monaco/modern-monaco/onig.wasm
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
7932
web/public/hoisted-modern-monaco/modern-monaco/shiki.mjs
Normal file
7932
web/public/hoisted-modern-monaco/modern-monaco/shiki.mjs
Normal file
File diff suppressed because one or more lines are too long
42
web/public/hoisted-modern-monaco/modern-monaco/ssr/index.mjs
Normal file
42
web/public/hoisted-modern-monaco/modern-monaco/ssr/index.mjs
Normal file
@ -0,0 +1,42 @@
|
||||
// src/ssr/index.ts
|
||||
import { setDefaultWasmLoader } from "../shiki.mjs";
|
||||
import { getWasmInstance } from "../shiki-wasm.mjs";
|
||||
|
||||
// src/ssr/ssr.ts
|
||||
import { getLanguageIdFromPath, initShiki } from "../shiki.mjs";
|
||||
import { render } from "../shiki.mjs";
|
||||
var ssrHighlighter;
|
||||
async function renderToString(input, options) {
|
||||
const { language, theme, shiki } = options ?? {};
|
||||
const filename = typeof input === "string" ? void 0 : input.filename;
|
||||
const highlighter = await (ssrHighlighter ?? (ssrHighlighter = initShiki(shiki)));
|
||||
const promises = [];
|
||||
if (theme && !highlighter.getLoadedThemes().includes(theme)) {
|
||||
console.info(`[modern-monaco] Loading theme '${theme}' from CDN...`);
|
||||
promises.push(highlighter.loadThemeFromCDN(theme));
|
||||
}
|
||||
if (language || filename) {
|
||||
const languageId = language ?? getLanguageIdFromPath(filename);
|
||||
if (languageId && !highlighter.getLoadedLanguages().includes(languageId)) {
|
||||
console.info(
|
||||
`[modern-monaco] Loading grammar '${languageId}' from CDN...`
|
||||
);
|
||||
promises.push(highlighter.loadGrammarFromCDN(languageId));
|
||||
}
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
await Promise.all(promises);
|
||||
}
|
||||
return render(highlighter, input, options);
|
||||
}
|
||||
async function renderToWebComponent(input, options) {
|
||||
const prerender = await renderToString(input, options);
|
||||
return '<monaco-editor><script type="application/json" class="monaco-editor-options">' + JSON.stringify([input, options]).replaceAll("/", "\\/") + '<\/script><div class="monaco-editor-prerender" style="width:100%;height:100%;">' + prerender + "</div></monaco-editor>";
|
||||
}
|
||||
|
||||
// src/ssr/index.ts
|
||||
setDefaultWasmLoader(getWasmInstance);
|
||||
export {
|
||||
renderToString,
|
||||
renderToWebComponent
|
||||
};
|
||||
@ -0,0 +1,41 @@
|
||||
// src/ssr/workerd.ts
|
||||
import { setDefaultWasmLoader } from "../shiki.mjs";
|
||||
|
||||
// src/ssr/ssr.ts
|
||||
import { getLanguageIdFromPath, initShiki } from "../shiki.mjs";
|
||||
import { render } from "../shiki.mjs";
|
||||
var ssrHighlighter;
|
||||
async function renderToString(input, options) {
|
||||
const { language, theme, shiki } = options ?? {};
|
||||
const filename = typeof input === "string" ? void 0 : input.filename;
|
||||
const highlighter = await (ssrHighlighter ?? (ssrHighlighter = initShiki(shiki)));
|
||||
const promises = [];
|
||||
if (theme && !highlighter.getLoadedThemes().includes(theme)) {
|
||||
console.info(`[modern-monaco] Loading theme '${theme}' from CDN...`);
|
||||
promises.push(highlighter.loadThemeFromCDN(theme));
|
||||
}
|
||||
if (language || filename) {
|
||||
const languageId = language ?? getLanguageIdFromPath(filename);
|
||||
if (languageId && !highlighter.getLoadedLanguages().includes(languageId)) {
|
||||
console.info(
|
||||
`[modern-monaco] Loading grammar '${languageId}' from CDN...`
|
||||
);
|
||||
promises.push(highlighter.loadGrammarFromCDN(languageId));
|
||||
}
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
await Promise.all(promises);
|
||||
}
|
||||
return render(highlighter, input, options);
|
||||
}
|
||||
async function renderToWebComponent(input, options) {
|
||||
const prerender = await renderToString(input, options);
|
||||
return '<monaco-editor><script type="application/json" class="monaco-editor-options">' + JSON.stringify([input, options]).replaceAll("/", "\\/") + '<\/script><div class="monaco-editor-prerender" style="width:100%;height:100%;">' + prerender + "</div></monaco-editor>";
|
||||
}
|
||||
|
||||
// src/ssr/workerd.ts
|
||||
setDefaultWasmLoader(import("../onig.wasm"));
|
||||
export {
|
||||
renderToString,
|
||||
renderToWebComponent
|
||||
};
|
||||
198
web/public/hoisted-modern-monaco/modern-monaco/util.mjs
Normal file
198
web/public/hoisted-modern-monaco/modern-monaco/util.mjs
Normal file
@ -0,0 +1,198 @@
|
||||
// src/util.ts
|
||||
var enc = /* @__PURE__ */ new TextEncoder();
|
||||
var dec = /* @__PURE__ */ new TextDecoder();
|
||||
function encode(data) {
|
||||
return typeof data === "string" ? enc.encode(data) : data;
|
||||
}
|
||||
function decode(data) {
|
||||
return data instanceof Uint8Array ? dec.decode(data) : data;
|
||||
}
|
||||
function defineProperty(obj, prop, value) {
|
||||
Object.defineProperty(obj, prop, { value });
|
||||
}
|
||||
function normalizeURL(uri) {
|
||||
return uri instanceof URL ? uri : new URL(uri, "file:///");
|
||||
}
|
||||
function filenameToURL(filename) {
|
||||
if (filename.startsWith("file://")) {
|
||||
filename = filename.slice(7);
|
||||
}
|
||||
const url = new URL(filename.replace(/[\/\\]+/g, "/"), "file:///");
|
||||
if (url.pathname.endsWith("/")) {
|
||||
url.pathname = url.pathname.slice(0, -1);
|
||||
}
|
||||
url.search = "";
|
||||
return url;
|
||||
}
|
||||
function isPlainObject(v) {
|
||||
return typeof v === "object" && v !== null && v.constructor === Object;
|
||||
}
|
||||
function isDigital(v) {
|
||||
return typeof v === "number" || typeof v === "string" && /^\d+$/.test(v);
|
||||
}
|
||||
function debunce(fn, delay) {
|
||||
let timer = null;
|
||||
return () => {
|
||||
if (timer !== null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
fn();
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
function createPersistTask(persist, delay = 500) {
|
||||
let timer = null;
|
||||
const askToExit = (e) => {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
};
|
||||
return () => {
|
||||
if (timer !== null) {
|
||||
return;
|
||||
}
|
||||
addEventListener("beforeunload", askToExit);
|
||||
timer = setTimeout(async () => {
|
||||
timer = null;
|
||||
await persist();
|
||||
removeEventListener("beforeunload", askToExit);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
function createSyncPersistTask(persist, delay = 500) {
|
||||
let timer = null;
|
||||
return () => {
|
||||
if (timer !== null) {
|
||||
return;
|
||||
}
|
||||
addEventListener("beforeunload", persist);
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
removeEventListener("beforeunload", persist);
|
||||
persist();
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
function createPersistStateStorage(storeKey, defaultValue) {
|
||||
let state;
|
||||
const init = defaultValue ?? {};
|
||||
const storeValue = localStorage.getItem(storeKey);
|
||||
if (storeValue) {
|
||||
try {
|
||||
for (const [key, value] of Object.entries(JSON.parse(storeValue))) {
|
||||
init[key] = Object.freeze(value);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
const persist = createSyncPersistTask(() => localStorage.setItem(storeKey, JSON.stringify(state)), 1e3);
|
||||
return state = createProxy(init, persist);
|
||||
}
|
||||
function createProxy(obj, onChange) {
|
||||
let filled = false;
|
||||
const proxy = new Proxy(/* @__PURE__ */ Object.create(null), {
|
||||
get(target, key) {
|
||||
return Reflect.get(target, key);
|
||||
},
|
||||
set(target, key, value) {
|
||||
if (isPlainObject(value) && !Object.isFrozen(value)) {
|
||||
value = createProxy(value, onChange);
|
||||
}
|
||||
const ok = Reflect.set(target, key, value);
|
||||
if (ok && filled) {
|
||||
onChange();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
});
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
proxy[key] = value;
|
||||
}
|
||||
filled = true;
|
||||
return proxy;
|
||||
}
|
||||
function supportLocalStorage() {
|
||||
if (globalThis.localStorage) {
|
||||
try {
|
||||
localStorage.setItem("..", "");
|
||||
localStorage.removeItem("..");
|
||||
return true;
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function promisifyIDBRequest(req) {
|
||||
return new Promise((resolve, reject) => {
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
async function openIDB(name, version = 1, ...stores) {
|
||||
const openRequest = indexedDB.open(name, version);
|
||||
const promises = [];
|
||||
openRequest.onupgradeneeded = () => {
|
||||
const db2 = openRequest.result;
|
||||
for (const { name: name2, keyPath, onCreate } of stores) {
|
||||
if (!db2.objectStoreNames.contains(name2)) {
|
||||
const store = db2.createObjectStore(name2, { keyPath });
|
||||
if (onCreate) {
|
||||
promises.push(onCreate(store));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const db = await promisifyIDBRequest(openRequest);
|
||||
await Promise.all(promises);
|
||||
return db;
|
||||
}
|
||||
function openIDBCursor(store, range, callback, direction) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const corsor = store.openCursor(range, direction);
|
||||
corsor.onsuccess = () => {
|
||||
const cursor = corsor.result;
|
||||
if (cursor) {
|
||||
if (callback(cursor) !== false) {
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
};
|
||||
corsor.onerror = () => {
|
||||
reject(corsor.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
function promiseWithResolvers() {
|
||||
if (Promise.withResolvers) {
|
||||
return Promise.withResolvers();
|
||||
}
|
||||
const p = /* @__PURE__ */ Object.create(null);
|
||||
p.promise = new Promise((resolve, reject) => {
|
||||
p.resolve = resolve;
|
||||
p.reject = reject;
|
||||
});
|
||||
return p;
|
||||
}
|
||||
export {
|
||||
createPersistStateStorage,
|
||||
createPersistTask,
|
||||
createProxy,
|
||||
createSyncPersistTask,
|
||||
debunce,
|
||||
decode,
|
||||
defineProperty,
|
||||
encode,
|
||||
filenameToURL,
|
||||
isDigital,
|
||||
isPlainObject,
|
||||
normalizeURL,
|
||||
openIDB,
|
||||
openIDBCursor,
|
||||
promiseWithResolvers,
|
||||
promisifyIDBRequest,
|
||||
supportLocalStorage
|
||||
};
|
||||
579
web/public/hoisted-modern-monaco/modern-monaco/workspace.mjs
Normal file
579
web/public/hoisted-modern-monaco/modern-monaco/workspace.mjs
Normal file
@ -0,0 +1,579 @@
|
||||
// src/workspace.ts
|
||||
import {
|
||||
createPersistStateStorage,
|
||||
createPersistTask,
|
||||
decode,
|
||||
encode,
|
||||
filenameToURL,
|
||||
normalizeURL,
|
||||
openIDB,
|
||||
openIDBCursor,
|
||||
promiseWithResolvers,
|
||||
promisifyIDBRequest,
|
||||
supportLocalStorage
|
||||
} from "./util.mjs";
|
||||
var NotFoundError = class extends Error {
|
||||
FS_ERROR = "NOT_FOUND";
|
||||
constructor(message) {
|
||||
super("No such file or directory: " + message);
|
||||
}
|
||||
};
|
||||
var Workspace = class {
|
||||
_monaco;
|
||||
_history;
|
||||
_fs;
|
||||
_viewState;
|
||||
_entryFile;
|
||||
constructor(options = {}) {
|
||||
const { name = "default", browserHistory, initialFiles, entryFile, customFS } = options;
|
||||
this._monaco = promiseWithResolvers();
|
||||
this._fs = customFS ?? new IndexedDBFileSystem("modern-monaco-workspace(" + name + ")");
|
||||
this._viewState = new WorkspaceStateStorage("modern-monaco-state(" + name + ")");
|
||||
this._entryFile = entryFile;
|
||||
if (initialFiles) {
|
||||
for (const [name2, data] of Object.entries(initialFiles)) {
|
||||
void this._fs.stat(name2).catch(async (err) => {
|
||||
if (err instanceof NotFoundError) {
|
||||
const { pathname } = filenameToURL(name2);
|
||||
const dir = pathname.slice(0, pathname.lastIndexOf("/"));
|
||||
if (dir) {
|
||||
await this._fs.createDirectory(dir);
|
||||
}
|
||||
await this._fs.writeFile(name2, data);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (browserHistory) {
|
||||
if (!globalThis.history) {
|
||||
throw new Error("Browser history is not supported.");
|
||||
}
|
||||
this._history = new BrowserHistory(browserHistory === true ? "/" : browserHistory.basePath);
|
||||
} else {
|
||||
this._history = new LocalStorageHistory(name);
|
||||
}
|
||||
}
|
||||
setupMonaco(monaco) {
|
||||
this._monaco.resolve(monaco);
|
||||
}
|
||||
get entryFile() {
|
||||
return this._entryFile;
|
||||
}
|
||||
get fs() {
|
||||
return this._fs;
|
||||
}
|
||||
get history() {
|
||||
return this._history;
|
||||
}
|
||||
get viewState() {
|
||||
return this._viewState;
|
||||
}
|
||||
async openTextDocument(uri, content, editor) {
|
||||
const monaco = await this._monaco.promise;
|
||||
const getEditor = async () => {
|
||||
const editors = monaco.editor.getEditors();
|
||||
const editor2 = editors.find((e) => e.hasWidgetFocus() || e.hasTextFocus()) ?? editors[0];
|
||||
if (!editor2) {
|
||||
return new Promise((resolve) => setTimeout(() => resolve(getEditor()), 100));
|
||||
}
|
||||
return editor2;
|
||||
};
|
||||
return this._openTextDocument(monaco, editor ?? await getEditor(), uri, void 0, content);
|
||||
}
|
||||
async _openTextDocument(monaco, editor, uri, selectionOrPosition, readonlyContent) {
|
||||
const fs = this._fs;
|
||||
const href = normalizeURL(uri).href;
|
||||
const content = readonlyContent ?? await fs.readTextFile(href);
|
||||
const viewState = await this.viewState.get(href);
|
||||
const modelUri = monaco.Uri.parse(href);
|
||||
const model = monaco.editor.getModel(modelUri) ?? monaco.editor.createModel(content, void 0, modelUri);
|
||||
if (!Reflect.has(model, "__OB__") && typeof readonlyContent !== "string") {
|
||||
const persist = createPersistTask(() => fs.writeFile(href, model.getValue(), { isModelContentChange: true }));
|
||||
const disposable = model.onDidChangeContent(persist);
|
||||
const unwatch = fs.watch(href, (kind, _, __, context) => {
|
||||
if (kind === "modify" && (!context || !context.isModelContentChange)) {
|
||||
fs.readTextFile(href).then((content2) => {
|
||||
if (model.getValue() !== content2) {
|
||||
model.setValue(content2);
|
||||
model.pushStackElement();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
model.onWillDispose(() => {
|
||||
Reflect.deleteProperty(model, "__OB__");
|
||||
disposable.dispose();
|
||||
unwatch();
|
||||
});
|
||||
Reflect.set(model, "__OB__", true);
|
||||
}
|
||||
editor.setModel(model);
|
||||
editor.updateOptions({ readOnly: typeof readonlyContent === "string" });
|
||||
if (typeof readonlyContent === "string") {
|
||||
const disposable = editor.onDidChangeModel(() => {
|
||||
model.dispose();
|
||||
disposable.dispose();
|
||||
});
|
||||
}
|
||||
if (selectionOrPosition) {
|
||||
if ("startLineNumber" in selectionOrPosition) {
|
||||
editor.setSelection(selectionOrPosition);
|
||||
} else {
|
||||
editor.setPosition(selectionOrPosition);
|
||||
}
|
||||
const pos = editor.getPosition();
|
||||
if (pos) {
|
||||
const svp = editor.getScrolledVisiblePosition(new monaco.Position(pos.lineNumber - 7, pos.column));
|
||||
if (svp) {
|
||||
editor.setScrollTop(svp.top);
|
||||
}
|
||||
}
|
||||
} else if (viewState) {
|
||||
editor.restoreViewState(viewState);
|
||||
}
|
||||
if (this._history.state.current !== href) {
|
||||
this._history.push(href);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
async showInputBox(options, token) {
|
||||
const monaco = await this._monaco.promise;
|
||||
return monaco.showInputBox(options, token);
|
||||
}
|
||||
async showQuickPick(items, options, token) {
|
||||
const monaco = await this._monaco.promise;
|
||||
return monaco.showQuickPick(items, options, token);
|
||||
}
|
||||
};
|
||||
var IndexedDBFileSystem = class {
|
||||
_watchers = /* @__PURE__ */ new Set();
|
||||
_db;
|
||||
constructor(scope) {
|
||||
this._db = new WorkspaceDatabase(
|
||||
scope,
|
||||
{ name: "fs-meta", keyPath: "url" },
|
||||
{ name: "fs-blob", keyPath: "url" }
|
||||
);
|
||||
}
|
||||
async _getIdbObjectStore(storeName, readwrite = false) {
|
||||
const db = await this._db.open();
|
||||
return db.transaction(storeName, readwrite ? "readwrite" : "readonly").objectStore(storeName);
|
||||
}
|
||||
async _getIdbObjectStores(readwrite = false) {
|
||||
const transaction = (await this._db.open()).transaction(["fs-meta", "fs-blob"], readwrite ? "readwrite" : "readonly");
|
||||
return [transaction.objectStore("fs-meta"), transaction.objectStore("fs-blob")];
|
||||
}
|
||||
async stat(name) {
|
||||
const url = filenameToURL(name).href;
|
||||
if (url === "file:///") {
|
||||
return { type: 2, version: 1, ctime: 0, mtime: 0, size: 0 };
|
||||
}
|
||||
const metaStore = await this._getIdbObjectStore("fs-meta");
|
||||
const stat = await promisifyIDBRequest(metaStore.get(url));
|
||||
if (!stat) {
|
||||
throw new NotFoundError(url);
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
async createDirectory(name) {
|
||||
const { pathname, href: url } = filenameToURL(name);
|
||||
const metaStore = await this._getIdbObjectStore("fs-meta", true);
|
||||
const exists = (url2) => promisifyIDBRequest(metaStore.get(url2)).then(Boolean);
|
||||
if (await exists(url)) return;
|
||||
const now = Date.now();
|
||||
const promises = [];
|
||||
const newDirs = [];
|
||||
let parent = pathname.slice(0, pathname.lastIndexOf("/"));
|
||||
while (parent) {
|
||||
const parentUrl = filenameToURL(parent).href;
|
||||
if (!await exists(parentUrl)) {
|
||||
const stat2 = { type: 2, version: 1, ctime: now, mtime: now, size: 0 };
|
||||
promises.push(promisifyIDBRequest(metaStore.add({ url: parentUrl, ...stat2 })));
|
||||
newDirs.push(parent);
|
||||
}
|
||||
parent = parent.slice(0, parent.lastIndexOf("/"));
|
||||
}
|
||||
const stat = { type: 2, version: 1, ctime: now, mtime: now, size: 0 };
|
||||
promises.push(promisifyIDBRequest(metaStore.add({ url, ...stat })));
|
||||
newDirs.push(pathname);
|
||||
await Promise.all(promises);
|
||||
for (const dir of newDirs) {
|
||||
this._notify("create", dir, 2);
|
||||
}
|
||||
}
|
||||
async readDirectory(name) {
|
||||
const { pathname } = filenameToURL(name);
|
||||
const stat = await this.stat(name);
|
||||
if (stat.type !== 2) {
|
||||
throw new Error(`read ${pathname}: not a directory`);
|
||||
}
|
||||
const metaStore = await this._getIdbObjectStore("fs-meta");
|
||||
const entries = [];
|
||||
const dir = "file://" + pathname + (pathname.endsWith("/") ? "" : "/");
|
||||
await openIDBCursor(metaStore, IDBKeyRange.lowerBound(dir, true), (cursor) => {
|
||||
const stat2 = cursor.value;
|
||||
if (stat2.url.startsWith(dir)) {
|
||||
const name2 = stat2.url.slice(dir.length);
|
||||
if (name2 !== "" && name2.indexOf("/") === -1) {
|
||||
entries.push([name2, stat2.type]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
async readFile(name) {
|
||||
const url = filenameToURL(name).href;
|
||||
const blobStore = await this._getIdbObjectStore("fs-blob");
|
||||
const file = await promisifyIDBRequest(blobStore.get(url));
|
||||
if (!file) {
|
||||
throw new NotFoundError(url);
|
||||
}
|
||||
return file.content;
|
||||
}
|
||||
async readTextFile(filename) {
|
||||
return this.readFile(filename).then(decode);
|
||||
}
|
||||
async writeFile(name, content, context) {
|
||||
const { pathname, href: url } = filenameToURL(name);
|
||||
const dir = pathname.slice(0, pathname.lastIndexOf("/"));
|
||||
if (dir) {
|
||||
try {
|
||||
if ((await this.stat(dir)).type !== 2) {
|
||||
throw new Error(`write ${pathname}: not a directory`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) {
|
||||
throw new Error(`write ${pathname}: no such file or directory`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
let oldStat = null;
|
||||
try {
|
||||
oldStat = await this.stat(url);
|
||||
} catch (error) {
|
||||
if (!(error instanceof NotFoundError)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (oldStat?.type === 2) {
|
||||
throw new Error(`write ${pathname}: is a directory`);
|
||||
}
|
||||
content = typeof content === "string" ? encode(content) : content;
|
||||
const now = Date.now();
|
||||
const newStat = {
|
||||
type: 1,
|
||||
version: (oldStat?.version ?? 0) + 1,
|
||||
ctime: oldStat?.ctime ?? now,
|
||||
mtime: now,
|
||||
size: content.byteLength
|
||||
};
|
||||
const [metaStore, blobStore] = await this._getIdbObjectStores(true);
|
||||
await Promise.all([
|
||||
promisifyIDBRequest(metaStore.put({ url, ...newStat })),
|
||||
promisifyIDBRequest(blobStore.put({ url, content }))
|
||||
]);
|
||||
this._notify(oldStat ? "modify" : "create", pathname, 1, context);
|
||||
}
|
||||
async delete(name, options) {
|
||||
const { pathname, href: url } = filenameToURL(name);
|
||||
const stat = await this.stat(url);
|
||||
if (stat.type === 1) {
|
||||
const [metaStore, blobStore] = await this._getIdbObjectStores(true);
|
||||
await Promise.all([
|
||||
promisifyIDBRequest(metaStore.delete(url)),
|
||||
promisifyIDBRequest(blobStore.delete(url))
|
||||
]);
|
||||
this._notify("remove", pathname, 1);
|
||||
} else if (stat.type === 2) {
|
||||
if (options?.recursive) {
|
||||
const promises = [];
|
||||
const [metaStore, blobStore] = await this._getIdbObjectStores(true);
|
||||
const deleted = [];
|
||||
promises.push(openIDBCursor(metaStore, IDBKeyRange.lowerBound(url), (cursor) => {
|
||||
const stat2 = cursor.value;
|
||||
if (stat2.url.startsWith(url)) {
|
||||
if (stat2.type === 1) {
|
||||
promises.push(promisifyIDBRequest(blobStore.delete(stat2.url)));
|
||||
}
|
||||
promises.push(promisifyIDBRequest(cursor.delete()));
|
||||
deleted.push([stat2.url, stat2.type]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
await Promise.all(promises);
|
||||
for (const [url2, type] of deleted) {
|
||||
this._notify("remove", new URL(url2).pathname, type);
|
||||
}
|
||||
} else {
|
||||
const entries = await this.readDirectory(url);
|
||||
if (entries.length > 0) {
|
||||
throw new Error(`delete ${url}: directory not empty`);
|
||||
}
|
||||
const metaStore = await this._getIdbObjectStore("fs-meta", true);
|
||||
await promisifyIDBRequest(metaStore.delete(url));
|
||||
this._notify("remove", pathname, 2);
|
||||
}
|
||||
} else {
|
||||
const metaStore = await this._getIdbObjectStore("fs-meta", true);
|
||||
await promisifyIDBRequest(metaStore.delete(url));
|
||||
this._notify("remove", pathname, stat.type);
|
||||
}
|
||||
}
|
||||
async copy(source, target, options) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
async rename(oldName, newName, options) {
|
||||
const { href: oldUrl, pathname: oldPath } = filenameToURL(oldName);
|
||||
const { href: newUrl, pathname: newPath } = filenameToURL(newName);
|
||||
const oldStat = await this.stat(oldUrl);
|
||||
try {
|
||||
const stat = await this.stat(newUrl);
|
||||
if (!options?.overwrite) {
|
||||
throw new Error(`rename ${oldUrl} to ${newUrl}: file exists`);
|
||||
}
|
||||
await this.delete(newUrl, stat.type === 2 ? { recursive: true } : void 0);
|
||||
} catch (error) {
|
||||
if (!(error instanceof NotFoundError)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const newPathDirname = newPath.slice(0, newPath.lastIndexOf("/"));
|
||||
if (newPathDirname) {
|
||||
try {
|
||||
if ((await this.stat(newPathDirname)).type !== 2) {
|
||||
throw new Error(`rename ${oldUrl} to ${newUrl}: Not a directory`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) {
|
||||
throw new Error(`rename ${oldUrl} to ${newUrl}: No such file or directory`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
const [metaStore, blobStore] = await this._getIdbObjectStores(true);
|
||||
const promises = [
|
||||
promisifyIDBRequest(metaStore.delete(oldUrl)),
|
||||
promisifyIDBRequest(metaStore.put({ ...oldStat, url: newUrl }))
|
||||
];
|
||||
const renameBlob = (oldUrl2, newUrl2) => openIDBCursor(blobStore, IDBKeyRange.only(oldUrl2), (cursor) => {
|
||||
promises.push(promisifyIDBRequest(blobStore.put({ url: newUrl2, content: cursor.value.content })));
|
||||
promises.push(promisifyIDBRequest(cursor.delete()));
|
||||
});
|
||||
const moved = [[oldPath, newPath, oldStat.type]];
|
||||
if (oldStat.type === 1) {
|
||||
promises.push(renameBlob(oldUrl, newUrl));
|
||||
} else if (oldStat.type === 2) {
|
||||
let dirUrl = oldUrl;
|
||||
if (!dirUrl.endsWith("/")) {
|
||||
dirUrl += "/";
|
||||
}
|
||||
const renamingChildren = openIDBCursor(
|
||||
metaStore,
|
||||
IDBKeyRange.lowerBound(dirUrl, true),
|
||||
(cursor) => {
|
||||
const stat = cursor.value;
|
||||
if (stat.url.startsWith(dirUrl)) {
|
||||
const url = newUrl + stat.url.slice(dirUrl.length - 1);
|
||||
if (stat.type === 1) {
|
||||
promises.push(renameBlob(stat.url, url));
|
||||
}
|
||||
promises.push(promisifyIDBRequest(metaStore.put({ ...stat, url })));
|
||||
promises.push(promisifyIDBRequest(cursor.delete()));
|
||||
moved.push([new URL(stat.url).pathname, new URL(url).pathname, stat.type]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
promises.push(renamingChildren);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
for (const [oldPath2, newPath2, type] of moved) {
|
||||
this._notify("remove", oldPath2, type);
|
||||
this._notify("create", newPath2, type);
|
||||
}
|
||||
}
|
||||
watch(filename, handleOrOptions, handle) {
|
||||
const options = typeof handleOrOptions === "function" ? void 0 : handleOrOptions;
|
||||
handle = typeof handleOrOptions === "function" ? handleOrOptions : handle;
|
||||
if (typeof handle !== "function") {
|
||||
throw new TypeError("handle must be a function");
|
||||
}
|
||||
const watcher = { pathname: filenameToURL(filename).pathname, recursive: options?.recursive ?? false, handle };
|
||||
this._watchers.add(watcher);
|
||||
return () => {
|
||||
this._watchers.delete(watcher);
|
||||
};
|
||||
}
|
||||
async _notify(kind, pathname, type, context) {
|
||||
for (const watcher of this._watchers) {
|
||||
if (watcher.pathname === pathname || watcher.recursive && (watcher.pathname === "/" || pathname.startsWith(watcher.pathname + "/"))) {
|
||||
watcher.handle(kind, pathname, type, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var WorkspaceDatabase = class {
|
||||
_db;
|
||||
constructor(name, ...stores) {
|
||||
const open = () => openIDB(name, 1, ...stores).then((db) => {
|
||||
db.onclose = () => {
|
||||
this._db = open();
|
||||
};
|
||||
return this._db = db;
|
||||
});
|
||||
this._db = open();
|
||||
}
|
||||
async open() {
|
||||
return await this._db;
|
||||
}
|
||||
};
|
||||
var WorkspaceStateStorage = class {
|
||||
#db;
|
||||
constructor(dbName) {
|
||||
this.#db = new WorkspaceDatabase(
|
||||
dbName,
|
||||
{
|
||||
name: "store",
|
||||
keyPath: "url"
|
||||
}
|
||||
);
|
||||
}
|
||||
async get(uri) {
|
||||
const url = normalizeURL(uri).href;
|
||||
const store = (await this.#db.open()).transaction("store", "readonly").objectStore("store");
|
||||
return promisifyIDBRequest(store.get(url)).then((result) => result?.state);
|
||||
}
|
||||
async save(uri, state) {
|
||||
const url = normalizeURL(uri).href;
|
||||
const store = (await this.#db.open()).transaction("store", "readwrite").objectStore("store");
|
||||
await promisifyIDBRequest(store.put({ url, state }));
|
||||
}
|
||||
};
|
||||
var LocalStorageHistory = class {
|
||||
_state;
|
||||
_maxHistory;
|
||||
_handlers = /* @__PURE__ */ new Set();
|
||||
constructor(scope, maxHistory = 100) {
|
||||
const defaultState = { "current": -1, "history": [] };
|
||||
this._state = supportLocalStorage() ? createPersistStateStorage("modern-monaco-workspace-history:" + scope, defaultState) : defaultState;
|
||||
this._maxHistory = maxHistory;
|
||||
}
|
||||
_onPopState() {
|
||||
for (const handler of this._handlers) {
|
||||
handler(this.state);
|
||||
}
|
||||
}
|
||||
get state() {
|
||||
return { current: this._state.history[this._state.current] ?? "" };
|
||||
}
|
||||
back() {
|
||||
this._state.current--;
|
||||
if (this._state.current < 0) {
|
||||
this._state.current = 0;
|
||||
}
|
||||
this._onPopState();
|
||||
}
|
||||
forward() {
|
||||
this._state.current++;
|
||||
if (this._state.current >= this._state.history.length) {
|
||||
this._state.current = this._state.history.length - 1;
|
||||
}
|
||||
this._onPopState();
|
||||
}
|
||||
push(name) {
|
||||
const url = filenameToURL(name);
|
||||
const history2 = this._state.history.slice(0, this._state.current + 1);
|
||||
history2.push(url.href);
|
||||
if (history2.length > this._maxHistory) {
|
||||
history2.shift();
|
||||
}
|
||||
this._state.history = history2;
|
||||
this._state.current = history2.length - 1;
|
||||
this._onPopState();
|
||||
}
|
||||
replace(name) {
|
||||
const url = filenameToURL(name);
|
||||
const history2 = [...this._state.history];
|
||||
if (this._state.current === -1) {
|
||||
this._state.current = 0;
|
||||
}
|
||||
history2[this._state.current] = url.href;
|
||||
this._state.history = history2;
|
||||
this._onPopState();
|
||||
}
|
||||
onChange(handler) {
|
||||
this._handlers.add(handler);
|
||||
return () => {
|
||||
this._handlers.delete(handler);
|
||||
};
|
||||
}
|
||||
};
|
||||
var BrowserHistory = class {
|
||||
_basePath = "";
|
||||
_current = "";
|
||||
_handlers = /* @__PURE__ */ new Set();
|
||||
constructor(basePath = "") {
|
||||
this._basePath = "/" + basePath.split("/").filter(Boolean).join("/");
|
||||
this._current = this._trimBasePath(location.pathname);
|
||||
window.addEventListener("popstate", () => {
|
||||
this._current = this._trimBasePath(location.pathname);
|
||||
this._onPopState();
|
||||
});
|
||||
}
|
||||
_trimBasePath(pathname) {
|
||||
if (pathname != "/" && pathname.startsWith(this._basePath)) {
|
||||
return new URL(pathname.slice(this._basePath.length), "file:///").href;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
_joinBasePath(url) {
|
||||
const basePath = this._basePath === "/" ? "" : this._basePath;
|
||||
if (url.protocol === "file:") {
|
||||
return basePath + url.pathname;
|
||||
}
|
||||
return basePath + "/" + url.href;
|
||||
}
|
||||
_onPopState() {
|
||||
for (const handler of this._handlers) {
|
||||
handler(this.state);
|
||||
}
|
||||
}
|
||||
get state() {
|
||||
return { current: this._current };
|
||||
}
|
||||
back() {
|
||||
history.back();
|
||||
}
|
||||
forward() {
|
||||
history.forward();
|
||||
}
|
||||
push(name) {
|
||||
const url = filenameToURL(name);
|
||||
history.pushState(null, "", this._joinBasePath(url));
|
||||
this._current = url.href;
|
||||
this._onPopState();
|
||||
}
|
||||
replace(name) {
|
||||
const url = filenameToURL(name);
|
||||
history.replaceState(null, "", this._joinBasePath(url));
|
||||
this._current = url.href;
|
||||
this._onPopState();
|
||||
}
|
||||
onChange(handler) {
|
||||
this._handlers.add(handler);
|
||||
return () => {
|
||||
this._handlers.delete(handler);
|
||||
};
|
||||
}
|
||||
};
|
||||
export {
|
||||
NotFoundError,
|
||||
Workspace
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,206 @@
|
||||
{
|
||||
"displayName": "JSON",
|
||||
"name": "json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"array": {
|
||||
"begin": "\\[",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.array.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "]",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.array.end.json"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.array.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
},
|
||||
{
|
||||
"match": ",",
|
||||
"name": "punctuation.separator.array.json"
|
||||
},
|
||||
{
|
||||
"match": "[^]\\s]",
|
||||
"name": "invalid.illegal.expected-array-separator.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"comments": {
|
||||
"patterns": [
|
||||
{
|
||||
"begin": "/\\*\\*(?!/)",
|
||||
"captures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.comment.json"
|
||||
}
|
||||
},
|
||||
"end": "\\*/",
|
||||
"name": "comment.block.documentation.json"
|
||||
},
|
||||
{
|
||||
"begin": "/\\*",
|
||||
"captures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.comment.json"
|
||||
}
|
||||
},
|
||||
"end": "\\*/",
|
||||
"name": "comment.block.json"
|
||||
},
|
||||
{
|
||||
"captures": {
|
||||
"1": {
|
||||
"name": "punctuation.definition.comment.json"
|
||||
}
|
||||
},
|
||||
"match": "(//).*$\\n?",
|
||||
"name": "comment.line.double-slash.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"constant": {
|
||||
"match": "\\b(?:true|false|null)\\b",
|
||||
"name": "constant.language.json"
|
||||
},
|
||||
"number": {
|
||||
"match": "-?(?:0|[1-9]\\d*)(?:(?:\\.\\d+)?(?:[Ee][-+]?\\d+)?)?",
|
||||
"name": "constant.numeric.json"
|
||||
},
|
||||
"object": {
|
||||
"begin": "\\{",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.dictionary.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "}",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.dictionary.end.json"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.dictionary.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#objectkey"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
},
|
||||
{
|
||||
"begin": ":",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.separator.dictionary.key-value.json"
|
||||
}
|
||||
},
|
||||
"end": "(,)|(?=})",
|
||||
"endCaptures": {
|
||||
"1": {
|
||||
"name": "punctuation.separator.dictionary.pair.json"
|
||||
}
|
||||
},
|
||||
"name": "meta.structure.dictionary.value.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#value"
|
||||
},
|
||||
{
|
||||
"match": "[^,\\s]",
|
||||
"name": "invalid.illegal.expected-dictionary-separator.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"match": "[^}\\s]",
|
||||
"name": "invalid.illegal.expected-dictionary-separator.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"objectkey": {
|
||||
"begin": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.support.type.property-name.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "\"",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.support.type.property-name.end.json"
|
||||
}
|
||||
},
|
||||
"name": "string.json support.type.property-name.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#stringcontent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"string": {
|
||||
"begin": "\"",
|
||||
"beginCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.begin.json"
|
||||
}
|
||||
},
|
||||
"end": "\"",
|
||||
"endCaptures": {
|
||||
"0": {
|
||||
"name": "punctuation.definition.string.end.json"
|
||||
}
|
||||
},
|
||||
"name": "string.quoted.double.json",
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#stringcontent"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stringcontent": {
|
||||
"patterns": [
|
||||
{
|
||||
"match": "\\\\(?:[\"/\\\\bfnrt]|u\\h{4})",
|
||||
"name": "constant.character.escape.json"
|
||||
},
|
||||
{
|
||||
"match": "\\\\.",
|
||||
"name": "invalid.illegal.unrecognized-string-escape.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"patterns": [
|
||||
{
|
||||
"include": "#constant"
|
||||
},
|
||||
{
|
||||
"include": "#number"
|
||||
},
|
||||
{
|
||||
"include": "#string"
|
||||
},
|
||||
{
|
||||
"include": "#array"
|
||||
},
|
||||
{
|
||||
"include": "#object"
|
||||
},
|
||||
{
|
||||
"include": "#comments"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scopeName": "source.json"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,590 @@
|
||||
{
|
||||
"colors": {
|
||||
"actionBar.toggledBackground": "#383a49",
|
||||
"activityBarBadge.background": "#007ACC",
|
||||
"checkbox.border": "#6B6B6B",
|
||||
"editor.background": "#1E1E1E",
|
||||
"editor.foreground": "#D4D4D4",
|
||||
"editor.inactiveSelectionBackground": "#3A3D41",
|
||||
"editor.selectionHighlightBackground": "#ADD6FF26",
|
||||
"editorIndentGuide.activeBackground1": "#707070",
|
||||
"editorIndentGuide.background1": "#404040",
|
||||
"input.placeholderForeground": "#A6A6A6",
|
||||
"list.activeSelectionIconForeground": "#FFF",
|
||||
"list.dropBackground": "#383B3D",
|
||||
"menu.background": "#252526",
|
||||
"menu.border": "#454545",
|
||||
"menu.foreground": "#CCCCCC",
|
||||
"menu.selectionBackground": "#0078d4",
|
||||
"menu.separatorBackground": "#454545",
|
||||
"ports.iconRunningProcessForeground": "#369432",
|
||||
"sideBarSectionHeader.background": "#0000",
|
||||
"sideBarSectionHeader.border": "#ccc3",
|
||||
"sideBarTitle.foreground": "#BBBBBB",
|
||||
"statusBarItem.remoteBackground": "#16825D",
|
||||
"statusBarItem.remoteForeground": "#FFF",
|
||||
"tab.lastPinnedBorder": "#ccc3",
|
||||
"tab.selectedBackground": "#222222",
|
||||
"tab.selectedForeground": "#ffffffa0",
|
||||
"terminal.inactiveSelectionBackground": "#3A3D41",
|
||||
"widget.border": "#303031"
|
||||
},
|
||||
"displayName": "Dark Plus",
|
||||
"name": "dark-plus",
|
||||
"semanticHighlighting": true,
|
||||
"semanticTokenColors": {
|
||||
"customLiteral": "#DCDCAA",
|
||||
"newOperator": "#C586C0",
|
||||
"numberLiteral": "#b5cea8",
|
||||
"stringLiteral": "#ce9178"
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
"scope": [
|
||||
"meta.embedded",
|
||||
"source.groovy.embedded",
|
||||
"string meta.image.inline.markdown",
|
||||
"variable.legacy.builtin.python"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#D4D4D4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "emphasis",
|
||||
"settings": {
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "strong",
|
||||
"settings": {
|
||||
"fontStyle": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "header",
|
||||
"settings": {
|
||||
"foreground": "#000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "comment",
|
||||
"settings": {
|
||||
"foreground": "#6A9955"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.language",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"constant.numeric",
|
||||
"variable.other.enummember",
|
||||
"keyword.operator.plus.exponent",
|
||||
"keyword.operator.minus.exponent"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#b5cea8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.regexp",
|
||||
"settings": {
|
||||
"foreground": "#646695"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.tag",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.name.tag.css",
|
||||
"entity.name.tag.less"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#d7ba7d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.other.attribute-name",
|
||||
"settings": {
|
||||
"foreground": "#9cdcfe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.other.attribute-name.class.css",
|
||||
"source.css entity.other.attribute-name.class",
|
||||
"entity.other.attribute-name.id.css",
|
||||
"entity.other.attribute-name.parent-selector.css",
|
||||
"entity.other.attribute-name.parent.less",
|
||||
"source.css entity.other.attribute-name.pseudo-class",
|
||||
"entity.other.attribute-name.pseudo-element.css",
|
||||
"source.css.less entity.other.attribute-name.id",
|
||||
"entity.other.attribute-name.scss"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#d7ba7d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "invalid",
|
||||
"settings": {
|
||||
"foreground": "#f44747"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.underline",
|
||||
"settings": {
|
||||
"fontStyle": "underline"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.bold",
|
||||
"settings": {
|
||||
"fontStyle": "bold",
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.heading",
|
||||
"settings": {
|
||||
"fontStyle": "bold",
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.italic",
|
||||
"settings": {
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.strikethrough",
|
||||
"settings": {
|
||||
"fontStyle": "strikethrough"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.inserted",
|
||||
"settings": {
|
||||
"foreground": "#b5cea8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.deleted",
|
||||
"settings": {
|
||||
"foreground": "#ce9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.changed",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "punctuation.definition.quote.begin.markdown",
|
||||
"settings": {
|
||||
"foreground": "#6A9955"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "punctuation.definition.list.begin.markdown",
|
||||
"settings": {
|
||||
"foreground": "#6796e6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.inline.raw",
|
||||
"settings": {
|
||||
"foreground": "#ce9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "punctuation.definition.tag",
|
||||
"settings": {
|
||||
"foreground": "#808080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.preprocessor",
|
||||
"entity.name.function.preprocessor"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.preprocessor.string",
|
||||
"settings": {
|
||||
"foreground": "#ce9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.preprocessor.numeric",
|
||||
"settings": {
|
||||
"foreground": "#b5cea8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.structure.dictionary.key.python",
|
||||
"settings": {
|
||||
"foreground": "#9cdcfe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.diff.header",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.type",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"storage.modifier",
|
||||
"keyword.operator.noexcept"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"string",
|
||||
"meta.embedded.assembly"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#ce9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string.tag",
|
||||
"settings": {
|
||||
"foreground": "#ce9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string.value",
|
||||
"settings": {
|
||||
"foreground": "#ce9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string.regexp",
|
||||
"settings": {
|
||||
"foreground": "#d16969"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.definition.template-expression.begin",
|
||||
"punctuation.definition.template-expression.end",
|
||||
"punctuation.section.embedded"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.template.expression"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#d4d4d4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.type.vendored.property-name",
|
||||
"support.type.property-name",
|
||||
"source.css variable",
|
||||
"source.coffee.embedded"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#9cdcfe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.control",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.operator",
|
||||
"settings": {
|
||||
"foreground": "#d4d4d4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.operator.new",
|
||||
"keyword.operator.expression",
|
||||
"keyword.operator.cast",
|
||||
"keyword.operator.sizeof",
|
||||
"keyword.operator.alignof",
|
||||
"keyword.operator.typeid",
|
||||
"keyword.operator.alignas",
|
||||
"keyword.operator.instanceof",
|
||||
"keyword.operator.logical.python",
|
||||
"keyword.operator.wordlike"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.other.unit",
|
||||
"settings": {
|
||||
"foreground": "#b5cea8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.section.embedded.begin.php",
|
||||
"punctuation.section.embedded.end.php"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.function.git-rebase",
|
||||
"settings": {
|
||||
"foreground": "#9cdcfe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.sha.git-rebase",
|
||||
"settings": {
|
||||
"foreground": "#b5cea8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"storage.modifier.import.java",
|
||||
"variable.language.wildcard.java",
|
||||
"storage.modifier.package.java"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#d4d4d4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.language",
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member",
|
||||
"entity.name.operator.custom-literal"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#DCDCAA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
"entity.name.namespace",
|
||||
"entity.other.attribute",
|
||||
"entity.name.scope-resolution",
|
||||
"entity.name.class",
|
||||
"storage.type.numeric.go",
|
||||
"storage.type.byte.go",
|
||||
"storage.type.boolean.go",
|
||||
"storage.type.string.go",
|
||||
"storage.type.uintptr.go",
|
||||
"storage.type.error.go",
|
||||
"storage.type.rune.go",
|
||||
"storage.type.cs",
|
||||
"storage.type.generic.cs",
|
||||
"storage.type.modifier.cs",
|
||||
"storage.type.variable.cs",
|
||||
"storage.type.annotation.java",
|
||||
"storage.type.generic.java",
|
||||
"storage.type.java",
|
||||
"storage.type.object.array.java",
|
||||
"storage.type.primitive.array.java",
|
||||
"storage.type.primitive.java",
|
||||
"storage.type.token.java",
|
||||
"storage.type.groovy",
|
||||
"storage.type.annotation.groovy",
|
||||
"storage.type.parameters.groovy",
|
||||
"storage.type.generic.groovy",
|
||||
"storage.type.object.array.groovy",
|
||||
"storage.type.primitive.array.groovy",
|
||||
"storage.type.primitive.groovy"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#4EC9B0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.type.cast.expr",
|
||||
"meta.type.new.expr",
|
||||
"support.constant.math",
|
||||
"support.constant.dom",
|
||||
"support.constant.json",
|
||||
"entity.other.inherited-class",
|
||||
"punctuation.separator.namespace.ruby"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#4EC9B0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.control",
|
||||
"source.cpp keyword.operator.new",
|
||||
"keyword.operator.delete",
|
||||
"keyword.other.using",
|
||||
"keyword.other.directive.using",
|
||||
"keyword.other.operator",
|
||||
"entity.name.operator"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#C586C0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable",
|
||||
"meta.definition.variable.name",
|
||||
"support.variable",
|
||||
"entity.name.variable",
|
||||
"constant.other.placeholder"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#9CDCFE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable.other.constant",
|
||||
"variable.other.enummember"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#4FC1FF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.object-literal.key"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#9CDCFE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.constant.property-value",
|
||||
"support.constant.font-name",
|
||||
"support.constant.media-type",
|
||||
"support.constant.media",
|
||||
"constant.other.color.rgb-value",
|
||||
"constant.other.rgb-value",
|
||||
"support.constant.color"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#CE9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.definition.group.regexp",
|
||||
"punctuation.definition.group.assertion.regexp",
|
||||
"punctuation.definition.character-class.regexp",
|
||||
"punctuation.character.set.begin.regexp",
|
||||
"punctuation.character.set.end.regexp",
|
||||
"keyword.operator.negation.regexp",
|
||||
"support.other.parenthesis.regexp"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#CE9178"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"constant.character.character-class.regexp",
|
||||
"constant.other.character-class.set.regexp",
|
||||
"constant.other.character-class.regexp",
|
||||
"constant.character.set.regexp"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#d16969"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.operator.or.regexp",
|
||||
"keyword.control.anchor.regexp"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#DCDCAA"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.operator.quantifier.regexp",
|
||||
"settings": {
|
||||
"foreground": "#d7ba7d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"constant.character",
|
||||
"constant.other.option"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#569cd6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.character.escape",
|
||||
"settings": {
|
||||
"foreground": "#d7ba7d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.label",
|
||||
"settings": {
|
||||
"foreground": "#C8C8C8"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "dark"
|
||||
}
|
||||
@ -0,0 +1,615 @@
|
||||
{
|
||||
"colors": {
|
||||
"actionBar.toggledBackground": "#dddddd",
|
||||
"activityBarBadge.background": "#007ACC",
|
||||
"checkbox.border": "#919191",
|
||||
"diffEditor.unchangedRegionBackground": "#f8f8f8",
|
||||
"editor.background": "#FFFFFF",
|
||||
"editor.foreground": "#000000",
|
||||
"editor.inactiveSelectionBackground": "#E5EBF1",
|
||||
"editor.selectionHighlightBackground": "#ADD6FF80",
|
||||
"editorIndentGuide.activeBackground1": "#939393",
|
||||
"editorIndentGuide.background1": "#D3D3D3",
|
||||
"editorSuggestWidget.background": "#F3F3F3",
|
||||
"input.placeholderForeground": "#767676",
|
||||
"list.activeSelectionIconForeground": "#FFF",
|
||||
"list.focusAndSelectionOutline": "#90C2F9",
|
||||
"list.hoverBackground": "#E8E8E8",
|
||||
"menu.border": "#D4D4D4",
|
||||
"notebook.cellBorderColor": "#E8E8E8",
|
||||
"notebook.selectedCellBackground": "#c8ddf150",
|
||||
"ports.iconRunningProcessForeground": "#369432",
|
||||
"searchEditor.textInputBorder": "#CECECE",
|
||||
"settings.numberInputBorder": "#CECECE",
|
||||
"settings.textInputBorder": "#CECECE",
|
||||
"sideBarSectionHeader.background": "#0000",
|
||||
"sideBarSectionHeader.border": "#61616130",
|
||||
"sideBarTitle.foreground": "#6F6F6F",
|
||||
"statusBarItem.errorBackground": "#c72e0f",
|
||||
"statusBarItem.remoteBackground": "#16825D",
|
||||
"statusBarItem.remoteForeground": "#FFF",
|
||||
"tab.lastPinnedBorder": "#61616130",
|
||||
"tab.selectedBackground": "#ffffffa5",
|
||||
"tab.selectedForeground": "#333333b3",
|
||||
"terminal.inactiveSelectionBackground": "#E5EBF1",
|
||||
"widget.border": "#d4d4d4"
|
||||
},
|
||||
"displayName": "Light Plus",
|
||||
"name": "light-plus",
|
||||
"semanticHighlighting": true,
|
||||
"semanticTokenColors": {
|
||||
"customLiteral": "#795E26",
|
||||
"newOperator": "#AF00DB",
|
||||
"numberLiteral": "#098658",
|
||||
"stringLiteral": "#a31515"
|
||||
},
|
||||
"tokenColors": [
|
||||
{
|
||||
"scope": [
|
||||
"meta.embedded",
|
||||
"source.groovy.embedded",
|
||||
"string meta.image.inline.markdown",
|
||||
"variable.legacy.builtin.python"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#000000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "emphasis",
|
||||
"settings": {
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "strong",
|
||||
"settings": {
|
||||
"fontStyle": "bold"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.diff.header",
|
||||
"settings": {
|
||||
"foreground": "#000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "comment",
|
||||
"settings": {
|
||||
"foreground": "#008000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.language",
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"constant.numeric",
|
||||
"variable.other.enummember",
|
||||
"keyword.operator.plus.exponent",
|
||||
"keyword.operator.minus.exponent"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#098658"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.regexp",
|
||||
"settings": {
|
||||
"foreground": "#811f3f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.tag",
|
||||
"settings": {
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.selector",
|
||||
"settings": {
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.other.attribute-name",
|
||||
"settings": {
|
||||
"foreground": "#e50000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.other.attribute-name.class.css",
|
||||
"source.css entity.other.attribute-name.class",
|
||||
"entity.other.attribute-name.id.css",
|
||||
"entity.other.attribute-name.parent-selector.css",
|
||||
"entity.other.attribute-name.parent.less",
|
||||
"source.css entity.other.attribute-name.pseudo-class",
|
||||
"entity.other.attribute-name.pseudo-element.css",
|
||||
"source.css.less entity.other.attribute-name.id",
|
||||
"entity.other.attribute-name.scss"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "invalid",
|
||||
"settings": {
|
||||
"foreground": "#cd3131"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.underline",
|
||||
"settings": {
|
||||
"fontStyle": "underline"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.bold",
|
||||
"settings": {
|
||||
"fontStyle": "bold",
|
||||
"foreground": "#000080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.heading",
|
||||
"settings": {
|
||||
"fontStyle": "bold",
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.italic",
|
||||
"settings": {
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.strikethrough",
|
||||
"settings": {
|
||||
"fontStyle": "strikethrough"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.inserted",
|
||||
"settings": {
|
||||
"foreground": "#098658"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.deleted",
|
||||
"settings": {
|
||||
"foreground": "#a31515"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.changed",
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.definition.quote.begin.markdown",
|
||||
"punctuation.definition.list.begin.markdown"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "markup.inline.raw",
|
||||
"settings": {
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "punctuation.definition.tag",
|
||||
"settings": {
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.preprocessor",
|
||||
"entity.name.function.preprocessor"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.preprocessor.string",
|
||||
"settings": {
|
||||
"foreground": "#a31515"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.preprocessor.numeric",
|
||||
"settings": {
|
||||
"foreground": "#098658"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "meta.structure.dictionary.key.python",
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage",
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.type",
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"storage.modifier",
|
||||
"keyword.operator.noexcept"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"string",
|
||||
"meta.embedded.assembly"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#a31515"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"string.comment.buffered.block.pug",
|
||||
"string.quoted.pug",
|
||||
"string.interpolated.pug",
|
||||
"string.unquoted.plain.in.yaml",
|
||||
"string.unquoted.plain.out.yaml",
|
||||
"string.unquoted.block.yaml",
|
||||
"string.quoted.single.yaml",
|
||||
"string.quoted.double.xml",
|
||||
"string.quoted.single.xml",
|
||||
"string.unquoted.cdata.xml",
|
||||
"string.quoted.double.html",
|
||||
"string.quoted.single.html",
|
||||
"string.unquoted.html",
|
||||
"string.quoted.single.handlebars",
|
||||
"string.quoted.double.handlebars"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string.regexp",
|
||||
"settings": {
|
||||
"foreground": "#811f3f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.definition.template-expression.begin",
|
||||
"punctuation.definition.template-expression.end",
|
||||
"punctuation.section.embedded"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.template.expression"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.constant.property-value",
|
||||
"support.constant.font-name",
|
||||
"support.constant.media-type",
|
||||
"support.constant.media",
|
||||
"constant.other.color.rgb-value",
|
||||
"constant.other.rgb-value",
|
||||
"support.constant.color"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.type.vendored.property-name",
|
||||
"support.type.property-name",
|
||||
"source.css variable",
|
||||
"source.coffee.embedded"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#e50000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.type.property-name.json"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword",
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.control",
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.operator",
|
||||
"settings": {
|
||||
"foreground": "#000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.operator.new",
|
||||
"keyword.operator.expression",
|
||||
"keyword.operator.cast",
|
||||
"keyword.operator.sizeof",
|
||||
"keyword.operator.alignof",
|
||||
"keyword.operator.typeid",
|
||||
"keyword.operator.alignas",
|
||||
"keyword.operator.instanceof",
|
||||
"keyword.operator.logical.python",
|
||||
"keyword.operator.wordlike"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.other.unit",
|
||||
"settings": {
|
||||
"foreground": "#098658"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.section.embedded.begin.php",
|
||||
"punctuation.section.embedded.end.php"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#800000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.function.git-rebase",
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.sha.git-rebase",
|
||||
"settings": {
|
||||
"foreground": "#098658"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"storage.modifier.import.java",
|
||||
"variable.language.wildcard.java",
|
||||
"storage.modifier.package.java"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.language",
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"entity.name.function",
|
||||
"support.function",
|
||||
"support.constant.handlebars",
|
||||
"source.powershell variable.other.member",
|
||||
"entity.name.operator.custom-literal"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#795E26"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.class",
|
||||
"support.type",
|
||||
"entity.name.type",
|
||||
"entity.name.namespace",
|
||||
"entity.other.attribute",
|
||||
"entity.name.scope-resolution",
|
||||
"entity.name.class",
|
||||
"storage.type.numeric.go",
|
||||
"storage.type.byte.go",
|
||||
"storage.type.boolean.go",
|
||||
"storage.type.string.go",
|
||||
"storage.type.uintptr.go",
|
||||
"storage.type.error.go",
|
||||
"storage.type.rune.go",
|
||||
"storage.type.cs",
|
||||
"storage.type.generic.cs",
|
||||
"storage.type.modifier.cs",
|
||||
"storage.type.variable.cs",
|
||||
"storage.type.annotation.java",
|
||||
"storage.type.generic.java",
|
||||
"storage.type.java",
|
||||
"storage.type.object.array.java",
|
||||
"storage.type.primitive.array.java",
|
||||
"storage.type.primitive.java",
|
||||
"storage.type.token.java",
|
||||
"storage.type.groovy",
|
||||
"storage.type.annotation.groovy",
|
||||
"storage.type.parameters.groovy",
|
||||
"storage.type.generic.groovy",
|
||||
"storage.type.object.array.groovy",
|
||||
"storage.type.primitive.array.groovy",
|
||||
"storage.type.primitive.groovy"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#267f99"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.type.cast.expr",
|
||||
"meta.type.new.expr",
|
||||
"support.constant.math",
|
||||
"support.constant.dom",
|
||||
"support.constant.json",
|
||||
"entity.other.inherited-class",
|
||||
"punctuation.separator.namespace.ruby"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#267f99"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.control",
|
||||
"source.cpp keyword.operator.new",
|
||||
"source.cpp keyword.operator.delete",
|
||||
"keyword.other.using",
|
||||
"keyword.other.directive.using",
|
||||
"keyword.other.operator",
|
||||
"entity.name.operator"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#AF00DB"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable",
|
||||
"meta.definition.variable.name",
|
||||
"support.variable",
|
||||
"entity.name.variable",
|
||||
"constant.other.placeholder"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#001080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"variable.other.constant",
|
||||
"variable.other.enummember"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0070C1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"meta.object-literal.key"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#001080"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"support.constant.property-value",
|
||||
"support.constant.font-name",
|
||||
"support.constant.media-type",
|
||||
"support.constant.media",
|
||||
"constant.other.color.rgb-value",
|
||||
"constant.other.rgb-value",
|
||||
"support.constant.color"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0451a5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"punctuation.definition.group.regexp",
|
||||
"punctuation.definition.group.assertion.regexp",
|
||||
"punctuation.definition.character-class.regexp",
|
||||
"punctuation.character.set.begin.regexp",
|
||||
"punctuation.character.set.end.regexp",
|
||||
"keyword.operator.negation.regexp",
|
||||
"support.other.parenthesis.regexp"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#d16969"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"constant.character.character-class.regexp",
|
||||
"constant.other.character-class.set.regexp",
|
||||
"constant.other.character-class.regexp",
|
||||
"constant.character.set.regexp"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#811f3f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword.operator.quantifier.regexp",
|
||||
"settings": {
|
||||
"foreground": "#000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"keyword.operator.or.regexp",
|
||||
"keyword.control.anchor.regexp"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#EE0000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": [
|
||||
"constant.character",
|
||||
"constant.other.option"
|
||||
],
|
||||
"settings": {
|
||||
"foreground": "#0000ff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.character.escape",
|
||||
"settings": {
|
||||
"foreground": "#EE0000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.label",
|
||||
"settings": {
|
||||
"foreground": "#000000"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": "light"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
109
web/scripts/hoist-modern-monaco.md
Normal file
109
web/scripts/hoist-modern-monaco.md
Normal file
@ -0,0 +1,109 @@
|
||||
# `hoist-modern-monaco.ts`
|
||||
|
||||
This script does more than just download and copy assets. It also applies a few targeted customizations so the hoisted `modern-monaco` setup works reliably in Dify.
|
||||
|
||||
All generated assets are written under:
|
||||
|
||||
```text
|
||||
public/hoisted-modern-monaco/
|
||||
```
|
||||
|
||||
That directory is expected to stay generated-only and is git-ignored.
|
||||
|
||||
It also generates:
|
||||
|
||||
```text
|
||||
app/components/base/modern-monaco/hoisted-config.ts
|
||||
```
|
||||
|
||||
That module is the runtime source of truth for:
|
||||
|
||||
- `tm-themes` version
|
||||
- `tm-grammars` version
|
||||
- the hoisted theme list
|
||||
- the hoisted language list
|
||||
- the local `modern-monaco` import map
|
||||
|
||||
## Customizations
|
||||
|
||||
### 1. Only download the Shiki assets Dify actually uses
|
||||
|
||||
By default, the script downloads these themes and grammars:
|
||||
|
||||
- themes: `light-plus`, `dark-plus`
|
||||
- languages: `javascript`, `json`, `python`
|
||||
|
||||
It also parses embedded grammar dependencies from `modern-monaco/dist/shiki.mjs` and pulls those in as well.
|
||||
At the moment, `javascript` also pulls in `html` and `css`.
|
||||
|
||||
Why:
|
||||
|
||||
- Avoid copying the full `tm-themes` and `tm-grammars` sets into `public`
|
||||
- Keep the current Dify editor use cases fully local
|
||||
- Keep the generated runtime config aligned with the actual hoisted assets
|
||||
|
||||
### 2. Rewrite the bare `typescript` import in the TypeScript worker
|
||||
|
||||
In the npm `dist` build of `modern-monaco`, `lsp/typescript/worker.mjs` still contains:
|
||||
|
||||
```js
|
||||
import ts from 'typescript'
|
||||
```
|
||||
|
||||
That bare import does not resolve when the file is executed directly from `public/hoisted-modern-monaco/modern-monaco/...` in the browser.
|
||||
The script downloads the TypeScript ESM build from `esm.sh`, stores it locally, and rewrites the import to a relative path pointing to:
|
||||
|
||||
```text
|
||||
public/hoisted-modern-monaco/typescript@<version>/es2022/typescript.mjs
|
||||
```
|
||||
|
||||
Why:
|
||||
|
||||
- Make the hoisted TypeScript worker runnable in the browser
|
||||
|
||||
### 3. Force the TypeScript worker to always use Blob bootstrap
|
||||
|
||||
In the original `modern-monaco` `lsp/typescript/setup.mjs`:
|
||||
|
||||
- cross-origin worker URLs use Blob bootstrap
|
||||
- same-origin worker URLs use `new Worker(workerUrl)`
|
||||
|
||||
Once the files are hoisted to same-origin `/hoisted-modern-monaco/modern-monaco/...`, the runtime falls into the second branch.
|
||||
In Dify, that caused the completion pipeline to break, with the TypeScript worker failing to resolve anonymous in-memory files.
|
||||
|
||||
The script rewrites that logic to always use:
|
||||
|
||||
```js
|
||||
const worker = new Worker(
|
||||
URL.createObjectURL(new Blob([`import "${workerUrl.href}"`], { type: 'application/javascript' })),
|
||||
{ type: 'module', name: 'typescript-worker' },
|
||||
)
|
||||
```
|
||||
|
||||
Why:
|
||||
|
||||
- Match the effective worker startup behavior used in the `esm.sh` setup
|
||||
- Restore completion behavior after local hoisting
|
||||
|
||||
## What this script does not do
|
||||
|
||||
- It does not change `modern-monaco` feature behavior
|
||||
- It does not register any custom LSP provider
|
||||
- It does not mirror the full `esm.sh` dependency graph
|
||||
|
||||
The current strategy is still:
|
||||
|
||||
- hoist the main `modern-monaco` modules and built-in LSP locally
|
||||
- hoist Shiki themes and grammars as local JSON assets
|
||||
- hoist TypeScript runtime as a local ESM file
|
||||
|
||||
## Things to re-check on upgrade
|
||||
|
||||
When upgrading `modern-monaco` or `typescript`, re-check these points first:
|
||||
|
||||
- whether `lsp/typescript/worker.mjs` still contains a bare `import ts from "typescript"`
|
||||
- whether the structure of `lsp/typescript/setup.mjs#createWebWorker()` has changed
|
||||
- whether the `tm-themes` and `tm-grammars` version extraction from `dist/shiki.mjs` still matches
|
||||
- whether Dify's editor theme/language usage has changed
|
||||
|
||||
If any of those change, the patch logic in this script may need to be updated as well.
|
||||
337
web/scripts/hoist-modern-monaco.ts
Normal file
337
web/scripts/hoist-modern-monaco.ts
Normal file
@ -0,0 +1,337 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { access, cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
type Args = {
|
||||
force: boolean
|
||||
themes: string[]
|
||||
languages: string[]
|
||||
}
|
||||
|
||||
const DEFAULT_THEMES = ['light-plus', 'dark-plus']
|
||||
const DEFAULT_LANGUAGES = ['javascript', 'json', 'python']
|
||||
const ESM_SH = 'https://esm.sh'
|
||||
const HOIST_DIR_NAME = 'hoisted-modern-monaco'
|
||||
const ROOT_DIR = path.resolve(fileURLToPath(new URL('..', import.meta.url)))
|
||||
const PUBLIC_DIR = path.join(ROOT_DIR, 'public')
|
||||
const HOIST_PUBLIC_DIR = path.join(PUBLIC_DIR, HOIST_DIR_NAME)
|
||||
const GENERATED_CONFIG_PATH = path.join(ROOT_DIR, 'app', 'components', 'base', 'modern-monaco', 'hoisted-config.ts')
|
||||
const MODERN_MONACO_DIR = path.join(ROOT_DIR, 'node_modules', 'modern-monaco')
|
||||
const MODERN_MONACO_DIST_DIR = path.join(MODERN_MONACO_DIR, 'dist')
|
||||
const MODERN_MONACO_PKG_PATH = path.join(MODERN_MONACO_DIR, 'package.json')
|
||||
const SHIKI_DIST_PATH = path.join(MODERN_MONACO_DIST_DIR, 'shiki.mjs')
|
||||
const TYPESCRIPT_PKG_PATH = path.join(ROOT_DIR, 'node_modules', 'typescript', 'package.json')
|
||||
const MODERN_MONACO_PUBLIC_DIR = path.join(HOIST_PUBLIC_DIR, 'modern-monaco')
|
||||
const HOIST_MANIFEST_PATH = path.join(MODERN_MONACO_PUBLIC_DIR, 'hoist-manifest.json')
|
||||
const TYPESCRIPT_SETUP_PATH = path.join(MODERN_MONACO_PUBLIC_DIR, 'lsp', 'typescript', 'setup.mjs')
|
||||
|
||||
function parseArgs(argv: string[]): Args {
|
||||
const args: Args = {
|
||||
force: false,
|
||||
themes: [...DEFAULT_THEMES],
|
||||
languages: [...DEFAULT_LANGUAGES],
|
||||
}
|
||||
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const value = argv[index]
|
||||
if (value === '--')
|
||||
continue
|
||||
if (value === '--force') {
|
||||
args.force = true
|
||||
continue
|
||||
}
|
||||
if (value === '--theme') {
|
||||
const theme = argv[index + 1]
|
||||
if (!theme)
|
||||
throw new Error('Missing value for --theme')
|
||||
args.themes.push(theme)
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
if (value === '--language') {
|
||||
const language = argv[index + 1]
|
||||
if (!language)
|
||||
throw new Error('Missing value for --language')
|
||||
args.languages.push(language)
|
||||
index += 1
|
||||
continue
|
||||
}
|
||||
|
||||
throw new Error(`Unknown argument: ${value}`)
|
||||
}
|
||||
|
||||
args.themes = [...new Set(args.themes)]
|
||||
args.languages = [...new Set(args.languages)]
|
||||
return args
|
||||
}
|
||||
|
||||
function log(message: string) {
|
||||
process.stdout.write(`${message}\n`)
|
||||
}
|
||||
|
||||
async function readJson<T>(filePath: string): Promise<T> {
|
||||
return JSON.parse(await readFile(filePath, 'utf8')) as T
|
||||
}
|
||||
|
||||
async function pathExists(filePath: string): Promise<boolean> {
|
||||
try {
|
||||
await access(filePath)
|
||||
return true
|
||||
}
|
||||
catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function requireMatch(text: string, pattern: RegExp, description: string): string {
|
||||
const match = text.match(pattern)
|
||||
if (!match?.[1])
|
||||
throw new Error(`Failed to resolve ${description}`)
|
||||
return match[1]
|
||||
}
|
||||
|
||||
function getEmbeddedLanguages(shikiText: string, language: string): string[] {
|
||||
const anchor = `name: "${language}"`
|
||||
const start = shikiText.indexOf(anchor)
|
||||
if (start === -1)
|
||||
return []
|
||||
const end = shikiText.indexOf(' }, {', start)
|
||||
const entry = shikiText.slice(start, end === -1 ? undefined : end)
|
||||
const match = entry.match(/embedded: \[([^\]]*)\]/)
|
||||
if (!match?.[1])
|
||||
return []
|
||||
return [...match[1].matchAll(/"([^"]+)"/g)].map(([, name]) => name)
|
||||
}
|
||||
|
||||
function resolveLanguages(shikiText: string, initialLanguages: string[]): string[] {
|
||||
const resolved = new Set(initialLanguages)
|
||||
const queue = [...initialLanguages]
|
||||
|
||||
while (queue.length > 0) {
|
||||
const language = queue.shift()
|
||||
if (!language)
|
||||
continue
|
||||
for (const embeddedLanguage of getEmbeddedLanguages(shikiText, language)) {
|
||||
if (resolved.has(embeddedLanguage))
|
||||
continue
|
||||
resolved.add(embeddedLanguage)
|
||||
queue.push(embeddedLanguage)
|
||||
}
|
||||
}
|
||||
|
||||
return [...resolved]
|
||||
}
|
||||
|
||||
async function fetchWithRetry(url: string, retries = 2): Promise<Response> {
|
||||
let lastError: unknown
|
||||
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP ${response.status} ${response.statusText}`)
|
||||
return response
|
||||
}
|
||||
catch (error) {
|
||||
lastError = error
|
||||
if (attempt === retries)
|
||||
break
|
||||
await new Promise(resolve => setTimeout(resolve, 300 * (attempt + 1)))
|
||||
}
|
||||
}
|
||||
throw new Error(`Failed to fetch ${url}: ${lastError instanceof Error ? lastError.message : String(lastError)}`)
|
||||
}
|
||||
|
||||
async function writeResponseToFile(url: string, filePath: string) {
|
||||
const response = await fetchWithRetry(url)
|
||||
const content = Buffer.from(await response.arrayBuffer())
|
||||
await mkdir(path.dirname(filePath), { recursive: true })
|
||||
await writeFile(filePath, content)
|
||||
}
|
||||
|
||||
async function resolveTypeScriptEsmPath(version: string): Promise<string> {
|
||||
const response = await fetchWithRetry(`${ESM_SH}/typescript@${version}`)
|
||||
const esmPath = response.headers.get('x-esm-path')
|
||||
if (!esmPath)
|
||||
throw new Error('Missing x-esm-path header for typescript')
|
||||
return esmPath
|
||||
}
|
||||
|
||||
function getRelativeImportPath(fromFilePath: string, toFilePath: string): string {
|
||||
return path.relative(path.dirname(fromFilePath), toFilePath).replaceAll(path.sep, '/')
|
||||
}
|
||||
|
||||
async function patchTypeScriptWorkerImport(workerFilePath: string, localTypeScriptPath: string) {
|
||||
const original = await readFile(workerFilePath, 'utf8')
|
||||
const relativeImportPath = getRelativeImportPath(
|
||||
workerFilePath,
|
||||
path.join(HOIST_PUBLIC_DIR, localTypeScriptPath.replace(/^\//, '')),
|
||||
)
|
||||
const next = original.replace('from "typescript";', `from "${relativeImportPath}";`)
|
||||
if (next === original)
|
||||
throw new Error('Failed to patch modern-monaco TypeScript worker import')
|
||||
await writeFile(workerFilePath, next)
|
||||
}
|
||||
|
||||
async function patchTypeScriptWorkerBootstrap(setupFilePath: string) {
|
||||
const original = await readFile(setupFilePath, 'utf8')
|
||||
const currentBlock = `function createWebWorker() {
|
||||
const workerUrl = new URL("./worker.mjs", import.meta.url);
|
||||
if (workerUrl.origin !== location.origin) {
|
||||
return new Worker(
|
||||
URL.createObjectURL(new Blob([\`import "\${workerUrl.href}"\`], { type: "application/javascript" })),
|
||||
{ type: "module", name: "typescript-worker" }
|
||||
);
|
||||
}
|
||||
return new Worker(workerUrl, { type: "module", name: "typescript-worker" });
|
||||
}`
|
||||
const nextBlock = `function createWebWorker() {
|
||||
const workerUrl = new URL("./worker.mjs", import.meta.url);
|
||||
return new Worker(
|
||||
URL.createObjectURL(new Blob([\`import "\${workerUrl.href}"\`], { type: "application/javascript" })),
|
||||
{ type: "module", name: "typescript-worker" }
|
||||
);
|
||||
}`
|
||||
const next = original.replace(currentBlock, nextBlock)
|
||||
if (next === original)
|
||||
throw new Error('Failed to patch modern-monaco TypeScript worker bootstrap')
|
||||
await writeFile(setupFilePath, next)
|
||||
}
|
||||
|
||||
async function writeManifest(filePath: string, manifest: object) {
|
||||
await writeFile(filePath, `${JSON.stringify(manifest, null, 2)}\n`)
|
||||
}
|
||||
|
||||
function toSingleQuotedLiteral(value: string): string {
|
||||
return `'${value.replaceAll('\\', '\\\\').replaceAll('\'', '\\\'')}'`
|
||||
}
|
||||
|
||||
function toReadonlyArrayLiteral(values: string[]): string {
|
||||
return `[${values.map(toSingleQuotedLiteral).join(', ')}] as const`
|
||||
}
|
||||
|
||||
function toReadonlyObjectLiteral(entries: Record<string, string>): string {
|
||||
const lines = Object.entries(entries).map(
|
||||
([key, value]) => ` ${toSingleQuotedLiteral(key)}: ${toSingleQuotedLiteral(value)},`,
|
||||
)
|
||||
return `{\n${lines.join('\n')}\n} as const`
|
||||
}
|
||||
|
||||
async function writeGeneratedConfig(
|
||||
filePath: string,
|
||||
options: {
|
||||
hoistBasePath: string
|
||||
tmThemesVersion: string
|
||||
tmGrammarsVersion: string
|
||||
themes: string[]
|
||||
languages: string[]
|
||||
importMap: Record<string, string>
|
||||
},
|
||||
) {
|
||||
const content = [
|
||||
'// This file is generated by scripts/hoist-modern-monaco.ts.',
|
||||
'// Do not edit it manually.',
|
||||
'',
|
||||
`export const HOIST_BASE_PATH = ${toSingleQuotedLiteral(options.hoistBasePath)} as const`,
|
||||
`export const TM_THEMES_VERSION = ${toSingleQuotedLiteral(options.tmThemesVersion)} as const`,
|
||||
`export const TM_GRAMMARS_VERSION = ${toSingleQuotedLiteral(options.tmGrammarsVersion)} as const`,
|
||||
`export const HOIST_THEME_IDS = ${toReadonlyArrayLiteral(options.themes)}`,
|
||||
`export const HOIST_LANGUAGE_IDS = ${toReadonlyArrayLiteral(options.languages)}`,
|
||||
`export const MODERN_MONACO_IMPORT_MAP = ${toReadonlyObjectLiteral(options.importMap)}`,
|
||||
'',
|
||||
].join('\n')
|
||||
|
||||
await mkdir(path.dirname(filePath), { recursive: true })
|
||||
await writeFile(filePath, content)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2))
|
||||
const modernMonacoPkg = await readJson<{ version: string }>(MODERN_MONACO_PKG_PATH)
|
||||
const typescriptPkg = await readJson<{ version: string }>(TYPESCRIPT_PKG_PATH)
|
||||
const shikiText = await readFile(SHIKI_DIST_PATH, 'utf8')
|
||||
const tmGrammarsVersion = requireMatch(shikiText, /var version = "([^"]+)";/, 'tm-grammars version')
|
||||
const tmThemesVersion = requireMatch(shikiText, /var version2 = "([^"]+)";/, 'tm-themes version')
|
||||
const languages = resolveLanguages(shikiText, args.languages)
|
||||
const themes = [...args.themes]
|
||||
const localTypeScriptPath = await resolveTypeScriptEsmPath(typescriptPkg.version)
|
||||
const localTypeScriptDir = localTypeScriptPath.replace(/^\//, '').split('/')[0] || ''
|
||||
const typeScriptPublicPath = path.join(HOIST_PUBLIC_DIR, localTypeScriptPath.replace(/^\//, ''))
|
||||
const typeScriptWorkerPath = path.join(MODERN_MONACO_PUBLIC_DIR, 'lsp', 'typescript', 'worker.mjs')
|
||||
|
||||
if (args.force) {
|
||||
await Promise.all([
|
||||
rm(HOIST_PUBLIC_DIR, { force: true, recursive: true }),
|
||||
rm(path.join(PUBLIC_DIR, 'modern-monaco'), { force: true, recursive: true }),
|
||||
rm(path.join(PUBLIC_DIR, `tm-themes@${tmThemesVersion}`), { force: true, recursive: true }),
|
||||
rm(path.join(PUBLIC_DIR, `tm-grammars@${tmGrammarsVersion}`), { force: true, recursive: true }),
|
||||
rm(path.join(PUBLIC_DIR, localTypeScriptDir), { force: true, recursive: true }),
|
||||
])
|
||||
}
|
||||
else if (await pathExists(HOIST_MANIFEST_PATH) && await pathExists(GENERATED_CONFIG_PATH)) {
|
||||
log(`modern-monaco hoist cache hit: public/${HOIST_DIR_NAME}`)
|
||||
return
|
||||
}
|
||||
|
||||
log(`Copying modern-monaco dist -> ${path.relative(ROOT_DIR, MODERN_MONACO_PUBLIC_DIR)}`)
|
||||
await rm(MODERN_MONACO_PUBLIC_DIR, { force: true, recursive: true })
|
||||
await cp(MODERN_MONACO_DIST_DIR, MODERN_MONACO_PUBLIC_DIR, { recursive: true })
|
||||
|
||||
log(`Downloading typescript ESM -> ${localTypeScriptPath}`)
|
||||
await writeResponseToFile(`${ESM_SH}${localTypeScriptPath}`, typeScriptPublicPath)
|
||||
await patchTypeScriptWorkerImport(typeScriptWorkerPath, localTypeScriptPath)
|
||||
await patchTypeScriptWorkerBootstrap(TYPESCRIPT_SETUP_PATH)
|
||||
|
||||
for (const theme of themes) {
|
||||
const themeUrl = `${ESM_SH}/tm-themes@${tmThemesVersion}/themes/${theme}.json`
|
||||
const themePath = path.join(HOIST_PUBLIC_DIR, `tm-themes@${tmThemesVersion}`, 'themes', `${theme}.json`)
|
||||
log(`Downloading theme ${theme}`)
|
||||
await writeResponseToFile(themeUrl, themePath)
|
||||
}
|
||||
|
||||
for (const language of languages) {
|
||||
const grammarUrl = `${ESM_SH}/tm-grammars@${tmGrammarsVersion}/grammars/${language}.json`
|
||||
const grammarPath = path.join(HOIST_PUBLIC_DIR, `tm-grammars@${tmGrammarsVersion}`, 'grammars', `${language}.json`)
|
||||
log(`Downloading grammar ${language}`)
|
||||
await writeResponseToFile(grammarUrl, grammarPath)
|
||||
}
|
||||
|
||||
const manifest = {
|
||||
generatedAt: new Date().toISOString(),
|
||||
modernMonacoVersion: modernMonacoPkg.version,
|
||||
tmGrammarsVersion,
|
||||
tmThemesVersion,
|
||||
typescriptVersion: typescriptPkg.version,
|
||||
localTypeScriptPath: `/${HOIST_DIR_NAME}/${localTypeScriptPath.replace(/^\//, '')}`,
|
||||
themes,
|
||||
languages,
|
||||
importMap: {
|
||||
'modern-monaco/editor-core': `/${HOIST_DIR_NAME}/modern-monaco/editor-core.mjs`,
|
||||
'modern-monaco/lsp': `/${HOIST_DIR_NAME}/modern-monaco/lsp/index.mjs`,
|
||||
},
|
||||
}
|
||||
|
||||
await writeManifest(HOIST_MANIFEST_PATH, manifest)
|
||||
await writeGeneratedConfig(GENERATED_CONFIG_PATH, {
|
||||
hoistBasePath: `/${HOIST_DIR_NAME}`,
|
||||
tmThemesVersion,
|
||||
tmGrammarsVersion,
|
||||
themes,
|
||||
languages,
|
||||
importMap: manifest.importMap,
|
||||
})
|
||||
|
||||
log('')
|
||||
log('modern-monaco hoist complete.')
|
||||
log(`- output dir: public/${HOIST_DIR_NAME}`)
|
||||
log(`- import map: modern-monaco/editor-core -> location.origin + "/${HOIST_DIR_NAME}/modern-monaco/editor-core.mjs"`)
|
||||
log(`- import map: modern-monaco/lsp -> location.origin + "/${HOIST_DIR_NAME}/modern-monaco/lsp/index.mjs"`)
|
||||
log(`- init option: cdn -> window.location.origin`)
|
||||
log(`- languages: ${languages.join(', ')}`)
|
||||
log(`- themes: ${themes.join(', ')}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
Reference in New Issue
Block a user