Compare commits

..

7 Commits

Author SHA1 Message Date
7deb2a7d95 Update documentation: rephrase server concept, enhance map description, and remove redundant sections 2026-04-27 16:06:20 +03:00
8cee39ad1c Refactor documentation structure and update links; remove outdated content 2026-04-25 13:53:11 +03:00
f622c09fd4 Add sturdy-slab image asset to the crafts directory 2026-04-25 00:30:30 +03:00
9227f2de30 Добавлены функции для аннотирования ссылок и улучшения стилей заголовков в документации 2026-04-23 19:38:55 +03:00
ecb0d8ebc2 Добавлены новые разделы документации: "Города и регистрация", "Крафты", "Приват и защита", "SkinRestore". Каждый раздел содержит информацию о статусе, что будет добавлено, и полезные советы для пользователей. 2026-04-22 06:26:33 +03:00
c38d538f93 Update story description and enhance sidebar styles
- Revised the story section description to reflect the server's narrative.
- Removed placeholder text and added a prompt for content creation.
- Improved sidebar link styles for better visibility and interaction.
- Added hover effects and styles for sections with sub-items in the table of contents.
- Introduced new styles for alcohol and contact link cards for consistent design.
- Enhanced map call-to-action button with new styles and hover effects.
2026-04-21 12:44:22 +03:00
e32e0fb972 Refactor code structure for improved readability and maintainability 2026-04-21 12:24:26 +03:00
71 changed files with 3560 additions and 2996 deletions

View File

@@ -1 +0,0 @@
^C

View File

@@ -1,5 +0,0 @@
> server-wiki@0.0.1 dev
> astro dev --host 0.0.0.0 --port 4321
[vite] connected.

View File

@@ -1,6 +1,54 @@
# Dependencies and build output
node_modules node_modules
dist
.astro .astro
dist
# VCS metadata
.git .git
.gitignore .gitignore
# Local Codex/dev artifacts
.codex*
.codex/
.codex-dev.*
.codex-cache/
.tmp*
*.tmp
# Logs and runtime junk
*.log
*.log.*
*.out
*.err
*.pid
*.pid.lock
logs
preview*.log
npm-debug.log* npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Env and local-only config
.env
.env.*
!.env.example
*.local
# OS/editor files
.DS_Store
Thumbs.db
.vscode
.idea
*.swp
*.swo
# Local caches
tmp
.cache
temp
coverage
.nyc_output
.pnpm-store
.npm
*.tsbuildinfo

47
.gitignore vendored
View File

@@ -1,4 +1,51 @@
node_modules/ node_modules/
.astro/ .astro/
dist/ dist/
# Local Codex/dev artifacts
.codex*
.codex/
.codex-dev.*
.codex-cache/
.tmp*
*.tmp
# Logs
*.log
*.log.*
*.out
*.err
*.pid
*.pid.lock
logs/
preview*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Env and local-only config
.env
.env.*
!.env.example
*.local
# OS files
.DS_Store .DS_Store
Thumbs.db
# Editor / local workspace junk
.vscode/
.idea/
*.swp
*.swo
# Caches / tooling
.cache/
tmp/
temp/
coverage/
.nyc_output/
.pnpm-store/
.npm/
*.tsbuildinfo

File diff suppressed because one or more lines are too long

93
Makefile Normal file
View File

@@ -0,0 +1,93 @@
# ================================
# Настройки
# ================================
COMPOSE=docker compose
SERVICE=primoguard
# Список команд
.PHONY: help install dev build preview up down restart logs pull update ps shell clean prune tunnel
# ================================
# Помощь
# ================================
help:
@echo "Доступные команды:"
@echo " make install - установить npm зависимости"
@echo " make dev - локальный запуск Astro для разработки"
@echo " make build - продакшн сборка Astro"
@echo " make preview - предпросмотр продакшн сборки"
@echo " make up - поднять Docker контейнер"
@echo " make down - остановить Docker контейнер"
@echo " make restart - перезапустить Docker контейнер"
@echo " make logs - смотреть логи Docker"
@echo " make pull - обновить проект через git pull"
@echo " make update - обновить проект и перезапустить Docker"
@echo " make ps - список контейнеров"
@echo " make shell - зайти внутрь контейнера"
@echo " make clean - остановить контейнер"
@echo " make prune - очистить Docker мусор"
@echo " make tunnel - запустить Cloudflare Tunnel"
# ================================
# Локальная разработка
# ================================
install:
npm install
dev:
npm run dev
build:
npm run build
preview:
npm run preview
# ================================
# Docker
# ================================
up:
$(COMPOSE) up -d --build
down:
$(COMPOSE) down
restart:
$(COMPOSE) down
$(COMPOSE) up -d --build
logs:
$(COMPOSE) logs -f
ps:
$(COMPOSE) ps
shell:
docker exec -it $(SERVICE) sh
# ================================
# Git + Docker
# ================================
pull:
git pull
update:
$(COMPOSE) down
git pull
$(COMPOSE) up -d --build
$(COMPOSE) logs -f
# ================================
# Очистка
# ================================
clean:
$(COMPOSE) down
prune:
docker system prune -f
# ================================
# Публичный доступ
# ================================
tunnel:
cloudflared tunnel --url http://127.0.0.1:4321 --protocol http2 --edge-ip-version 4

View File

@@ -1,6 +1,7 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight'; import starlight from '@astrojs/starlight';
export default defineConfig({ export default defineConfig({
site: 'https://wiki.example.com', site: 'https://wiki.example.com',
vite: { vite: {
@@ -25,6 +26,7 @@ export default defineConfig({
} }
}, },
tagline: 'Нереалистичный архив игрового мира', tagline: 'Нереалистичный архив игрового мира',
favicon: '/photo_2026-03-14_11-18-15.jpg',
customCss: ['/src/styles/wiki-egames.css'], customCss: ['/src/styles/wiki-egames.css'],
lastUpdated: true, lastUpdated: true,
disable404Route: true, disable404Route: true,
@@ -45,48 +47,40 @@ export default defineConfig({
label: 'О сервере', label: 'О сервере',
items: [ items: [
{ label: 'Главная', link: '/' }, { label: 'Главная', link: '/' },
{ label: 'Сюжет', link: '/story/' },
{ label: 'Начало игры', link: '/getting-started/' } { label: 'Начало игры', link: '/getting-started/' }
] ]
}, },
{ {
label: 'Введение', label: 'Введение',
items: [ items: [
{ label: 'Часто задаваемые вопросы', link: '/faq/' }, { label: 'О проекте', link: '/about-server/' },
{ label: 'Правила', link: '/rules/' } { label: 'Правила', link: '/rules/' },
{ label: 'Часто задаваемые вопросы', link: '/faq/' }
] ]
}, },
{ {
label: 'Игровые системы', label: 'Игровые системы',
items: [ items: [
{ label: 'Гайды', link: '/guides/' }, { label: 'Общее', link: '/general/' },
{ label: 'Механики', link: '/mechanics/' },
{ label: 'Профессии / классы / расы', link: '/professions/' },
{ label: 'Экономика', link: '/economy/' },
{ label: 'Предметы и ресурсы', link: '/items-resources/' },
{ label: 'Команды', link: '/commands/' }, { label: 'Команды', link: '/commands/' },
{ label: 'Города', link: '/cities/' },
{ label: 'Крафты', link: '/crafting/' },
{ {
label: 'Алкоголь', label: 'Алкоголь',
items: [ items: [
{ label: 'Варка алкоголя', link: '/alcohol-brewing/' }, { label: 'Как варить', link: '/alcohol-brewing/' },
{ label: 'Рецепты напитков', link: '/alcohol-recipes/' } { label: 'Рецепты напитков', link: '/alcohol-recipes/' }
] ]
} },
{ label: 'EasyArmorStands', link: '/easyarmorstands/' }
] ]
}, },
{ {
label: 'Техническое', label: 'Тех. Информация',
items: [ items: [
{ label: 'Все IP', link: '/all-ip/' }, { label: 'Все IP', link: '/all-ip/' },
{ label: 'Контакты и ссылки', link: '/contacts/' } { label: 'Контакты и ссылки', link: '/contacts/' }
] ]
},
{
label: 'Для редакторов',
items: [
{ label: 'Как редактировать wiki', link: '/editing/' },
{ label: 'Шаблон новой страницы', link: '/templates/new-page-template/' }
]
} }
] ]
}) })

View File

