mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-09-26 12:01:57 +02:00
Bundle interactive OpenAPI 3.0 documentation for all XC_VM APIs into the
docsify site and align the docs visual with the Swagger UI page.
API reference
- Add self-contained Swagger UI host page (_media/swagger-ui.html) with a
tab per specification (Admin / System / Player / Playlist) plus nested
Documentation and Swagger views; deep-linkable via ?spec=<key>.
- Add OpenAPI 3.0 specs: admin-api (renamed from openapi.yaml, 104 ops) and
new system-api (31 actions), player-api (XtreamCodes) and playlist-api,
generated from the existing prose guides and controllers.
- Add EN/RU hub page (api/swagger.md) and wire it into the sidebars.
- Remove now-obsolete prose guides (system_api.md, xtreamcodes_api.md,
playlist.md) after verifying full coverage in the specs.
- Rebrand XUI.ONE -> XC_VM across the spec and host page; point links to
github.com/Vateron-Media/XC_VM.
Theming
- Add shared design tokens (_media/theme-tokens.css) consumed by both the
docs and the Swagger page as a single source of truth.
- Switch docsify to docsify-themeable (Simple / Simple Dark) and drop ~150
lines of hand-written CSS.
- Add a light/dark toggle synced across both pages via localStorage['theme'];
default to dark.
154 lines
5.4 KiB
HTML
154 lines
5.4 KiB
HTML
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>XC_VM Documentation</title>
|
|
<meta name="google-site-verification" content="6t0LoIeFksrjF4c9sqUEsVXiQNxLp2hgoqo0KryT-sE">
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
<meta name="keywords" content="xc_vm,iptv,panel,documentation,api,streaming">
|
|
<meta name="description" content="XC_VM IPTV Panel Documentation">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
|
|
|
<!-- docsify-themeable: Simple (light) + Simple Dark (dark).
|
|
Toggled at runtime by the theme button below (localStorage['theme']),
|
|
kept in sync with the Swagger page via the same key + theme-tokens.css. -->
|
|
<link rel="stylesheet" id="xc-theme-light" href="//cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple.css">
|
|
<link rel="stylesheet" id="xc-theme-dark" href="//cdn.jsdelivr.net/npm/docsify-themeable@0/dist/css/theme-simple-dark.css">
|
|
<link rel="stylesheet" href="_media/theme-tokens.css">
|
|
<script>
|
|
// Apply the saved theme before render (default dark) to avoid a flash.
|
|
(function () {
|
|
var t = localStorage.getItem('theme') === 'light' ? 'light' : 'dark';
|
|
document.documentElement.setAttribute('data-theme', t);
|
|
var d = document.getElementById('xc-theme-dark');
|
|
var l = document.getElementById('xc-theme-light');
|
|
if (d) d.disabled = (t !== 'dark');
|
|
if (l) l.disabled = (t === 'dark');
|
|
})();
|
|
</script>
|
|
<style>
|
|
/* Align docsify-themeable's accent + font with the Swagger page via the
|
|
shared --xc-* tokens; the Simple / Simple Dark themes handle the rest. */
|
|
:root {
|
|
--theme-color: var(--xc-accent);
|
|
--link-color: var(--xc-accent);
|
|
--base-font-family: var(--xc-font);
|
|
--content-max-width: 900px;
|
|
}
|
|
|
|
/* Light/dark toggle button (mirrors the Swagger page toggle) */
|
|
#xc-theme-toggle {
|
|
position: fixed;
|
|
bottom: 18px;
|
|
right: 18px;
|
|
z-index: 100;
|
|
width: 38px;
|
|
height: 38px;
|
|
border-radius: 50%;
|
|
border: 1px solid var(--xc-border);
|
|
background: var(--xc-panel);
|
|
color: var(--xc-text);
|
|
font-size: 1.05rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
#xc-theme-toggle:hover {
|
|
border-color: var(--xc-accent);
|
|
color: var(--xc-accent);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="app"></div>
|
|
<script>
|
|
// Redirect root to en-us BEFORE Docsify loads
|
|
if (
|
|
location.hash === '' ||
|
|
location.hash === '#' ||
|
|
location.hash === '#/'
|
|
) {
|
|
location.replace('#/en-us/');
|
|
}
|
|
</script>
|
|
|
|
<script>
|
|
// Language detection
|
|
var lang = location.hash.match(/#\/(ru-ru|en-us)\//);
|
|
|
|
if (lang) {
|
|
document.documentElement.setAttribute('lang', lang[1]);
|
|
}
|
|
|
|
window.$docsify = {
|
|
alias: {
|
|
'/.*/_navbar.md': '/_navbar.md',
|
|
'/ru-ru/(.*)': '/ru/$1',
|
|
'/en-us/(.*)': '/en/$1',
|
|
},
|
|
auto2top: true,
|
|
executeScript: true,
|
|
loadSidebar: true,
|
|
loadNavbar: true,
|
|
mergeNavbar: true,
|
|
maxLevel: 4,
|
|
subMaxLevel: 2,
|
|
name: 'XC_VM',
|
|
nameLink: {
|
|
'/ru-ru/': '#/ru-ru/',
|
|
'/en-us/': '#/en-us/',
|
|
'/': '#/en-us/',
|
|
},
|
|
search: {
|
|
placeholder: 'Search...',
|
|
noData: 'No results',
|
|
paths: 'auto',
|
|
depth: 4,
|
|
},
|
|
};
|
|
</script>
|
|
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/docsify-themeable@0/dist/js/docsify-themeable.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markdown.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-nginx.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-php.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-sql.min.js"></script>
|
|
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
|
|
|
|
|
|
<div class="progress" style="opacity: 0; width: 0%;"></div>
|
|
|
|
<button id="xc-theme-toggle" title="Toggle light / dark" aria-label="Toggle theme"></button>
|
|
<script>
|
|
// Light/dark toggle, synced with the Swagger page via localStorage['theme'].
|
|
// Switches between the docsify-themeable Simple (light) and Simple Dark themes.
|
|
(function () {
|
|
var btn = document.getElementById('xc-theme-toggle');
|
|
var darkCss = document.getElementById('xc-theme-dark');
|
|
var lightCss = document.getElementById('xc-theme-light');
|
|
function isDark() {
|
|
return document.documentElement.getAttribute('data-theme') !== 'light';
|
|
}
|
|
function apply(theme) {
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
if (darkCss) darkCss.disabled = (theme !== 'dark');
|
|
if (lightCss) lightCss.disabled = (theme === 'dark');
|
|
btn.textContent = theme === 'dark' ? '☀️' : '🌙';
|
|
}
|
|
apply(isDark() ? 'dark' : 'light');
|
|
btn.addEventListener('click', function () {
|
|
var next = isDark() ? 'light' : 'dark';
|
|
localStorage.setItem('theme', next);
|
|
apply(next);
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
|
|
</html> |