Добавлены новые разделы документации: "Города и регистрация", "Крафты", "Приват и защита", "SkinRestore". Каждый раздел содержит информацию о статусе, что будет добавлено, и полезные советы для пользователей.

This commit is contained in:
2026-04-22 06:26:33 +03:00
parent c38d538f93
commit ecb0d8ebc2
28 changed files with 835 additions and 118 deletions

View File

@@ -3,7 +3,6 @@ import config from 'virtual:starlight/user-config';
import LanguageSelect from 'virtual:starlight/components/LanguageSelect';
import Search from 'virtual:starlight/components/Search';
import SiteTitle from 'virtual:starlight/components/SiteTitle';
import SocialIcons from 'virtual:starlight/components/SocialIcons';
const shouldRenderSearch =
@@ -21,7 +20,15 @@ const topLinks = [
<div class="wiki-header-shell">
<div class="header wiki-header">
<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="Быстрая навигация">
{
topLinks.map((link) => (
@@ -89,10 +96,148 @@ const topLinks = [
</div>
<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>
<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 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 enhanceSidebarGroupLinks = () => {
const targets = [{ label: 'Алкоголь', href: '/alcohol/' }];
const labelNodes = document.querySelectorAll('.sidebar-content details > summary .group-label .large');
@@ -164,6 +309,10 @@ const topLinks = [
});
enhanceSidebarGroupLinks();
sanitizeNavLabels();
sanitizeDocHeadings();
decorateSectionIcons();
bindCopyIpChips();
setReadProgress();
window.addEventListener('scroll', setReadProgress, { passive: true });
window.addEventListener('resize', setReadProgress);
@@ -177,6 +326,10 @@ const topLinks = [
document.addEventListener('astro:page-load', () => {
enhanceSidebarGroupLinks();
sanitizeNavLabels();
sanitizeDocHeadings();
decorateSectionIcons();
bindCopyIpChips();
setReadProgress();
});
</script>
@@ -203,10 +356,70 @@ const topLinks = [
padding: 0.25rem;
margin: -0.25rem;
min-width: 0;
gap: 1rem;
gap: 1.1rem;
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 {
display: none;
gap: 0.45rem;
@@ -327,51 +540,56 @@ const topLinks = [
.wiki-back-to-top {
position: fixed;
top: auto;
right: max(0.75rem, env(safe-area-inset-right));
bottom: max(1.1rem, env(safe-area-inset-bottom));
right: max(1.35rem, calc(env(safe-area-inset-right) + 0.8rem));
bottom: max(1.45rem, calc(env(safe-area-inset-bottom) + 0.7rem));
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;
background: linear-gradient(
140deg,
color-mix(in srgb, var(--wiki-purple-2) 78%, #101522),
color-mix(in srgb, var(--wiki-blue-2) 82%, #101522)
color-mix(in srgb, var(--wiki-purple-2) 74%, #101522),
color-mix(in srgb, var(--wiki-blue-2) 84%, #101522)
);
color: #f6f9ff;
font-family: 'Sora', sans-serif;
font-size: 1.1rem;
font-weight: 600;
letter-spacing: 0.01em;
width: 2.6rem;
height: 2.6rem;
width: 3.1rem;
height: 3.1rem;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
line-height: 1;
box-shadow:
0 10px 24px hsl(220 95% 62% / 0.24),
0 0 0 1px hsl(214 88% 70% / 0.22);
0 0 0 1px hsl(214 88% 70% / 0.24),
0 0 24px hsl(217 100% 68% / 0.38),
0 0 42px hsl(221 100% 65% / 0.22);
opacity: 0;
visibility: hidden;
transform: translateY(6px);
transform: translateY(8px) scale(0.96);
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 {
opacity: 1;
visibility: visible;
transform: translateY(0);
transform: translateY(0) scale(1);
pointer-events: auto;
}
.wiki-back-to-top:hover {
border-color: color-mix(in srgb, #9ec4ff 80%, transparent);
box-shadow:
0 14px 28px hsl(220 95% 62% / 0.32),
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 {
@@ -415,12 +633,36 @@ const topLinks = [
}
@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 {
top: auto;
right: max(0.62rem, env(safe-area-inset-right));
bottom: max(1rem, env(safe-area-inset-bottom));
width: 2.5rem;
height: 2.5rem;
right: max(1rem, calc(env(safe-area-inset-right) + 0.52rem));
bottom: max(1.18rem, calc(env(safe-area-inset-bottom) + 0.58rem));
width: 2.85rem;
height: 2.85rem;
}
.wiki-back-to-top__icon {
width: 1.34rem;
height: 1.34rem;
}
}
}