diff --git a/docs-site/astro.config.mjs b/docs-site/astro.config.mjs
index 8b4d068..0511b0a 100644
--- a/docs-site/astro.config.mjs
+++ b/docs-site/astro.config.mjs
@@ -13,6 +13,7 @@ export default defineConfig({
plugins: [
starlightThemeNova({
nav: [
+ { label: 'Демо', href: '/demo/' },
{ label: 'Установка', href: '/getting-started/setup/' },
{ label: 'GitHub', href: 'https://github.com/3252a8/remnawave-minishop' },
{ label: 'Telegram', href: 'https://t.me/remnawave_minishop' }
@@ -59,7 +60,7 @@ export default defineConfig({
label: 'Начало',
items: [
{ label: 'Обзор', slug: 'getting-started/overview' },
- { label: 'Демо', slug: 'demo' },
+ { label: 'Демо-режим', slug: 'getting-started/demo' },
{ label: 'Установка', slug: 'getting-started/setup' },
{ label: 'Развертывание', slug: 'getting-started/deployment' },
{ label: 'Настройка окружения', slug: 'getting-started/configuration' },
diff --git a/docs-site/scripts/sync-docs.mjs b/docs-site/scripts/sync-docs.mjs
index b901393..2773de5 100644
--- a/docs-site/scripts/sync-docs.mjs
+++ b/docs-site/scripts/sync-docs.mjs
@@ -8,7 +8,7 @@ const sourceDir = path.join(repoRoot, 'docs');
const outputDir = path.join(siteRoot, 'src', 'content', 'docs');
const descriptions = {
- 'demo/index.md': 'Статический демо-режим Remnawave Minishop с моковыми данными для Mini App и админки.',
+ 'getting-started/demo.md': 'Как устроен статический демо-режим Remnawave Minishop и почему он собирается только вместе с документацией.',
'index.md': 'Документация по запуску, настройке и сопровождению Telegram Mini App для Remnawave.',
'getting-started/overview.md': 'Что входит в Remnawave Minishop и как связаны бот, Mini App, backend, worker и Remnawave Panel.',
'getting-started/setup.md': 'Минимальный путь запуска Remnawave Minishop через Docker Compose.',
diff --git a/docs-site/src/components/Header.astro b/docs-site/src/components/Header.astro
index f9e69ec..9ce168a 100644
--- a/docs-site/src/components/Header.astro
+++ b/docs-site/src/components/Header.astro
@@ -51,6 +51,10 @@ function navIcon(href: string) {
}
return undefined
}
+
+function isDemoHref(href: string) {
+ return href === '/demo/' || href === '/demo'
+}
---
@@ -70,12 +74,14 @@ function navIcon(href: string) {
const href = getI18nText(item.href, route)
const label = getI18nText(item.label, route)
const icon = navIcon(href)
+ const demo = isDemoHref(href)
return (
diff --git a/docs-site/src/pages/demo.astro b/docs-site/src/pages/demo.astro
new file mode 100644
index 0000000..320aebb
--- /dev/null
+++ b/docs-site/src/pages/demo.astro
@@ -0,0 +1,142 @@
+---
+const defaultDemoSrc = '/demo/runtime/app.html?screen=home&mock=tariffs';
+const docsHref = '/getting-started/demo/';
+---
+
+
+
+
+
+
+
+
+ Remnawave Minishop Demo
+
+
+
+
+
+
+
+
diff --git a/docs-site/src/styles/custom.css b/docs-site/src/styles/custom.css
index a1c69f9..7bc7793 100644
--- a/docs-site/src/styles/custom.css
+++ b/docs-site/src/styles/custom.css
@@ -30,6 +30,17 @@
flex-shrink: 0;
}
+ .minishop-demo-nav-link {
+ border: 1px solid rgb(0 254 122 / 38%);
+ color: var(--sl-color-white);
+ background: rgb(0 254 122 / 8%);
+ }
+
+ .minishop-demo-nav-link:hover {
+ border-color: #00fe7a;
+ color: #00fe7a;
+ }
+
.hero {
gap: clamp(2rem, 6vw, 5rem);
padding-block: clamp(3.5rem, 10vw, 6.5rem);
@@ -100,113 +111,4 @@
padding: 0.08rem 0.28rem;
}
- .demo-shell {
- overflow: hidden;
- margin-block: 1.5rem;
- border: 1px solid var(--sl-color-gray-5);
- border-radius: 8px;
- background: var(--sl-color-bg);
- }
-
- .demo-shell__bar {
- display: flex;
- gap: 1rem;
- align-items: center;
- justify-content: space-between;
- padding: 0.85rem 1rem;
- border-bottom: 1px solid var(--sl-color-gray-5);
- background: var(--sl-color-bg-nav);
- }
-
- .demo-shell__bar strong,
- .demo-shell__bar span {
- display: block;
- }
-
- .demo-shell__bar strong {
- color: var(--sl-color-white);
- font-size: 0.95rem;
- }
-
- .demo-shell__bar span {
- color: var(--sl-color-gray-2);
- font-size: 0.82rem;
- }
-
- .demo-shell__exit {
- flex: 0 0 auto;
- border: 1px solid var(--sl-color-gray-5);
- border-radius: 6px;
- padding: 0.4rem 0.65rem;
- color: var(--sl-color-white);
- font-size: 0.84rem;
- text-decoration: none;
- }
-
- .demo-shell__exit:hover {
- border-color: var(--sl-color-text-accent);
- color: var(--sl-color-text-accent);
- }
-
- .demo-shell__nav {
- display: flex;
- gap: 0.45rem;
- overflow-x: auto;
- padding: 0.7rem 1rem 0;
- scrollbar-width: thin;
- }
-
- .demo-shell__nav--admin {
- padding-top: 0.5rem;
- padding-bottom: 0.7rem;
- border-bottom: 1px solid var(--sl-color-gray-5);
- }
-
- .demo-shell__nav a {
- flex: 0 0 auto;
- border: 1px solid var(--sl-color-gray-5);
- border-radius: 999px;
- padding: 0.34rem 0.62rem;
- color: var(--sl-color-gray-1);
- font-size: 0.82rem;
- line-height: 1.2;
- text-decoration: none;
- white-space: nowrap;
- }
-
- .demo-shell__nav a:hover {
- border-color: var(--sl-color-text-accent);
- color: var(--sl-color-text-accent);
- }
-
- .demo-shell__frame {
- display: block;
- width: 100%;
- height: min(78vh, 52rem);
- min-height: 42rem;
- border: 0;
- background: #0b0f17;
- }
-
- @media (max-width: 42rem) {
- .demo-shell {
- margin-inline: calc(var(--sl-content-pad-x, 1rem) * -1);
- border-inline: 0;
- border-radius: 0;
- }
-
- .demo-shell__bar {
- display: grid;
- align-items: start;
- }
-
- .demo-shell__exit {
- width: fit-content;
- }
-
- .demo-shell__frame {
- height: 46rem;
- min-height: 46rem;
- }
- }
}
diff --git a/docs/demo/index.md b/docs/demo/index.md
deleted file mode 100644
index d936436..0000000
--- a/docs/demo/index.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Демо
-
-Это статический демо-режим Remnawave Minishop для документации. Он собирается вместе с docs-сайтом, использует моковые данные в браузере и не подключается к backend.
-
-
-
-Демо-рантайм генерируется во время сборки сайта документации и не является частью продовой сборки Mini App.
diff --git a/docs/getting-started/demo.md b/docs/getting-started/demo.md
new file mode 100644
index 0000000..c17b12c
--- /dev/null
+++ b/docs/getting-started/demo.md
@@ -0,0 +1,28 @@
+# Демо-режим
+
+Демо-режим показывает статическую сборку Remnawave Minishop с моковыми данными. Он нужен для документации и предпросмотра интерфейса: Mini App, пользовательские сценарии и админка открываются в браузере без backend, базы данных и внешних API.
+
+[Открыть демо](/demo/)
+
+## Как собирается
+
+При сборке сайта документации запускается `docs-site/scripts/build-demo-runtime.mjs`. Скрипт:
+
+- собирает отдельный frontend-бандл в Vite mode `docs-demo`;
+- использует entrypoint `frontend/src/docsDemoEntry.js`, где подключены моковые данные и mock API;
+- дополнительно собирает обычный admin-бандл, чтобы админка работала внутри демо;
+- копирует JS/CSS, темы, default-brand ассеты и локали в `docs-site/public/demo/runtime/`;
+- генерирует `app.html`, который грузит demo runtime и встроенные переводы.
+
+Папка `docs-site/public/demo/runtime/` не хранится в репозитории. Она создается на build step и попадает в итоговый `docs-site/dist/`, поэтому Cloudflare Pages публикует демо вместе с остальным docs-сайтом.
+
+## Почему это не попадает в production
+
+Обычная production-сборка Mini App использует `frontend/src/main.js` и не импортирует `previewMock`, `mockApi` или demo entrypoint. Моковый runtime подключается только в Vite mode `docs-demo`, который вызывается из сборки документации.
+
+## Быстрые ссылки
+
+- [Главный экран демо](/demo/)
+- [Админка: пользователи](/demo/?screen=admin&admin_section=users&mock=tariffs)
+- [Пробный период](/demo/?screen=trial&mock=trial)
+- [Устройства](/demo/?screen=devices&mock=devices)