setIsOpen(false)} />
-
-
-
- {
- setQuery(e.target.value);
- setSelectedIndex(0); // Reset selected index when query changes
- }}
- onKeyDown={handleKeyDown}
- />
-
-
-
- {query && (
-
- {results.length > 0 ? (
-
- {results.map((result, index) => (
- -
-
-
- ))}
-
- ) : (
-
-
No results found for "{query}"
-
- )}
-
- )}
-
- {!query && (
-
-
Type to search documentation...
-
- )}
-
- >
- )}
- >
- );
-}
-
diff --git a/apps/web-docs/src/components/docs/TableOfContents.module.css b/apps/web-docs/src/components/docs/TableOfContents.module.css
deleted file mode 100644
index c08a0de4..00000000
--- a/apps/web-docs/src/components/docs/TableOfContents.module.css
+++ /dev/null
@@ -1,121 +0,0 @@
-.toc {
- position: sticky;
- top: 5rem;
- align-self: flex-start;
- max-height: calc(100vh - 6rem);
- overflow-y: auto;
- padding-left: 2rem;
- font-size: 0.85rem;
- min-width: 200px;
- max-width: 280px;
-}
-
-.tocHeader {
- font-weight: 600;
- font-size: 0.75rem;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--foreground-muted);
- margin-bottom: 1rem;
-}
-
-.tocNav {
- position: relative;
-}
-
-.tocList {
- list-style: none;
- padding: 0;
- margin: 0;
- display: flex;
- flex-direction: column;
- gap: 0.25rem;
-}
-
-.tocItem {
- position: relative;
-}
-
-.tocLink {
- display: block;
- color: var(--foreground-muted);
- text-decoration: none;
- transition: color 0.15s ease;
- padding: 0.25rem 0;
- line-height: 1.5;
- word-wrap: break-word;
- overflow-wrap: break-word;
-}
-
-.tocLink:hover {
- color: var(--foreground);
-}
-
-.tocItemActive .tocLink {
- color: var(--foreground);
- font-weight: 500;
-}
-
-/* Level-specific indentation */
-.tocLevel1 {
- padding-left: 0;
-}
-
-.tocLevel2 {
- padding-left: 0;
-}
-
-.tocLevel3 {
- padding-left: 0.75rem;
-}
-
-.tocLevel4 {
- padding-left: 1.5rem;
-}
-
-.tocLevel5 {
- padding-left: 2.25rem;
-}
-
-.tocLevel6 {
- padding-left: 3rem;
-}
-
-/* Active indicator */
-.tocItemActive::before {
- content: '';
- position: absolute;
- left: -1rem;
- top: 50%;
- transform: translateY(-50%);
- width: 2px;
- height: 1.2rem;
- background: var(--foreground);
- border-radius: 1px;
-}
-
-/* Scrollbar styling */
-.toc::-webkit-scrollbar {
- width: 6px;
-}
-
-.toc::-webkit-scrollbar-track {
- background: transparent;
-}
-
-.toc::-webkit-scrollbar-thumb {
- background: var(--border);
- border-radius: 3px;
-}
-
-.toc::-webkit-scrollbar-thumb:hover {
- background: var(--foreground-muted);
-}
-
-/* Mobile responsive */
-@media (max-width: 1024px) {
- .toc {
- display: none;
- }
-}
-
diff --git a/apps/web-docs/src/components/docs/TableOfContents.tsx b/apps/web-docs/src/components/docs/TableOfContents.tsx
deleted file mode 100644
index 66d989d5..00000000
--- a/apps/web-docs/src/components/docs/TableOfContents.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { usePathname } from 'next/navigation';
-import styles from './TableOfContents.module.css';
-
-type HeadingItem = {
- id: string;
- text: string;
- level: number;
-};
-
-type TableOfContentsProps = {
- locale: 'en' | 'vi';
-};
-
-export default function TableOfContents({ locale }: TableOfContentsProps) {
- const [headings, setHeadings] = useState
([]);
- const [activeId, setActiveId] = useState('');
- const pathname = usePathname();
-
- // Extract headings from content
- useEffect(() => {
- const extractHeadings = (): HeadingItem[] => {
- const content = document.querySelector('[data-docs-content]');
- if (!content) return [];
-
- const headingElements = content.querySelectorAll('h1, h2, h3, h4, h5, h6');
- const headingItems: HeadingItem[] = [];
-
- headingElements.forEach((heading) => {
- const text = heading.textContent?.trim() || '';
- const id = heading.id;
-
- if (id && text) {
- const level = parseInt(heading.tagName.charAt(1));
- headingItems.push({ id, text, level });
- }
- });
-
- return headingItems;
- };
-
- // Function to update headings
- const updateHeadings = () => {
- const headingItems = extractHeadings();
- setHeadings(headingItems);
- // Reset active ID if we have no headings (e.g. page transition)
- if (headingItems.length === 0) {
- setActiveId('');
- }
- };
-
- // Set up MutationObserver to watch for content changes
- const content = document.querySelector('[data-docs-content]');
- let observer: MutationObserver | null = null;
- let timers: NodeJS.Timeout[] = [];
-
- const cleanup = () => {
- if (observer) {
- observer.disconnect();
- observer = null;
- }
- timers.forEach(t => clearTimeout(t));
- timers = [];
- };
-
- // Try extracting immediately
- const initialTimer = setTimeout(updateHeadings, 0);
- timers.push(initialTimer);
-
- if (content) {
- // Watch for changes in the content structure (loading, hydration)
- observer = new MutationObserver(() => {
- updateHeadings();
- });
-
- observer.observe(content, {
- childList: true,
- subtree: true,
- // No longer watching attributes since we don't modify them
- });
- }
-
- // Try periodically for dynamic content
- const tryMultipleTimes = [100, 300, 500, 1000];
- tryMultipleTimes.forEach((delay) => {
- const timer = setTimeout(updateHeadings, delay);
- timers.push(timer);
- });
-
- return cleanup;
- }, [pathname]); // Re-run when pathname changes
-
- // Set up intersection observer for active heading
- useEffect(() => {
- if (headings.length === 0) return;
-
- const observerOptions = {
- root: null,
- rootMargin: '-80px 0px -80% 0px',
- threshold: 0,
- };
-
- const observerCallback = (entries: IntersectionObserverEntry[]) => {
- entries.forEach((entry) => {
- if (entry.isIntersecting) {
- setActiveId(entry.target.id);
- }
- });
- };
-
- const observer = new IntersectionObserver(observerCallback, observerOptions);
-
- // Observe all headings
- headings.forEach((item) => {
- const element = document.getElementById(item.id);
- if (element) {
- observer.observe(element);
- }
- });
-
- return () => {
- observer.disconnect();
- };
- }, [headings]);
-
- // Always render TOC container, even if empty (for debugging and layout consistency)
-
- const handleClick = (e: React.MouseEvent, id: string) => {
- e.preventDefault();
- const element = document.getElementById(id);
- if (element) {
- const offsetTop = element.getBoundingClientRect().top + window.pageYOffset - 80;
- window.scrollTo({
- top: offsetTop,
- behavior: 'smooth',
- });
- setActiveId(id);
- }
- };
-
- return (
-
- );
-}
-
diff --git a/apps/web-docs/src/components/docs/TableWrapper.tsx b/apps/web-docs/src/components/docs/TableWrapper.tsx
deleted file mode 100644
index 4121c9b6..00000000
--- a/apps/web-docs/src/components/docs/TableWrapper.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-'use client';
-
-import { useEffect, useRef, useState } from 'react';
-
-interface TableWrapperProps {
- children: React.ReactNode;
-}
-
-export default function TableWrapper({ children }: TableWrapperProps) {
- const wrapperRef = useRef(null);
- const [canScrollLeft, setCanScrollLeft] = useState(false);
- const [canScrollRight, setCanScrollRight] = useState(false);
-
- const updateScrollState = () => {
- if (!wrapperRef.current) return;
-
- const { scrollLeft, scrollWidth, clientWidth } = wrapperRef.current;
- setCanScrollLeft(scrollLeft > 0);
- setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 1);
- };
-
- useEffect(() => {
- const wrapper = wrapperRef.current;
- if (!wrapper) return;
-
- // Initial check
- updateScrollState();
-
- // Update on scroll
- wrapper.addEventListener('scroll', updateScrollState);
-
- // Update on resize
- const resizeObserver = new ResizeObserver(updateScrollState);
- resizeObserver.observe(wrapper);
-
- // Check periodically for dynamic content
- const interval = setInterval(updateScrollState, 100);
-
- return () => {
- wrapper.removeEventListener('scroll', updateScrollState);
- resizeObserver.disconnect();
- clearInterval(interval);
- };
- }, []);
-
- return (
-
- {children}
-
- );
-}
-
diff --git a/apps/web-docs/src/components/home/CTA.module.css b/apps/web-docs/src/components/home/CTA.module.css
deleted file mode 100644
index 0c8fb32d..00000000
--- a/apps/web-docs/src/components/home/CTA.module.css
+++ /dev/null
@@ -1,99 +0,0 @@
-.section {
- padding: 8rem 2rem;
- background: #000000;
- position: relative;
- perspective: 1200px;
-}
-
-.container {
- max-width: 1200px;
- margin: 0 auto;
-}
-
-.content {
- text-align: center;
- max-width: 700px;
- margin: 0 auto;
- transform-style: preserve-3d;
-}
-
-.title {
- font-size: clamp(2.5rem, 5vw, 4rem);
- font-weight: 800;
- letter-spacing: -0.05em;
- color: var(--foreground);
- margin-bottom: 1.5rem;
- line-height: 1.1;
- transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
-}
-
-.content:hover .title {
- transform: translateZ(20px) rotateX(-5deg);
-}
-
-.desc {
- font-size: clamp(1.1rem, 2vw, 1.25rem);
- color: var(--foreground-muted);
- line-height: 1.7;
- margin-bottom: 3rem;
- transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
-}
-
-.content:hover .desc {
- transform: translateZ(10px);
-}
-
-.button {
- display: inline-flex;
- align-items: center;
- gap: 0.5rem;
- background: var(--foreground);
- color: var(--background);
- padding: 1rem 2.5rem;
- border-radius: 9999px;
- font-weight: 600;
- font-size: 1rem;
- transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- border: none;
- cursor: pointer;
- transform-style: preserve-3d;
- position: relative;
- overflow: hidden;
-}
-
-.button::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- width: 0;
- height: 0;
- border-radius: 50%;
- background: rgba(0, 0, 0, 0.2);
- transform: translate(-50%, -50%);
- transition: width 0.6s, height 0.6s;
-}
-
-.button:hover::before {
- width: 300px;
- height: 300px;
-}
-
-.button:hover {
- transform: translateY(-4px) rotateX(5deg) rotateY(-5deg) scale(1.05) translateZ(10px);
- box-shadow: 0 20px 40px rgba(255, 255, 255, 0.3),
- 0 0 0 1px rgba(255, 255, 255, 0.1);
-}
-
-.button:active {
- transform: translateY(-2px) rotateX(2deg) rotateY(-2deg) scale(1.02) translateZ(5px);
-}
-
-@media (max-width: 768px) {
- .section {
- padding: 4rem 1rem;
- }
-}
-
diff --git a/apps/web-docs/src/components/home/CTA.tsx b/apps/web-docs/src/components/home/CTA.tsx
deleted file mode 100644
index 5791b6ac..00000000
--- a/apps/web-docs/src/components/home/CTA.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-'use client';
-
-import { useTranslations } from 'next-intl';
-import { useRouter } from 'next/navigation';
-import { ArrowRight } from 'lucide-react';
-import styles from './CTA.module.css';
-
-export default function CTA() {
- const t = useTranslations('HomePage');
- const router = useRouter();
-
- return (
-
-
-
-
{t('ctaTitle')}
-
{t('ctaDesc')}
-
-
-
-
- );
-}
-
diff --git a/apps/web-docs/src/components/home/Features.module.css b/apps/web-docs/src/components/home/Features.module.css
deleted file mode 100644
index 5d863cfd..00000000
--- a/apps/web-docs/src/components/home/Features.module.css
+++ /dev/null
@@ -1,126 +0,0 @@
-.section {
- padding: 8rem 2rem;
- background: #000000;
- position: relative;
- perspective: 1200px;
-}
-
-.container {
- max-width: 1200px;
- margin: 0 auto;
-}
-
-.title {
- font-size: clamp(2rem, 4vw, 3rem);
- font-weight: 800;
- text-align: center;
- margin-bottom: 4rem;
- color: var(--foreground);
- letter-spacing: -0.05em;
-}
-
-.grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
- gap: 2rem;
-}
-
-.card {
- background: #0a0a0a;
- border: 1px solid var(--border);
- padding: 2.5rem;
- border-radius: 16px;
- transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
- position: relative;
- overflow: hidden;
-}
-
-.card::before {
- content: '';
- position: absolute;
- top: -50%;
- left: -50%;
- width: 200%;
- height: 200%;
- background: radial-gradient(circle, rgba(255, 255, 255, 0.05) 0%, transparent 70%);
- opacity: 0;
- transition: opacity 0.4s ease;
- pointer-events: none;
-}
-
-.card:hover::before {
- opacity: 1;
- animation: cardGlow 2s ease-in-out infinite;
-}
-
-.card:hover {
- border-color: var(--foreground-muted);
- transform: translateY(-8px) rotateX(5deg) translateZ(20px);
- box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5),
- 0 0 0 1px rgba(255, 255, 255, 0.1);
-}
-
-.iconWrapper {
- margin-bottom: 1.5rem;
- color: var(--foreground);
- transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
- display: inline-block;
-}
-
-.card:hover .iconWrapper {
- transform: translateZ(15px) rotateY(15deg) scale(1.1);
-}
-
-.cardTitle {
- font-size: 1.5rem;
- font-weight: 700;
- margin-bottom: 1rem;
- color: var(--foreground);
- transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
-}
-
-.card:hover .cardTitle {
- transform: translateZ(10px);
-}
-
-.cardDesc {
- color: var(--foreground-muted);
- line-height: 1.7;
- font-size: 1rem;
- transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
-}
-
-.card:hover .cardDesc {
- transform: translateZ(5px);
-}
-
-@keyframes cardGlow {
- 0%, 100% {
- opacity: 0.3;
- transform: scale(1);
- }
- 50% {
- opacity: 0.6;
- transform: scale(1.1);
- }
-}
-
-@media (max-width: 768px) {
- .section {
- padding: 4rem 1rem;
- }
-
- .grid {
- grid-template-columns: 1fr;
- gap: 1.5rem;
- }
-
- .card {
- padding: 2rem;
- }
-}
-
diff --git a/apps/web-docs/src/components/home/Features.tsx b/apps/web-docs/src/components/home/Features.tsx
deleted file mode 100644
index 8b598963..00000000
--- a/apps/web-docs/src/components/home/Features.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import { useTranslations } from 'next-intl';
-import { ShieldCheck, Zap, Cpu } from 'lucide-react';
-import styles from './Features.module.css';
-
-export default function Features() {
- const t = useTranslations('Features');
-
- const features = [
- {
- icon: ,
- title: t('xmssTitle'),
- desc: t('xmssDesc')
- },
- {
- icon: ,
- title: t('mldsaTitle'),
- desc: t('mldsaDesc')
- },
- {
- icon: ,
- title: t('evmTitle'),
- desc: t('evmDesc')
- }
- ];
-
- return (
-
-
-
{t('title')}
-
- {features.map((feature, index) => (
-
-
{feature.icon}
-
{feature.title}
-
{feature.desc}
-
- ))}
-
-
-
- );
-}
-
diff --git a/apps/web-docs/src/components/home/Hero.module.css b/apps/web-docs/src/components/home/Hero.module.css
deleted file mode 100644
index a4cb518e..00000000
--- a/apps/web-docs/src/components/home/Hero.module.css
+++ /dev/null
@@ -1,224 +0,0 @@
-.hero {
- position: relative;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- min-height: 90vh;
- text-align: center;
- padding: 4rem 1rem;
- overflow: hidden;
- background: #000000;
- perspective: 1000px;
-}
-
-.heroContent {
- position: relative;
- z-index: 10;
- max-width: 900px;
- transform-style: preserve-3d;
- pointer-events: auto;
-}
-
-.sceneLoader {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 1;
- background: transparent;
-}
-
-.title {
- font-size: clamp(3rem, 8vw, 6rem);
- font-weight: 800;
- letter-spacing: -0.05em;
- margin-bottom: 2rem;
- background: linear-gradient(to bottom right, #ffffff, #888888);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- line-height: 1.1;
- animation: fadeInUp3D 0.8s ease-out;
- transform-style: preserve-3d;
- transition: transform 0.1s ease-out;
-}
-
-.subtitle {
- font-size: clamp(1.1rem, 2.5vw, 1.75rem);
- color: var(--foreground-muted);
- max-width: 700px;
- margin: 0 auto 3rem;
- line-height: 1.6;
- animation: fadeInUp 0.8s ease-out 0.2s both;
-}
-
-.ctaGroup {
- display: flex;
- gap: 1rem;
- justify-content: center;
- flex-wrap: wrap;
- animation: fadeInUp 0.8s ease-out 0.4s both;
- position: relative;
- z-index: 10;
- pointer-events: auto;
-}
-
-.ctaButton {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- background: var(--foreground);
- color: var(--background);
- padding: 1rem 2.5rem;
- border-radius: 9999px;
- font-weight: 600;
- font-size: 1rem;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- border: none;
- cursor: pointer;
- transform-style: preserve-3d;
- position: relative;
- overflow: hidden;
- pointer-events: auto;
- z-index: 10;
-}
-
-.ctaButton::before {
- content: '';
- position: absolute;
- top: 50%;
- left: 50%;
- width: 0;
- height: 0;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- transform: translate(-50%, -50%);
- transition: width 0.6s, height 0.6s;
-}
-
-.ctaButton:hover::before {
- width: 300px;
- height: 300px;
-}
-
-.ctaButton:hover {
- transform: translateY(-4px) rotateX(5deg) rotateY(-5deg) scale(1.05);
- box-shadow: 0 20px 40px rgba(255, 255, 255, 0.3),
- 0 0 0 1px rgba(255, 255, 255, 0.1);
-}
-
-.ctaButton:active {
- transform: translateY(-2px) rotateX(2deg) rotateY(-2deg) scale(1.02);
-}
-
-.secondaryButton {
- background: transparent;
- color: var(--foreground);
- padding: 1rem 2.5rem;
- border-radius: 9999px;
- font-weight: 600;
- font-size: 1rem;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- border: 1px solid var(--border);
- cursor: pointer;
- transform-style: preserve-3d;
- position: relative;
- overflow: hidden;
- pointer-events: auto;
- z-index: 10;
-}
-
-.secondaryButton::before {
- content: '';
- position: absolute;
- top: 0;
- left: -100%;
- width: 100%;
- height: 100%;
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
- transition: left 0.5s;
-}
-
-.secondaryButton:hover::before {
- left: 100%;
-}
-
-.secondaryButton:hover {
- background: rgba(255, 255, 255, 0.05);
- border-color: var(--foreground-muted);
- transform: translateY(-2px) rotateX(3deg) rotateY(3deg);
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
-}
-
-.gradientOrb {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%) translateZ(0);
- width: 600px;
- height: 600px;
- background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
- border-radius: 50%;
- filter: blur(80px);
- animation: pulse3D 8s ease-in-out infinite, rotate3D 20s linear infinite;
- z-index: 2;
- transform-style: preserve-3d;
- pointer-events: none;
-}
-
-@keyframes fadeInUp3D {
- from {
- opacity: 0;
- transform: translateY(30px) translateZ(-100px) rotateX(-10deg);
- }
- to {
- opacity: 1;
- transform: translateY(0) translateZ(0) rotateX(0deg);
- }
-}
-
-@keyframes pulse3D {
- 0%, 100% {
- opacity: 0.3;
- transform: translate(-50%, -50%) translateZ(0) scale(1) rotateY(0deg);
- }
- 50% {
- opacity: 0.5;
- transform: translate(-50%, -50%) translateZ(50px) scale(1.1) rotateY(180deg);
- }
-}
-
-@keyframes rotate3D {
- 0% {
- transform: translate(-50%, -50%) translateZ(0) rotateX(0deg) rotateY(0deg);
- }
- 100% {
- transform: translate(-50%, -50%) translateZ(0) rotateX(360deg) rotateY(360deg);
- }
-}
-
-@media (max-width: 768px) {
- .hero {
- min-height: 80vh;
- padding: 2rem 1rem;
- }
-
- .ctaGroup {
- flex-direction: column;
- width: 100%;
- }
-
- .ctaButton,
- .secondaryButton {
- width: 100%;
- justify-content: center;
- }
-
- .gradientOrb {
- width: 400px;
- height: 400px;
- }
-}
-
diff --git a/apps/web-docs/src/components/home/Hero.tsx b/apps/web-docs/src/components/home/Hero.tsx
deleted file mode 100644
index 683abfbe..00000000
--- a/apps/web-docs/src/components/home/Hero.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-'use client';
-
-import { useTranslations, useLocale } from 'next-intl';
-import { useRouter } from 'next/navigation';
-import { useEffect, useRef, useState, Suspense } from 'react';
-import { ArrowRight } from 'lucide-react';
-import dynamic from 'next/dynamic';
-import styles from './Hero.module.css';
-
-const Scene3D = dynamic(() => import('./Scene3D'), {
- ssr: false,
- loading: () =>
-});
-
-export default function Hero() {
- const t = useTranslations('HomePage');
- const locale = useLocale();
- const router = useRouter();
- const heroRef = useRef(null);
- const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
-
- const handleGetStarted = () => {
- router.push(`/${locale}/docs`);
- };
-
- useEffect(() => {
- const handleMouseMove = (e: MouseEvent) => {
- if (!heroRef.current) return;
-
- const rect = heroRef.current.getBoundingClientRect();
- const x = ((e.clientX - rect.left) / rect.width - 0.5) * 20;
- const y = ((e.clientY - rect.top) / rect.height - 0.5) * 20;
-
- setMousePosition({ x, y });
- };
-
- window.addEventListener('mousemove', handleMouseMove);
- return () => window.removeEventListener('mousemove', handleMouseMove);
- }, []);
-
- return (
-
- }>
-
-
-
-
{t('title')}
-
{t('subtitle')}
-
-
-
-
-
-
-
- );
-}
-
diff --git a/apps/web-docs/src/components/home/LargeText.module.css b/apps/web-docs/src/components/home/LargeText.module.css
deleted file mode 100644
index f41596d2..00000000
--- a/apps/web-docs/src/components/home/LargeText.module.css
+++ /dev/null
@@ -1,69 +0,0 @@
-.section {
- padding: 8rem 2rem;
- background: #000000;
- overflow: hidden;
- perspective: 1500px;
- position: relative;
-}
-
-.container {
- max-width: 1400px;
- margin: 0 auto;
-}
-
-.textWrapper {
- display: flex;
- flex-direction: column;
- gap: 1rem;
- text-align: center;
- transform-style: preserve-3d;
-}
-
-.text {
- font-size: clamp(4rem, 15vw, 12rem);
- font-weight: 900;
- letter-spacing: -0.05em;
- color: var(--foreground);
- line-height: 0.9;
- opacity: 0.1;
- transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
- transform-style: preserve-3d;
- transform: translateZ(0);
- position: relative;
- cursor: pointer;
-}
-
-.text:hover {
- opacity: 0.7;
- color: #ffffff;
- transform: translateZ(50px) rotateX(5deg) scale(1.05);
- text-shadow: 0 0 40px rgba(255, 255, 255, 0.3);
-}
-
-.text:nth-child(1) {
- animation: float3D 6s ease-in-out infinite;
-}
-
-.text:nth-child(2) {
- animation: float3D 6s ease-in-out infinite 0.3s;
-}
-
-@keyframes float3D {
- 0%, 100% {
- transform: translateZ(0) rotateX(0deg);
- }
- 50% {
- transform: translateZ(20px) rotateX(2deg);
- }
-}
-
-@media (max-width: 768px) {
- .section {
- padding: 4rem 1rem;
- }
-
- .textWrapper {
- gap: 0.5rem;
- }
-}
-
diff --git a/apps/web-docs/src/components/home/LargeText.tsx b/apps/web-docs/src/components/home/LargeText.tsx
deleted file mode 100644
index 43094773..00000000
--- a/apps/web-docs/src/components/home/LargeText.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useTranslations } from 'next-intl';
-import styles from './LargeText.module.css';
-
-export default function LargeText() {
- const t = useTranslations('HomePage');
-
- return (
-
-
-
- {t('largeText')}
- {t('largeText2')}
-
-
-
- );
-}
-
diff --git a/apps/web-docs/src/components/home/Products.module.css b/apps/web-docs/src/components/home/Products.module.css
deleted file mode 100644
index 3409ca50..00000000
--- a/apps/web-docs/src/components/home/Products.module.css
+++ /dev/null
@@ -1,202 +0,0 @@
-.section {
- padding: 8rem 2rem;
- background: #000000;
- position: relative;
- perspective: 1200px;
-}
-
-.container {
- max-width: 1400px;
- margin: 0 auto;
-}
-
-.header {
- text-align: center;
- margin-bottom: 5rem;
-}
-
-.tagline {
- display: inline-block;
- font-size: 0.875rem;
- color: var(--foreground-muted);
- letter-spacing: 0.1em;
- text-transform: uppercase;
- margin-bottom: 1rem;
- font-weight: 500;
-}
-
-.title {
- font-size: clamp(2.5rem, 5vw, 4rem);
- font-weight: 800;
- letter-spacing: -0.05em;
- color: var(--foreground);
- line-height: 1.1;
-}
-
-.grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
- gap: 2rem;
-}
-
-.card {
- background: #0a0a0a;
- border: 1px solid var(--border);
- padding: 2.5rem;
- border-radius: 16px;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- display: flex;
- flex-direction: column;
- height: 100%;
- position: relative;
- transform-style: preserve-3d;
- will-change: transform;
- overflow: hidden;
-}
-
-.card::before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, transparent 50%);
- opacity: 0;
- transition: opacity 0.3s ease;
- pointer-events: none;
- z-index: 1;
-}
-
-.card:hover::before {
- opacity: 1;
-}
-
-.card:hover {
- border-color: var(--foreground-muted);
- box-shadow: 0 30px 60px rgba(0, 0, 0, 0.5),
- 0 0 0 1px rgba(255, 255, 255, 0.1);
-}
-
-.cardGlow {
- position: absolute;
- top: -50%;
- left: -50%;
- width: 200%;
- height: 200%;
- background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%);
- opacity: 0;
- transition: opacity 0.3s ease;
- pointer-events: none;
- z-index: 0;
-}
-
-.card:hover .cardGlow {
- opacity: 1;
- animation: glowPulse 2s ease-in-out infinite;
-}
-
-.cardHeader {
- margin-bottom: 1.5rem;
- position: relative;
- z-index: 2;
-}
-
-.iconWrapper {
- margin-bottom: 1.5rem;
- color: var(--foreground);
- display: inline-block;
- transition: transform 0.3s ease;
- transform-style: preserve-3d;
-}
-
-.card:hover .iconWrapper {
- transform: translateZ(10px) rotateY(5deg);
-}
-
-.cardTitle {
- font-size: 1.5rem;
- font-weight: 700;
- margin-bottom: 0;
- color: var(--foreground);
- line-height: 1.2;
- position: relative;
- z-index: 2;
- transition: transform 0.3s ease;
- transform-style: preserve-3d;
-}
-
-.card:hover .cardTitle {
- transform: translateZ(5px);
-}
-
-.cardDesc {
- color: var(--foreground-muted);
- line-height: 1.7;
- font-size: 1rem;
- margin-bottom: 2rem;
- flex-grow: 1;
- position: relative;
- z-index: 2;
- transition: transform 0.3s ease;
- transform-style: preserve-3d;
-}
-
-.card:hover .cardDesc {
- transform: translateZ(3px);
-}
-
-.cardButton {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- background: transparent;
- color: var(--foreground);
- padding: 0.75rem 0;
- border: none;
- font-weight: 600;
- font-size: 1rem;
- cursor: pointer;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- width: fit-content;
- position: relative;
- z-index: 2;
- transform-style: preserve-3d;
-}
-
-.cardButton:hover {
- color: var(--foreground-muted);
- gap: 0.75rem;
- transform: translateX(5px) translateZ(10px);
-}
-
-@keyframes glowPulse {
- 0%, 100% {
- opacity: 0.3;
- transform: scale(1);
- }
- 50% {
- opacity: 0.6;
- transform: scale(1.1);
- }
-}
-
-@media (max-width: 768px) {
- .section {
- padding: 4rem 1rem;
- }
-
- .header {
- margin-bottom: 3rem;
- }
-
- .grid {
- grid-template-columns: 1fr;
- gap: 1.5rem;
- }
-
- .card {
- padding: 2rem;
- }
-}
-
diff --git a/apps/web-docs/src/components/home/Products.tsx b/apps/web-docs/src/components/home/Products.tsx
deleted file mode 100644
index 7fdc4efb..00000000
--- a/apps/web-docs/src/components/home/Products.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-'use client';
-
-import { useTranslations } from 'next-intl';
-import { useRouter } from 'next/navigation';
-import { useState, useRef } from 'react';
-import { Network, Code, BookOpen, ArrowRight } from 'lucide-react';
-import styles from './Products.module.css';
-
-interface CardProps {
- product: {
- icon: React.ReactNode;
- title: string;
- desc: string;
- cta: string;
- href: string;
- };
- index: number;
-}
-
-function ProductCard({ product, index }: CardProps) {
- const router = useRouter();
- const cardRef = useRef(null);
- const [transform, setTransform] = useState('');
-
- const handleMouseMove = (e: React.MouseEvent) => {
- if (!cardRef.current) return;
-
- const card = cardRef.current;
- const rect = card.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
-
- const centerX = rect.width / 2;
- const centerY = rect.height / 2;
-
- const rotateX = (y - centerY) / 10;
- const rotateY = (centerX - x) / 10;
-
- setTransform(`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateZ(20px)`);
- };
-
- const handleMouseLeave = () => {
- setTransform('perspective(1000px) rotateX(0) rotateY(0) translateZ(0)');
- };
-
- return (
-
-
-
-
{product.icon}
-
{product.title}
-
-
{product.desc}
-
-
- );
-}
-
-export default function Products() {
- const t = useTranslations('HomePage');
-
- const products = [
- {
- icon: ,
- title: t('networkTitle'),
- desc: t('networkDesc'),
- cta: t('networkCta'),
- href: '/docs'
- },
- {
- icon: ,
- title: t('apiTitle'),
- desc: t('apiDesc'),
- cta: t('apiCta'),
- href: '/docs'
- },
- {
- icon: ,
- title: t('docsTitle'),
- desc: t('docsDesc'),
- cta: t('docsCta'),
- href: '/docs'
- }
- ];
-
- return (
-
-
-
- [{t('tagline')}]
-
{t('productsTitle')}
-
-
- {products.map((product, index) => (
-
- ))}
-
-
-
- );
-}
-
diff --git a/apps/web-docs/src/components/home/Scene3D.tsx b/apps/web-docs/src/components/home/Scene3D.tsx
deleted file mode 100644
index bd632ab2..00000000
--- a/apps/web-docs/src/components/home/Scene3D.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-'use client';
-
-import { useRef, useMemo, useState, useEffect } from 'react';
-import { Canvas, useFrame, useThree } from '@react-three/fiber';
-import * as THREE from 'three';
-
-const generateParticleData = (count: number) => {
- const positions = new Float32Array(count * 3);
- const particlesData = [];
-
- for (let i = 0; i < count; i++) {
- const time = Math.random() * 100;
- const factor = 20 + Math.random() * 100;
- const speed = 0.01 + Math.random() / 200;
- const x = (Math.random() - 0.5) * 2000;
- const y = (Math.random() - 0.5) * 2000;
- const z = (Math.random() - 0.5) * 2000;
-
- positions[i * 3] = x;
- positions[i * 3 + 1] = y;
- positions[i * 3 + 2] = z;
-
- particlesData.push({ time, factor, speed, x, y, z });
- }
-
- const geometry = new THREE.BufferGeometry();
- geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
-
- return { particlesData, geometry };
-};
-
-function Particles({ count = 3000 }: { count?: number }) {
- const mesh = useRef(null);
- const light = useRef(null);
-
- const { particlesData, geometry } = useMemo(() => generateParticleData(count), [count]);
-
- useFrame(() => {
- if (!mesh.current || !mesh.current.geometry) return;
-
- const positions = mesh.current.geometry.attributes.position as THREE.BufferAttribute;
-
- particlesData.forEach((particle, i) => {
- const t = (particle.time += particle.speed);
-
- positions.setXYZ(
- i,
- particle.x + Math.cos((t / 10) * particle.factor) + (Math.sin(t * 1) * particle.factor) / 10,
- particle.y + Math.sin((t / 10) * particle.factor) + (Math.cos(t * 2) * particle.factor) / 10,
- particle.z + Math.cos((t / 10) * particle.factor) + (Math.sin(t * 3) * particle.factor) / 10
- );
- });
-
- positions.needsUpdate = true;
- if (mesh.current) {
- mesh.current.rotation.x += 0.0005;
- mesh.current.rotation.y += 0.001;
- }
- });
-
- return (
- <>
-
-
-
-
- >
- );
-}
-
-function MeshGeometry() {
- const meshRef = useRef(null);
- const meshRef2 = useRef(null);
-
- useFrame((state, delta) => {
- if (meshRef.current) {
- meshRef.current.rotation.x += delta * 0.1;
- meshRef.current.rotation.y += delta * 0.15;
-
- const time = state.clock.getElapsedTime();
- meshRef.current.position.y = Math.sin(time * 0.5) * 0.5;
- }
-
- if (meshRef2.current) {
- meshRef2.current.rotation.x -= delta * 0.08;
- meshRef2.current.rotation.y -= delta * 0.12;
-
- const time = state.clock.getElapsedTime();
- meshRef2.current.position.y = Math.cos(time * 0.5) * 0.5;
- }
- });
-
- return (
- <>
-
-
-
-
-
-
-
-
- >
- );
-}
-
-function CameraController() {
- const { camera } = useThree();
- const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
-
- useEffect(() => {
- const handleMouseMove = (e: MouseEvent) => {
- const x = (e.clientX / window.innerWidth) * 2 - 1;
- const y = -(e.clientY / window.innerHeight) * 2 + 1;
- setMousePos({ x, y });
- };
-
- window.addEventListener('mousemove', handleMouseMove);
- return () => window.removeEventListener('mousemove', handleMouseMove);
- }, []);
-
- useFrame(() => {
- const targetX = mousePos.x * 2;
- const targetY = mousePos.y * 2;
- camera.position.set(
- camera.position.x + (targetX - camera.position.x) * 0.05,
- camera.position.y + (targetY - camera.position.y) * 0.05,
- camera.position.z
- );
- camera.lookAt(0, 0, 0);
- });
-
- return null;
-}
-
-export default function Scene3D() {
- return (
-
- );
-}
-
diff --git a/apps/web-docs/src/components/layout/DocsLayout.module.css b/apps/web-docs/src/components/layout/DocsLayout.module.css
deleted file mode 100644
index c35671ac..00000000
--- a/apps/web-docs/src/components/layout/DocsLayout.module.css
+++ /dev/null
@@ -1,807 +0,0 @@
-.container {
- padding: 3.5rem 2rem 4rem;
- max-width: 1400px;
- margin: 0 auto;
- display: grid;
- grid-template-columns: minmax(220px, 1fr) minmax(0, 3fr) minmax(200px, 1fr);
- gap: 2.75rem;
- position: relative;
-}
-
-.sidebarToggle {
- display: none;
- position: fixed;
- top: 5.5rem;
- left: 1rem;
- z-index: 101;
- background: rgba(0, 0, 0, 0.9);
- backdrop-filter: blur(10px);
- border: 1px solid var(--border);
- border-radius: 0.5rem;
- padding: 0.75rem;
- color: var(--foreground);
- cursor: pointer;
- transition: all 0.2s;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
-}
-
-.sidebarToggle:hover {
- background: rgba(0, 0, 0, 0.9);
-}
-
-.sidebarOverlay {
- display: none;
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0, 0, 0, 0.5);
- z-index: 101;
- backdrop-filter: blur(2px);
-}
-
-.sidebar {
- position: sticky;
- top: 5rem;
- align-self: flex-start;
- font-size: 0.85rem;
- transition: transform 0.3s ease-in-out;
-}
-
-.sidebarHeader {
- display: none;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 1rem;
- padding-bottom: 1rem;
- border-bottom: 1px solid var(--border);
-}
-
-.sidebarClose {
- display: none;
- background: none;
- border: none;
- color: var(--foreground);
- cursor: pointer;
- padding: 0.5rem;
- border-radius: 0.25rem;
- transition: background 0.2s;
-}
-
-.sidebarClose:hover {
- background: rgba(255, 255, 255, 0.1);
-}
-
-.sidebarLabel {
- font-weight: 600;
- margin-bottom: 0.75rem;
- text-transform: uppercase;
- font-size: 0.75rem;
- color: var(--foreground-muted);
- letter-spacing: 0.08em;
-}
-
-.sidebarNav {
- display: flex;
- flex-direction: column;
- gap: 1.25rem;
-}
-
-.sidebarGroupTitle {
- font-weight: 600;
- margin-bottom: 0.5rem;
- font-size: 0.8rem;
- color: var(--foreground);
-}
-
-.sidebarGroupItems {
- display: flex;
- flex-direction: column;
- gap: 0.3rem;
-}
-
-.sidebarLink {
- color: var(--foreground-muted);
- text-decoration: none;
- font-size: 0.85rem;
- transition: color 0.15s ease;
-}
-
-.sidebarLink:hover {
- color: var(--foreground);
-}
-
-.sidebarLinkActive {
- color: var(--foreground);
- font-weight: 600;
-}
-
-.content {
- min-width: 0;
- max-width: 760px;
-}
-
-.contentBody {
- width: 100%;
- min-width: 0;
- overflow-x: hidden;
-}
-
-.contentHeaderRow {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- gap: 1.5rem;
- margin-bottom: 1.5rem;
- flex-wrap: wrap;
-}
-
-.header {
- flex: 1;
-}
-
-.headerCategory {
- font-size: 0.8rem;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--foreground-muted);
- margin-bottom: 0.25rem;
-}
-
-.headerTitle {
- font-size: 1.7rem;
- font-weight: 600;
- letter-spacing: -0.03em;
- margin-bottom: 0.5rem;
- color: var(--foreground);
-}
-
-
-.content :global(h1),
-.content :global(h2),
-.content :global(h3),
-.content :global(h4),
-.content :global(h5),
-.content :global(h6) {
- font-weight: 600;
- letter-spacing: -0.03em;
- color: var(--foreground);
- scroll-margin-top: 5rem;
-}
-
-.content :global(h1) {
- font-size: 2.25rem;
- line-height: 1.2;
- margin-top: 0;
- margin-bottom: 1.5rem;
- font-weight: 700;
-}
-
-.content :global(h2) {
- font-size: 1.75rem;
- line-height: 1.3;
- margin-top: 2.5rem;
- margin-bottom: 1rem;
- font-weight: 600;
-}
-
-.content :global(h3) {
- font-size: 1.35rem;
- line-height: 1.4;
- margin-top: 2rem;
- margin-bottom: 0.75rem;
- font-weight: 600;
-}
-
-.content :global(h4) {
- font-size: 1.15rem;
- line-height: 1.4;
- margin-top: 1.5rem;
- margin-bottom: 0.75rem;
- font-weight: 600;
-}
-
-.content :global(h5) {
- font-size: 1rem;
- line-height: 1.5;
- margin-top: 1.25rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
-}
-
-.content :global(h6) {
- font-size: 0.95rem;
- line-height: 1.5;
- margin-top: 1rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.05em;
-}
-
-.content :global(p) {
- font-size: 0.95rem;
- line-height: 1.7;
- color: var(--foreground);
- margin-bottom: 0.9rem;
-}
-
-.content :global(ul) {
- padding-left: 1.3rem;
- margin-bottom: 1rem;
-}
-
-.content :global(li) {
- font-size: 0.95rem;
- line-height: 1.7;
- color: var(--foreground);
-}
-
-.content :global(code),
-.content :global(pre) {
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
- monospace;
- font-size: 0.85rem;
- color: var(--foreground);
-}
-
-.content :global(code:not(pre code)) {
- background: rgba(255, 255, 255, 0.1);
- padding: 0.2rem 0.4rem;
- border-radius: 0.25rem;
- font-size: 0.85em;
-}
-
-.content :global(pre) {
- padding: 0.9rem 1rem;
- border-radius: 0.5rem;
- border: 1px solid var(--border);
- background: rgba(255, 255, 255, 0.02);
- overflow: auto;
-}
-
-/* Table styling */
-.content :global(table) {
- width: 100%;
- max-width: 100%;
- border-collapse: collapse;
- margin: 2rem 0;
- font-size: 0.9rem;
- display: table !important;
- table-layout: auto;
- border: 1px solid var(--border);
- border-radius: 0.5rem;
- overflow: hidden;
- background: #0a0a0a;
-}
-
-/* Table inside wrapper should not have margin */
-.content :global(.table-wrapper > table),
-.content :global(div:has(> table) > table) {
- margin: 0;
- min-width: 100%;
-}
-
-.content :global(thead) {
- background: rgba(255, 255, 255, 0.05);
-}
-
-.content :global(thead tr) {
- border-bottom: 2px solid var(--border);
-}
-
-.content :global(th) {
- padding: 0.75rem 1rem;
- text-align: left;
- font-weight: 600;
- color: var(--foreground);
- border-right: 1px solid var(--border);
- font-size: 0.85rem;
- text-transform: none;
- letter-spacing: normal;
- white-space: nowrap;
-}
-
-.content :global(th:first-child) {
- min-width: 180px;
- white-space: normal;
-}
-
-.content :global(th:last-child) {
- border-right: none;
-}
-
-.content :global(tbody) {
- background: transparent;
-}
-
-.content :global(td) {
- padding: 0.75rem 1rem;
- color: var(--foreground);
- border-bottom: 1px solid var(--border);
- border-right: 1px solid var(--border);
- vertical-align: top;
- word-wrap: break-word;
-}
-
-.content :global(td:last-child) {
- border-right: none;
-}
-
-.content :global(tbody tr:last-child td) {
- border-bottom: none;
-}
-
-.content :global(tr:hover) {
- background: rgba(255, 255, 255, 0.02);
-}
-
-.content :global(tbody tr:nth-child(even)) {
- background: rgba(255, 255, 255, 0.01);
-}
-
-/* Table wrapper for horizontal scroll on mobile */
-.content :global(.table-wrapper),
-.content :global(div:has(> table)) {
- overflow-x: auto;
- overflow-y: hidden;
- margin: 2rem 0;
- -webkit-overflow-scrolling: touch;
- width: 100%;
- max-width: 100%;
- position: relative;
- scrollbar-width: thin;
- scrollbar-color: rgba(255, 255, 255, 0.2) transparent;
-}
-
-/* Ensure table wrapper doesn't overflow on mobile */
-.content :global(div:has(> table)) {
- min-width: 0;
-}
-
-/* Custom scrollbar styling for webkit browsers */
-.content :global(.table-wrapper::-webkit-scrollbar),
-.content :global(div:has(> table)::-webkit-scrollbar) {
- height: 8px;
-}
-
-.content :global(.table-wrapper::-webkit-scrollbar-track),
-.content :global(div:has(> table)::-webkit-scrollbar-track) {
- background: rgba(255, 255, 255, 0.05);
- border-radius: 4px;
-}
-
-.content :global(.table-wrapper::-webkit-scrollbar-thumb),
-.content :global(div:has(> table)::-webkit-scrollbar-thumb) {
- background: rgba(255, 255, 255, 0.2);
- border-radius: 4px;
-}
-
-.content :global(.table-wrapper::-webkit-scrollbar-thumb:hover),
-.content :global(div:has(> table)::-webkit-scrollbar-thumb:hover) {
- background: rgba(255, 255, 255, 0.3);
-}
-
-/* Visual indicators for scrollable content - gradient shadows */
-.content :global(.table-wrapper::before),
-.content :global(div:has(> table)::before) {
- content: '';
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- width: 30px;
- background: linear-gradient(to right, rgba(0, 0, 0, 0.4), transparent);
- pointer-events: none;
- opacity: 0;
- transition: opacity 0.3s ease;
- z-index: 1;
-}
-
-.content :global(.table-wrapper::after),
-.content :global(div:has(> table)::after) {
- content: '';
- position: absolute;
- right: 0;
- top: 0;
- bottom: 0;
- width: 30px;
- background: linear-gradient(to left, rgba(0, 0, 0, 0.4), transparent);
- pointer-events: none;
- opacity: 0;
- transition: opacity 0.3s ease;
- z-index: 1;
-}
-
-/* Show gradient when scrollable */
-.content :global(.table-wrapper.scrollable-left::before),
-.content :global(div:has(> table).scrollable-left::before) {
- opacity: 1;
-}
-
-.content :global(.table-wrapper.scrollable-right::after),
-.content :global(div:has(> table).scrollable-right::after) {
- opacity: 1;
-}
-
-/* Tablet responsive styles */
-@media (max-width: 1024px) {
- .container {
- grid-template-columns: minmax(220px, 1fr) minmax(0, 3fr);
- gap: 2rem;
- }
-}
-
-/* Mobile responsive styles */
-@media (max-width: 768px) {
- .container {
- padding: 1rem;
- grid-template-columns: 1fr;
- gap: 0;
- }
-
- .sidebarToggle {
- display: block;
- }
-
- .sidebarOverlay {
- display: block;
- }
-
- .sidebar {
- position: fixed;
- top: 0;
- left: 0;
- height: 100vh;
- width: 280px;
- max-width: 85vw;
- background: rgba(0, 0, 0, 0.95);
- backdrop-filter: blur(20px);
- z-index: 102;
- padding: 1.5rem;
- overflow-y: auto;
- transform: translateX(-100%);
- border-right: 1px solid var(--border);
- }
-
- .sidebarOpen {
- transform: translateX(0);
- }
-
- .sidebarHeader {
- display: flex;
- }
-
- .sidebarClose {
- display: block;
- }
-
- .content {
- width: 100%;
- max-width: 100%;
- padding: 0;
- }
-
- .contentHeaderRow {
- flex-direction: column;
- gap: 1rem;
- margin-bottom: 1.5rem;
- }
-
- .header {
- width: 100%;
- }
-
- .headerTitle {
- font-size: 1.25rem;
- line-height: 1.3;
- margin-bottom: 0.5rem;
- }
-
- .content :global(h1) {
- font-size: 1.75rem;
- line-height: 1.25;
- margin-top: 0;
- margin-bottom: 1rem;
- font-weight: 700;
- }
-
- .content :global(h2) {
- font-size: 1.375rem;
- line-height: 1.3;
- margin-top: 1.75rem;
- margin-bottom: 0.75rem;
- font-weight: 600;
- }
-
- .content :global(h3) {
- font-size: 1.125rem;
- line-height: 1.35;
- margin-top: 1.5rem;
- margin-bottom: 0.625rem;
- font-weight: 600;
- }
-
- .content :global(h4) {
- font-size: 1rem;
- line-height: 1.4;
- margin-top: 1.25rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- }
-
- .content :global(h5) {
- font-size: 0.9375rem;
- line-height: 1.45;
- margin-top: 1rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- }
-
- .content :global(h6) {
- font-size: 0.875rem;
- line-height: 1.5;
- margin-top: 0.875rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.05em;
- }
-
- .content :global(p) {
- font-size: 0.9375rem;
- line-height: 1.65;
- margin-bottom: 0.875rem;
- }
-
- .content :global(ul),
- .content :global(ol) {
- margin-bottom: 0.875rem;
- }
-
- .content :global(li) {
- font-size: 0.9375rem;
- line-height: 1.65;
- margin-bottom: 0.25rem;
- }
-
- /* Scroll margin for mobile */
- .content :global(h1),
- .content :global(h2),
- .content :global(h3),
- .content :global(h4),
- .content :global(h5),
- .content :global(h6) {
- scroll-margin-top: 4rem;
- }
-
- .content :global(p) {
- font-size: 0.9375rem;
- line-height: 1.65;
- }
-
- /* Responsive table */
- .content :global(table) {
- min-width: 600px;
- font-size: 0.85rem;
- }
-
- .content :global(th),
- .content :global(td) {
- padding: 0.5rem 0.75rem;
- font-size: 0.8rem;
- }
-
- .content :global(th:first-child) {
- min-width: 120px;
- }
-
- /* Table wrapper responsive - prevent overflow */
- .content :global(.table-wrapper),
- .content :global(div:has(> table)) {
- width: 100%;
- max-width: 100%;
- overflow-x: auto;
- overflow-y: hidden;
- -webkit-overflow-scrolling: touch;
- margin-left: 0;
- margin-right: 0;
- padding-left: 0;
- padding-right: 0;
- position: relative;
- }
-
- /* Ensure table doesn't break out of wrapper */
- .content :global(div:has(> table) > table),
- .content :global(.table-wrapper > table) {
- margin-left: 0;
- margin-right: 0;
- }
-
- /* Enhanced scrollbar on mobile */
- .content :global(.table-wrapper::-webkit-scrollbar),
- .content :global(div:has(> table)::-webkit-scrollbar) {
- height: 6px;
- }
-
- .content :global(.table-wrapper::-webkit-scrollbar-thumb),
- .content :global(div:has(> table)::-webkit-scrollbar-thumb) {
- background: rgba(255, 255, 255, 0.3);
- }
-}
-
-@media (max-width: 480px) {
- .container {
- padding: 0.75rem;
- }
-
- .sidebarToggle {
- top: 5rem;
- left: 0.75rem;
- padding: 0.5rem;
- }
-
- .sidebar {
- width: 100%;
- max-width: 100%;
- }
-
- .headerTitle {
- font-size: 1.125rem;
- line-height: 1.35;
- margin-bottom: 0.5rem;
- }
-
- .content :global(h1) {
- font-size: 1.5rem;
- line-height: 1.3;
- margin-top: 0;
- margin-bottom: 0.875rem;
- font-weight: 700;
- }
-
- .content :global(h2) {
- font-size: 1.25rem;
- line-height: 1.35;
- margin-top: 1.5rem;
- margin-bottom: 0.625rem;
- font-weight: 600;
- }
-
- .content :global(h3) {
- font-size: 1.0625rem;
- line-height: 1.4;
- margin-top: 1.25rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- }
-
- .content :global(h4) {
- font-size: 0.9375rem;
- line-height: 1.45;
- margin-top: 1rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- }
-
- .content :global(h5) {
- font-size: 0.875rem;
- line-height: 1.5;
- margin-top: 0.875rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- }
-
- .content :global(h6) {
- font-size: 0.8125rem;
- line-height: 1.5;
- margin-top: 0.75rem;
- margin-bottom: 0.5rem;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.05em;
- }
-
- /* Scroll margin for small mobile */
- .content :global(h1),
- .content :global(h2),
- .content :global(h3),
- .content :global(h4),
- .content :global(h5),
- .content :global(h6) {
- scroll-margin-top: 3.5rem;
- }
-
- .content :global(p) {
- font-size: 0.9375rem;
- line-height: 1.65;
- margin-bottom: 0.875rem;
- }
-
- .content :global(ul),
- .content :global(ol) {
- margin-bottom: 0.875rem;
- }
-
- .content :global(li) {
- font-size: 0.9375rem;
- line-height: 1.65;
- }
-
- .content :global(code),
- .content :global(pre) {
- font-size: 0.8125rem;
- }
-
- /* Table wrapper responsive - prevent overflow on small mobile */
- .content :global(.table-wrapper),
- .content :global(div:has(> table)) {
- width: 100%;
- max-width: 100%;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- margin-left: 0;
- margin-right: 0;
- padding-left: 0;
- padding-right: 0;
- }
-}
-
-/* Extra small mobile devices */
-@media (max-width: 375px) {
- .container {
- padding: 0.5rem;
- }
-
- .content :global(h1) {
- font-size: 1.375rem;
- margin-bottom: 0.75rem;
- }
-
- .content :global(h2) {
- font-size: 1.125rem;
- margin-top: 1.25rem;
- margin-bottom: 0.5rem;
- }
-
- .content :global(h3) {
- font-size: 1rem;
- margin-top: 1rem;
- margin-bottom: 0.5rem;
- }
-
- .content :global(h4) {
- font-size: 0.9375rem;
- margin-top: 0.875rem;
- margin-bottom: 0.5rem;
- }
-
- .content :global(h5) {
- font-size: 0.875rem;
- margin-top: 0.75rem;
- }
-
- .content :global(h6) {
- font-size: 0.8125rem;
- margin-top: 0.75rem;
- }
-
- .headerTitle {
- font-size: 1rem;
- }
-
- /* Table wrapper responsive - prevent overflow on extra small mobile */
- .content :global(.table-wrapper),
- .content :global(div:has(> table)) {
- width: 100%;
- max-width: 100%;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- margin-left: 0;
- margin-right: 0;
- padding-left: 0;
- padding-right: 0;
- }
-}
-
diff --git a/apps/web-docs/src/components/layout/DocsLayout.tsx b/apps/web-docs/src/components/layout/DocsLayout.tsx
deleted file mode 100644
index 4c61649a..00000000
--- a/apps/web-docs/src/components/layout/DocsLayout.tsx
+++ /dev/null
@@ -1,150 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import Navbar from '@/components/layout/Navbar';
-import Footer from '@/components/layout/Footer';
-import type { DocsLocale, NavGroup } from '@/lib/docs-types';
-import Link from 'next/link';
-import { Menu, X } from 'lucide-react';
-import styles from './DocsLayout.module.css';
-import DocsSearch from '../docs/DocsSearch';
-import TableOfContents from '../docs/TableOfContents';
-
-type DocsLayoutProps = {
- locale: DocsLocale;
- currentSlug: string;
- children: React.ReactNode;
- docsNavigation: NavGroup[];
-};
-
-
-export default function DocsLayout({ locale, currentSlug, children, docsNavigation }: DocsLayoutProps) {
- const [isSidebarOpen, setIsSidebarOpen] = useState(false);
-
- const currentGroup = docsNavigation.find(group =>
- group.items.some(item => item.slug === currentSlug)
- );
- const currentItem = currentGroup?.items.find(item => item.slug === currentSlug);
-
- const categoryLabel = currentGroup?.label[locale] ?? '';
- const pageTitle = currentItem?.label[locale] ?? '';
-
- const toggleSidebar = () => {
- setIsSidebarOpen(!isSidebarOpen);
- };
-
- const closeSidebar = () => {
- setIsSidebarOpen(false);
- };
-
- // Close sidebar when clicking outside on mobile
- useEffect(() => {
- const handleClickOutside = (event: MouseEvent) => {
- const target = event.target as HTMLElement;
- if (isSidebarOpen && !target.closest(`.${styles.sidebar}`) && !target.closest(`.${styles.sidebarToggle}`)) {
- closeSidebar();
- }
- };
-
- if (isSidebarOpen) {
- document.addEventListener('click', handleClickOutside);
- document.body.style.overflow = 'hidden';
- } else {
- document.body.style.overflow = '';
- }
-
- return () => {
- document.removeEventListener('click', handleClickOutside);
- document.body.style.overflow = '';
- };
- }, [isSidebarOpen]);
-
- return (
-
-
-
- {/* Sidebar toggle button for mobile */}
-
-
- {/* Overlay for mobile */}
- {isSidebarOpen && (
-
- )}
-
- {/* Sidebar trái: nav docs nhiều cấp */}
-
-
- {/* Nội dung MDX ở giữa */}
-
-
- {pageTitle && (
-
- {categoryLabel && {categoryLabel}
}
- {pageTitle}
-
- )}
-
-
-
-
- {children}
-
-
-
- {/* Table of Contents bên phải */}
-
-
-
-
- );
-}
-
diff --git a/apps/web-docs/src/components/layout/Footer.module.css b/apps/web-docs/src/components/layout/Footer.module.css
deleted file mode 100644
index bc1dd7a3..00000000
--- a/apps/web-docs/src/components/layout/Footer.module.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.footer {
- border-top: 1px solid var(--border);
- padding: 3rem 2rem;
- background: #000;
- text-align: center;
-}
-
-.text {
- color: var(--foreground-muted);
- font-size: 0.875rem;
- margin-bottom: 0.5rem;
-}
-
-.subtext {
- color: #555;
- font-size: 0.75rem;
-}
-
diff --git a/apps/web-docs/src/components/layout/Footer.tsx b/apps/web-docs/src/components/layout/Footer.tsx
deleted file mode 100644
index a58152f3..00000000
--- a/apps/web-docs/src/components/layout/Footer.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { useTranslations } from 'next-intl';
-import styles from './Footer.module.css';
-
-export default function Footer() {
- const t = useTranslations('Footer');
-
- return (
-
- );
-}
-
diff --git a/apps/web-docs/src/components/layout/Navbar.module.css b/apps/web-docs/src/components/layout/Navbar.module.css
deleted file mode 100644
index 34dc8a72..00000000
--- a/apps/web-docs/src/components/layout/Navbar.module.css
+++ /dev/null
@@ -1,181 +0,0 @@
-.navbar {
- border-bottom: 1px solid var(--border);
- position: sticky;
- top: 0;
- background: rgba(0, 0, 0, 0.8);
- backdrop-filter: blur(10px);
- z-index: 100;
-}
-
-.navbarContainer {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 1.5rem 2rem;
- max-width: 100%;
-}
-
-.logo {
- font-weight: 700;
- font-size: 1.25rem;
- letter-spacing: -0.05em;
- z-index: 101;
- color: var(--foreground);
- text-decoration: none;
- transition: opacity 0.2s;
- cursor: pointer;
-}
-
-.logo:hover {
- opacity: 0.8;
-}
-
-.menuButton {
- display: none;
- background: none;
- border: none;
- color: var(--foreground);
- cursor: pointer;
- padding: 0.5rem;
- z-index: 101;
-}
-
-.menuButton:hover {
- opacity: 0.8;
-}
-
-.overlay {
- display: none;
-}
-
-.navLinks {
- display: flex;
- gap: 2rem;
- align-items: center;
-}
-
-.link {
- font-size: 0.9rem;
- color: var(--foreground-muted);
- transition: color 0.2s;
-}
-
-.link:hover {
- color: var(--foreground);
-}
-
-/* Mobile menu elements - hidden on desktop */
-.mobileMenuHeader,
-.mobileMenuTitle,
-.closeMenuButton {
- display: none;
-}
-
-/* Mobile styles */
-@media (max-width: 768px) {
- .navbarContainer {
- padding: 1rem 1.5rem;
- }
-
- .menuButton {
- display: block;
- }
-
- .navLinks {
- position: fixed;
- top: 0;
- right: 0;
- width: 100%;
- max-width: 300px;
- height: 100vh;
- background: rgba(0, 0, 0, 0.95);
- backdrop-filter: blur(20px);
- flex-direction: column;
- align-items: flex-start;
- padding: 1.5rem;
- padding-top: 5rem;
- gap: 1.5rem;
- transform: translateX(100%);
- transition: transform 0.3s ease-in-out;
- z-index: 102;
- border-left: 1px solid var(--border);
- overflow-y: auto;
- }
-
- .mobileMenuHeader {
- display: flex !important;
- justify-content: space-between;
- align-items: center;
- width: 100%;
- margin-bottom: 0.5rem;
- padding-bottom: 1rem;
- border-bottom: 1px solid var(--border);
- }
-
- .mobileMenuTitle {
- display: block !important;
- font-weight: 600;
- font-size: 1rem;
- color: var(--foreground);
- }
-
- .closeMenuButton {
- display: flex !important;
- background: none;
- border: none;
- color: var(--foreground);
- cursor: pointer;
- padding: 0.5rem;
- border-radius: 0.25rem;
- transition: background 0.2s;
- align-items: center;
- justify-content: center;
- }
-
- .closeMenuButton:hover {
- background: rgba(255, 255, 255, 0.1);
- }
-
- .overlay {
- display: block;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.5);
- z-index: 101;
- backdrop-filter: blur(2px);
- }
-
- .navLinksOpen {
- transform: translateX(0);
- }
-
- .link {
- font-size: 1rem;
- width: 100%;
- padding: 0.75rem 0;
- }
-}
-
-@media (max-width: 480px) {
- .navbarContainer {
- padding: 1rem;
- }
-
- .logo {
- font-size: 1.1rem;
- }
-
- .navLinks {
- max-width: 100%;
- padding: 1rem;
- padding-top: 4rem;
- }
-
- .mobileMenuHeader {
- padding-bottom: 0.75rem;
- }
-}
-
diff --git a/apps/web-docs/src/components/layout/Navbar.tsx b/apps/web-docs/src/components/layout/Navbar.tsx
deleted file mode 100644
index 7914cd2b..00000000
--- a/apps/web-docs/src/components/layout/Navbar.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { useTranslations, useLocale } from 'next-intl';
-import Link from 'next/link';
-import { Menu, X } from 'lucide-react';
-import LanguageSwitcher from '../common/LanguageSwitcher';
-import styles from './Navbar.module.css';
-
-export default function Navbar() {
- const [isMenuOpen, setIsMenuOpen] = useState(false);
- const locale = useLocale();
- const tHome = useTranslations('HomePage');
- const tDocs = useTranslations('Docs');
-
- const toggleMenu = () => {
- setIsMenuOpen(!isMenuOpen);
- };
-
- const closeMenu = () => {
- setIsMenuOpen(false);
- };
-
- return (
-
- );
-}
-
diff --git a/apps/web-docs/src/components/mdx/Mermaid.tsx b/apps/web-docs/src/components/mdx/Mermaid.tsx
deleted file mode 100644
index 6d3384eb..00000000
--- a/apps/web-docs/src/components/mdx/Mermaid.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-"use client";
-
-import React, { useEffect, useRef, useState } from "react";
-import mermaid from "mermaid";
-
-interface MermaidProps {
- chart: string;
-}
-
-const Mermaid: React.FC = ({ chart }) => {
- const ref = useRef(null);
- const [rendered, setRendered] = useState("");
-
- useEffect(() => {
- // Only verify we're on client side
- if (typeof window === "undefined") return;
-
- // Initialize mermaid
- mermaid.initialize({
- startOnLoad: false,
- theme: 'base',
- securityLevel: 'loose',
- fontFamily: 'inherit',
- });
-
- let isMounted = true;
-
- const renderChart = async () => {
- try {
- if (!ref.current) return;
-
- // Use a unique ID for each render to avoid conflicts
- const id = `mermaid-${Math.random().toString(36).substring(2, 9)}`;
-
- // Render the chart
- const { svg } = await mermaid.render(id, chart);
-
- if (isMounted) {
- setRendered(svg);
- }
- } catch (error) {
- console.error("Mermaid rendering failed:", error);
- // Show error in UI
- if (isMounted) {
- setRendered(`Failed to render diagram
`);
- }
- }
- };
-
- renderChart();
-
- return () => {
- isMounted = false;
- };
- }, [chart]);
-
- return (
-
- );
-};
-
-export default Mermaid;
diff --git a/apps/web-docs/src/docs/navigation.ts b/apps/web-docs/src/docs/navigation.ts
deleted file mode 100644
index c3bd5475..00000000
--- a/apps/web-docs/src/docs/navigation.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// EN: Type definitions for documentation navigation
-// VI: Định nghĩa types cho documentation navigation
-export type { DocsLocale, NavItem as DocsNavItem, NavGroup as DocsNavGroup } from '@/lib/docs-types';
-
-
-// EN: Dynamic navigation generation
-// VI: Tạo navigation động
-import { generateDocsNavigation } from '@/lib/docs-generator';
-
-// EN: Cache navigation to avoid regenerating on every request
-// VI: Cache navigation để tránh tạo lại với mỗi request
-let cachedNavigation: DocsNavGroup[] | null = null;
-
-export function getDocsNavigation(): DocsNavGroup[] {
- if (!cachedNavigation) {
- cachedNavigation = generateDocsNavigation();
- }
- return cachedNavigation;
-}
-
-// EN: Export cached navigation for backwards compatibility
-// VI: Export cached navigation cho tương thích ngược
-export const docsNavigation = getDocsNavigation();
-
-
-
diff --git a/apps/web-docs/src/docs/registry.ts b/apps/web-docs/src/docs/registry.ts
deleted file mode 100644
index 9f7ee307..00000000
--- a/apps/web-docs/src/docs/registry.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import type { DocsLocale, DEFAULT_DOC_SLUG } from '@/lib/docs-types';
-
-// EN: Server-side registry - DO NOT import into client components
-// VI: Registry phía server - KHÔNG import vào client components
-// This file should only be imported by Server Components
-
-// EN: Default doc slug to load when no slug is specified
-// VI: Slug mặc định khi không có slug được chỉ định
-export const defaultDocSlug = DEFAULT_DOC_SLUG;
-
-// EN: Note: getAvailableDocs is a server-side function that uses fs
-// VI: Lưu ý: getAvailableDocs là hàm server-side sử dụng fs
-// It should only be called from Server Components, not Client Components
diff --git a/apps/web-docs/src/i18n/request.ts b/apps/web-docs/src/i18n/request.ts
deleted file mode 100644
index 645fdaad..00000000
--- a/apps/web-docs/src/i18n/request.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { getRequestConfig } from 'next-intl/server';
-import { notFound } from 'next/navigation';
-
-const locales = ['en', 'vi'];
-
-const messageImports = {
- en: () => import('../messages/en.json'),
- vi: () => import('../messages/vi.json')
-} as const;
-
-export default getRequestConfig(async ({ requestLocale }) => {
- // requestLocale is a Promise in Next.js 16, need to await it
- const locale = await requestLocale;
-
- console.log('getRequestConfig called with requestLocale:', locale);
-
- // Validate that the incoming `locale` parameter is valid
- if (!locale || !locales.includes(locale as any)) {
- console.log('Invalid locale:', locale);
- notFound();
- }
-
- const messages = (await messageImports[locale as keyof typeof messageImports]()).default;
- console.log('Messages loaded for', locale);
- return {
- locale: locale as string,
- messages
- };
-});
diff --git a/apps/web-docs/src/lib/docs-generator.ts b/apps/web-docs/src/lib/docs-generator.ts
deleted file mode 100644
index 335430bc..00000000
--- a/apps/web-docs/src/lib/docs-generator.ts
+++ /dev/null
@@ -1,267 +0,0 @@
-// EN: Server-side utility to generate docs navigation from filesystem
-// VI: Utility phía server để tạo docs navigation từ filesystem
-
-import fs from 'fs';
-import path from 'path';
-import matter from 'gray-matter';
-import type { DocsLocale, NavItem, NavGroup } from './docs-types';
-
-interface DocFile {
- slug: string;
- title: string;
- category: string;
- filePath: string;
-}
-
-// EN: Get absolute path to docs directory
-// VI: Lấy đường dẫn tuyệt đối tới thư mục docs
-const DOCS_ROOT = path.join(process.cwd(), '../../docs');
-
-// EN: Category display order and labels
-// VI: Thứ tự hiển thị và nhãn cho các danh mục
-const CATEGORY_CONFIG: Record = {
- 'guides': { order: 1, label: { en: 'Guides', vi: 'Hướng dẫn' } },
- 'architecture': { order: 2, label: { en: 'Architecture', vi: 'Kiến trúc' } },
- 'skills': { order: 3, label: { en: 'Skills', vi: 'Kỹ năng' } },
- 'templates': { order: 4, label: { en: 'Templates', vi: 'Mẫu' } },
- 'api': { order: 5, label: { en: 'API Reference', vi: 'Tài liệu API' } },
- 'runbooks': { order: 6, label: { en: 'Runbooks', vi: 'Sổ tay vận hành' } },
- 'onboarding': { order: 7, label: { en: 'Onboarding', vi: 'Hướng dẫn mới' } },
-};
-
-// EN: Custom ordering for specific items within categories
-// VI: Thứ tự tùy chỉnh cho các items cụ thể trong mỗi category
-// Items not listed here will be sorted alphabetically after the ordered ones
-const ITEM_ORDER: Record = {
- 'guides': [
- 'getting-started', // EN: Always show getting started first / VI: Luôn hiển thị getting started đầu tiên
- 'local-development', // EN: Then local setup / VI: Sau đó là local setup
- 'development',
- 'local-deployment',
- 'deployment',
- 'kubernetes-local',
- 'mermaid',
- 'neon-database',
- 'observability',
- 'troubleshooting',
- 'iam-migration',
- ],
- 'architecture': [
- 'system-design', // EN: Overview first / VI: Overview trước
- 'microservices-communication',
- 'caching-architecture',
- 'data-consistency-patterns',
- 'event-driven-architecture',
- 'security-architecture',
- 'observability-architecture',
- ],
- 'skills': [
- 'project-rules', // EN: Project rules first / VI: Project rules trước
- 'comment-code', // EN: Then coding standards / VI: Sau đó là coding standards
- 'api-design', // EN: Then API design / VI: Sau đó là API design
- ],
-};
-
-
-/**
- * EN: Extract title from markdown file
- * VI: Trích xuất tiêu đề từ file markdown
- */
-function extractTitle(filePath: string): string {
- try {
- const content = fs.readFileSync(filePath, 'utf-8');
-
- // EN: Try to parse frontmatter first
- // VI: Thử parse frontmatter trước
- try {
- const { data } = matter(content);
- if (data.name) return data.name;
- if (data.title) return data.title;
- } catch (e) {
- // Ignore frontmatter parsing errors
- }
-
- // EN: Fallback to first H1 heading
- // VI: Fallback về H1 heading đầu tiên
- const h1Match = content.match(/^#\s+(.+)$/m);
- if (h1Match) {
- // EN: Remove bilingual suffix if exists (e.g., "Title / Tiêu đề" -> "Title")
- // VI: Xóa hậu tố bilingual nếu tồn tại
- const title = h1Match[1].split('/')[0].trim();
- return title;
- }
-
- // EN: Final fallback to filename
- // VI: Fallback cuối cùng là tên file
- const filename = path.basename(filePath, '.md');
- return filename
- .split('-')
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
- .join(' ');
- } catch (error) {
- // EN: If file read fails, use filename
- // VI: Nếu đọc file thất bại, dùng tên file
- const filename = path.basename(filePath, '.md');
- return filename
- .split('-')
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
- .join(' ');
- }
-}
-
-/**
- * EN: Scan docs directory for a specific locale
- * VI: Quét thư mục docs cho một locale cụ thể
- */
-function scanDocsForLocale(locale: DocsLocale): DocFile[] {
- const localeDir = path.join(DOCS_ROOT, locale);
- const docs: DocFile[] = [];
-
- if (!fs.existsSync(localeDir)) {
- return docs;
- }
-
- // EN: Read all category directories
- // VI: Đọc tất cả thư mục danh mục
- const categories = fs.readdirSync(localeDir, { withFileTypes: true })
- .filter(dirent => dirent.isDirectory())
- .map(dirent => dirent.name);
-
- for (const category of categories) {
- const categoryPath = path.join(localeDir, category);
- const files = fs.readdirSync(categoryPath)
- .filter(file => file.endsWith('.md') && file !== 'README.md');
-
- for (const file of files) {
- const filePath = path.join(categoryPath, file);
- const slug = `${category}/${path.basename(file, '.md')}`;
- const title = extractTitle(filePath);
-
- docs.push({
- slug,
- title,
- category,
- filePath
- });
- }
- }
-
- return docs;
-}
-
-/**
- * EN: Generate navigation structure with bilingual support
- * VI: Tạo cấu trúc navigation với hỗ trợ đa ngôn ngữ
- */
-export function generateDocsNavigation(): NavGroup[] {
- const enDocs = scanDocsForLocale('en');
- const viDocs = scanDocsForLocale('vi');
-
- // EN: Create a map of slug to titles in both languages
- // VI: Tạo map từ slug tới tiêu đề ở cả hai ngôn ngữ
- const docTitles = new Map();
-
- enDocs.forEach(doc => {
- docTitles.set(doc.slug, {
- en: doc.title,
- vi: doc.title // Default to EN if VI not found
- });
- });
-
- viDocs.forEach(doc => {
- const existing = docTitles.get(doc.slug);
- if (existing) {
- existing.vi = doc.title;
- } else {
- docTitles.set(doc.slug, { en: doc.title, vi: doc.title });
- }
- });
-
- // EN: Group by category
- // VI: Nhóm theo danh mục
- const groups = new Map();
-
- docTitles.forEach((labels, slug) => {
- const category = slug.split('/')[0];
- if (!groups.has(category)) {
- groups.set(category, []);
- }
-
- groups.get(category)!.push({
- id: slug.replace(/\//g, '-'),
- slug,
- label: labels
- });
- });
-
- // EN: Convert to NavGroup array with sorting
- // VI: Chuyển thành mảng NavGroup với sắp xếp
- const navGroups: NavGroup[] = [];
-
- groups.forEach((items, category) => {
- const config = CATEGORY_CONFIG[category] || {
- order: 999,
- label: { en: category.charAt(0).toUpperCase() + category.slice(1), vi: category.charAt(0).toUpperCase() + category.slice(1) }
- };
-
- // EN: Sort items by custom order first, then alphabetically
- // VI: Sắp xếp items theo thứ tự tùy chỉnh trước, sau đó theo alphabet
- const customOrder = ITEM_ORDER[category] || [];
-
- items.sort((a, b) => {
- const aSlugName = a.slug.split('/')[1]; // EN: Get filename from slug / VI: Lấy filename từ slug
- const bSlugName = b.slug.split('/')[1];
-
- const aIndex = customOrder.indexOf(aSlugName);
- const bIndex = customOrder.indexOf(bSlugName);
-
- // EN: If both have custom order, use that / VI: Nếu cả hai có thứ tự tùy chỉnh, dùng nó
- if (aIndex !== -1 && bIndex !== -1) {
- return aIndex - bIndex;
- }
-
- // EN: Items with custom order come first / VI: Items có thứ tự tùy chỉnh xuất hiện trước
- if (aIndex !== -1) return -1;
- if (bIndex !== -1) return 1;
-
- // EN: Otherwise sort alphabetically / VI: Ngược lại sắp xếp theo alphabet
- return a.label.en.localeCompare(b.label.en);
- });
-
-
- navGroups.push({
- id: category,
- label: config.label,
- items
- });
- });
-
- // EN: Sort groups by order
- // VI: Sắp xếp groups theo thứ tự
- navGroups.sort((a, b) => {
- const orderA = CATEGORY_CONFIG[a.id]?.order || 999;
- const orderB = CATEGORY_CONFIG[b.id]?.order || 999;
- return orderA - orderB;
- });
-
- return navGroups;
-}
-
-/**
- * EN: Get list of available doc slugs for a locale
- * VI: Lấy danh sách slugs có sẵn cho một locale
- */
-export function getAvailableDocs(locale: DocsLocale): string[] {
- const docs = scanDocsForLocale(locale);
- return docs.map(doc => doc.slug);
-}
-
-/**
- * EN: Check if a doc exists for a locale
- * VI: Kiểm tra xem một doc có tồn tại cho locale không
- */
-export function docExists(locale: DocsLocale, slug: string): boolean {
- const slugPath = slug.replace(/\//g, path.sep);
- const filePath = path.join(DOCS_ROOT, locale, `${slugPath}.md`);
- return fs.existsSync(filePath);
-}
diff --git a/apps/web-docs/src/lib/docs-types.ts b/apps/web-docs/src/lib/docs-types.ts
deleted file mode 100644
index 6af25b59..00000000
--- a/apps/web-docs/src/lib/docs-types.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-// EN: Shared types and constants for docs (safe for client components)
-// VI: Types và constants chung cho docs (an toàn cho client components)
-
-export type DocsLocale = 'en' | 'vi';
-
-export interface NavItem {
- id: string;
- slug: string;
- label: { en: string; vi: string };
-}
-
-export interface NavGroup {
- id: string;
- label: { en: string; vi: string };
- items: NavItem[];
-}
-
-// EN: Default doc slug to load when no slug is specified
-// VI: Slug mặc định khi không có slug được chỉ định
-export const DEFAULT_DOC_SLUG = 'guides/getting-started';
diff --git a/apps/web-docs/src/mdx-components.tsx b/apps/web-docs/src/mdx-components.tsx
deleted file mode 100644
index a543d92f..00000000
--- a/apps/web-docs/src/mdx-components.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-// import type { MDXComponents } from 'mdx/types';
-import React, { ComponentType } from 'react';
-
-export type MDXComponents = {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- [key: string]: ComponentType;
-};
-import TableWrapper from '@/components/docs/TableWrapper';
-import Mermaid from '@/components/mdx/Mermaid';
-
-// Helper function to extract text from React children
-function getTextFromChildren(children: React.ReactNode): string {
- if (typeof children === 'string') {
- return children;
- }
- if (typeof children === 'number') {
- return children.toString();
- }
- if (Array.isArray(children)) {
- return children.map(getTextFromChildren).join('');
- }
- if (children && typeof children === 'object' && 'props' in children) {
- return getTextFromChildren((children as { props: { children: React.ReactNode } }).props.children);
- }
- return '';
-}
-
-// Helper function to generate slug from text
-function generateSlug(text: string): string {
- return text
- .toLowerCase()
- .replace(/[^\w\s-]/g, '') // Remove special characters
- .replace(/\s+/g, '-') // Replace spaces with hyphens
- .replace(/-+/g, '-') // Replace multiple hyphens with single hyphen
- .trim();
-}
-
-// Helper function to create heading component with ID
-function createHeadingComponent(level: 1 | 2 | 3 | 4 | 5 | 6) {
- return function Heading({ children, ...props }: React.HTMLAttributes) {
- const text = getTextFromChildren(children);
- const id = text ? generateSlug(text) : '';
-
- const headingProps = { id, ...props };
-
- switch (level) {
- case 1:
- return {children}
;
- case 2:
- return {children}
;
- case 3:
- return {children}
;
- case 4:
- return {children}
;
- case 5:
- return {children}
;
- case 6:
- return {children}
;
- default:
- return {children}
;
- }
- };
-}
-
-export function useMDXComponents(components: MDXComponents): MDXComponents {
- return {
- // Custom heading components with auto-generated IDs
- h1: createHeadingComponent(1),
- h2: createHeadingComponent(2),
- h3: createHeadingComponent(3),
- h4: createHeadingComponent(4),
- h5: createHeadingComponent(5),
- // Custom table component with wrapper for responsive scrolling
- table: ({ children, ...props }: React.HTMLAttributes) => (
-
-
-
- ),
- // Custom pre component to handle mermaid diagrams
- pre: ({ children, ...props }: React.HTMLAttributes) => {
- // Check if children is a code element
- const codeElement = React.Children.toArray(children)[0];
- if (
- React.isValidElement(codeElement) &&
- (codeElement as React.ReactElement<{ className?: string }>).props.className?.includes('language-mermaid')
- ) {
- // Extract text content from the code element
- const chart = getTextFromChildren((codeElement as React.ReactElement<{ children?: React.ReactNode }>).props.children);
- return ;
- }
-
- // Default pre rendering
- return {children};
- },
- // Ensure other components work
- ...components,
- };
-}
-
diff --git a/apps/web-docs/src/messages/en.json b/apps/web-docs/src/messages/en.json
deleted file mode 100644
index f6348c7d..00000000
--- a/apps/web-docs/src/messages/en.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "HomePage": {
- "title": "Goodgo Documentation",
- "subtitle": "Post-Quantum Security. EVM Compatibility. Web3-Ready.",
- "getStarted": "Get Started",
- "learnMore": "Learn More",
- "tagline": "Products",
- "productsTitle": "Blockchain for the quantum era",
- "networkTitle": "Goodgo Documentation",
- "networkDesc": "A post-quantum secure, EVM-compatible blockchain designed for long-term security and Web3 readiness.",
- "networkCta": "Explore network",
- "apiTitle": "Developer API",
- "apiDesc": "Build dApps with post-quantum security. Seamlessly integrate with existing Ethereum tooling and infrastructure.",
- "apiCta": "Start building",
- "docsTitle": "Documentation",
- "docsDesc": "Learn how to integrate Goodgo into your applications. Explore guides covering architecture, security, and common use cases.",
- "docsCta": "Read docs",
- "largeText": "Secure",
- "largeText2": "The Future",
- "ctaTitle": "Build on Goodgo",
- "ctaDesc": "Join the post-quantum blockchain revolution. Start building secure, future-proof applications today.",
- "ctaButton": "Get started"
- },
- "Features": {
- "title": "Why Goodgo?",
- "xmssTitle": "XMSS (Stateful)",
- "xmssDesc": "Offers robust security but limits the number of signatures per wallet.",
- "mldsaTitle": "ML-DSA (Stateless)",
- "mldsaDesc": "Allows for an unlimited number of signatures, providing greater flexibility.",
- "evmTitle": "EVM Compatibility",
- "evmDesc": "Seamlessly integrate with existing Ethereum tools and infrastructure."
- },
- "Docs": {
- "title": "Goodgo Documentation",
- "intro": "This page gives you a high-level overview of how Goodgo works and how to start integrating it.",
- "sections": {
- "arch": {
- "title": "Architecture Overview",
- "desc1": "Goodgo is designed as a post-quantum secure, EVM-compatible network that focuses on long-term security.",
- "desc2": "The protocol separates execution, consensus and data availability layers to enable scalability and flexibility."
- },
- "security": {
- "title": "Post-Quantum Security",
- "desc1": "Wallets and validator keys can use post-quantum schemes like XMSS and ML-DSA to resist quantum attacks.",
- "desc2": "Application developers can gradually migrate critical signing flows to post-quantum primitives without breaking existing UX."
- },
- "evm": {
- "title": "EVM & Web3 Tooling",
- "desc1": "Goodgo keeps compatibility with the Ethereum JSON-RPC and tooling ecosystem so existing dApps can be ported easily.",
- "desc2": "Most common frameworks (Hardhat, Foundry, wagmi, etc.) can connect to Goodgo with minimal configuration changes."
- }
- }
- },
- "Footer": {
- "copyright": "© 2025 Goodgo Documentation"
- }
-}
\ No newline at end of file
diff --git a/apps/web-docs/src/messages/vi.json b/apps/web-docs/src/messages/vi.json
deleted file mode 100644
index 90accd4d..00000000
--- a/apps/web-docs/src/messages/vi.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "HomePage": {
- "title": "Mạng Lưới Goodgo",
- "subtitle": "Bảo mật Hậu Lượng Tử. Tương thích EVM. Sẵn sàng cho Web3.",
- "getStarted": "Bắt đầu ngay",
- "learnMore": "Tìm hiểu thêm",
- "tagline": "Sản phẩm",
- "productsTitle": "Blockchain cho kỷ nguyên lượng tử",
- "networkTitle": "Goodgo Documentation",
- "networkDesc": "Một blockchain bảo mật hậu lượng tử, tương thích EVM được thiết kế cho bảo mật dài hạn và sẵn sàng cho Web3.",
- "networkCta": "Khám phá mạng lưới",
- "apiTitle": "API Nhà phát triển",
- "apiDesc": "Xây dựng dApp với bảo mật hậu lượng tử. Tích hợp liền mạch với các công cụ và cơ sở hạ tầng Ethereum hiện có.",
- "apiCta": "Bắt đầu xây dựng",
- "docsTitle": "Tài liệu",
- "docsDesc": "Tìm hiểu cách tích hợp Goodgo vào ứng dụng của bạn. Khám phá các hướng dẫn về kiến trúc, bảo mật và các trường hợp sử dụng phổ biến.",
- "docsCta": "Đọc tài liệu",
- "largeText": "Bảo vệ",
- "largeText2": "Tương lai",
- "ctaTitle": "Xây dựng trên Goodgo",
- "ctaDesc": "Tham gia cuộc cách mạng blockchain hậu lượng tử. Bắt đầu xây dựng các ứng dụng an toàn, sẵn sàng cho tương lai ngay hôm nay.",
- "ctaButton": "Bắt đầu"
- },
- "Features": {
- "title": "Tại sao chọn Goodgo?",
- "xmssTitle": "XMSS (Có trạng thái)",
- "xmssDesc": "Cung cấp bảo mật mạnh mẽ nhưng giới hạn số lượng chữ ký mỗi ví.",
- "mldsaTitle": "ML-DSA (Không trạng thái)",
- "mldsaDesc": "Cho phép số lượng chữ ký không giới hạn, mang lại sự linh hoạt cao hơn.",
- "evmTitle": "Tương thích EVM",
- "evmDesc": "Tích hợp liền mạch với các công cụ và cơ sở hạ tầng Ethereum hiện có."
- },
- "Docs": {
- "title": "Tài liệu Goodgo Documentation",
- "intro": "Trang này cung cấp cái nhìn tổng quan ở mức cao về cách Goodgo hoạt động và cách bắt đầu tích hợp.",
- "sections": {
- "arch": {
- "title": "Tổng quan kiến trúc",
- "desc1": "Goodgo được thiết kế như một mạng lưới bảo mật hậu lượng tử, tương thích EVM và tập trung vào bảo mật dài hạn.",
- "desc2": "Giao thức tách biệt các lớp thực thi, đồng thuận và khả dụng dữ liệu để đạt được khả năng mở rộng và linh hoạt."
- },
- "security": {
- "title": "Bảo mật hậu lượng tử",
- "desc1": "Ví và khóa validator có thể sử dụng các sơ đồ chữ ký hậu lượng tử như XMSS và ML-DSA để chống lại tấn công lượng tử.",
- "desc2": "Nhà phát triển có thể dần dần chuyển các luồng ký quan trọng sang các nguyên thủy hậu lượng tử mà không phá vỡ trải nghiệm người dùng hiện tại."
- },
- "evm": {
- "title": "EVM & Hệ sinh thái Web3",
- "desc1": "Goodgo giữ tương thích với JSON-RPC và hệ sinh thái công cụ Ethereum nên dApp hiện tại có thể được port dễ dàng.",
- "desc2": "Hầu hết framework phổ biến (Hardhat, Foundry, wagmi, v.v.) có thể kết nối với Goodgo chỉ với vài thay đổi cấu hình."
- }
- }
- },
- "Footer": {
- "copyright": "© 2025 Goodgo Documentation"
- }
-}
\ No newline at end of file
diff --git a/apps/web-docs/src/proxy.ts b/apps/web-docs/src/proxy.ts
deleted file mode 100644
index f0261115..00000000
--- a/apps/web-docs/src/proxy.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import createMiddleware from 'next-intl/middleware';
-
-export default createMiddleware({
- // A list of all locales that are supported
- locales: ['en', 'vi'],
-
- // Used when no locale matches
- defaultLocale: 'en'
-});
-
-export const config = {
- // Match only internationalized pathnames
- matcher: [
- // Match all pathnames except for
- // - … if they start with `/api`, `/_next` or `/_vercel`
- // - … the ones containing a dot (e.g. `favicon.ico`)
- '/((?!api|_next|_vercel|.*\\..*).*)'
- ]
-};
-
diff --git a/apps/web-docs/src/types/mdx.d.ts b/apps/web-docs/src/types/mdx.d.ts
deleted file mode 100644
index 5fde675a..00000000
--- a/apps/web-docs/src/types/mdx.d.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-declare module "*.md" {
- import type { ComponentType } from 'react';
- const Component: ComponentType;
- export default Component;
-}
diff --git a/apps/web-docs/tsconfig.json b/apps/web-docs/tsconfig.json
deleted file mode 100644
index cf9c65d3..00000000
--- a/apps/web-docs/tsconfig.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2017",
- "lib": ["dom", "dom.iterable", "esnext"],
- "allowJs": true,
- "skipLibCheck": true,
- "strict": true,
- "noEmit": true,
- "esModuleInterop": true,
- "module": "esnext",
- "moduleResolution": "bundler",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "jsx": "react-jsx",
- "incremental": true,
- "plugins": [
- {
- "name": "next"
- }
- ],
- "paths": {
- "@/*": ["./src/*"]
- }
- },
- "include": [
- "next-env.d.ts",
- "**/*.ts",
- "**/*.tsx",
- ".next/types/**/*.ts",
- ".next/dev/types/**/*.ts",
- "**/*.mts"
- ],
- "exclude": ["node_modules"]
-}
diff --git a/apps/web-docs/vercel.json b/apps/web-docs/vercel.json
deleted file mode 100644
index bb775f49..00000000
--- a/apps/web-docs/vercel.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "buildCommand": "npm run build",
- "devCommand": "npm run dev",
- "installCommand": "npm install",
- "framework": "nextjs"
-}
-