refactor(docs): [SITE-03] migrate desktop editor and integrate typed generation

This commit is contained in:
Harvey Zhao committed 2026-09-14 09:25:46 +08:00
1 parent a468cb2c39
commit 2f130c58f1
37 files changed
+1921 -305

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
// Generated by yarn build:site-assets. Do not edit.
"use strict";
(() => {
// packages/artplayer-vitepress/browser/editor-preferences.ts
function readPreference(key, storage = () => localStorage) {
try {
return storage().getItem(key) === "true";
} catch {
return false;
}
}
// packages/artplayer-vitepress/browser/bootstrap.ts
function loadScript(url) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
const clear = () => {
script.onload = null;
script.onerror = null;
};
script.onload = () => {
clear();
resolve();
};
script.onerror = () => {
clear();
script.remove();
reject(new Error(`Editor bootstrap failed: ${url}`));
};
script.src = url;
document.body.appendChild(script);
});
}
async function bootstrap() {
await loadScript(
readPreference("prod") ? "./compiled/artplayer.js" : "./uncompiled/artplayer/index.js"
);
await loadScript("./assets/js/vs/loader.js");
await loadScript("./assets/js/common.js");
}
void bootstrap().catch((error) => console.error(error));
})();
+408 -237
View File
@@ -1,245 +1,416 @@
/* global Artplayer, monaco */
(function () {
Artplayer.DEBUG = true
if (Artplayer.utils.isMobile) {
window.location.href = `./mobile.html${location.search}`
// Generated by yarn build:site-assets. Do not edit.
"use strict";
(() => {
// packages/artplayer-vitepress/browser/editor-files.ts
function readEditorFile(file, signal, createReader = () => new FileReader()) {
return new Promise((resolve, reject) => {
const reader = createReader();
function clear() {
reader.onload = null;
reader.onerror = null;
reader.onabort = null;
signal.removeEventListener("abort", cancel);
}
function fail(error) {
clear();
reject(error);
}
function cancel() {
reader.abort();
fail(new Error(`File import cancelled: ${file.name}`));
}
reader.onload = () => {
if (typeof reader.result !== "string") {
fail(new Error(`File is not text: ${file.name}`));
return;
}
const value = reader.result;
clear();
resolve(value);
};
reader.onerror = () => fail(reader.error || new Error(`Reading file failed: ${file.name}`));
reader.onabort = () => fail(new Error(`File import cancelled: ${file.name}`));
signal.addEventListener("abort", cancel, { once: true });
try {
if (signal.aborted)
cancel();
else reader.readAsText(file);
} catch (error) {
fail(error);
}
});
}
const $codeMirror = document.querySelector('.codeMirrorWrap')
const $lib = document.querySelector('.libsInput')
const $run = document.querySelector('.run')
const $popups = document.querySelector('.popups')
const $console = document.querySelector('.console')
const $prod = document.querySelector('#prod')
const $ts = document.querySelector('#ts')
const $code = document.querySelector('#code')
const $log = document.querySelector('#log')
const $file = document.querySelector('#file')
const $editor = document.querySelector('#editor')
window.consoleLog($console)
// Helper functions for localStorage
const getStorageBoolean = key => localStorage.getItem(key) === 'true'
const setStorageBoolean = (key, value) => localStorage.setItem(key, value ? 'true' : 'false')
$prod.checked = getStorageBoolean('prod')
$ts.checked = getStorageBoolean('ts')
$code.checked = getStorageBoolean('code')
$log.checked = getStorageBoolean('log')
if ($code.checked) {
$editor.style.display = 'none'
}
if ($log.checked) {
$console.style.display = 'none'
}
let editor = null
let initialization = 0
require.config({ paths: { vs: './assets/js/vs' } })
require(['vs/editor/editor.main'], async () => {
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: false,
})
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES6,
allowNonTsExtensions: true,
})
const libUris = [
'./assets/ts/artplayer-plugin-ads.d.ts',
'./assets/ts/artplayer-plugin-ambilight.d.ts',
'./assets/ts/artplayer-plugin-asr.d.ts',
'./assets/ts/artplayer-plugin-audio-track.d.ts',
'./assets/ts/artplayer-plugin-auto-thumbnail.d.ts',
'./assets/ts/artplayer-plugin-chapter.d.ts',
'./assets/ts/artplayer-plugin-chromecast.d.ts',
'./assets/ts/artplayer-plugin-danmuku-mask.d.ts',
'./assets/ts/artplayer-plugin-danmuku.d.ts',
'./assets/ts/artplayer-plugin-dash-control.d.ts',
'./assets/ts/artplayer-plugin-document-pip.d.ts',
'./assets/ts/artplayer-plugin-hls-control.d.ts',
'./assets/ts/artplayer-plugin-jassub.d.ts',
'./assets/ts/artplayer-plugin-multiple-subtitles.d.ts',
'./assets/ts/artplayer-plugin-vast.d.ts',
'./assets/ts/artplayer-plugin-vtt-thumbnail.d.ts',
'./assets/ts/artplayer-proxy-canvas.d.ts',
'./assets/ts/artplayer-proxy-mediabunny.d.ts',
'./assets/ts/artplayer-tool-iframe.d.ts',
'./assets/ts/artplayer-tool-thumbnail.d.ts',
'./assets/ts/artplayer.d.ts',
'./assets/ts/artplayer-i18n.d.ts',
]
for (let index = 0; index < libUris.length; index++) {
const libUri = libUris[index]
const libSource = await (await fetch(libUri)).text()
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, libUri)
monaco.editor.createModel(libSource, 'typescript', monaco.Uri.parse(libUri))
}
const disposable = monaco.editor.onDidCreateEditor(() => {
disposable.dispose()
setTimeout(initApp, 1000)
})
editor = monaco.editor.create($codeMirror, {
theme: 'vs-dark',
folding: true,
automaticLayout: true,
quickSuggestions: {
other: true,
comments: true,
strings: true,
function createFileImports(document, label) {
const controller = new AbortController();
const elements = [];
let queue = Promise.resolve();
return {
add(files) {
const work = queue.then(async () => {
for (const file of files) {
const name = file.name.toLowerCase().trim();
if (!name.endsWith(".js") && !name.endsWith(".css"))
continue;
const text = await readEditorFile(file, controller.signal);
if (controller.signal.aborted)
return;
const element = document.createElement(
name.endsWith(".css") ? "style" : "script"
);
element.textContent = text;
document.body.appendChild(element);
elements.push(element);
label(name);
}
});
queue = work.catch(() => {
});
return work;
},
model: monaco.editor.createModel(
dispose() {
controller.abort();
for (const element of elements) element.remove();
elements.length = 0;
}
};
}
// packages/artplayer-vitepress/browser/editor-libraries.ts
var libUris = [
"./assets/ts/artplayer-plugin-ads.d.ts",
"./assets/ts/artplayer-plugin-ambilight.d.ts",
"./assets/ts/artplayer-plugin-asr.d.ts",
"./assets/ts/artplayer-plugin-audio-track.d.ts",
"./assets/ts/artplayer-plugin-auto-thumbnail.d.ts",
"./assets/ts/artplayer-plugin-chapter.d.ts",
"./assets/ts/artplayer-plugin-chromecast.d.ts",
"./assets/ts/artplayer-plugin-danmuku-mask.d.ts",
"./assets/ts/artplayer-plugin-danmuku.d.ts",
"./assets/ts/artplayer-plugin-dash-control.d.ts",
"./assets/ts/artplayer-plugin-document-pip.d.ts",
"./assets/ts/artplayer-plugin-hls-control.d.ts",
"./assets/ts/artplayer-plugin-jassub.d.ts",
"./assets/ts/artplayer-plugin-multiple-subtitles.d.ts",
"./assets/ts/artplayer-plugin-vast.d.ts",
"./assets/ts/artplayer-plugin-vtt-thumbnail.d.ts",
"./assets/ts/artplayer-proxy-canvas.d.ts",
"./assets/ts/artplayer-proxy-mediabunny.d.ts",
"./assets/ts/artplayer-tool-iframe.d.ts",
"./assets/ts/artplayer-tool-thumbnail.d.ts",
"./assets/ts/artplayer.d.ts",
"./assets/ts/artplayer-i18n.d.ts"
];
// packages/artplayer-vitepress/browser/editor-monaco.ts
async function createMonaco(host, container, typescript, signal) {
host.require.config({ paths: { vs: "./assets/js/vs" } });
await new Promise(
(resolve, reject) => host.require(["vs/editor/editor.main"], resolve, reject)
);
await new Promise(
(resolve, reject) => host.require(
[
'var art = new Artplayer({',
'\tcontainer: \'.artplayer-app\',',
'\turl: \'/assets/sample/video.mp4\',',
'});',
].join('\n'),
$ts.checked ? 'typescript' : 'javascript',
),
})
})
async function loadLib(libs) {
$lib.value = decodeURIComponent(libs || '')
return window.ArtplayerDocsLoader.loadLibraries(libs)
}
async function runExample(name, current) {
const text = await window.ArtplayerDocsLoader.exampleSource(name)
if (current !== initialization)
return
editor.setValue(text)
runCode()
}
function loadCode(code, example, current) {
if (current !== initialization)
return
if (example)
return runExample(example, current)
if (code) {
editor.setValue(decodeURIComponent(code).trim())
runCode()
}
else {
return runExample('index', current)
}
}
function runCode() {
window.dispatchEvent(new Event('artplayer:example:cleanup'))
// Destroy all existing instances
Artplayer.instances.slice().forEach(art => art.destroy(true))
const value = editor.getValue()
// eslint-disable-next-line no-eval -- Preserve the editor's classic-script Run scope for user-provided examples.
eval(value)
window.art = Artplayer.instances[0]
}
function initApp() {
const current = ++initialization
const urlParams = window.ArtplayerDocsLoader.parameters(window.location.href)
const { code, libs, example } = urlParams
loadLib(libs)
.then(() => loadCode(code, example, current))
.catch((err) => {
console.error('Failed to initialize app:', err)
})
}
function restart() {
const libs = encodeURIComponent($lib.value)
const code = encodeURIComponent(editor.getValue())
const url = `${window.location.origin}${window.location.pathname}?libs=${libs}&code=${code}`
history.pushState(null, null, url)
initApp()
}
function readFile(file) {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onload = () => resolve(reader.result)
reader.readAsText(file)
})
}
$run.addEventListener('click', () => {
restart()
})
$popups.addEventListener('click', function (event) {
if (event.target === this) {
this.style.display = 'none'
}
})
$prod.addEventListener('change', () => {
setStorageBoolean('prod', $prod.checked)
window.location.reload()
})
$ts.addEventListener('change', () => {
setStorageBoolean('ts', $ts.checked)
window.location.reload()
})
$code.addEventListener('change', () => {
setStorageBoolean('code', $code.checked)
window.location.reload()
})
$log.addEventListener('change', () => {
setStorageBoolean('log', $log.checked)
window.location.reload()
})
$file.addEventListener('change', async () => {
for (let index = 0; index < $file.files.length; index++) {
const file = $file.files[index]
const name = file.name.toLowerCase().trim()
if (name.endsWith('.css')) {
const text = await readFile(file)
const $style = document.createElement('style')
$style.textContent = text
document.body.appendChild($style)
$lib.value = `\n[${name}]`
$lib.value = $lib.value.trim()
"vs/basic-languages/typescript/typescript",
"vs/basic-languages/javascript/javascript",
"vs/language/typescript/tsMode"
],
resolve,
reject
)
);
if (signal.aborted)
throw new Error("Editor initialization cancelled");
const api = host.monaco;
const types = api.languages.typescript;
const resources = [];
const temporary = /* @__PURE__ */ new Set();
let editor;
let disposed = false;
const dispose = () => {
if (disposed)
return;
disposed = true;
signal.removeEventListener("abort", dispose);
editor?.dispose();
editor = void 0;
for (const model of temporary) model.dispose();
temporary.clear();
for (const resource of resources.splice(0).reverse()) resource.dispose();
};
signal.addEventListener("abort", dispose, { once: true });
try {
for (const defaults of [
types.javascriptDefaults,
types.typescriptDefaults
]) {
defaults.setDiagnosticsOptions({
noSemanticValidation: defaults === types.javascriptDefaults,
noSyntaxValidation: false
});
defaults.setCompilerOptions({
target: types.ScriptTarget.ES2015,
module: types.ModuleKind.None,
allowNonTsExtensions: true,
noEmit: false
});
}
if (name.endsWith('.js')) {
const text = await readFile(file)
const $script = document.createElement('script')
$script.textContent = text
document.body.appendChild($script)
$lib.value = `\n[${name}]`
$lib.value = $lib.value.trim()
for (const uri of libUris) {
const response = await host.fetch(uri, { signal });
if (!response.ok) {
throw new Error(
`Loading editor declaration failed: ${uri} (${response.status})`
);
}
const source = await response.text();
if (signal.aborted)
throw new Error("Editor initialization cancelled");
resources.push(
types.javascriptDefaults.addExtraLib(source, uri),
types.typescriptDefaults.addExtraLib(source, uri)
);
resources.push(
api.editor.createModel(source, "typescript", api.Uri.parse(uri))
);
}
const model = api.editor.createModel(
"var art = new Artplayer({\n container: '.artplayer-app',\n url: '/assets/sample/video.mp4',\n});",
typescript ? "typescript" : "javascript"
);
resources.push(model);
editor = api.editor.create(container, {
theme: "vs-dark",
folding: true,
automaticLayout: true,
quickSuggestions: { other: true, comments: true, strings: true },
model
});
return {
editor,
async compile(source) {
if (disposed)
throw new Error("Editor was disposed");
if (!typescript)
return source;
const model2 = api.editor.createModel(source, "typescript");
temporary.add(model2);
try {
const worker = await (await types.getTypeScriptWorker())(model2.uri);
const file = model2.uri.toString();
const syntax = await worker.getSyntacticDiagnostics(file);
if (syntax.length) {
throw new Error(
`TypeScript syntax error: ${syntax.map((item) => typeof item.messageText === "string" ? item.messageText : item.messageText.messageText).join("; ")}`
);
}
const output = await worker.getEmitOutput(file);
const script = output.outputFiles.find(
(file2) => file2.name.endsWith(".js")
);
if (output.emitSkipped || !script)
throw new Error("TypeScript worker did not emit JavaScript");
return script.text;
} finally {
temporary.delete(model2);
if (!model2.isDisposed())
model2.dispose();
}
},
dispose
};
} catch (error) {
dispose();
throw error;
}
})
}
window.addEventListener('error', (err) => {
console.error(err)
})
window.addEventListener('unhandledrejection', (err) => {
console.error(err)
})
document.addEventListener('keydown', (event) => {
if ((event.ctrlKey || event.metaKey) && event.key === 's') {
event.preventDefault()
restart()
// packages/artplayer-vitepress/browser/editor-preferences.ts
function readPreference(key, storage = () => localStorage) {
try {
return storage().getItem(key) === "true";
} catch {
return false;
}
})
})()
}
function writePreference(key, value, storage = () => localStorage) {
try {
storage().setItem(key, value ? "true" : "false");
return true;
} catch {
return false;
}
}
// packages/artplayer-vitepress/browser/editor-session.ts
function createEditorSession(options) {
let generation = 0;
let disposed = false;
return {
async run({
libs = "",
code,
example
}) {
const current = ++generation;
const active = () => !disposed && current === generation;
if (!active())
return;
await options.loadLibraries(libs);
if (!active())
return;
const source = example ? await options.exampleSource(example) : code ? decodeURIComponent(code).trim() : await options.exampleSource("index");
if (!active())
return;
options.writeCode(source);
const executable = await options.compile(source);
if (active())
options.execute(executable);
},
dispose() {
disposed = true;
generation++;
}
};
}
function executeClassic(code) {
new Function(code)();
}
// packages/artplayer-vitepress/browser/editor.ts
async function installEditor(host) {
const document = host.document;
const element = (selector) => {
const node = document.querySelector(selector);
if (!node)
throw new Error(`Missing editor element: ${selector}`);
return node;
};
host.Artplayer.DEBUG = true;
if (host.Artplayer.utils.isMobile) {
host.location.href = `./mobile.html${host.location.search}`;
return;
}
const run = element(".run");
const libs = element(".libsInput");
const file = element("#file");
const popup = element(".popups");
const output = element(".console");
host.consoleLog(output);
const controller = new AbortController();
const cleanups = [];
const listen = (target, type, callback) => {
target.addEventListener(type, callback);
cleanups.push(() => target.removeEventListener(type, callback));
};
const report = (error) => {
if (!controller.signal.aborted)
host.console.error(error);
};
listen(host, "error", (event) => host.console.error(event));
listen(host, "unhandledrejection", (event) => host.console.error(event));
for (const key of ["prod", "ts", "code", "log"]) {
const checkbox = element(`#${key}`);
checkbox.checked = readPreference(key, () => host.localStorage);
listen(checkbox, "change", () => {
if (writePreference(key, checkbox.checked, () => host.localStorage)) {
host.location.reload();
} else {
checkbox.checked = readPreference(key, () => host.localStorage);
report(
new Error("Browser storage is unavailable; preference was not saved")
);
}
});
}
if (element("#code").checked)
element("#editor").style.display = "none";
if (element("#log").checked)
output.style.display = "none";
const imports = createFileImports(document, (name) => {
libs.value = `[${name}]`;
});
cleanups.push(() => imports.dispose());
cleanups.push(() => {
host.dispatchEvent(new Event("artplayer:example:cleanup"));
for (const art of host.Artplayer.instances.slice()) {
try {
art.destroy(true);
} catch (error) {
host.console.error(error);
}
}
host.art = void 0;
});
listen(file, "change", () => {
void imports.add(Array.from(file.files || [])).catch(report);
});
listen(popup, "click", (event) => {
if (event.target === popup)
popup.style.display = "none";
});
const onPageHide = (event) => {
if (event.persisted)
return;
controller.abort();
for (const cleanup of cleanups.reverse()) {
try {
cleanup();
} catch (error) {
host.console.error(error);
}
}
};
host.addEventListener("pagehide", onPageHide);
cleanups.push(() => host.removeEventListener("pagehide", onPageHide));
run.setAttribute("aria-disabled", "true");
try {
let restart2 = function() {
const code = encodeURIComponent(monaco.editor.getValue());
const dependencies = encodeURIComponent(libs.value);
host.history.pushState(
null,
"",
`${host.location.origin}${host.location.pathname}?libs=${dependencies}&code=${code}`
);
void session.run({ libs: dependencies, code }).catch(report);
};
var restart = restart2;
const monaco = await createMonaco(
host,
element(".codeMirrorWrap"),
element("#ts").checked,
controller.signal
);
const session = createEditorSession({
loadLibraries: (encoded) => {
libs.value = decodeURIComponent(encoded);
return host.ArtplayerDocsLoader.loadLibraries(encoded);
},
exampleSource: (name) => host.ArtplayerDocsLoader.exampleSource(name),
writeCode: (code) => monaco.editor.setValue(code),
compile: monaco.compile,
execute: (code) => {
host.dispatchEvent(new Event("artplayer:example:cleanup"));
for (const art of host.Artplayer.instances.slice()) art.destroy(true);
executeClassic(code);
host.art = host.Artplayer.instances[0];
}
});
cleanups.push(() => session.dispose(), monaco.dispose);
listen(run, "click", restart2);
listen(document, "keydown", (event) => {
const key = event;
if ((key.ctrlKey || key.metaKey) && key.key === "s") {
key.preventDefault();
restart2();
}
});
run.removeAttribute("aria-disabled");
await session.run(host.ArtplayerDocsLoader.parameters(host.location.href));
} catch (error) {
report(error);
}
}
void installEditor(window).catch(
(error) => console.error(error)
);
})();
+1 -23
View File
@@ -230,29 +230,7 @@
</div>
<script src="./assets/js/console.js"></script>
<script src="./assets/js/loader.js"></script>
<script>
var artplayer = document.createElement('script');
var monacoLoader = document.createElement('script');
var common = document.createElement('script');
artplayer.onload = function () {
monacoLoader.src = "./assets/js/vs/loader.js";
document.body.appendChild(monacoLoader);
}
monacoLoader.onload = function () {
common.src = "./assets/js/common.js";
document.body.appendChild(common);
}
if (localStorage.getItem('prod') === 'true') {
artplayer.src = "./compiled/artplayer.js"
} else {
artplayer.src = "./uncompiled/artplayer/index.js"
}
document.body.appendChild(artplayer);
</script>
<script src="./assets/js/bootstrap.js"></script>
<script>
var isLocalhost = Boolean(
window.location.hostname === 'localhost' ||