@@ -1,11 +1,15 @@
name: parabox-wiki
services: services:
wiki: wiki:
build: build:
context: . context: .
dockerfile: Dockerfile dockerfile: Dockerfile
container_name: parabox-wiki container_name: parabox-wiki
ports: #ports:
- "4321:80" #- "4321:80"
restart: unless-stopped restart: unless-stopped
networks:
- proxy
networks:
proxy:
external: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
public/brand-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
public/crafts/bell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
public/crafts/bundle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
public/crafts/dispenser.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
public/crafts/jukebox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
public/crafts/loom.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/crafts/name-tag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -2,10 +2,11 @@
import { Icon } from '@astrojs/starlight/components'; import { Icon } from '@astrojs/starlight/components';
const { editUrl } = Astro.locals.starlightRoute; const { editUrl } = Astro.locals.starlightRoute;
const showEditorDocs = import.meta.env.PUBLIC_SHOW_EDITOR_DOCS === 'true';
--- ---
{ {
editUrl && ( showEditorDocs && editUrl && (
<a href={editUrl} class="wiki-edit-link sl-flex print:hidden" target="_blank" rel="noopener noreferrer"> <a href={editUrl} class="wiki-edit-link sl-flex print:hidden" target="_blank" rel="noopener noreferrer">
<Icon name="pencil" size="1.05em" /> <Icon name="pencil" size="1.05em" />
Редактировать страницу Редактировать страницу

View File

@@ -3,7 +3,6 @@ import config from 'virtual:starlight/user-config';
import LanguageSelect from 'virtual:starlight/components/LanguageSelect'; import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
import Search from 'virtual:starlight/components/Search'; import Search from 'virtual:starlight/components/Search';
import SiteTitle from 'virtual:starlight/components/SiteTitle';
import SocialIcons from 'virtual:starlight/components/SocialIcons'; import SocialIcons from 'virtual:starlight/components/SocialIcons';
const shouldRenderSearch = const shouldRenderSearch =
@@ -11,8 +10,8 @@ const shouldRenderSearch =
const topLinks = [ const topLinks = [
{ label: 'Старт', href: '/getting-started/' }, { label: 'Старт', href: '/getting-started/' },
{ label: 'Гайды', href: '/guides/' }, { label: 'Команды', href: '/commands/' },
{ label: 'Механики', href: '/mechanics/' }, { label: 'Города', href: '/cities/' },
{ label: 'FAQ', href: '/faq/' }, { label: 'FAQ', href: '/faq/' },
{ label: 'Правила', href: '/rules/' } { label: 'Правила', href: '/rules/' }
]; ];
@@ -21,7 +20,15 @@ const topLinks = [
<div class="wiki-header-shell"> <div class="wiki-header-shell">
<div class="header wiki-header"> <div class="header wiki-header">
<div class="title-wrapper sl-flex"> <div class="title-wrapper sl-flex">
<SiteTitle /> <a class="wiki-brand" href="/" aria-label="PARABOX PROJECT">
<span class="wiki-brand__icon" aria-hidden="true">
<img src="/brand-logo.png" alt="" loading="eager" decoding="async" />
</span>
<span class="wiki-brand__copy">
<span class="wiki-brand__title">PARABOX</span>
<span class="wiki-brand__subtitle">PROJECT</span>
</span>
</a>
<nav class="wiki-top-nav" aria-label="Быстрая навигация"> <nav class="wiki-top-nav" aria-label="Быстрая навигация">
{ {
topLinks.map((link) => ( topLinks.map((link) => (
@@ -89,10 +96,365 @@ const topLinks = [
</div> </div>
<button class="wiki-back-to-top" type="button" aria-label="Прокрутить в начало" data-back-to-top> <button class="wiki-back-to-top" type="button" aria-label="Прокрутить в начало" data-back-to-top>
<span aria-hidden="true">↑</span> <svg aria-hidden="true" class="wiki-back-to-top__icon" viewBox="0 0 24 24" fill="none">
<path d="M12 18V6" />
<path d="m6.8 11.2 5.2-5.2 5.2 5.2" />
</svg>
</button> </button>
<script> <script>
const emojiPattern = (() => {
try {
return new RegExp('[\\p{Extended_Pictographic}\\u2600-\\u27BF]', 'gu');
} catch {
return /[\u2600-\u27BF]/g;
}
})();
const stripDecorativeSymbols = (value) =>
value
.replace(emojiPattern, '')
.replace(/\s{2,}/g, ' ')
.trim();
const normalizeLabelKey = (value) => stripDecorativeSymbols(value).toLowerCase();
const minimalIconByHeading = new Map([
['настройки', '/icons/minimal-solid/gear.png'],
['личные сообщения', '/icons/minimal-solid/message.png'],
['игнор', '/icons/minimal-solid/safe.png'],
['развлечения', '/icons/minimal-solid/game-controller.png'],
['rp и взаимодействие', '/icons/minimal-solid/community.png'],
['статус игрока', '/icons/minimal-solid/user-profile.png'],
['информация', '/icons/minimal-solid/exclamation.png'],
['символы', '/icons/minimal-solid/keyboard.png']
]);
const sanitizeNavLabels = () => {
const navLinks = document.querySelectorAll('.sidebar-content a, starlight-toc a, mobile-starlight-toc a');
navLinks.forEach((linkNode) => {
if (!(linkNode instanceof HTMLElement)) return;
const sourceText = linkNode.textContent;
if (!sourceText) return;
const normalizedText = stripDecorativeSymbols(sourceText);
if (!normalizedText || normalizedText === sourceText.trim()) return;
linkNode.textContent = normalizedText;
});
};
const sanitizeDocHeadings = () => {
const headings = document.querySelectorAll('.sl-markdown-content :is(h1, h2, h3, h4, h5, h6)');
headings.forEach((headingNode) => {
if (!(headingNode instanceof HTMLElement)) return;
const sourceText = headingNode.textContent;
if (!sourceText) return;
const normalizedText = stripDecorativeSymbols(sourceText);
if (!normalizedText || normalizedText === sourceText.trim()) return;
headingNode.textContent = normalizedText;
});
};
const decorateSectionIcons = () => {
const headings = document.querySelectorAll('.sl-markdown-content h3');
headings.forEach((headingNode) => {
if (!(headingNode instanceof HTMLElement)) return;
const sourceText = headingNode.textContent;
if (!sourceText) return;
const normalizedText = normalizeLabelKey(sourceText);
const iconPath = minimalIconByHeading.get(normalizedText);
if (!iconPath) return;
if (headingNode.querySelector('.minimal-heading-icon')) return;
const iconNode = document.createElement('span');
iconNode.className = 'minimal-heading-icon';
iconNode.setAttribute('aria-hidden', 'true');
iconNode.style.setProperty('--icon-url', `url('${iconPath}')`);
headingNode.prepend(iconNode);
});
};
const trimDestination = (value, maxLength = 42) => {
if (value.length <= maxLength) return value;
return `${value.slice(0, Math.max(0, maxLength - 1))}…`;
};
const decodePath = (value) => {
try {
return decodeURIComponent(value);
} catch {
return value;
}
};
const annotateLinkDestinations = () => {
const links = document.querySelectorAll('a[href]');
links.forEach((linkNode) => {
if (!(linkNode instanceof HTMLAnchorElement)) return;
if (linkNode.dataset.linkAnnotated === 'true') return;
const rawHref = linkNode.getAttribute('href')?.trim();
if (!rawHref || rawHref.toLowerCase().startsWith('javascript:')) return;
let parsedUrl;
try {
parsedUrl = new URL(rawHref, window.location.href);
} catch {
return;
}
const isHashOnly = rawHref.startsWith('#');
const isHttp = parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
const isMail = parsedUrl.protocol === 'mailto:';
const isTel = parsedUrl.protocol === 'tel:';
const isExternal = isHttp && parsedUrl.origin !== window.location.origin;
const isSamePageAnchor =
!isExternal && !!parsedUrl.hash && parsedUrl.pathname === window.location.pathname;
const isContentLink = linkNode.closest('.sl-markdown-content') instanceof HTMLElement;
const shouldDecorate =
isContentLink &&
!isHashOnly &&
!linkNode.classList.contains('sl-anchor-link') &&
!linkNode.classList.contains('map-cta-button') &&
!linkNode.classList.contains('map-ghost-button') &&
!linkNode.classList.contains('sl-link-button');
if (shouldDecorate) {
let kind = 'internal';
if (isExternal) kind = 'external';
else if (isMail) kind = 'mail';
else if (isTel) kind = 'tel';
else if (isSamePageAnchor) kind = 'anchor';
linkNode.dataset.linkKind = kind;
let destination = '';
if (isExternal) destination = parsedUrl.hostname.replace(/^www\./, '');
else if (isMail) destination = rawHref.replace(/^mailto:/i, '');
else if (isTel) destination = rawHref.replace(/^tel:/i, '');
else destination = `${decodePath(parsedUrl.pathname)}${parsedUrl.hash || ''}`;
if (destination) {
linkNode.dataset.linkDestination = trimDestination(destination);
}
}
if (isExternal) {
const relValues = new Set((linkNode.rel || '').split(/\s+/).filter(Boolean));
relValues.add('noopener');
relValues.add('noreferrer');
linkNode.rel = Array.from(relValues).join(' ');
if (!linkNode.target) {
linkNode.target = '_blank';
}
}
if (!linkNode.title) {
if (isHashOnly || isSamePageAnchor) {
linkNode.title = `Переход к разделу: ${parsedUrl.hash || rawHref}`;
} else if (isExternal) {
linkNode.title = `Внешняя ссылка: ${parsedUrl.hostname.replace(/^www\./, '')}`;
} else if (isMail) {
linkNode.title = `Написать: ${rawHref.replace(/^mailto:/i, '')}`;
} else if (isTel) {
linkNode.title = `Позвонить: ${rawHref.replace(/^tel:/i, '')}`;
} else {
linkNode.title = `Перейти: ${decodePath(parsedUrl.pathname)}${parsedUrl.hash || ''}`;
}
}
linkNode.dataset.linkAnnotated = 'true';
});
};
const copyTextToClipboard = async (value) => {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(value);
return;
}
const helperInput = document.createElement('textarea');
helperInput.value = value;
helperInput.setAttribute('readonly', '');
helperInput.style.position = 'fixed';
helperInput.style.opacity = '0';
document.body.appendChild(helperInput);
helperInput.select();
document.execCommand('copy');
helperInput.remove();
};
const bindCopyIpChips = () => {
const copyButtons = document.querySelectorAll('[data-copy-ip]');
copyButtons.forEach((buttonNode) => {
if (!(buttonNode instanceof HTMLButtonElement)) return;
if (buttonNode.dataset.copyBound === 'true') return;
buttonNode.dataset.copyBound = 'true';
buttonNode.classList.add('is-copy-ready');
if (!buttonNode.title) {
buttonNode.title = 'Нажмите, чтобы скопировать';
}
buttonNode.addEventListener('click', async () => {
const value = buttonNode.dataset.copyIp?.trim();
if (!value) return;
try {
await copyTextToClipboard(value);
buttonNode.classList.add('is-copied');
buttonNode.setAttribute('aria-label', `Скопировано: ${value}`);
window.setTimeout(() => {
buttonNode.classList.remove('is-copied');
buttonNode.setAttribute('aria-label', `Скопировать: ${value}`);
}, 1200);
} catch {
buttonNode.classList.add('is-copy-error');
buttonNode.setAttribute('aria-label', `Не удалось скопировать: ${value}`);
window.setTimeout(() => {
buttonNode.classList.remove('is-copy-error');
buttonNode.setAttribute('aria-label', `Скопировать: ${value}`);
}, 1400);
}
});
});
};
const normalizeCraftSearch = (value) =>
stripDecorativeSymbols(value)
.toLowerCase()
.replace(/ё/g, 'е')
.replace(/\s+/g, ' ')
.trim();
const bindCraftCatalogFilters = () => {
const catalogNodes = document.querySelectorAll('[data-craft-catalog]');
catalogNodes.forEach((catalogNode) => {
if (!(catalogNode instanceof HTMLElement)) return;
if (catalogNode.dataset.craftBound === 'true') return;
catalogNode.dataset.craftBound = 'true';
const searchInput = catalogNode.querySelector('[data-craft-search]');
const filterButtons = Array.from(catalogNode.querySelectorAll('[data-craft-filter]')).filter(
(node) => node instanceof HTMLButtonElement
);
const groupNodes = Array.from(catalogNode.querySelectorAll('[data-craft-group]')).filter(
(node) => node instanceof HTMLElement
);
const emptyState = catalogNode.querySelector('[data-craft-empty]');
let activeFilter = 'all';
const activeButton = filterButtons.find(
(buttonNode) =>
buttonNode.classList.contains('is-active') ||
buttonNode.getAttribute('aria-pressed') === 'true'
);
if (activeButton?.dataset.craftFilter) {
activeFilter = activeButton.dataset.craftFilter;
}
const applyFilters = () => {
const searchQuery =
searchInput instanceof HTMLInputElement ? normalizeCraftSearch(searchInput.value) : '';
filterButtons.forEach((buttonNode) => {
const isActive = (buttonNode.dataset.craftFilter || 'all') === activeFilter;
buttonNode.classList.toggle('is-active', isActive);
buttonNode.setAttribute('aria-pressed', String(isActive));
});
let visibleCount = 0;
groupNodes.forEach((groupNode) => {
const defaultCategory = groupNode.dataset.craftCategory || '';
const itemNodes = Array.from(groupNode.querySelectorAll('[data-craft-item]')).filter(
(node) => node instanceof HTMLElement
);
let groupVisibleCount = 0;
itemNodes.forEach((itemNode) => {
const itemCategory = itemNode.dataset.craftCategory || defaultCategory;
const itemName =
itemNode.dataset.craftName || itemNode.querySelector('h3')?.textContent || '';
const normalizedName = normalizeCraftSearch(itemName);
const matchesCategory = activeFilter === 'all' || itemCategory === activeFilter;
const matchesSearch = !searchQuery || normalizedName.includes(searchQuery);
const isVisible = matchesCategory && matchesSearch;
itemNode.hidden = !isVisible;
if (isVisible) groupVisibleCount += 1;
});
groupNode.hidden = groupVisibleCount === 0;
visibleCount += groupVisibleCount;
});
if (emptyState instanceof HTMLElement) {
emptyState.hidden = visibleCount > 0;
}
};
filterButtons.forEach((buttonNode) => {
buttonNode.addEventListener('click', () => {
const nextFilter = buttonNode.dataset.craftFilter || 'all';
if (nextFilter === activeFilter) return;
activeFilter = nextFilter;
applyFilters();
});
});
if (searchInput instanceof HTMLInputElement) {
searchInput.addEventListener('input', applyFilters);
searchInput.addEventListener('search', applyFilters);
}
applyFilters();
});
};
const enhanceSidebarGroupLinks = () => {
const targets = [];
if (targets.length === 0) return;
const labelNodes = document.querySelectorAll('.sidebar-content details > summary .group-label .large');
labelNodes.forEach((labelNode) => {
if (!(labelNode instanceof HTMLElement)) return;
const text = labelNode.textContent?.trim();
const target = targets.find((item) => item.label === text);
if (!target) return;
if (labelNode.querySelector('a[data-sidebar-group-link]')) return;
const link = document.createElement('a');
link.href = target.href;
link.className = 'sidebar-group-link';
link.dataset.sidebarGroupLink = 'true';
link.textContent = target.label;
link.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
window.location.href = target.href;
});
labelNode.replaceChildren(link);
});
};
const setReadProgress = () => { const setReadProgress = () => {
const progressEl = document.querySelector('[data-read-progress]'); const progressEl = document.querySelector('[data-read-progress]');
const backToTopBtn = document.querySelector('[data-back-to-top]'); const backToTopBtn = document.querySelector('[data-back-to-top]');
@@ -135,6 +497,13 @@ const topLinks = [
}); });
}); });
enhanceSidebarGroupLinks();
sanitizeNavLabels();
sanitizeDocHeadings();
decorateSectionIcons();
annotateLinkDestinations();
bindCopyIpChips();
bindCraftCatalogFilters();
setReadProgress(); setReadProgress();
window.addEventListener('scroll', setReadProgress, { passive: true }); window.addEventListener('scroll', setReadProgress, { passive: true });
window.addEventListener('resize', setReadProgress); window.addEventListener('resize', setReadProgress);
@@ -146,7 +515,16 @@ const topLinks = [
bindReadProgress(); bindReadProgress();
} }
document.addEventListener('astro:page-load', setReadProgress); document.addEventListener('astro:page-load', () => {
enhanceSidebarGroupLinks();
sanitizeNavLabels();
sanitizeDocHeadings();
decorateSectionIcons();
annotateLinkDestinations();
bindCopyIpChips();
bindCraftCatalogFilters();
setReadProgress();
});
</script> </script>
<style> <style>
@@ -171,10 +549,70 @@ const topLinks = [
padding: 0.25rem; padding: 0.25rem;
margin: -0.25rem; margin: -0.25rem;
min-width: 0; min-width: 0;
gap: 1rem; gap: 1.1rem;
align-items: center; align-items: center;
} }
.wiki-brand {
display: inline-flex;
align-items: center;
gap: 0.55rem;
text-decoration: none;
flex-shrink: 0;
border: 1px solid transparent;
border-radius: 0.3rem;
padding: 0.2rem 0.28rem 0.2rem 0.22rem;
transition: border-color 150ms ease, background 150ms ease;
}
.wiki-brand:hover {
border-color: color-mix(in srgb, var(--sl-color-accent) 32%, transparent);
background: color-mix(in srgb, var(--sl-color-accent-low) 64%, transparent);
}
.wiki-brand__icon {
width: 1.85rem;
height: 1.85rem;
display: inline-flex;
align-items: center;
justify-content: center;
border: 0;
background: transparent;
box-shadow: none;
}
.wiki-brand__icon img {
width: 100%;
height: 100%;
display: block;
border-radius: 0;
object-fit: contain;
}
.wiki-brand__copy {
display: inline-flex;
flex-direction: column;
align-items: flex-start;
line-height: 1;
}
.wiki-brand__title {
font-family: 'Sora', sans-serif;
font-size: 1.43rem;
font-weight: 700;
letter-spacing: 0.02em;
color: var(--sl-color-white);
}
.wiki-brand__subtitle {
margin-top: 0.14rem;
font-family: 'Sora', sans-serif;
font-size: 0.56rem;
font-weight: 700;
letter-spacing: 0.28em;
color: var(--sl-color-gray-3);
}
.wiki-top-nav { .wiki-top-nav {
display: none; display: none;
gap: 0.45rem; gap: 0.45rem;
@@ -295,51 +733,56 @@ const topLinks = [
.wiki-back-to-top { .wiki-back-to-top {
position: fixed; position: fixed;
top: auto; top: auto;
right: max(0.75rem, env(safe-area-inset-right)); right: max(1.35rem, calc(env(safe-area-inset-right) + 0.8rem));
bottom: max(1.1rem, env(safe-area-inset-bottom)); bottom: max(1.45rem, calc(env(safe-area-inset-bottom) + 0.7rem));
z-index: 30; z-index: 30;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 70%, transparent); border: 1px solid color-mix(in srgb, var(--sl-color-accent) 74%, transparent);
border-radius: 999px; border-radius: 999px;
background: linear-gradient( background: linear-gradient(
140deg, 140deg,
color-mix(in srgb, var(--wiki-purple-2) 78%, #101522), color-mix(in srgb, var(--wiki-purple-2) 74%, #101522),
color-mix(in srgb, var(--wiki-blue-2) 82%, #101522) color-mix(in srgb, var(--wiki-blue-2) 84%, #101522)
); );
color: #f6f9ff; color: #f6f9ff;
font-family: 'Sora', sans-serif; width: 3.1rem;
font-size: 1.1rem; height: 3.1rem;
font-weight: 600;
letter-spacing: 0.01em;
width: 2.6rem;
height: 2.6rem;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 0; padding: 0;
line-height: 1;
box-shadow: box-shadow:
0 10px 24px hsl(220 95% 62% / 0.24), 0 0 0 1px hsl(214 88% 70% / 0.24),
0 0 0 1px hsl(214 88% 70% / 0.22); 0 0 24px hsl(217 100% 68% / 0.38),
0 0 42px hsl(221 100% 65% / 0.22);
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transform: translateY(6px); transform: translateY(8px) scale(0.96);
pointer-events: none; pointer-events: none;
transition: opacity 160ms ease, transform 160ms ease, border-color 160ms ease; transition: opacity 180ms ease, transform 180ms ease, border-color 180ms ease, box-shadow 180ms ease;
}
.wiki-back-to-top__icon {
width: 1.5rem;
height: 1.5rem;
stroke: currentColor;
stroke-width: 2.15;
stroke-linecap: round;
stroke-linejoin: round;
} }
.wiki-back-to-top.is-visible { .wiki-back-to-top.is-visible {
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
transform: translateY(0); transform: translateY(0) scale(1);
pointer-events: auto; pointer-events: auto;
} }
.wiki-back-to-top:hover { .wiki-back-to-top:hover {
border-color: color-mix(in srgb, #9ec4ff 80%, transparent); border-color: color-mix(in srgb, #9ec4ff 80%, transparent);
box-shadow: box-shadow:
0 14px 28px hsl(220 95% 62% / 0.32),
0 0 0 1px hsl(214 88% 70% / 0.35), 0 0 0 1px hsl(214 88% 70% / 0.35),
0 0 18px hsl(220 96% 68% / 0.34); 0 0 28px hsl(219 97% 70% / 0.44),
0 0 50px hsl(221 98% 68% / 0.27);
} }
.social-icons::after { .social-icons::after {
@@ -383,12 +826,36 @@ const topLinks = [
} }
@media (max-width: 50rem) { @media (max-width: 50rem) {
.wiki-brand {
padding: 0.14rem 0.2rem;
gap: 0.42rem;
}
.wiki-brand__icon {
width: 1.55rem;
height: 1.55rem;
}
.wiki-brand__title {
font-size: 1.08rem;
}
.wiki-brand__subtitle {
font-size: 0.46rem;
letter-spacing: 0.24em;
}
.wiki-back-to-top { .wiki-back-to-top {
top: auto; top: auto;
right: max(0.62rem, env(safe-area-inset-right)); right: max(1rem, calc(env(safe-area-inset-right) + 0.52rem));
bottom: max(1rem, env(safe-area-inset-bottom)); bottom: max(1.18rem, calc(env(safe-area-inset-bottom) + 0.58rem));
width: 2.5rem; width: 2.85rem;
height: 2.5rem; height: 2.85rem;
}
.wiki-back-to-top__icon {
width: 1.34rem;
height: 1.34rem;
} }
} }
} }

View File

@@ -5,7 +5,7 @@ description: "Концепция мира, формат сервера и осн
## Концепция ## Концепция
Сервер построен вокруг идеи «архива реальностей»: каждая локация выглядит как отдельный фрагмент мира со своей физикой и историей. Сервер построен как единый живой мир: у разных локаций свой стиль, история и атмосфера.
### Формат ### Формат
@@ -13,14 +13,6 @@ description: "Концепция мира, формат сервера и осн
- Выживание с RPG-элементами - Выживание с RPG-элементами
- Упор на исследование и кооператив - Упор на исследование и кооператив
## Лор в двух словах
После Раскола Неба мир разделился на слои. Игроки находят обломки карт, восстанавливают порталы и открывают утраченные ремёсла.
:::caution
Некоторые сюжетные зоны открываются только после выполнения цепочки квестов.
:::
## Техническая база ## Техническая база
| Параметр | Значение | | Параметр | Значение |
@@ -31,7 +23,7 @@ description: "Концепция мира, формат сервера и осн
<div class="see-also"> <div class="see-also">
<p>Смотри также</p> <p>Смотри также</p>
<a href="/mechanics/">Механики</a> · <a href="/team/">Команда проекта</a> <a href="/general/">Общее</a> · <a href="/faq/">FAQ</a>
</div> </div>

View File

@@ -50,11 +50,11 @@ description: "Полное руководство по ферментации,
Всего существует 10 уровней качества: Всего существует 10 уровней качества:
- `` — уровень 1 - `I` — уровень 1
- `` — уровень 2 - `II` — уровень 2
- `★☆` — уровень 3 - `III` — уровень 3
- `...` - `...`
- `★★★★★ (10)` — максимальное качество - `X` — максимальное качество (уровень 10)
:::note :::note
Чем точнее соблюдён рецепт, тем выше качество. Чем точнее соблюдён рецепт, тем выше качество.

View File

@@ -1,14 +1,54 @@
--- ---
title: "Все IP" title: "Все IP"
description: "Черновик страницы с адресами сервера." description: "Адреса и порты сервера PARABOX для Java и Bedrock."
--- ---
## Все IP ## Основные адреса
Это временная страница-заглушка. <div class="ip-main-grid">
<article class="ip-main-card">
<h3>Java 1.21.11</h3>
<ul>
<li>
Основной:
<button class="copy-ip-chip" type="button" data-copy-ip="mc.parabox.su" aria-label="Скопировать: mc.parabox.su">
<code>mc.parabox.su</code>
</button>
</li>
<li>
AnyCast-прокси:
<button class="copy-ip-chip" type="button" data-copy-ip="mine.rpcm.su" aria-label="Скопировать: mine.rpcm.su">
<code>mine.rpcm.su</code>
</button>
</li>
</ul>
</article>
- Основной IP: `example-1` <article class="ip-main-card">
- Резервный IP: `example-2` <h3>Bedrock 26.2</h3>
- Тестовый IP: `example-3` <ul>
<li>
Основной:
<button class="copy-ip-chip" type="button" data-copy-ip="mc.parabox.su" aria-label="Скопировать: mc.parabox.su">
<code>mc.parabox.su</code>
</button>
</li>
<li>
Запасной IP:
<button class="copy-ip-chip" type="button" data-copy-ip="213.152.43.52" aria-label="Скопировать: 213.152.43.52">
<code>213.152.43.52</code>
</button>
</li>
<li>
Порт:
<button class="copy-ip-chip" type="button" data-copy-ip="25875" aria-label="Скопировать: 25875">
<code>25875</code>
</button>
</li>
</ul>
</article>
</div>
Замените эти значения на реальные адреса сервера. <div class="ip-main-note">
Используйте запасной адрес, если основной IP временно недоступен.
</div>

View File

@@ -0,0 +1,40 @@
---
title: "Города"
description: "Раздел для представления городов и проектов игроков."
---
## Города и проекты игроков
На PARABOX можно создать свой город, собрать жителей и развивать территорию вместе с другими игроками.
<div class="lore-grid">
<article class="lore-card city-feature-card">
<p><strong class="city-highlight">Представить свой город.</strong> Расскажите о проекте и его отличительных чертах.</p>
</article>
<article class="lore-card city-feature-card">
<p><strong class="city-highlight">Найти жителей.</strong> Публикуйте набор и указывайте роли, которые сейчас нужны городу.</p>
</article>
<article class="lore-card city-feature-card">
<p><strong class="city-highlight">Показывать развитие.</strong> Делитесь скриншотами, координатами и текущими задачами.</p>
</article>
<article class="lore-card city-feature-card">
<p><strong class="city-highlight">Организовывать союзы.</strong> Договаривайтесь о торговле, ивентах и совместных проектах.</p>
</article>
</div>
## Шаблон публикации города
```text
Название города:
Координаты:
Мэр / управляющий орган:
Описание:
Набор жителей: да / нет
Правила города:
```
Подробный процесс входа на сервер описан в статье <a href="/getting-started/">«Начало игры»</a>.
:::tip
Чем понятнее и полнее описание города, тем быстрее к вам присоединятся игроки.
:::

View File

@@ -1,14 +1,82 @@
--- ---
title: "Команды" title: "Команды"
description: "Черновик страницы с командами сервера." description: "Справочник игровых команд: чат, личные сообщения, RP и полезные функции."
--- ---
## Команды ## Команды
Это временная страница-заглушка. Ниже собраны основные команды сервера, сгруппированные по категориям.
- `/spawn` — пример команды 1 ### Настройки
- `/home` — пример команды 2
- `/rtp` — пример команды 3
Позже замените этот список на реальные команды вашего сервера. - `/chatsetting` - открыть меню настроек чата (уведомления, отображение, звук и т.д.).
### Личные сообщения
- `/tell <ник> <сообщение>` - отправить личное сообщение игроку.
- `/msg`, `/w`, `/m`, `/message`, `/send` - альтернативы команды `/tell`.
- `/r` - ответить последнему игроку.
### Игнор
- `/ignore <ник>` - игнорировать сообщения игрока.
### Развлечения
- `/ball <вопрос>` - задать вопрос и получить случайный ответ.
- `/tictactoe <ник>` - сыграть в крестики-нолики.
- `/rockpaperscissors <ник>` - камень-ножницы-бумага.
- `/try` - проверить удачу (случайный шанс).
- `/coin` - подбросить монетку.
- `/dice` - бросить кубик.
### Кастомные пластинки
- `/disc burn <url> [name]` - создать кастомную пластинку из аудио по URL.
- `/disc erase` - вернуть кастомную пластинку к обычному виду.
- `/disc search <query>` - найти треки на YouTube.
:::note
Для `/disc burn` нужно держать музыкальную пластинку в руке.
:::
### RP и взаимодействие
- `/do <действие>` - RP-описание действия от третьего лица.
- `/anon <сообщение>` - отправить анонимное сообщение.
- `/helper` - попросить помощь.
### Статус игрока
- `/afk` - включить или выключить режим AFK.
- `/crawl` - позволяет игроку ползти.
- `/sit` - позволяет игроку сидеть.
- `/lay` - позволяет игроку лечь.
### ImageFrame (изображения на картах)
- `/imageframe select` - выбрать рамки, в которые будет поставлено изображение.
- `/imageframe create <name> <url> <width> <height>` - создать новое изображение на картах.
- `/imageframe create <name> <url> selection` - создать изображение и сразу разместить его в выбранных рамках.
- `/imageframe get <name>` - получить ранее созданное вами изображение.
- `/imageframe get <name> selection` - поставить ранее созданное изображение сразу в выбранные рамки.
- `/imageframe list` - показать список ваших созданных изображений.
- `/imageframe info` - показать информацию об изображении, которое вы держите в руке.
- `/imageframe refresh [image_name] [new_url]` - обновить изображение из источника (при необходимости с новой ссылкой).
- `/imageframe rename <name> <new_name>` - переименовать ваше изображение.
- `/imageframe delete <name>` - удалить ваше изображение.
:::note
Для `/imageframe create` используйте прямую ссылку на изображение.
`<width>` и `<height>` - это размер в картах (например `2 2` для сетки 2x2).
:::
### Информация
- `/ping` - узнать задержку (пинг).
- `/toponline` - посмотреть топ игроков по наигранному времени.
- `/breweryx info` - показать уровень опьянения.
### Символы
- `/symbol` - открыть меню символов.

View File

@@ -1,19 +1,24 @@
--- ---
title: "Контакты / ссылки" title: "Контакты и ссылки"
description: "Каналы связи, соцсети, поддержка и полезные внешние ресурсы." description: "Официальные контакты сервера и связь с администрацией."
--- ---
## Официальные ссылки ## Официальные ссылки
- Discord: `https://discord.gg/example` <div class="lore-grid">
- GitHub wiki: `https://github.com/your-org/parabox` <a class="lore-card contact-link-card" href="https://discord.gg/V5gD2a3HpQ" target="_blank" rel="noopener noreferrer">
- Почта поддержки: `support@example.com` <h3>Discord сервера</h3>
<p>Официальный Discord проекта.</p>
## Когда писать в поддержку </a>
<a class="lore-card contact-link-card" href="https://t.me/verdise" target="_blank" rel="noopener noreferrer">
1. Потеря предметов из-за серверного сбоя. <h3>Владелец проекта</h3>
2. Баги квестов и непроходимые этапы. <p>Связь в Telegram.</p>
3. Вопросы по платежам и привилегиям. </a>
<a class="lore-card contact-link-card" href="https://t.me/s1s2s3sf" target="_blank" rel="noopener noreferrer">
<h3>Администратор проекта</h3>
<p>Связь в Telegram.</p>
</a>
</div>
## Шаблон тикета ## Шаблон тикета
@@ -26,8 +31,5 @@ description: "Каналы связи, соцсети, поддержка и п
<div class="see-also"> <div class="see-also">
<p>Смотри также</p> <p>Смотри также</p>
<a href="/faq/">FAQ</a> · <a href="/team/">Команда проекта</a> <a href="/faq/">FAQ</a> · <a href="/getting-started/">Начало игры</a>
</div> </div>

View File

@@ -0,0 +1,155 @@
---
title: "Крафты"
description: "Раздел по кастомным крафтам сервера PARABOX."
---
## Каталог крафтов
<div class="craft-catalog" data-craft-catalog>
<div class="craft-controls">
<label class="craft-search-field" for="craft-search-input">
<span>Поиск по названию</span>
<input
id="craft-search-input"
class="craft-search-input"
type="search"
placeholder="Например, наковальня или книга"
data-craft-search
/>
</label>
<div class="craft-filter-group" role="group" aria-label="Фильтр крафтов по категориям">
<button type="button" class="craft-filter-chip is-active" data-craft-filter="all" aria-pressed="true">Все</button>
<button type="button" class="craft-filter-chip" data-craft-filter="special" aria-pressed="false">Спец-предметы</button>
<button type="button" class="craft-filter-chip" data-craft-filter="blocks" aria-pressed="false">Блоки и станции</button>
<button type="button" class="craft-filter-chip" data-craft-filter="smelting" aria-pressed="false">Переработка</button>
</div>
</div>
<p class="craft-empty" data-craft-empty hidden>По вашему запросу крафты не найдены.</p>
<section class="craft-group" data-craft-group data-craft-category="special">
<h3>Спец-предметы</h3>
<div class="craft-grid">
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="невидимая рамка">
<h3>Невидимая рамка</h3>
<img class="craft-card-image" src="/crafts/invisible-item-frame.png" alt="Рецепт невидимой рамки" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="невидимая светящаяся рамка">
<h3>Невидимая светящаяся рамка</h3>
<img class="craft-card-image" src="/crafts/invisible-glow-item-frame.png" alt="Рецепт невидимой светящейся рамки" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="невидимый свет">
<h3>Невидимый свет</h3>
<img class="craft-card-image" src="/crafts/invisible-light.png" alt="Рецепт невидимого света" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="чародейская книга">
<h3>Чародейская книга</h3>
<img class="craft-card-image" src="/crafts/enchanted-book.png" alt="Рецепт чародейской книги" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="книга знаний">
<h3>Книга знаний</h3>
<img class="craft-card-image" src="/crafts/knowledge-book.png" alt="Рецепт книги знаний" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="дыхание дракона">
<h3>Дыхание дракона</h3>
<img class="craft-card-image" src="/crafts/dragons-breath.png" alt="Рецепт дыхания дракона" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="светящиеся чернила">
<h3>Светящиеся чернила</h3>
<img class="craft-card-image" src="/crafts/glow-ink-sac.png" alt="Рецепт светящихся чернил" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="мембрана фантома">
<h3>Мембрана фантома</h3>
<img class="craft-card-image" src="/crafts/phantom-membrane.png" alt="Рецепт мембраны фантома" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="бирка">
<h3>Бирка</h3>
<img class="craft-card-image" src="/crafts/name-tag.png" alt="Рецепт бирки" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="special" data-craft-name="мешок">
<h3>Мешок</h3>
<img class="craft-card-image" src="/crafts/bundle.png" alt="Рецепт мешка" loading="lazy" />
</article>
</div>
</section>
<section class="craft-group" data-craft-group data-craft-category="blocks">
<h3>Блоки и рабочие станции</h3>
<div class="craft-grid">
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="стол лучника">
<h3>Стол лучника</h3>
<img class="craft-card-image" src="/crafts/fletching-table.png" alt="Рецепт стола лучника" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="стол картографа">
<h3>Стол картографа</h3>
<img class="craft-card-image" src="/crafts/cartography-table.png" alt="Рецепт стола картографа" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="ткацкий станок">
<h3>Ткацкий станок</h3>
<img class="craft-card-image" src="/crafts/loom.png" alt="Рецепт ткацкого станка" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="нотный блок">
<h3>Нотный блок</h3>
<img class="craft-card-image" src="/crafts/note-block.png" alt="Рецепт нотного блока" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="проигрыватель">
<h3>Проигрыватель</h3>
<img class="craft-card-image" src="/crafts/jukebox.png" alt="Рецепт проигрывателя" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="раздатчик">
<h3>Раздатчик</h3>
<img class="craft-card-image" src="/crafts/dispenser.png" alt="Рецепт раздатчика" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="колокол">
<h3>Колокол</h3>
<img class="craft-card-image" src="/crafts/bell.png" alt="Рецепт колокола" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="обтесанное бревно">
<h3>Обтесанное бревно</h3>
<img class="craft-card-image" src="/crafts/stripped-log-stonecutter.png" alt="Рецепт обтесанного бревна в камнерезе" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="прочный полублок">
<h3>Прочный полублок</h3>
<img class="craft-card-image" src="/crafts/sturdy-slab.png" alt="Рецепт прочного полублока" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="наковальня вариант 1">
<h3>Наковальня (вариант 1)</h3>
<img class="craft-card-image" src="/crafts/anvil-variant-base.png" alt="Первый вариант рецепта наковальни" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="blocks" data-craft-name="наковальня вариант 2">
<h3>Наковальня (вариант 2)</h3>
<img class="craft-card-image" src="/crafts/anvil-variant-l.png" alt="Второй вариант рецепта наковальни" loading="lazy" />
</article>
</div>
</section>
<section class="craft-group" data-craft-group data-craft-category="smelting">
<h3>Переработка и конверсия</h3>
<div class="craft-grid">
<article class="lore-card craft-card" data-craft-item data-craft-category="smelting" data-craft-name="компас из компаса восстановления">
<h3>Компас из компаса восстановления</h3>
<img class="craft-card-image" src="/crafts/recovery-compass-to-compass-smelting.png" alt="Переплавка компаса восстановления в обычный компас" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="smelting" data-craft-name="железный слиток из компаса">
<h3>Железный слиток из компаса</h3>
<img class="craft-card-image" src="/crafts/compass-to-iron-ingot-smelting.png" alt="Переплавка компаса в железный слиток" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="smelting" data-craft-name="золотой слиток из часов">
<h3>Золотой слиток из часов</h3>
<img class="craft-card-image" src="/crafts/clock-to-gold-ingot-smelting.png" alt="Переплавка часов в золотой слиток" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="smelting" data-craft-name="красное стекло">
<h3>Красное стекло</h3>
<img class="craft-card-image" src="/crafts/red-glass-smelting.png" alt="Переплавка в красное стекло" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="smelting" data-craft-name="алмазная руда вариант 1">
<h3>Алмазная руда (вариант 1)</h3>
<img class="craft-card-image" src="/crafts/diamond-ore-variant-k.png" alt="Первый вариант рецепта алмазной руды" loading="lazy" />
</article>
<article class="lore-card craft-card" data-craft-item data-craft-category="smelting" data-craft-name="алмазная руда вариант 2">
<h3>Алмазная руда (вариант 2)</h3>
<img class="craft-card-image" src="/crafts/diamond-ore-variant-s.png" alt="Второй вариант рецепта алмазной руды" loading="lazy" />
</article>
</div>
</section>
</div>

View File

@@ -1,33 +0,0 @@
---
title: "Донат / привилегии"
description: "Добровольная поддержка сервера, наборы и прозрачные правила привилегий."
---
## Принципы
- Донат не должен ломать баланс мира.
- Боевые преимущества ограничены и прозрачны.
- Все пакеты имеют чёткое описание.
## Уровни поддержки
| Уровень | Что входит |
| --- | --- |
| Traveler | Косметика + расширенный /home |
| Patron | Больше слотов хранилища + титул |
| Curator | Эксклюзивные визуальные эффекты |
## Политика возвратов
Возвраты рассматриваются через тикет-систему в течение 7 дней после покупки.
:::note
Покупка привилегий - способ поддержать развитие сервера, а не обязательное условие комфортной игры.
:::
<div class="see-also">
<p>Смотри также</p>
<a href="/rules/">Правила</a> · <a href="/contacts/">Контакты / ссылки</a>
</div>

View File

@@ -0,0 +1,156 @@
---
title: "EasyArmorStands — как пользоваться"
description: "Пошаговая инструкция по редактированию стоек, display-объектов и текста через EasyArmorStands."
---
## 1. Получить инструмент
Выполните команду:
`/eas give`
Возьмите выданный предмет в руку — это инструмент редактирования.
## 2. Выбрать объект
Наведитесь на нужный объект:
- стойка для брони
- текст
- block/item display
Нажмите ПКМ.
После этого появятся линии и точки — это режим редактирования.
## 3. Выбрать часть (для стойки)
Вы увидите:
- белые линии — части тела
- жёлтую точку — центр
Кликните по нужной части:
- хотите двигать руку — кликните по руке
- хотите двигать всё — кликните по центру
## 4. Двигать объект
После выбора части:
- двигайте мышкой / смотрите в сторону
- объект будет двигаться
Управление:
- `ПКМ` — подтвердить
- `ЛКМ` — отменить
## 5. Поворот
Выберите инструмент `Rotate` (через меню или переключение).
- двигайте камерой
- объект будет вращаться
Управление:
- `ПКМ` — сохранить
- `ЛКМ` — отменить
## 6. Масштаб (увеличение/уменьшение)
Для display-объектов (блок, предмет, текст):
- откройте меню (`F`)
- найдите `scale`
- увеличьте или уменьшите размер
## 7. Открыть меню
Нажмите `F`.
В меню можно:
- выбрать инструмент (`Move` / `Rotate`)
- менять размер
- включать настройки
- надевать броню
## 8. Как поставить блок (Block Display)
Используйте один из способов:
- `Shift + ЛКМ` по блоку
- `/eas block`
После этого можно:
- двигать блок
- вращать
- масштабировать
## 9. Как поставить предмет (Item Display)
- откройте меню (`F`)
- вставьте предмет в слот
Предмет появится как display-объект.
Дальше можно:
- двигать
- вращать
- увеличивать
## 10. Как сделать текст
Выполните:
`/eas text`
Появится текстовый объект. Далее можно:
- двигать его
- менять размер
- редактировать текст
Чтобы текст всегда смотрел на игрока:
- в меню установите `billboard = center`
- используйте `/eas reset`
## 11. Работа с группой
- зажмите `Shift + ПКМ` по объектам
- выберите несколько объектов
После этого `ПКМ` редактирует всё сразу.
## 12. Отмена действий
- `/eas undo`
- `/eas redo`
## 13. Быстрые действия
- `/eas clone` — копировать
- `/eas align` — выровнять по центру блока
## Простой алгоритм
1. `/eas give`
2. ПКМ по объекту
3. Выберите часть
4. Подвигайте мышкой
5. ПКМ для подтверждения
Это базовый рабочий цикл.
## Самое главное
- всё делается мышкой
- `ПКМ` = подтвердить
- `ЛКМ` = отменить
- `F` = меню

View File

@@ -1,38 +0,0 @@
---
title: "Экономика"
description: "Валюты, рынки, аукцион, контракты и рекомендации по торговле."
---
## Валюты
- `Aurum` - базовая торговая валюта.
- `Echo` - редкая валютная единица для реликвий.
## Как работает рынок
### Аукцион
Игроки выставляют предметы на открытый рынок.
### Контракты
Сделки между игроками с фиксированными условиями.
## Мини-стратегия для старта
| Шаг | Действие | Ожидаемый эффект |
| --- | --- | --- |
| 1 | Продавать излишки еды | Быстрый стартовый капитал |
| 2 | Копить железо и редстоун | Стабильный спрос |
| 3 | Вкладываться в алхимию | Высокая маржа |
:::caution
Не держи все активы в одном виде ресурса: цены цикличны.
:::
<div class="see-also">
<p>Смотри также</p>
<a href="/items-resources/">Предметы и ресурсы</a> · <a href="/donate/">Донат / привилегии</a>
</div>

View File

@@ -1,64 +0,0 @@
---
title: "Как редактировать wiki"
description: "Простая инструкция по добавлению и изменению страниц в Markdown."
---
## Быстрый принцип
Каждая статья - отдельный `.md`-файл в `src/content/docs/`.
## Минимальный frontmatter
```yaml
---
title: "Название страницы"
description: "Короткое описание страницы"
---
```
## Как добавить новую страницу
1. Создай файл в `src/content/docs/`, например `new-biome.md`.
2. Добавь frontmatter (`title`, `description`).
3. Напиши контент в Markdown (заголовки, списки, таблицы, код).
4. Добавь ссылку в `sidebar` в `astro.config.mjs`.
## Полезные блоки Starlight
```md
:::note
Заметка
:::
:::tip
Подсказка
:::
:::caution
Предупреждение
:::
:::danger
Опасность
:::
```
## Пример spoiler/details
```md
<details>
<summary>Показать секрет</summary>
Здесь скрытый текст.
</details>
```
## Где редактируется меню
Меню и порядок разделов настраиваются в `astro.config.mjs` -> `starlight({ sidebar: [...] })`.
<div class="see-also">
<p>Смотри также</p>
<a href="/templates/new-page-template/">Шаблон новой страницы</a>
</div>

View File

@@ -1,40 +1,16 @@
--- ---
title: "FAQ" title: "FAQ"
description: "Ответы на частые вопросы игроков о запуске, прогрессе и аккаунте." description: "Частый вопрос по регистрации на сервере."
--- ---
## Основные вопросы ## Основной вопрос
<details> <details>
<summary>Как попасть на сервер?</summary> <summary>Как зарегистрироваться на сервере?</summary>
Добавь адрес сервера в клиент Minecraft Java Edition и пройди короткое интро на спавне. При первом запуске зарегистрируйтесь командой <code>/reg вашпароль повторпароля</code>. При следующем входе используйте <code>/l вашпароль</code>.
</details> </details>
<details>
<summary>Можно ли играть одному?</summary>
Да. Большая часть контента доступна соло, но рейды и разломы удобнее проходить группой.
</details>
<details>
<summary>Что делать, если потерял вещи?</summary>
Проверь лог смертей и открой тикет в Discord с точным временем события.
</details>
## Технические вопросы
### Минимальные рекомендации
- 8 ГБ RAM на ПК
- Java 21+
- Стабильный интернет
:::tip
Перед обращением в поддержку приложи скриншот ошибки и список модов.
:::
<div class="see-also"> <div class="see-also">
<p>Смотри также</p> <p>Смотри также</p>
<a href="/getting-started/">Начало игры</a> · <a href="/contacts/">Контакты / ссылки</a> <a href="/getting-started/">Начало игры</a>
</div> </div>

View File

@@ -0,0 +1,61 @@
---
title: "Общее"
description: "Общая информация и веб-карта сервера PARABOX."
---
## Веб-карта сервера
Интерактивная веб-карта отображает мир в реальном времени и помогает быстро ориентироваться на сервере.
На карте вы увидите:
- расположение городов и построек игроков;
- дороги, биомы и основные локации;
- масштаб карты.
<div class="map-actions">
<a class="map-cta-button" href="https://map.rpcm.su/#world:0:0:0:1500:0:0:0:0:perspective" target="_blank" rel="noopener noreferrer">
Открыть веб-карту
</a>
</div>
---
## Как сменить скин
На сервере доступна система скинов, которая позволяет установить любой внешний вид. Скин будет отображаться в чате, на карте и в других местах.
### Способ 1. Через команду
- для установки: `/skin set <ссылка на скин / ник лицензионного аккаунта>`
- для сброса: `/skin clear`
### Способ 2. Через сайт SkinRestorer
1. Загрузите скин файлом на сайт: [SkinRestorer](https://skinsrestorer.net/upload)
2. Нажмите кнопку `GENERATE` и скопируйте полученную команду.
3. Введите команду в чате Minecraft.
---
## ImageForm (бета): гайд
`ImageForm` позволяет делать map-art автоматически по ссылке на изображение, без ручной постройки пиксель-арт схем.
### Как использовать
1. Подготовьте прямую ссылку на картинку (форматы `png`/`jpg`/`webp`).
2. Зайдите на сервер и вызовите инструмент ImageForm (команда/интерфейс, доступный на вашей роли).
3. Вставьте ссылку на изображение и подтвердите создание.
4. Выберите размер и формат карты (если сервер предложит параметры).
5. Дождитесь генерации и заберите готовый результат.
### Советы по качеству
- используйте изображения с хорошим контрастом и без мелкого текста;
- лучше всего подходят квадратные или близкие к квадрату картинки;
- при необходимости уменьшите изображение заранее, чтобы карта читалась лучше.
:::note
ImageForm работает в бета-режиме: отдельные параметры или ограничения могут меняться в следующих патчах.
:::

View File

@@ -1,12 +1,22 @@
--- ---
title: "Начало игры" title: "Начало игры"
description: "Черновик раздела. Здесь будет ваш текст о старте." description: "Как подать заявку на сервер PARABOX и что указать в анкете."
--- ---
## Начало игры ## Как вступить
Это временный текст-заглушка для раздела про старт игрока. Чтобы попасть на сервер, отправьте заявку через бота.
После одобрения вы получите доступ к миру PARABOX.
В первые минуты герой просыпается у древнего маяка, получает рюкзак с картой и слышит голос хранителя, который шепчет о пути через туманные равнины. <div class="see-also">
<p>Подача заявки через бота</p>
<a href="https://t.me/RPCM_MINE_BOT" target="_blank" rel="noopener noreferrer"><strong>@RPCM_MINE_BOT</strong></a>
</div>
Когда будете готовы, замените этот текст на свой подробный гайд. ## Форма анкеты
Чтобы вступить, заполните короткую анкету в боте:
1. Как к вам обращаться (имя или псевдоним).
2. Ваш никнейм для привязки к серверу.
3. От какого проекта вы пришли, или откуда узнали о сервере.

View File

@@ -1,50 +0,0 @@
---
title: "Гайды"
description: "Практические сценарии: выживание, прогресс, босс-раны и командная игра."
---
## Навигатор по гайдам
<div class="lore-grid">
<article class="lore-card">
<h3>Соло-старт</h3>
<p>Как стабильно развиться одному игроку за первые два вечера.</p>
</article>
<article class="lore-card">
<h3>Гильдейский старт</h3>
<p>Роли в группе, распределение ресурсов и ротация задач.</p>
</article>
<article class="lore-card">
<h3>Босс-раны</h3>
<p>Минимальный набор, тактики и контроль рисков на арене.</p>
</article>
</div>
## Пример структурированного гайда
### Подготовка
- 32+ еды
- Броня не ниже железной
- 2 тотема или 3 зелья лечения
### Выполнение
1. Активируй обелиск.
2. Удерживай центральную платформу 3 волны.
3. Сфокусируй урон по призывателю на 4-й фазе.
### Завершение
Разбери добычу по ролям: танк, саппорт, дамагер.
:::danger
Не заходи в событие с поломанной бронёй: штраф на урон и критический риск потери лута.
:::
<div class="see-also">
<p>Смотри также</p>
<a href="/mechanics/">Механики</a> · <a href="/professions/">Профессии / классы / расы</a>
</div>

View File

@@ -1,27 +1,41 @@
--- ---
title: "ParaBox" title: "Project PARABOX"
description: "Черновик главной страницы. Текст будет заполнен позже." description: "Единый сервер РПКМ: города проектов, экономика, выживание и взаимодействие между игроками."
hero: hero:
tagline: Временный текст для главной страницы tagline: Единый мир для проектов и игроков
actions: actions:
- text: Начать игру - text: Начать игру
link: /getting-started/ link: /getting-started/
icon: right-arrow icon: right-arrow
variant: primary variant: primary
- text: Читать сюжет - text: Смотреть все IP
link: /story/ link: /all-ip/
variant: minimal variant: minimal
--- ---
## Главная ## Project PARABOX
Это временный блок с тестовым содержимым. Здесь позже появится вступление о сервере, его атмосфере и ключевых особенностях. Project PARABOX объединяет проекты РПКМ и игроков в едином живом мире: выживание, развитие городов, торговля и совместные события происходят на одной карте.
:::note ## Города, регистрация и первые шаги
Текст-заглушка. После финального наполнения этот раздел можно полностью переписать.
1. Подайте заявку через бота и дождитесь подтверждения.
2. Зайдите на сервер по одному из IP выше.
3. Зарегистрируйтесь или войдите в игровую систему.
4. Выберите город для вступления или создайте свой проектный город.
5. Ознакомьтесь с правилами и базовыми механиками.
Подробный разбор процесса вступления находится в статье <a href="/getting-started/">«Начало игры»</a>, а информация по городам — в разделе <a href="/cities/">«Города»</a>.
## Основные особенности
- Система городов с возможностью набора жителей.
- Экономика и торговля между игроками.
- Ивенты и активности.
- Классическое выживание с социальным взаимодействием.
:::tip
Чем быстрее вы подключитесь по IP и выберете точку старта, тем проще будет влиться в текущие города и события.
::: :::
## Черновой абзац ## Добро пожаловать и приятной игры
Пыль поднимается над каменными дорогами, на горизонте мерцают порталы, а старые башни хранят забытые руны. Здесь начинается история, которую вы позже заполните своими правилами, лором и целями.

View File

@@ -1,42 +0,0 @@
---
title: "Предметы и ресурсы"
description: "Каталог ключевых материалов, редких артефактов и их применение."
---
## Базовые ресурсы
- Древесина
- Камень
- Железо
- Кварц
## Редкие материалы
### Пыль кометы
Используется для усиления портальных якорей.
### Лунный обсидиан
Нужен для защитных печатей и высокоуровневых рецептов.
## Пример блока внимания
:::danger
Артефакты с меткой «Связанный» нельзя передавать или продавать.
:::
## Таблица предметов
| Предмет | Где найти | Для чего нужен |
| --- | --- | --- |
| Пыль кометы | Небесный дождь | Ритуалы усиления |
| Лунный обсидиан | Глубинные разломы | Структуры защиты |
| Эхо-кристалл | Рейдовые боссы | Улучшение реликвий |
<div class="see-also">
<p>Смотри также</p>
<a href="/mechanics/">Механики</a> · <a href="/guides/">Гайды</a>
</div>

View File

@@ -1,41 +0,0 @@
---
title: "Механики"
description: "Основные игровые системы: энергия, климат, аномалии и мировые события."
---
## Энергия эфира
Эфир усиливает способности и влияет на доступ к скрытым зонам.
| Уровень эфира | Эффект |
| --- | --- |
| 0-20 | Базовые действия |
| 21-60 | Бонус к добыче и скорости |
| 61+ | Открываются артефактные узлы |
### Как повышать эфир
- Выполнять ежедневные контракты.
- Закрывать аномалии в группе.
- Исследовать архивные башни.
## Мировые события
### Разломы
Появляются раз в несколько часов и требуют командной обороны.
### Небесный дождь
Даёт редкие ингредиенты, но привлекает элитных мобов.
:::note
`h2` и `h3` автоматически попадают в оглавление страницы справа.
:::
<div class="see-also">
<p>Смотри также</p>
<a href="/items-resources/">Предметы и ресурсы</a> · <a href="/rules/">Правила</a>
</div>

View File

@@ -1,42 +0,0 @@
---
title: "Профессии / классы / расы"
description: "Гибкая RPG-система: роли, бонусы, ограничения и синергия в группе."
---
## Расы
- Астралы: бонус к мане и исследованию.
- Форджеры: ускоренный крафт и переработка руд.
- Вейлборны: скрытность и мобильность.
## Классы
### Страж
Высокая выживаемость и контроль зоны.
### Проводник
Баффы команды и лечение в бою.
### Пилигрим
Урон по одиночным целям и ускорение перемещения.
## Профессии
1. Алхимик
2. Инженер
3. Хранитель ферм
4. Архивариус-торговец
:::tip
Лучше выбрать профессию, которая дополняет класс, а не дублирует его сильные стороны.
:::
<div class="see-also">
<p>Смотри также</p>
<a href="/economy/">Экономика</a> · <a href="/guides/">Гайды</a>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
---
title: "Сюжет"
description: "Черновик сюжета. Заполните своим лором."
---
## Сюжет
Это временный блок, чтобы раздел уже был в меню.
Над архипелагом горят три луны, по ночам реки светятся серебром, а у подножия черной цитадели начинают исчезать целые караваны.
Позже вы сможете полностью заменить этот текст на основной сюжет сервера.

View File

@@ -1,34 +0,0 @@
---
title: "Команда проекта"
description: "Кто развивает сервер: роли, ответственность и зоны поддержки."
---
## Состав команды
| Роль | Зона ответственности |
| --- | --- |
| Lead Admin | Архитектура мира и баланс |
| Content Designer | Квесты, лор, события |
| Tech Admin | Стабильность, плагины, бэкапы |
| Community Manager | Коммуникация с игроками |
## Как мы работаем
### Принцип 1
Решения по балансу принимаются на основе статистики, а не эмоций.
### Принцип 2
Критические правки публикуются с changelog.
## Как присоединиться
Следи за открытыми ролями в Discord и оставляй заявку с примерами работ.
<div class="see-also">
<p>Смотри также</p>
<a href="/contacts/">Контакты / ссылки</a> · <a href="/about-server/">О сервере</a>
</div>

View File

@@ -1,48 +0,0 @@
---
title: "Шаблон новой страницы"
description: "Готовый шаблон Markdown-страницы для быстрого добавления новых материалов."
---
## Копируй и используй
```md
---
title: "Название раздела"
description: "Краткое описание страницы"
---
## Введение
Коротко опиши, что на странице.
## Основной раздел
### Подраздел
- Пункт 1
- Пункт 2
:::tip
Добавляй подсказки через стандартные блоки Starlight.
:::
## Таблица
| Колонка | Значение |
| --- | --- |
| Пример | Текст |
<div class="see-also">
<p>Смотри также</p>
<a href="/getting-started/">Начало игры</a>
</div>
```
## Рекомендации по структуре
- Один файл = одна тема.
- Пиши короткие абзацы.
- Используй `h2` и `h3`, чтобы оглавление было полезным.
- Добавляй блок «Смотри также» в конце страницы.

View File

@@ -169,7 +169,7 @@ starlight-menu-button button::before {
} }
starlight-menu-button[aria-expanded='true'] button::before { starlight-menu-button[aria-expanded='true'] button::before {
content: ''; content: '×';
font-size: 1.02rem; font-size: 1.02rem;
letter-spacing: 0; letter-spacing: 0;
transform: none; transform: none;
@@ -226,6 +226,21 @@ starlight-menu-button button:hover {
background: color-mix(in srgb, var(--sl-color-accent-low) 32%, transparent); background: color-mix(in srgb, var(--sl-color-accent-low) 32%, transparent);
} }
.sidebar-content .sidebar-group-link {
display: inline-block;
padding: 0;
border: 0;
border-radius: 0;
color: inherit;
text-decoration: none;
background: transparent;
}
.sidebar-content .sidebar-group-link:hover {
text-decoration: none;
background: transparent;
}
.sidebar-content :is(a[aria-current='page'], a[aria-current='page']:hover) { .sidebar-content :is(a[aria-current='page'], a[aria-current='page']:hover) {
background: color-mix(in srgb, var(--sl-color-gray-6) 62%, transparent); background: color-mix(in srgb, var(--sl-color-gray-6) 62%, transparent);
color: var(--sl-color-white); color: var(--sl-color-white);
@@ -305,9 +320,26 @@ starlight-menu-button button:hover {
transform: translateY(-1px); transform: translateY(-1px);
} }
/* Единый эффект свечения в момент нажатия для всех кнопок/кнопкоподобных элементов. */
:is(
button:not(:disabled),
.sl-link-button,
.map-cta-button,
.map-ghost-button,
.copy-ip-chip
):active {
transform: translateY(0) scale(0.98) !important;
box-shadow:
0 0 0 1px color-mix(in srgb, var(--sl-color-accent) 34%, transparent),
0 0 18px color-mix(in srgb, var(--sl-color-accent) 34%, transparent),
0 0 34px color-mix(in srgb, var(--wiki-blue-2) 26%, transparent) !important;
filter: saturate(1.08);
}
.sl-markdown-content h1, .sl-markdown-content h1,
.sl-markdown-content h2, .sl-markdown-content h2,
.sl-markdown-content h3 { .sl-markdown-content h3,
.sl-markdown-content h4 {
font-family: 'Sora', sans-serif; font-family: 'Sora', sans-serif;
letter-spacing: 0.005em; letter-spacing: 0.005em;
line-height: 1.24; line-height: 1.24;
@@ -331,6 +363,40 @@ starlight-menu-button button:hover {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
.sl-markdown-content h4 {
margin-top: 1rem;
margin-bottom: 0.36rem;
font-size: clamp(0.96rem, 0.93rem + 0.18vw, 1.07rem);
color: color-mix(in srgb, var(--sl-color-white) 86%, var(--wiki-blue-2));
}
.sl-markdown-content .sl-heading-wrapper.level-h3 > h3 {
display: inline-flex;
align-items: center;
gap: 0.52rem;
}
.sl-markdown-content .minimal-heading-icon {
width: 1em;
height: 1em;
display: inline-block;
flex-shrink: 0;
opacity: 0.98;
background: linear-gradient(
150deg,
color-mix(in srgb, var(--wiki-purple-2) 76%, #ffffff),
color-mix(in srgb, var(--wiki-blue-2) 82%, #ffffff)
);
-webkit-mask-image: var(--icon-url);
mask-image: var(--icon-url);
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
}
.sl-markdown-content p, .sl-markdown-content p,
.sl-markdown-content li { .sl-markdown-content li {
color: var(--wiki-text); color: var(--wiki-text);
@@ -355,6 +421,61 @@ starlight-menu-button button:hover {
text-decoration-thickness: 0.07em; text-decoration-thickness: 0.07em;
} }
.sl-markdown-content :is(p, li, td, blockquote) a[href]:not(.sl-anchor-link):not(.map-cta-button):not(
.map-ghost-button
):not(.sl-link-button):not([href^='#']) {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.44rem;
max-width: min(100%, 100ch);
margin: 0.1rem 0.2rem 0.1rem 0;
padding: 0.55rem 1rem;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 48%, transparent);
border-radius: 0.2rem;
background: linear-gradient(
110deg,
color-mix(in srgb, var(--wiki-blue-2) 76%, #233145),
color-mix(in srgb, var(--wiki-purple-2) 66%, #273850)
);
color: #f8fbff;
text-decoration: none;
font-family: 'Sora', sans-serif;
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.008em;
line-height: 1;
overflow-wrap: anywhere;
box-shadow: 0 6px 14px color-mix(in srgb, var(--sl-color-accent) 18%, transparent);
transition: transform 170ms ease, border-color 170ms ease, box-shadow 170ms ease;
}
.sl-markdown-content :is(p, li, td, blockquote) a[href]:not(.sl-anchor-link):not(.map-cta-button):not(
.map-ghost-button
):not(.sl-link-button):not([href^='#']):hover {
color: #ffffff;
text-decoration: none;
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--sl-color-accent) 64%, transparent);
box-shadow: 0 10px 18px color-mix(in srgb, var(--sl-color-accent) 24%, transparent);
}
.sl-markdown-content :is(p, li, td, blockquote) a[href]:not(.sl-anchor-link):not(.map-cta-button):not(
.map-ghost-button
):not(.sl-link-button):not([href^='#']):focus-visible {
outline: none;
border-color: color-mix(in srgb, var(--sl-color-accent) 72%, transparent);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--sl-color-accent) 30%, transparent),
0 0 0 4px color-mix(in srgb, var(--sl-color-accent) 20%, transparent);
}
.sl-markdown-content :is(p, li, td, blockquote) a[href]:not(.sl-anchor-link):not(.map-cta-button):not(
.map-ghost-button
):not(.sl-link-button):not([href^='#']):active {
transform: translateY(0) scale(0.98);
}
.sl-markdown-content code { .sl-markdown-content code {
border-radius: 0.16rem; border-radius: 0.16rem;
border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 78%, transparent); border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 78%, transparent);
@@ -415,6 +536,28 @@ starlight-toc a {
transition: background 180ms ease, color 180ms ease, transform 180ms ease; transition: background 180ms ease, color 180ms ease, transform 180ms ease;
} }
/* Разделы, внутри которых есть подпункты, выделяем сильнее. */
starlight-toc li:has(> ul) > a,
mobile-starlight-toc li:has(> ul) > a {
font-weight: 700;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 22%, transparent);
background: linear-gradient(
90deg,
color-mix(in srgb, var(--wiki-purple-2) 20%, transparent),
color-mix(in srgb, var(--wiki-blue-2) 12%, transparent)
);
}
starlight-toc li:has(> ul) > a:hover,
mobile-starlight-toc li:has(> ul) > a:hover {
border-color: color-mix(in srgb, var(--sl-color-accent) 44%, transparent);
background: linear-gradient(
90deg,
color-mix(in srgb, var(--wiki-purple-2) 30%, transparent),
color-mix(in srgb, var(--wiki-blue-2) 18%, transparent)
);
}
starlight-toc a::before { starlight-toc a::before {
content: ''; content: '';
position: absolute; position: absolute;
@@ -585,6 +728,197 @@ starlight-theme-select .caret {
margin-bottom: 0.45rem; margin-bottom: 0.45rem;
} }
.craft-grid {
display: grid;
gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
grid-auto-rows: 1fr;
margin: 1rem 0 1.3rem;
}
.craft-grid > .craft-card {
margin-top: 0;
}
.craft-catalog {
margin-top: 0.95rem;
}
.craft-controls {
display: grid;
gap: 0.72rem;
margin: 0 0 1rem;
}
.craft-search-field {
display: grid;
gap: 0.36rem;
font-weight: 700;
font-size: 0.95rem;
color: color-mix(in srgb, var(--wiki-text) 92%, var(--sl-color-white));
}
.craft-search-input {
width: 100%;
min-height: 2.42rem;
border-radius: 0.18rem;
border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 82%, transparent);
background: color-mix(in srgb, var(--wiki-card) 92%, transparent);
color: var(--wiki-text);
padding: 0.5rem 0.68rem;
font: inherit;
transition: border-color 150ms ease, box-shadow 150ms ease;
}
.craft-search-input::placeholder {
color: color-mix(in srgb, var(--wiki-muted) 78%, var(--sl-color-white));
}
.craft-search-input:focus-visible {
outline: none;
border-color: color-mix(in srgb, var(--sl-color-accent) 68%, transparent);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--sl-color-accent) 30%, transparent),
0 0 0 4px color-mix(in srgb, var(--sl-color-accent) 18%, transparent);
}
.craft-filter-group {
display: flex;
flex-wrap: wrap;
gap: 0.48rem;
}
.craft-filter-chip {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.1rem;
border-radius: 999px;
border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 85%, transparent);
background: color-mix(in srgb, var(--wiki-card) 92%, transparent);
color: color-mix(in srgb, var(--wiki-text) 92%, var(--sl-color-white));
padding: 0.4rem 0.82rem;
font-family: 'Sora', sans-serif;
font-weight: 600;
font-size: 0.84rem;
letter-spacing: 0.008em;
cursor: pointer;
transition:
transform 150ms ease,
border-color 150ms ease,
background 150ms ease,
color 150ms ease;
}
.craft-filter-chip:hover {
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--sl-color-accent) 42%, transparent);
}
.craft-filter-chip.is-active {
border-color: color-mix(in srgb, var(--sl-color-accent) 56%, transparent);
background: linear-gradient(
120deg,
color-mix(in srgb, var(--wiki-blue-2) 74%, #233145),
color-mix(in srgb, var(--wiki-purple-2) 64%, #273850)
);
color: #f7fbff;
}
.craft-empty {
margin: 0.18rem 0 1.02rem;
border: 1px dashed color-mix(in srgb, var(--sl-color-accent) 34%, transparent);
border-radius: 0.16rem;
background: color-mix(in srgb, var(--sl-color-accent-low) 66%, transparent);
padding: 0.62rem 0.74rem;
font-size: 0.93rem;
}
.craft-group {
margin-bottom: 1.15rem;
}
.craft-group > h3 {
margin-bottom: 0.58rem;
}
.craft-card {
padding: 0.82rem 0.86rem;
display: flex;
flex-direction: column;
height: 100%;
}
.craft-card:hover {
transform: none;
}
.craft-card h3 {
margin: 0 0 0.6rem;
font-size: 1rem;
line-height: 1.35;
min-height: calc(1.35em * 2);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.craft-card-image {
display: block;
width: 100%;
aspect-ratio: 346 / 167;
object-fit: cover;
border-radius: 0.18rem;
border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 88%, transparent);
background: color-mix(in srgb, #ffffff 18%, transparent);
box-shadow: var(--sl-shadow-sm);
image-rendering: pixelated;
}
.craft-card[hidden],
.craft-group[hidden] {
display: none !important;
}
.alcohol-nav-card {
display: block;
color: inherit;
text-decoration: none;
}
.alcohol-nav-card:hover {
text-decoration: none;
}
.alcohol-nav-card p {
margin-bottom: 0;
}
.contact-link-card {
display: block;
color: inherit;
text-decoration: none;
}
.contact-link-card:hover {
text-decoration: none;
}
.contact-link-card p {
margin-bottom: 0;
}
.city-feature-card p {
margin-bottom: 0;
}
.city-highlight {
font-size: inherit;
font-weight: 700;
line-height: inherit;
}
.command-block { .command-block {
border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 88%, transparent); border: 1px solid color-mix(in srgb, var(--sl-color-hairline) 88%, transparent);
border-radius: 0.16rem; border-radius: 0.16rem;
@@ -615,6 +949,221 @@ starlight-theme-select .caret {
font-weight: 700; font-weight: 700;
} }
.ip-main-grid {
display: grid;
gap: 0.96rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
grid-auto-rows: minmax(0, 1fr);
align-items: stretch;
margin: 0.95rem 0 0.8rem;
}
.ip-main-card {
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 28%, var(--sl-color-hairline));
border-radius: 0.2rem;
padding: 1rem 1.02rem;
height: 100%;
min-height: 11.2rem;
display: flex;
flex-direction: column;
background:
linear-gradient(
160deg,
color-mix(in srgb, var(--wiki-purple-2) 12%, transparent),
color-mix(in srgb, var(--wiki-blue-2) 8%, transparent)
),
color-mix(in srgb, var(--wiki-card) 96%, transparent);
}
.ip-main-grid > .ip-main-card {
margin: 0 !important;
align-self: stretch;
}
.ip-main-grid > .ip-main-card + .ip-main-card {
margin-top: 0 !important;
}
.ip-main-card h3 {
margin: 0 0 0.52rem;
font-family: 'Sora', sans-serif;
font-size: 1.02rem;
line-height: 1.25;
}
.ip-main-card ul {
margin: 0;
padding-inline-start: 1.06rem;
display: grid;
gap: 0.52rem;
align-content: start;
}
.ip-main-card li {
margin: 0;
line-height: 1.4;
}
.copy-ip-chip {
appearance: none;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 48%, transparent);
border-radius: 0.2rem;
background: linear-gradient(
110deg,
color-mix(in srgb, var(--wiki-blue-2) 76%, #233145),
color-mix(in srgb, var(--wiki-purple-2) 66%, #273850)
);
color: #f8fbff;
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2rem;
padding: 0.36rem 0.72rem;
margin: 0 0 0 0.24rem;
cursor: pointer;
text-decoration: none;
font-family: 'Sora', sans-serif;
font-weight: 600;
font-size: 0.86rem;
letter-spacing: 0.008em;
line-height: 1;
box-shadow: 0 6px 14px color-mix(in srgb, var(--sl-color-accent) 18%, transparent);
transition: transform 170ms ease, border-color 170ms ease, box-shadow 170ms ease;
}
.copy-ip-chip code {
margin: 0;
border: 0;
background: transparent;
display: inline-block;
padding: 0;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: 1.2;
color: inherit;
}
.copy-ip-chip:hover {
color: #ffffff;
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--sl-color-accent) 66%, transparent);
box-shadow: 0 10px 18px color-mix(in srgb, var(--sl-color-accent) 24%, transparent);
}
.copy-ip-chip:focus-visible {
outline: none;
border-color: color-mix(in srgb, var(--sl-color-accent) 72%, transparent);
box-shadow:
0 0 0 1px color-mix(in srgb, var(--sl-color-accent) 30%, transparent),
0 0 0 4px color-mix(in srgb, var(--sl-color-accent) 20%, transparent);
}
.copy-ip-chip.is-copied {
border-color: color-mix(in srgb, #7ed49b 66%, transparent);
background: linear-gradient(
110deg,
color-mix(in srgb, #63b888 76%, #223b2f),
color-mix(in srgb, #5ccd9f 66%, #26463b)
);
}
.copy-ip-chip.is-copied::after {
content: '';
}
.copy-ip-chip.is-copy-error {
border-color: color-mix(in srgb, #ef6f85 74%, transparent);
background: linear-gradient(
110deg,
color-mix(in srgb, #d56b7f 78%, #3b242a),
color-mix(in srgb, #cf6f90 68%, #3f2532)
);
}
.copy-ip-chip.is-copy-error::after {
content: '';
}
.ip-main-note {
margin-top: 0.52rem;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 30%, transparent);
border-radius: 0.18rem;
padding: 0.58rem 0.72rem;
background: color-mix(in srgb, var(--sl-color-accent-low) 66%, transparent);
color: var(--wiki-text);
font-size: 0.94rem;
}
.map-actions {
margin-top: 1rem;
display: flex;
flex-wrap: wrap;
gap: 0.62rem;
}
.map-cta-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.44rem;
padding: 0.55rem 1rem;
border-radius: 0.2rem;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 48%, transparent);
background: linear-gradient(
110deg,
color-mix(in srgb, var(--wiki-blue-2) 76%, #233145),
color-mix(in srgb, var(--wiki-purple-2) 66%, #273850)
);
color: #f8fbff;
text-decoration: none;
font-family: 'Sora', sans-serif;
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.008em;
box-shadow: 0 6px 14px color-mix(in srgb, var(--sl-color-accent) 18%, transparent);
transition: transform 170ms ease, border-color 170ms ease, box-shadow 170ms ease;
}
.map-cta-button:hover {
color: #ffffff;
text-decoration: none;
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--sl-color-accent) 66%, transparent);
box-shadow: 0 10px 18px color-mix(in srgb, var(--sl-color-accent) 24%, transparent);
}
.map-ghost-button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 2.44rem;
padding: 0.55rem 1rem;
border-radius: 0.2rem;
border: 1px solid color-mix(in srgb, var(--sl-color-accent) 48%, transparent);
background: linear-gradient(
110deg,
color-mix(in srgb, var(--wiki-blue-2) 76%, #233145),
color-mix(in srgb, var(--wiki-purple-2) 66%, #273850)
);
color: #f8fbff;
text-decoration: none;
font-family: 'Sora', sans-serif;
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.008em;
box-shadow: 0 6px 14px color-mix(in srgb, var(--sl-color-accent) 18%, transparent);
transition: transform 170ms ease, border-color 170ms ease, box-shadow 170ms ease;
}
.map-ghost-button:hover {
color: #ffffff;
text-decoration: none;
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--sl-color-accent) 66%, transparent);
box-shadow: 0 10px 18px color-mix(in srgb, var(--sl-color-accent) 24%, transparent);
}
@media (min-width: 50rem) { @media (min-width: 50rem) {
.hero { .hero {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@@ -631,6 +1180,10 @@ starlight-theme-select .caret {
} }
@media (max-width: 50rem) { @media (max-width: 50rem) {
.ip-main-grid {
grid-template-columns: 1fr;
}
.wiki-header { .wiki-header {
gap: 0.5rem; gap: 0.5rem;
} }
@@ -710,4 +1263,19 @@ starlight-theme-select .caret {
border-radius: 0.16rem; border-radius: 0.16rem;
padding: 0.92rem 0.84rem; padding: 0.92rem 0.84rem;
} }
.map-actions {
flex-direction: column;
align-items: stretch;
}
.map-cta-button,
.map-ghost-button {
width: 100%;
justify-content: center;
}
.copy-ip-chip {
margin-inline-start: 0.2rem;
}
} }