1099 lines
No EOL
37 KiB
Svelte
1099 lines
No EOL
37 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from 'svelte';
|
|
import { fly, fade, slide } from 'svelte/transition';
|
|
import { marked } from 'marked';
|
|
import 'prismjs';
|
|
import 'prismjs/components/prism-typescript';
|
|
import 'prismjs/components/prism-javascript';
|
|
import 'prismjs/components/prism-css';
|
|
import 'prismjs/components/prism-json';
|
|
import 'prismjs/components/prism-bash';
|
|
import 'prismjs/components/prism-markdown';
|
|
|
|
// Documentation structure - for now this is left as is until I finish writing the rest of the docs, this is just placeholder for structure and what can be done
|
|
let docStructure = [
|
|
{
|
|
category: "Getting Started",
|
|
icon: "rocket_launch",
|
|
items: [
|
|
{ title: "Introduction", file: "1-intro" },
|
|
{ title: "Installation", file: "1-installation" },
|
|
{ title: "Familiarisation, States", file: "1-fam-states-intro" },
|
|
{ title: "How TA Works, Info", file: "1-taInfo" },
|
|
]
|
|
},
|
|
{
|
|
category: "State Manager",
|
|
icon: "account_tree",
|
|
items: [
|
|
{ title: "Introduction to the StateManager", file: "2-stateManager-intro" },
|
|
{ title: "All Functions", file: "2-stateManager-allFunctions" },
|
|
{ title: "Emit", file: "2-stateManager-emit" },
|
|
{ title: "GetKnownServers", file: "2-stateManager-getKnownServers" },
|
|
{ title: "GetMatch", file: "2-stateManager-getMatch" },
|
|
{ title: "GetMatches", file: "2-stateManager-getMatches" },
|
|
{ title: "GetQualifier", file: "2-stateManager-getQualifier" },
|
|
{ title: "GetQualifiers", file: "2-stateManager-getQualifiers" },
|
|
{ title: "GetSelfGuid", file: "2-stateManager-getSelfGuid" },
|
|
{ title: "GetTournament", file: "2-stateManager-getTournament" },
|
|
{ title: "GetTournaments", file: "2-stateManager-getTournaments" },
|
|
{ title: "GetUser", file: "2-stateManager-getUser" },
|
|
{ title: "GetUsers", file: "2-stateManager-getUsers" },
|
|
{ title: "HandlePacket", file: "2-stateManager-handlePacket" },
|
|
{ title: "On", file: "2-stateManager-on" },
|
|
{ title: "Once", file: "2-stateManager-once" },
|
|
{ title: "RemoveListener", file: "2-stateManager-removeListener" },
|
|
]
|
|
},
|
|
{
|
|
category: "Client",
|
|
icon: "api",
|
|
items: [
|
|
{ title: "Introduction to the Client", file: "3-client-intro" },
|
|
{ title: "All Functions", file: "3-client-allFunctions" },
|
|
{ title: "AddAuthorizedUser", file: "3-client-addAuthorizedUser" },
|
|
{ title: "AddQualifierMaps", file: "3-client-addQualifierMaps" },
|
|
{ title: "AddServer", file: "3-client-addServer" },
|
|
{ title: "AddTournamentPool", file: "3-client-addTournamentPool" },
|
|
{ title: "AddTournamentPoolMaps", file: "3-client-addTournamentPoolMaps" },
|
|
{ title: "AddTournamentRole", file: "3-client-addTournamentRole" },
|
|
{ title: "AddTournamentTeam", file: "3-client-addTournamentTeam" },
|
|
{ title: "AddUserToMatch", file: "3-client-addUserToMatch" },
|
|
{ title: "Connect", file: "3-client-connect" },
|
|
{ title: "CreateMatch", file: "3-client-createMatch" },
|
|
{ title: "CreateQualifierEvent", file: "3-client-createQualifierEvent" },
|
|
{ title: "CreateTournament", file: "3-client-createTournament" },
|
|
{ title: "DelayTestFinished", file: "3-client-delayTestFinished" },
|
|
{ title: "DeleteMatch", file: "3-client-deleteMatch" },
|
|
{ title: "DeleteQualifierEvent", file: "3-client-deleteQualifierEvent" },
|
|
{ title: "DeleteTournament", file: "3-client-deleteTournament" },
|
|
{ title: "Disconnect", file: "3-client-disconnect" },
|
|
{ title: "Emit", file: "3-client-emit" },
|
|
{ title: "FlipColors", file: "3-client-flipColors" },
|
|
{ title: "FlipHands", file: "3-client-flipHands" },
|
|
{ title: "GenerateBotToken", file: "3-client-generateBotToken" },
|
|
{ title: "GetAuthorizedUsers", file: "3-client-getAuthorizedUsers" },
|
|
{ title: "GetBotTokensForUser", file: "3-client-getBotTokensForUser" },
|
|
{ title: "GetDiscordInfo", file: "3-client-getDiscordInfo" },
|
|
{ title: "GetLeaderboard", file: "3-client-getLeaderboard" },
|
|
{ title: "IsConnected", file: "3-client-isConnected" },
|
|
{ title: "IsConnecting", file: "3-client-isConnecting" },
|
|
{ title: "JoinTournament", file: "3-client-joinTournament" },
|
|
{ title: "LoadImage", file: "3-client-loadImage" },
|
|
{ title: "LoadSong", file: "3-client-loadSong" },
|
|
{ title: "On", file: "3-client-on" },
|
|
{ title: "Once", file: "3-client-once" },
|
|
{ title: "PlaySong", file: "3-client-playSong" },
|
|
{ title: "RemoveAuthorizedUser", file: "3-client-removeAuthorizedUser" },
|
|
{ title: "RemoveListener", file: "3-client-removeListener" },
|
|
{ title: "RemoveQualifierMap", file: "3-client-removeQualifierMap" },
|
|
{ title: "RemoveTournamentPool", file: "3-client-removeTournamentPool" },
|
|
{ title: "RemoveTournamentPoolMap", file: "3-client-removeTournamentPoolMap" },
|
|
{ title: "RemoveTournamentRole", file: "3-client-removeTournamentRole" },
|
|
{ title: "RemoveTournamentTeam", file: "3-client-removeTournamentTeam" },
|
|
{ title: "RemoveUserFromMatch", file: "3-client-removeUserFromMatch" },
|
|
{ title: "ReturnToMenu", file: "3-client-returnToMenu" },
|
|
{ title: "RevokeBotToken", file: "3-client-revokeBotToken" },
|
|
{ title: "SendResponse", file: "3-client-sendResponse" },
|
|
{ title: "SetAuthToken", file: "3-client-setAuthToken" },
|
|
{ title: "SetMatchLeader", file: "3-client-setMatchLeader" },
|
|
{ title: "SetMatchMap", file: "3-client-setMatchMap" },
|
|
{ title: "SetQualifierFlags", file: "3-client-setQualifierFlags" },
|
|
{ title: "SetQualifierImage", file: "3-client-setQualifierImage" },
|
|
{ title: "SetQualifierInfoChannel", file: "3-client-setQualifierInfoChannel" },
|
|
{ title: "SetQualifierLeaderboardSort", file: "3-client-setQualifierLeaderboardSort" },
|
|
{ title: "SetQualifierName", file: "3-client-setQualifierName" },
|
|
{ title: "SetTournamentAllowUnauthorizedView", file: "3-client-setTournamentAllowUnauthorizedView" },
|
|
{ title: "SetTournamentBannedMods", file: "3-client-setTournamentBannedMods" },
|
|
{ title: "SetTournamentEnablePools", file: "3-client-setTournamentEnablePools" },
|
|
{ title: "SetTournamentEnableTeams", file: "3-client-setTournamentEnableTeams" },
|
|
{ title: "SetTournamentImage", file: "3-client-setTournamentImage" },
|
|
{ title: "SetTournamentName", file: "3-client-setTournamentName" },
|
|
{ title: "SetTournamentPoolName", file: "3-client-setTournamentPoolName" },
|
|
{ title: "SetTournamentRoleName", file: "3-client-setTournamentRoleName" },
|
|
{ title: "SetTournamentRolePermissions", file: "3-client-setTournamentRolePermissions" },
|
|
{ title: "SetTournamentScoreUpdateFrequency", file: "3-client-setTournamentScoreUpdateFrequency" },
|
|
{ title: "SetTournamentShowQualifierButton", file: "3-client-setTournamentShowQualifierButton" },
|
|
{ title: "SetTournamentShowTournamentButton", file: "3-client-setTournamentShowTournamentButton" },
|
|
{ title: "SetTournamentTeamImage", file: "3-client-setTournamentTeamImage" },
|
|
{ title: "SetTournamentTeamName", file: "3-client-setTournamentTeamName" },
|
|
{ title: "ShowLoadedImage", file: "3-client-showLoadedImage" },
|
|
{ title: "ShowPrompt", file: "3-client-showPrompt" },
|
|
{ title: "StateManager", file: "3-client-stateManager" },
|
|
{ title: "UpdateQualifierMap", file: "3-client-updateQualifierMap" },
|
|
{ title: "UpdateTournamentPoolMap", file: "3-client-updateTournamentPoolMap" },
|
|
{ title: "UpdateUser", file: "3-client-updateUser" }
|
|
]
|
|
},
|
|
{
|
|
category: "Models",
|
|
icon: "view_in_ar",
|
|
items: [
|
|
{ title: "4-", file: "" },
|
|
{ title: "Endpoints", file: "" },
|
|
{ title: "Response Types", file: "" }
|
|
]
|
|
},
|
|
{
|
|
category: "Enums",
|
|
icon: "view_in_ar",
|
|
items: [
|
|
{ title: "5-", file: "" },
|
|
{ title: "Endpoints", file: "" },
|
|
{ title: "Response Types", file: "" }
|
|
]
|
|
},
|
|
{
|
|
category: "Events",
|
|
icon: "view_in_ar",
|
|
items: [
|
|
{ title: "6-", file: "" },
|
|
{ title: "Endpoints", file: "" },
|
|
{ title: "Response Types", file: "" }
|
|
]
|
|
},
|
|
{
|
|
category: "Best Practices",
|
|
icon: "view_in_ar",
|
|
items: [
|
|
{ title: "7-", file: "" },
|
|
{ title: "Endpoints", file: "" },
|
|
{ title: "Response Types", file: "" }
|
|
]
|
|
}
|
|
];
|
|
|
|
// Flatten categories for URL handling
|
|
let allDocs = docStructure.flatMap(category =>
|
|
category.items.map(item => ({
|
|
...item,
|
|
category: category.category
|
|
}))
|
|
);
|
|
|
|
// State
|
|
let currentDoc: string = "";
|
|
let activeCategory: string = "";
|
|
let markdownContent: string = "";
|
|
let htmlContent: string = "";
|
|
let isLoading: boolean = true;
|
|
let isMobileMenuOpen: boolean = false;
|
|
let error: string | null = null;
|
|
|
|
// Handle hash changes
|
|
function handleHashChange() {
|
|
const hash = window.location.hash.slice(1); // Remove the # character
|
|
|
|
if (hash) {
|
|
const [category, doc] = hash.split("/");
|
|
|
|
if (category && doc) {
|
|
// Both category and doc are specified
|
|
activeCategory = decodeURIComponent(category);
|
|
currentDoc = decodeURIComponent(doc);
|
|
} else {
|
|
// Only category is specified, load first doc in that category
|
|
activeCategory = decodeURIComponent(hash);
|
|
const categoryData = docStructure.find(c => c.category === activeCategory);
|
|
if (categoryData && categoryData.items.length > 0) {
|
|
currentDoc = categoryData.items[0].file;
|
|
}
|
|
}
|
|
} else {
|
|
// No hash, load first doc of first category
|
|
if (docStructure.length > 0) {
|
|
activeCategory = docStructure[0].category;
|
|
currentDoc = docStructure[0].items[0].file;
|
|
}
|
|
}
|
|
|
|
loadMarkdownContent();
|
|
}
|
|
|
|
// Load markdown content
|
|
async function loadMarkdownContent() {
|
|
isLoading = true;
|
|
error = null;
|
|
|
|
try {
|
|
// Fetch using API from lib folder
|
|
const response = await fetch(`/api/docs/${currentDoc}.md`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to load documentation: ${response.statusText}`);
|
|
}
|
|
|
|
markdownContent = await response.text();
|
|
htmlContent = await marked.parse(markdownContent);
|
|
} catch (err) {
|
|
console.error("Error loading markdown:", err);
|
|
error = "Could not load the requested documentation.";
|
|
htmlContent = "";
|
|
} finally {
|
|
isLoading = false;
|
|
}
|
|
}
|
|
|
|
// Toggle category expansion
|
|
function toggleCategory(category: string) {
|
|
if (activeCategory === category) {
|
|
activeCategory = "";
|
|
} else {
|
|
activeCategory = category;
|
|
|
|
// Find first doc in category and set it as current
|
|
const categoryData = docStructure.find(c => c.category === category);
|
|
if (categoryData && categoryData.items.length > 0) {
|
|
categoryData.items.sort((a, b) => categoryData.items.indexOf(a) - categoryData.items.indexOf(b));
|
|
currentDoc = categoryData.items[0].file;
|
|
window.location.hash = `${encodeURIComponent(category)}/${encodeURIComponent(currentDoc)}`;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Select a specific doc
|
|
function selectDoc(category: string, doc: string) {
|
|
activeCategory = category;
|
|
currentDoc = doc;
|
|
window.location.hash = `${encodeURIComponent(category)}/${encodeURIComponent(doc)}`;
|
|
|
|
// On mobile, close the sidebar after selection
|
|
if (window.innerWidth < 768) {
|
|
isMobileMenuOpen = false;
|
|
}
|
|
}
|
|
|
|
// Toggle mobile menu
|
|
function toggleMobileMenu() {
|
|
isMobileMenuOpen = !isMobileMenuOpen;
|
|
}
|
|
|
|
function highlightCodeBlocks() {
|
|
// Make sure Prism is available
|
|
if (typeof window !== 'undefined' && (window as any).Prism) {
|
|
// Find all pre code elements and highlight them
|
|
const codeBlocks = document.querySelectorAll('pre code');
|
|
codeBlocks.forEach((block: Element) => {
|
|
// Get the language class if it exists (e.g., language-typescript)
|
|
const classes = block.classList;
|
|
let language = 'typescript'; // Default language
|
|
|
|
for (let i = 0; i < classes.length; i++) {
|
|
if (classes[i].startsWith('language-')) {
|
|
language = classes[i].replace('language-', '');
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Add the language class if it doesn't exist
|
|
if (!block.classList.contains(`language-${language}`)) {
|
|
block.classList.add(`language-${language}`);
|
|
}
|
|
|
|
// Make sure the parent pre has the language class too
|
|
const pre = block.parentElement;
|
|
if (pre && !pre.classList.contains(`language-${language}`)) {
|
|
pre.classList.add(`language-${language}`);
|
|
}
|
|
|
|
// Highlight the code block
|
|
(window as any).Prism.highlightElement(block);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Get current document index and navigation info
|
|
function getCurrentDocInfo(currentDocData: any) {
|
|
if (!currentDocData) return null;
|
|
|
|
const currentIndex = allDocs.findIndex(doc => doc.file === currentDoc);
|
|
return {
|
|
current: currentDocData,
|
|
index: currentIndex,
|
|
total: allDocs.length
|
|
};
|
|
}
|
|
|
|
// Navigate to previous document
|
|
function navigatePrevious() {
|
|
const docInfo = getCurrentDocInfo(allDocs.find(doc => doc.file === currentDoc));
|
|
if (!docInfo || docInfo.index <= 0) return;
|
|
|
|
const prevDoc = allDocs[docInfo.index - 1];
|
|
selectDoc(prevDoc.category, prevDoc.file);
|
|
}
|
|
|
|
// Navigate to next document
|
|
function navigateNext() {
|
|
const docInfo = getCurrentDocInfo(allDocs.find(doc => doc.file === currentDoc));
|
|
if (!docInfo || docInfo.index >= docInfo.total - 1) return;
|
|
|
|
const nextDoc = allDocs[docInfo.index + 1];
|
|
selectDoc(nextDoc.category, nextDoc.file);
|
|
}
|
|
|
|
// Get previous and next document info for button states
|
|
function getNavigationState(currentDocData: any) {
|
|
const docInfo = getCurrentDocInfo(currentDocData);
|
|
if (!docInfo) return { hasPrev: false, hasNext: false, prev: null, next: null };
|
|
|
|
const hasPrev = docInfo.index > 0;
|
|
const hasNext = docInfo.index < docInfo.total - 1;
|
|
|
|
return {
|
|
hasPrev,
|
|
hasNext,
|
|
prev: hasPrev ? allDocs[docInfo.index - 1] : null,
|
|
next: hasNext ? allDocs[docInfo.index + 1] : null
|
|
};
|
|
}
|
|
|
|
// Reactive statement to get navigation state
|
|
$: navigationState = getNavigationState(allDocs.find(doc => doc.file === currentDoc));
|
|
|
|
onMount(() => {
|
|
// Initialise content based on URL hash or load default
|
|
handleHashChange();
|
|
|
|
// Listen for hash changes
|
|
window.addEventListener('hashchange', handleHashChange);
|
|
|
|
return () => {
|
|
window.removeEventListener('hashchange', handleHashChange);
|
|
};
|
|
});
|
|
|
|
$: if (htmlContent && !isLoading) {
|
|
setTimeout(() => {
|
|
highlightCodeBlocks();
|
|
}, 0);
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Documentation | Tournament Assistant</title>
|
|
</svelte:head>
|
|
|
|
<div class="documentation-container">
|
|
<!-- Mobile menu toggle -->
|
|
<button class="mobile-menu-toggle" on:click={toggleMobileMenu}>
|
|
<span class="material-icons">{isMobileMenuOpen ? 'close' : 'menu'}</span>
|
|
<span>Documentation</span>
|
|
</button>
|
|
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar" class:open={isMobileMenuOpen}>
|
|
<div class="sidebar-header">
|
|
<h2>Documentation</h2>
|
|
<button class="close-sidebar" on:click={toggleMobileMenu}>
|
|
<span class="material-icons">close</span>
|
|
</button>
|
|
</div>
|
|
|
|
<nav class="sidebar-nav">
|
|
{#each docStructure as category}
|
|
<div class="category">
|
|
<button
|
|
class="category-header"
|
|
class:active={activeCategory === category.category}
|
|
on:click={() => toggleCategory(category.category)}
|
|
>
|
|
<div class="category-header-content">
|
|
<span class="material-icons">{category.icon}</span>
|
|
<span>{category.category}</span>
|
|
</div>
|
|
<span class="material-icons">
|
|
{activeCategory === category.category ? 'expand_less' : 'expand_more'}
|
|
</span>
|
|
</button>
|
|
|
|
{#if activeCategory === category.category}
|
|
<!-- svelte-ignore missing-declaration -->
|
|
<div class="category-items" transition:slide={{duration: 200}}>
|
|
{#each category.items.sort((a, b) => category.items.indexOf(a) - category.items.indexOf(b)) as item}
|
|
<button
|
|
class="doc-link"
|
|
class:active={currentDoc === item.file}
|
|
on:click={() => selectDoc(category.category, item.file)}
|
|
>
|
|
<span>{item.title}</span>
|
|
</button>
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
</nav>
|
|
</aside>
|
|
|
|
<!-- Main content -->
|
|
<main class="content">
|
|
{#if isLoading}
|
|
<div class="loading-container" in:fade={{duration: 200}}>
|
|
<div class="loading-spinner"></div>
|
|
<p>Loading documentation...</p>
|
|
</div>
|
|
{:else if error}
|
|
<div class="error-container" in:fly={{y: 20, duration: 300}}>
|
|
<span class="material-icons">error_outline</span>
|
|
<h3>Documentation Not Found</h3>
|
|
<p>{error}</p>
|
|
<button class="error-action-btn" on:click={handleHashChange}>
|
|
<span class="material-icons">refresh</span>
|
|
Try Again
|
|
</button>
|
|
</div>
|
|
{:else}
|
|
<div class="markdown-container" in:fly={{y: 20, duration: 300}}>
|
|
{@html htmlContent}
|
|
</div>
|
|
<!-- Navigation buttons -->
|
|
<div class="doc-navigation">
|
|
<button
|
|
class="nav-btn prev-btn"
|
|
class:disabled={!navigationState.hasPrev}
|
|
disabled={!navigationState.hasPrev}
|
|
on:click={navigatePrevious}
|
|
>
|
|
<span class="material-icons">arrow_back</span>
|
|
<div class="nav-btn-content">
|
|
<span class="nav-btn-label">Previous</span>
|
|
{#if navigationState.prev}
|
|
<span class="nav-btn-title">{navigationState.prev.title}</span>
|
|
{/if}
|
|
</div>
|
|
</button>
|
|
|
|
<button
|
|
class="nav-btn next-btn"
|
|
class:disabled={!navigationState.hasNext}
|
|
disabled={!navigationState.hasNext}
|
|
on:click={navigateNext}
|
|
>
|
|
<span class="material-icons">arrow_forward</span>
|
|
<div class="nav-btn-content">
|
|
<span class="nav-btn-label">Next</span>
|
|
{#if navigationState.next}
|
|
<span class="nav-btn-title">{navigationState.next.title}</span>
|
|
{/if}
|
|
</div>
|
|
</button>
|
|
</div>
|
|
{/if}
|
|
</main>
|
|
</div>
|
|
|
|
<style>
|
|
/* Documentation specific styles */
|
|
.documentation-container {
|
|
display: flex;
|
|
height: calc(100vh - var(--navbar-height));
|
|
background-color: var(--bg-primary);
|
|
position: relative;
|
|
}
|
|
|
|
/* Sidebar styles */
|
|
.sidebar {
|
|
width: 330px;
|
|
background-color: var(--bg-secondary);
|
|
border-right: 1px solid var(--border-color);
|
|
height: 100%;
|
|
overflow-y: auto;
|
|
transition: transform 0.3s ease;
|
|
z-index: 5;
|
|
border-radius: 1rem;
|
|
}
|
|
|
|
.sidebar-header {
|
|
padding: 1.5rem;
|
|
border-bottom: 1px solid var(--border-color);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.sidebar-header h2 {
|
|
margin: 0;
|
|
font-size: 1.25rem;
|
|
color: var(--accent-color);
|
|
}
|
|
|
|
.close-sidebar {
|
|
display: none;
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-secondary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.sidebar-nav {
|
|
padding: 1rem 0;
|
|
border-radius: 1rem;
|
|
}
|
|
|
|
.category {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.category-header {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.75rem 1.5rem;
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-primary);
|
|
font-size: 1rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s ease;
|
|
text-align: left;
|
|
}
|
|
|
|
.category-header:hover {
|
|
background-color: var(--bg-tertiary);
|
|
}
|
|
|
|
.category-header.active {
|
|
background: linear-gradient(90deg, var(--accent-color) 0.5%, var(--bg-tertiary) 0.5%);
|
|
}
|
|
|
|
.category-header-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.category-items {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding-left: 2.5rem;
|
|
}
|
|
|
|
.doc-link {
|
|
padding: 0.5rem 1rem;
|
|
margin: 0.125rem 0;
|
|
background: none;
|
|
border: none;
|
|
color: var(--text-secondary);
|
|
font-size: 0.875rem;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
border-radius: 0.25rem;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.doc-link:hover {
|
|
background-color: var(--bg-tertiary);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.doc-link.active {
|
|
background-color: rgba(79, 70, 229, 0.15);
|
|
color: var(--accent-color);
|
|
box-shadow: 0 0 8px rgba(79, 70, 229, 0.2);
|
|
}
|
|
|
|
/* Mobile menu toggle */
|
|
.mobile-menu-toggle {
|
|
display: none;
|
|
position: fixed;
|
|
top: calc(var(--navbar-height) + 1rem);
|
|
left: 1rem;
|
|
z-index: 10;
|
|
padding: 0.5rem 1rem;
|
|
background-color: var(--accent-color);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 0.375rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
/* Main content */
|
|
.content {
|
|
flex: 1;
|
|
padding: 2rem;
|
|
overflow-y: auto;
|
|
max-width: 100%;
|
|
}
|
|
|
|
/* Loading styles */
|
|
.loading-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
gap: 1rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 2.5rem;
|
|
height: 2.5rem;
|
|
border: 0.25rem solid rgba(79, 70, 229, 0.3);
|
|
border-top: 0.25rem solid var(--accent-color);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Error styles */
|
|
.error-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 60%;
|
|
text-align: center;
|
|
padding: 2rem;
|
|
gap: 1rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.error-container .material-icons {
|
|
font-size: 3rem;
|
|
color: var(--danger-color);
|
|
}
|
|
|
|
.error-container h3 {
|
|
margin: 0;
|
|
font-size: 1.5rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.error-action-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.5rem 1rem;
|
|
background-color: var(--bg-tertiary);
|
|
color: var(--text-primary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 0.375rem;
|
|
margin-top: 1rem;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.error-action-btn:hover {
|
|
background-color: var(--accent-color);
|
|
border-color: var(--accent-color);
|
|
box-shadow: 0 0 12px var(--accent-glow);
|
|
}
|
|
|
|
.doc-navigation {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 1rem;
|
|
padding: 2rem;
|
|
border-top: 1px solid var(--border-color);
|
|
background-color: var(--bg-primary);
|
|
}
|
|
|
|
.nav-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 1rem 1.5rem;
|
|
background-color: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 0.5rem;
|
|
color: var(--text-primary);
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
font-family: inherit;
|
|
min-height: 4rem;
|
|
flex: 1;
|
|
max-width: auto;
|
|
}
|
|
|
|
.nav-btn:hover:not(.disabled) {
|
|
background-color: var(--accent-color);
|
|
border-color: var(--accent-color);
|
|
box-shadow: 0 0 12px var(--accent-glow);
|
|
color: white;
|
|
}
|
|
|
|
.nav-btn.disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
background-color: var(--bg-tertiary);
|
|
border-color: var(--border-color);
|
|
}
|
|
|
|
.nav-btn-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
text-align: left;
|
|
}
|
|
|
|
.next-btn .nav-btn-content {
|
|
align-items: flex-end;
|
|
text-align: right;
|
|
}
|
|
|
|
.nav-btn-label {
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
opacity: 0.8;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.nav-btn-title {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.prev-btn {
|
|
margin-right: auto;
|
|
}
|
|
|
|
.next-btn {
|
|
margin-left: auto;
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.doc-navigation {
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
padding: 1rem;
|
|
}
|
|
|
|
.nav-btn {
|
|
max-width: none;
|
|
justify-content: center;
|
|
}
|
|
|
|
.nav-btn-content {
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
|
|
.next-btn .nav-btn-content {
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
}
|
|
|
|
/* Markdown content styling */
|
|
.markdown-container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.markdown-container :global(h1) {
|
|
font-size: 2rem;
|
|
font-weight: 700;
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 0.5rem;
|
|
border-bottom: 1px solid var(--border-color);
|
|
color: var(--accent-color);
|
|
}
|
|
|
|
.markdown-container :global(h2) {
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
margin-top: 2rem;
|
|
margin-bottom: 1rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.markdown-container :global(h3) {
|
|
font-size: 1.25rem;
|
|
font-weight: 600;
|
|
margin-top: 1.5rem;
|
|
margin-bottom: 0.75rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.markdown-container :global(p) {
|
|
margin-bottom: 1rem;
|
|
line-height: 1.6;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.markdown-container :global(ul), .markdown-container :global(ol) {
|
|
margin-bottom: 1rem;
|
|
padding-left: 1.5rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.markdown-container :global(li) {
|
|
margin-bottom: 0.5rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.markdown-container :global(code) {
|
|
background-color: var(--bg-tertiary);
|
|
padding: 0.2em 0.4em;
|
|
border-radius: 0.25rem;
|
|
font-family: monospace;
|
|
font-size: 0.875em;
|
|
}
|
|
|
|
.markdown-container :global(pre) {
|
|
background-color: var(--bg-tertiary);
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
overflow-x: auto;
|
|
margin-bottom: 1.5rem;
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.markdown-container :global(pre code) {
|
|
background-color: transparent;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
display: block;
|
|
}
|
|
|
|
.markdown-container :global(blockquote) {
|
|
border-left: 4px solid var(--accent-color);
|
|
padding-left: 1rem;
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
margin-bottom: 1rem;
|
|
color: var(--text-secondary);
|
|
background-color: rgba(79, 70, 229, 0.07);
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 0 0.375rem 0.375rem 0;
|
|
}
|
|
|
|
.markdown-container :global(table) {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.markdown-container :global(th), .markdown-container :global(td) {
|
|
padding: 0.75rem;
|
|
border: 1px solid var(--border-color);
|
|
text-align: left;
|
|
}
|
|
|
|
.markdown-container :global(th) {
|
|
background-color: var(--bg-tertiary);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.markdown-container :global(tr:nth-child(even)) {
|
|
background-color: rgba(45, 45, 45, 0.5);
|
|
}
|
|
|
|
.markdown-container :global(a) {
|
|
color: var(--accent-color);
|
|
text-decoration: none;
|
|
border-bottom: 1px dotted var(--accent-color);
|
|
transition: border-bottom 0.2s ease;
|
|
}
|
|
|
|
.markdown-container :global(a:hover) {
|
|
border-bottom: 1px solid var(--accent-color);
|
|
}
|
|
|
|
.markdown-container :global(hr) {
|
|
border: none;
|
|
border-top: 1px solid var(--border-color);
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.markdown-container :global(img) {
|
|
max-width: 100%;
|
|
border-radius: 0.5rem;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
/* Custom info box style */
|
|
.markdown-container :global(.info-box),
|
|
.markdown-container :global(.warning-box),
|
|
.markdown-container :global(.success-box) {
|
|
border-radius: 0.5rem;
|
|
padding: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
display: flex;
|
|
gap: 1rem;
|
|
align-items: flex-start;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.markdown-container :global(.info-box) {
|
|
background-color: rgba(59, 130, 246, 0.1);
|
|
border-left: 4px solid #3b82f6;
|
|
}
|
|
|
|
.markdown-container :global(.warning-box) {
|
|
background-color: rgba(245, 158, 11, 0.1);
|
|
border-left: 4px solid #f59e0b;
|
|
}
|
|
|
|
.markdown-container :global(.success-box) {
|
|
background-color: rgba(34, 197, 94, 0.1);
|
|
border-left: 4px solid #22c55e;
|
|
}
|
|
|
|
/* Responsive styles */
|
|
@media (max-width: 768px) {
|
|
.mobile-menu-toggle {
|
|
display: flex;
|
|
}
|
|
|
|
.sidebar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
transform: translateX(-100%);
|
|
z-index: 999;
|
|
}
|
|
|
|
.sidebar.open {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.close-sidebar {
|
|
display: block;
|
|
}
|
|
|
|
.content {
|
|
padding: 4rem 1rem 2rem;
|
|
}
|
|
}
|
|
|
|
/* PrismJS VSCode-like Dark Theme */
|
|
.markdown-container :global(pre) {
|
|
background-color: #1e1e1e;
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
overflow-x: auto;
|
|
margin-bottom: 1.5rem;
|
|
border: 1px solid var(--border-color);
|
|
}
|
|
|
|
.markdown-container :global(code[class*="language-"]),
|
|
.markdown-container :global(pre[class*="language-"]) {
|
|
color: #d4d4d4;
|
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
|
text-align: left;
|
|
white-space: pre;
|
|
word-spacing: normal;
|
|
word-break: normal;
|
|
word-wrap: normal;
|
|
line-height: 1.5;
|
|
tab-size: 4;
|
|
hyphens: none;
|
|
}
|
|
|
|
/* Tokens */
|
|
.markdown-container :global(.token.comment),
|
|
.markdown-container :global(.token.prolog),
|
|
.markdown-container :global(.token.doctype),
|
|
.markdown-container :global(.token.cdata) {
|
|
color: #6a9955;
|
|
}
|
|
|
|
.markdown-container :global(.token.punctuation) {
|
|
color: #d4d4d4;
|
|
}
|
|
|
|
.markdown-container :global(.token.namespace) {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.markdown-container :global(.token.property),
|
|
.markdown-container :global(.token.tag),
|
|
.markdown-container :global(.token.boolean),
|
|
.markdown-container :global(.token.number),
|
|
.markdown-container :global(.token.constant),
|
|
.markdown-container :global(.token.symbol) {
|
|
color: #b5cea8;
|
|
}
|
|
|
|
.markdown-container :global(.token.selector),
|
|
.markdown-container :global(.token.attr-name),
|
|
.markdown-container :global(.token.string),
|
|
.markdown-container :global(.token.char),
|
|
.markdown-container :global(.token.builtin) {
|
|
color: #ce9178;
|
|
}
|
|
|
|
.markdown-container :global(.token.operator),
|
|
.markdown-container :global(.token.entity),
|
|
.markdown-container :global(.token.url),
|
|
.markdown-container :global(.language-css .token.string),
|
|
.markdown-container :global(.style .token.string) {
|
|
color: #d4d4d4;
|
|
}
|
|
|
|
.markdown-container :global(.token.atrule),
|
|
.markdown-container :global(.token.attr-value),
|
|
.markdown-container :global(.token.keyword) {
|
|
color: #569cd6;
|
|
}
|
|
|
|
.markdown-container :global(.token.function) {
|
|
color: #dcdcaa;
|
|
}
|
|
|
|
.markdown-container :global(.token.regex),
|
|
.markdown-container :global(.token.important),
|
|
.markdown-container :global(.token.variable) {
|
|
color: #d16969;
|
|
}
|
|
|
|
.markdown-container :global(.token.important),
|
|
.markdown-container :global(.token.bold) {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.markdown-container :global(.token.italic) {
|
|
font-style: italic;
|
|
}
|
|
|
|
.markdown-container :global(.token.constant) {
|
|
color: #9cdcfe;
|
|
}
|
|
|
|
.markdown-container :global(.token.class-name) {
|
|
color: #4ec9b0;
|
|
}
|
|
|
|
.markdown-container :global(.token.parameter) {
|
|
color: #9cdcfe;
|
|
}
|
|
|
|
.markdown-container :global(.token.interpolation) {
|
|
color: #9cdcfe;
|
|
}
|
|
|
|
.markdown-container :global(.token.punctuation.interpolation-punctuation) {
|
|
color: #569cd6;
|
|
}
|
|
|
|
.markdown-container :global(.token.entity) {
|
|
cursor: help;
|
|
}
|
|
|
|
/* Line highlighting */
|
|
.markdown-container :global(pre[data-line]) {
|
|
position: relative;
|
|
}
|
|
|
|
.markdown-container :global(pre[class*="language-"] > code[class*="language-"]) {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
</style> |