feat: добавить лендинг, переписать документацию и унифицировать генерацию

- Добавлен лендинг на React + Vite с темой и карточками навигации
- Добавлен модуль темы (src/infra/theme) с поддержкой system/light/dark
- Документация переписана: разделы «Модули», «Сегменты», «Компонент»
- Добавлена страница навигации docs/index.md
- Генерация llms.txt переведена на парсинг сайдбара VitePress
- Описания для llms.txt вынесены в frontmatter (поле description)
- Удалена директория generated/, архив ZIP убран с лендинга
- Удалены английская документация, README_RU, concat-md.js
- Добавлен vite-плагин для UTF-8 заголовков текстовых артефактов
- Caddyfile обновлён: charset=utf-8 для llms.txt и ARCHITECTURE.md
This commit is contained in:
2026-05-01 21:00:25 +03:00
parent 5553ece16d
commit d69fca16fe
43 changed files with 3877 additions and 1282 deletions

79
vite.config.ts Normal file
View File

@@ -0,0 +1,79 @@
import { defineConfig, Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import { readFileSync, existsSync } from 'fs'
import { resolve } from 'path'
const pkg = JSON.parse(readFileSync('./package.json', 'utf8'))
const textArtifacts: Record<string, string> = {
'/llms.txt': 'text/plain; charset=utf-8',
'/llms-full.txt': 'text/plain; charset=utf-8',
'/ARCHITECTURE.md': 'text/markdown; charset=utf-8',
}
function serveTextArtifacts(): Plugin {
return {
name: 'serve-text-artifacts',
configureServer(server) {
const publicDir = resolve('public')
server.middlewares.use((req, res, next) => {
const url = req.url?.split('?')[0]
const contentType = url ? textArtifacts[url] : undefined
if (!url || !contentType) return next()
const filePath = resolve(publicDir, url.slice(1))
if (!existsSync(filePath)) return next()
res.setHeader('Content-Type', contentType)
res.end(readFileSync(filePath))
})
},
}
}
function serveDocs(): Plugin {
return {
name: 'serve-docs',
configureServer(server) {
const publicDir = resolve('public')
server.middlewares.use((req, _res, next) => {
const url = req.url?.split('?')[0]
if (!url?.startsWith('/docs')) return next()
const filePath = resolve(publicDir, url.slice(1))
if (url === '/docs') {
if (existsSync(resolve(filePath, 'index.html'))) {
req.url = '/docs/index.html'
return next()
}
}
if (url.endsWith('/')) {
if (existsSync(resolve(filePath, 'index.html'))) {
req.url = url + 'index.html'
return next()
}
}
if (!url.split('/').pop()?.includes('.')) {
if (existsSync(filePath + '.html')) {
req.url = url + '.html'
return next()
}
}
next()
})
},
}
}
export default defineConfig({
plugins: [react(), serveTextArtifacts(), serveDocs()],
define: {
__BUILD_VERSION__: JSON.stringify(`v${pkg.version}`),
},
})