fix(docs): [SITE-LOAD-01] serialize example loading and preserve navigation

This commit is contained in:
Harvey Zhao committed 2026-09-14 07:52:32 +08:00
1 parent 76499e7a3e
commit 647f3e7f12
29 files changed
+1977 -256

No files matched your search

+39 -109
View File
@@ -1,3 +1,4 @@
/* global Artplayer, monaco */
(function () {
Artplayer.DEBUG = true
@@ -5,22 +6,22 @@
window.location.href = `./mobile.html${location.search}`
}
let $codeMirror = document.querySelector('.codeMirrorWrap')
let $lib = document.querySelector('.libsInput')
let $run = document.querySelector('.run')
let $popups = document.querySelector('.popups')
let $console = document.querySelector('.console')
let $prod = document.querySelector('#prod')
let $ts = document.querySelector('#ts')
let $code = document.querySelector('#code')
let $log = document.querySelector('#log')
let $file = document.querySelector('#file')
let $editor = document.querySelector('#editor')
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 getStorageBoolean = key => localStorage.getItem(key) === 'true'
const setStorageBoolean = (key, value) => localStorage.setItem(key, value ? 'true' : 'false')
$prod.checked = getStorageBoolean('prod')
@@ -37,7 +38,7 @@
}
let editor = null
let loadedLibs = []
let initialization = 0
require.config({ paths: { vs: './assets/js/vs' } })
require(['vs/editor/editor.main'], async () => {
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
@@ -50,7 +51,7 @@
allowNonTsExtensions: true,
})
let libUris = [
const libUris = [
'./assets/ts/artplayer-plugin-ads.d.ts',
'./assets/ts/artplayer-plugin-ambilight.d.ts',
'./assets/ts/artplayer-plugin-asr.d.ts',
@@ -76,8 +77,8 @@
]
for (let index = 0; index < libUris.length; index++) {
let libUri = libUris[index]
let libSource = await (await fetch(libUri)).text()
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))
}
@@ -108,122 +109,51 @@
})
})
function getURLParameters(url) {
return (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce((params, pair) => {
const index = pair.indexOf('=')
params[pair.slice(0, index)] = pair.slice(index + 1)
return params
}, {})
async function loadLib(libs) {
$lib.value = decodeURIComponent(libs || '')
return window.ArtplayerDocsLoader.loadLibraries(libs)
}
function getExt(url) {
if (url.includes('?')) {
return getExt(url.split('?')[0])
}
if (url.includes('#')) {
return getExt(url.split('#')[0])
}
return url.trim().toLowerCase().split('.').pop()
async function runExample(name, current) {
const text = await window.ArtplayerDocsLoader.exampleSource(name)
if (current !== initialization)
return
editor.setValue(text)
runCode()
}
function loadScript(url) {
return new Promise((resolve, reject) => {
const originalDefine = window.define
window.define = undefined
const script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
script.onload = () => {
window.define = originalDefine
resolve(url)
}
script.onerror = () => {
window.define = originalDefine
reject(new Error(`Loading script failed: ${url}`))
}
document.head.appendChild(script)
})
}
function loadStyle(url) {
return new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
link.onload = () => resolve(url)
link.onerror = () => reject(new Error(`Loading style failed: ${url}`))
document.head.appendChild(link)
})
}
function loadLib(libs) {
const libPromises = []
const libsDecoded = decodeURIComponent(libs || '')
const urls = libsDecoded
.split(/\r?\n/)
.filter((url) => url.trim() && !loadedLibs.includes(url))
urls.forEach((url) => {
const ext = getExt(url)
if (ext === 'js') {
libPromises.push(loadScript(url))
} else if (ext === 'css') {
libPromises.push(loadStyle(url))
}
})
$lib.value = libsDecoded
return Promise.all(libPromises)
}
function runExample(name) {
fetch(`./assets/example/${name}.js`)
.then((response) => {
return response.text()
})
.then((text) => {
editor.setValue(text)
runCode()
})
.catch((err) => {
console.error(err)
})
}
function loadCode(code, example) {
if (example) {
runExample(example)
}
else if (code) {
function loadCode(code, example, current) {
if (current !== initialization)
return
if (example)
return runExample(example, current)
if (code) {
editor.setValue(decodeURIComponent(code).trim())
runCode()
}
else {
runExample('index')
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))
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 urlParams = getURLParameters(window.location.href)
const current = ++initialization
const urlParams = window.ArtplayerDocsLoader.parameters(window.location.href)
const { code, libs, example } = urlParams
loadLib(libs)
.then((result) => {
loadedLibs.push(...result)
loadCode(code, example)
})
.then(() => loadCode(code, example, current))
.catch((err) => {
console.error('Failed to initialize app:', err)
})
+135
View File
@@ -0,0 +1,135 @@
// Generated by yarn build:site-assets. Do not edit.
"use strict";
(() => {
// packages/artplayer-vitepress/browser/loader.ts
function parameters(url) {
const result = {};
for (const part of new URL(url).search.slice(1).split("&")) {
const index = part.indexOf("=");
if (index >= 0)
result[part.slice(0, index)] = part.slice(index + 1);
}
return result;
}
function suspendDefine(host) {
const descriptor = Object.getOwnPropertyDescriptor(host, "define");
if (descriptor?.configurable === false) {
if (!("value" in descriptor) || !descriptor.writable)
throw new Error("Cannot temporarily suspend the AMD loader");
Object.defineProperty(host, "define", { value: void 0 });
} else {
Object.defineProperty(host, "define", { value: void 0, configurable: true, writable: true, enumerable: descriptor?.enumerable ?? true });
}
return () => {
if (descriptor)
Object.defineProperty(host, "define", descriptor);
else Reflect.deleteProperty(host, "define");
};
}
function createLibraryLoader(host = window) {
const loaded = /* @__PURE__ */ new Set();
let queue = Promise.resolve();
let cancel;
let disposed = false;
const ensureActive = () => {
if (disposed)
throw new Error("Documentation loader was disposed");
};
function load(url, script) {
return new Promise((resolve, reject) => {
const element = host.document.createElement(script ? "script" : "link");
let restore;
let settled = false;
function finish(error) {
if (settled)
return;
settled = true;
element.removeEventListener("load", success);
element.removeEventListener("error", failure);
cancel = void 0;
try {
restore?.();
} catch (cause) {
error = new Error(`Failed to restore AMD loader: ${String(cause)}`);
}
if (error) {
element.remove();
reject(error);
} else {
resolve();
}
}
function success() {
finish();
}
function failure() {
finish(new Error(`Loading ${script ? "script" : "style"} failed: ${url}`));
}
element.addEventListener("load", success);
element.addEventListener("error", failure);
cancel = () => finish(new Error(`Cancelled documentation dependency: ${url}`));
try {
ensureActive();
if (script) {
restore = suspendDefine(host);
const node = element;
node.type = "text/javascript";
node.src = url;
node.async = false;
} else {
const node = element;
node.rel = "stylesheet";
node.href = url;
}
host.document.head.appendChild(element);
} catch (cause) {
finish(cause instanceof Error ? cause : new Error(String(cause)));
}
});
}
return {
loadLibraries(encoded = "") {
const work = queue.then(async () => {
ensureActive();
const urls = decodeURIComponent(encoded).split(/\r?\n/).map((url) => url.trim()).filter(Boolean);
const result = [];
for (const url of urls) {
ensureActive();
const absolute = new URL(url, host.location.href);
const extension = absolute.pathname.toLowerCase().split(".").pop();
if (extension !== "js" && extension !== "css")
continue;
if (!loaded.has(absolute.href)) {
await load(absolute.href, extension === "js");
loaded.add(absolute.href);
}
result.push(url);
}
return result;
});
queue = work.catch(() => {
});
return work;
},
dispose() {
disposed = true;
cancel?.();
loaded.clear();
}
};
}
async function exampleSource(name, request = fetch) {
const response = await request(`./assets/example/${name}.js`);
if (!response.ok)
throw new Error(`Loading example failed: ${name} (${response.status})`);
return response.text();
}
// packages/artplayer-vitepress/browser/editor-loader.ts
var loader = createLibraryLoader();
Object.assign(window, { ArtplayerDocsLoader: { ...loader, exampleSource, parameters } });
window.addEventListener("pagehide", (event) => {
if (!event.persisted)
loader.dispose();
});
})();
+137 -95
View File
@@ -1,100 +1,142 @@
(function () {
Artplayer.DEBUG = true;
function getURLParameters(url) {
return (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(function (a, v) {
return (a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a;
}, {});
// Generated by yarn build:site-assets. Do not edit.
"use strict";
(() => {
// packages/artplayer-vitepress/browser/loader.ts
function parameters(url) {
const result = {};
for (const part of new URL(url).search.slice(1).split("&")) {
const index = part.indexOf("=");
if (index >= 0)
result[part.slice(0, index)] = part.slice(index + 1);
}
function getExt(url) {
if (url.includes('?')) {
return getExt(url.split('?')[0]);
}
if (url.includes('#')) {
return getExt(url.split('#')[0]);
}
return url.trim().toLowerCase().split('.').pop();
return result;
}
function suspendDefine(host) {
const descriptor = Object.getOwnPropertyDescriptor(host, "define");
if (descriptor?.configurable === false) {
if (!("value" in descriptor) || !descriptor.writable)
throw new Error("Cannot temporarily suspend the AMD loader");
Object.defineProperty(host, "define", { value: void 0 });
} else {
Object.defineProperty(host, "define", { value: void 0, configurable: true, writable: true, enumerable: descriptor?.enumerable ?? true });
}
function loadScript(url) {
return new Promise(function (resolve, reject) {
var define2 = window.define;
window.define = undefined;
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = function () {
window.define = define2;
resolve(url);
};
script.onerror = function () {
reject(new Error('Loading script failed:' + url));
};
document.querySelector('head').appendChild(script);
return () => {
if (descriptor)
Object.defineProperty(host, "define", descriptor);
else Reflect.deleteProperty(host, "define");
};
}
function createLibraryLoader(host = window) {
const loaded = /* @__PURE__ */ new Set();
let queue = Promise.resolve();
let cancel;
let disposed = false;
const ensureActive = () => {
if (disposed)
throw new Error("Documentation loader was disposed");
};
function load(url, script) {
return new Promise((resolve, reject) => {
const element = host.document.createElement(script ? "script" : "link");
let restore;
let settled = false;
function finish(error) {
if (settled)
return;
settled = true;
element.removeEventListener("load", success);
element.removeEventListener("error", failure);
cancel = void 0;
try {
restore?.();
} catch (cause) {
error = new Error(`Failed to restore AMD loader: ${String(cause)}`);
}
if (error) {
element.remove();
reject(error);
} else {
resolve();
}
}
function success() {
finish();
}
function failure() {
finish(new Error(`Loading ${script ? "script" : "style"} failed: ${url}`));
}
element.addEventListener("load", success);
element.addEventListener("error", failure);
cancel = () => finish(new Error(`Cancelled documentation dependency: ${url}`));
try {
ensureActive();
if (script) {
restore = suspendDefine(host);
const node = element;
node.type = "text/javascript";
node.src = url;
node.async = false;
} else {
const node = element;
node.rel = "stylesheet";
node.href = url;
}
host.document.head.appendChild(element);
} catch (cause) {
finish(cause instanceof Error ? cause : new Error(String(cause)));
}
});
}
return {
loadLibraries(encoded = "") {
const work = queue.then(async () => {
ensureActive();
const urls = decodeURIComponent(encoded).split(/\r?\n/).map((url) => url.trim()).filter(Boolean);
const result = [];
for (const url of urls) {
ensureActive();
const absolute = new URL(url, host.location.href);
const extension = absolute.pathname.toLowerCase().split(".").pop();
if (extension !== "js" && extension !== "css")
continue;
if (!loaded.has(absolute.href)) {
await load(absolute.href, extension === "js");
loaded.add(absolute.href);
}
result.push(url);
}
return result;
});
}
function loadStyle(url) {
return new Promise(function (resolve, reject) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = url;
link.onload = function () {
resolve(url);
};
link.onerror = function () {
reject(new Error('Loading style failed:' + url));
};
document.querySelector('head').appendChild(link);
queue = work.catch(() => {
});
}
return work;
},
dispose() {
disposed = true;
cancel?.();
loaded.clear();
}
};
}
async function exampleSource(name, request = fetch) {
const response = await request(`./assets/example/${name}.js`);
if (!response.ok)
throw new Error(`Loading example failed: ${name} (${response.status})`);
return response.text();
}
function loadLib(libs) {
var libPromise = [];
var libsDecode = decodeURIComponent(libs || '');
libsDecode
.split(/\r?\n/)
.forEach(function (url) {
var ext = getExt(url);
if (ext === 'js') {
libPromise.push(loadScript(url));
} else if (ext === 'css') {
libPromise.push(loadStyle(url));
}
});
return Promise.all(libPromise);
}
function runExample(name) {
fetch('./assets/example/' + name + '.js')
.then(function (response) {
return response.text();
})
.then(function (text) {
eval(text);
})
}
function loadCode(code, example) {
if (example) {
runExample(example);
} else if (code) {
eval(decodeURIComponent(code).trim());
} else {
runExample('mobile');
}
}
var _getURLParameters = getURLParameters(window.location.href),
code = _getURLParameters.code,
libs = _getURLParameters.libs;
example = _getURLParameters.example;
loadLib(libs)
.then(function () {
loadCode(code, example);
})
})();
// packages/artplayer-vitepress/browser/mobile.ts
window.Artplayer.DEBUG = true;
var loader = createLibraryLoader();
window.addEventListener("pagehide", (event) => {
if (!event.persisted)
loader.dispose();
});
async function start() {
const { code, libs, example } = parameters(location.href);
await loader.loadLibraries(libs);
const source = example ? await exampleSource(example) : code ? decodeURIComponent(code).trim() : await exampleSource("mobile");
new Function(source)();
}
start().catch((error) => console.error("Failed to initialize mobile example:", error));
})();
+1
View File
@@ -229,6 +229,7 @@
<div class="popinner"></div>
</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');