style(web): refine aesthetic to archival precision, remove generic gradients and glowing blobs
CI / typecheck + tests (push) Canceled after 0s
CI / build + deploy (Nix) — api (push) Canceled after 0s
CI / build + deploy (Nix) — mcp (push) Canceled after 0s
CI / build + deploy (Nix) — worker (push) Canceled after 0s
CI / build + deploy (web) (push) Canceled after 0s

This commit is contained in:
asepharyana
2026-08-22 15:12:00 +07:00
parent 011571e12d
commit 067115ba13
15 changed files with 604 additions and 745 deletions
+38 -47
View File
@@ -5,18 +5,12 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import {
Search,
FileText,
Plus,
Compass,
Sparkles,
ArrowRight,
BookOpen,
Flag,
Layers,
SunMoon,
Command,
Sparkles,
X,
Flame,
} from "lucide-react";
interface Hit {
@@ -49,7 +43,7 @@ export default function CommandMenu() {
{
id: "create-doc",
title: "Create New Document",
subtitle: "Write technical doc, writeup, or research note",
subtitle: "Write technical doc, writeup, or note",
icon: Plus,
href: "/create",
},
@@ -69,14 +63,14 @@ export default function CommandMenu() {
},
{
id: "browse-writeups",
title: "CTF & Challenge Writeups",
title: "CTF & Writeups Directory",
subtitle: "Pwn, Reverse, Web, Crypto writeups and solutions",
icon: Flag,
href: "/writeups",
},
{
id: "browse-research",
title: "Deep-Dive Research",
title: "Research & Benchmarks",
subtitle: "Architecture notes, benchmarks, experiments",
icon: Sparkles,
href: "/research",
@@ -160,11 +154,11 @@ export default function CommandMenu() {
<button
onClick={() => setOpen(true)}
type="button"
className="hidden md:flex items-center gap-2.5 px-3 py-1.5 bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] text-xs text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-lg transition-all shadow-xs cursor-pointer group"
className="hidden md:flex items-center gap-2.5 px-3 py-1.5 bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--text-muted)] text-xs text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md transition-all shadow-xs cursor-pointer group"
>
<Search className="w-3.5 h-3.5 text-[var(--text-dim)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors" />
<span className="font-sans">Search knowledge...</span>
<kbd className="ml-2 font-mono bg-[var(--bg-surface)] px-1.5 py-0.5 rounded border border-[var(--border-color)] text-[10px] text-[var(--text-dim)] shadow-xs">
<Search className="w-3.5 h-3.5 text-[var(--text-dim)] group-hover:text-[var(--text-primary)] transition-colors" />
<span className="font-sans">Search...</span>
<kbd className="ml-2 font-mono bg-[var(--bg-surface)] px-1.5 py-0.2 rounded border border-[var(--border-color)] text-[10px] text-[var(--text-muted)]">
⌘K
</kbd>
</button>
@@ -175,31 +169,31 @@ export default function CommandMenu() {
<div className="fixed inset-0 z-50 flex items-start justify-center pt-16 sm:pt-24 px-4">
{/* Backdrop */}
<div
className="fixed inset-0 bg-slate-950/60 dark:bg-black/80 backdrop-blur-md transition-opacity"
className="fixed inset-0 bg-black/60 backdrop-blur-xs transition-opacity"
onClick={() => setOpen(false)}
/>
{/* Omnisearch Modal */}
<div className="relative w-full max-w-2xl bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-2xl shadow-2xl overflow-hidden animate-fade-in ring-1 ring-white/10">
{/* Modal */}
<div className="relative w-full max-w-2xl bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-xl shadow-2xl overflow-hidden animate-fade-in">
{/* Search input bar */}
<div className="flex items-center gap-3 px-4 sm:px-5 py-4 border-b border-[var(--border-color)] bg-[var(--bg-elevated)]/70">
<Search className="w-5 h-5 text-[var(--brand)] dark:text-[var(--accent)] shrink-0" />
<div className="flex items-center gap-3 px-4 sm:px-5 py-3.5 border-b border-[var(--border-color)] bg-[var(--bg-elevated)]">
<Search className="w-4 h-4 text-[var(--text-muted)] shrink-0" />
<input
ref={inputRef}
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={handleKeyNavigation}
placeholder="Search documents, writeups, concepts, or commands..."
className="w-full bg-transparent text-[var(--text-primary)] placeholder-[var(--text-dim)] text-sm sm:text-base focus:outline-none"
placeholder="Search documents, writeups, concepts, or tools..."
className="w-full bg-transparent text-[var(--text-primary)] placeholder-[var(--text-dim)] text-sm focus:outline-none"
/>
{loading && (
<div className="w-4 h-4 border-2 border-[var(--brand)] border-t-transparent rounded-full animate-spin shrink-0" />
<div className="w-4 h-4 border-2 border-[var(--text-muted)] border-t-transparent rounded-full animate-spin shrink-0" />
)}
<button
onClick={() => setOpen(false)}
type="button"
className="text-[11px] font-mono text-[var(--text-dim)] hover:text-[var(--text-primary)] bg-[var(--bg-surface)] px-2 py-0.5 rounded border border-[var(--border-color)]"
className="text-[10px] font-mono text-[var(--text-dim)] hover:text-[var(--text-primary)] bg-[var(--bg-surface)] px-1.5 py-0.5 rounded border border-[var(--border-color)]"
>
ESC
</button>
@@ -208,9 +202,9 @@ export default function CommandMenu() {
{/* Results / Quick Actions List */}
<div className="max-h-[22rem] overflow-y-auto p-2">
{query.trim() === "" ? (
<div className="p-2 space-y-1">
<div className="px-3 py-1.5 text-[10px] font-mono uppercase tracking-wider text-[var(--text-dim)]">
Quick Shortcuts & Actions
<div className="p-1 space-y-0.5">
<div className="px-2.5 py-1 text-[10px] font-mono uppercase tracking-wider text-[var(--text-muted)]">
Quick Shortcuts
</div>
{quickActions.map((action, idx) => {
const Icon = action.icon;
@@ -220,18 +214,18 @@ export default function CommandMenu() {
key={action.id}
onClick={() => action.href && handleNavigate(action.href)}
onMouseEnter={() => setSelectedIndex(idx)}
className={`flex items-center justify-between gap-3 px-3 py-2.5 rounded-xl cursor-pointer transition-all ${
className={`flex items-center justify-between gap-3 px-3 py-2 rounded-lg cursor-pointer transition-colors ${
isSelected
? "bg-[var(--brand)]/15 border border-[var(--brand)]/40 text-[var(--text-primary)] shadow-xs"
: "hover:bg-[var(--bg-elevated)] text-[var(--text-secondary)] border border-transparent"
? "bg-[var(--bg-elevated)] text-[var(--text-primary)]"
: "hover:bg-[var(--bg-elevated)] text-[var(--text-secondary)]"
}`}
>
<div className="flex items-center gap-3">
<div className={`p-2 rounded-lg ${isSelected ? "bg-[var(--brand)] text-white" : "bg-[var(--bg-elevated)] text-[var(--text-muted)]"}`}>
<Icon className="w-4 h-4" />
<div className="flex items-center gap-2.5">
<div className={`p-1.5 rounded ${isSelected ? "bg-[var(--brand)] text-[var(--brand-fg)]" : "bg-[var(--bg-elevated)] text-[var(--text-muted)]"}`}>
<Icon className="w-3.5 h-3.5" />
</div>
<div>
<div className="text-xs sm:text-sm font-semibold text-[var(--text-primary)]">
<div className="text-xs font-semibold text-[var(--text-primary)]">
{action.title}
</div>
<div className="text-[11px] text-[var(--text-muted)]">
@@ -239,17 +233,14 @@ export default function CommandMenu() {
</div>
</div>
</div>
<ArrowRight className={`w-3.5 h-3.5 text-[var(--text-dim)] ${isSelected ? "text-[var(--brand)] translate-x-0.5" : ""} transition-all`} />
<ArrowRight className={`w-3.5 h-3.5 text-[var(--text-dim)] ${isSelected ? "text-[var(--text-primary)]" : ""}`} />
</div>
);
})}
</div>
) : results.length === 0 && !loading ? (
<div className="px-4 py-12 text-center text-xs text-[var(--text-muted)] space-y-2">
<div className="px-4 py-10 text-center text-xs text-[var(--text-muted)] space-y-1">
<p>No knowledge base items matched &quot;{query}&quot;</p>
<p className="text-[11px] text-[var(--text-dim)]">
Try searching with partial words or check semantic search
</p>
</div>
) : (
<ul className="space-y-1 p-1">
@@ -260,24 +251,24 @@ export default function CommandMenu() {
key={hit.slug}
onClick={() => handleNavigate(`/${hit.slug}`)}
onMouseEnter={() => setSelectedIndex(index)}
className={`flex items-start justify-between gap-3 px-3 py-2.5 rounded-xl cursor-pointer transition-all ${
className={`flex items-start justify-between gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-colors ${
isSelected
? "bg-[var(--brand)]/15 border border-[var(--brand)]/40 text-[var(--text-primary)] shadow-xs"
? "bg-[var(--bg-elevated)] text-[var(--text-primary)] border border-[var(--border-color)]"
: "hover:bg-[var(--bg-elevated)] text-[var(--text-secondary)] border border-transparent"
}`}
>
<div className="min-w-0 flex-1">
<div className="flex items-center gap-2 mb-0.5">
<span className="text-[10px] font-mono px-2 py-0.5 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded text-[var(--brand)] dark:text-[var(--accent)] uppercase font-semibold">
<span className="text-[10px] font-mono px-1.5 py-0.2 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded text-[var(--text-muted)] uppercase font-semibold">
{hit.section}
</span>
<span className={`text-xs sm:text-sm font-semibold ${isSelected ? "text-[var(--brand)] dark:text-[var(--accent)]" : "text-[var(--text-primary)]"} truncate`}>
<span className={`text-xs sm:text-sm font-semibold ${isSelected ? "text-[var(--text-primary)]" : "text-[var(--text-primary)]"} truncate`}>
{hit.title}
</span>
</div>
{hit.snippet && (
<p
className="text-xs text-[var(--text-muted)] line-clamp-1 mt-1 [&>mark]:bg-[var(--brand)]/30 [&>mark]:text-[var(--text-primary)] [&>mark]:rounded [&>mark]:px-0.5"
className="text-xs text-[var(--text-muted)] line-clamp-1 mt-1 [&>mark]:bg-[var(--bg-elevated-hover)] [&>mark]:text-[var(--text-primary)] [&>mark]:font-semibold"
dangerouslySetInnerHTML={{ __html: hit.snippet }}
/>
)}
@@ -293,15 +284,15 @@ export default function CommandMenu() {
</div>
{/* Footer */}
<div className="flex items-center justify-between px-4 py-2.5 border-t border-[var(--border-color)] bg-[var(--bg-elevated)]/60 text-[11px] text-[var(--text-dim)]">
<div className="flex items-center justify-between px-4 py-2 border-t border-[var(--border-color)] bg-[var(--bg-elevated)] text-[10px] text-[var(--text-muted)] font-mono">
<div className="flex items-center gap-2">
<span>Navigate: <kbd className="bg-[var(--bg-surface)] px-1 py-0.5 rounded border border-[var(--border-color)]">↑↓</kbd></span>
<span>Select: <kbd className="bg-[var(--bg-surface)] px-1 py-0.5 rounded border border-[var(--border-color)]">↵</kbd></span>
<span>[ ↑↓ NAVIGATE ]</span>
<span>[ ↵ SELECT ]</span>
</div>
<Link
href={`/search?q=${encodeURIComponent(query)}`}
onClick={() => setOpen(false)}
className="text-[var(--brand)] dark:text-[var(--accent)] hover:underline font-medium flex items-center gap-1"
className="hover:text-[var(--text-primary)] font-medium flex items-center gap-1"
>
<span>Full Search Console</span>
<ArrowRight className="w-3 h-3" />
+9 -10
View File
@@ -8,7 +8,6 @@ import {
Share2,
FileDown,
Edit3,
ExternalLink,
} from "lucide-react";
interface DocActionsProps {
@@ -39,13 +38,13 @@ export default function DocActions({ slug, body, canEdit }: DocActionsProps) {
<button
onClick={handleCopyMarkdown}
type="button"
title="Copy raw markdown content to clipboard"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-lg transition-all shadow-xs cursor-pointer"
title="Copy raw markdown content"
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated)] border border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md transition-colors shadow-xs cursor-pointer"
>
{copiedMd ? (
<>
<Check className="w-3.5 h-3.5 text-emerald-500" />
<span className="text-emerald-500 font-medium">Copied MD</span>
<span className="text-emerald-500 font-medium">Copied</span>
</>
) : (
<>
@@ -60,12 +59,12 @@ export default function DocActions({ slug, body, canEdit }: DocActionsProps) {
onClick={handleCopyLink}
type="button"
title="Copy link to document"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-lg transition-all shadow-xs cursor-pointer"
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated)] border border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md transition-colors shadow-xs cursor-pointer"
>
{copiedLink ? (
<>
<Check className="w-3.5 h-3.5 text-emerald-500" />
<span className="text-emerald-500 font-medium">Copied Link</span>
<span className="text-emerald-500 font-medium">Copied</span>
</>
) : (
<>
@@ -78,17 +77,17 @@ export default function DocActions({ slug, body, canEdit }: DocActionsProps) {
{/* Export PDF */}
<Link
href={`/${slug}/export`}
title="Export document or parent folder to publication-grade PDF"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated)] border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-lg transition-all shadow-xs"
title="Export document to publication-grade PDF"
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated)] border border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md transition-colors shadow-xs"
>
<FileDown className="w-3.5 h-3.5" />
<span>Export PDF</span>
<span>PDF</span>
</Link>
{/* Edit Link */}
<Link
href={canEdit ? `/${slug}?edit=1` : `/login?redirect=/${slug}`}
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs bg-[var(--brand)]/10 hover:bg-[var(--brand)]/20 border border-[var(--brand)]/30 hover:border-[var(--brand)]/60 text-[var(--brand)] dark:text-[var(--accent)] rounded-lg transition-all font-medium shadow-xs"
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-primary)] rounded-md transition-colors font-medium shadow-xs"
>
<Edit3 className="w-3.5 h-3.5" />
<span>Edit</span>
+33 -57
View File
@@ -10,18 +10,10 @@ import {
Heading3,
Code,
Link as LinkIcon,
Table as TableIcon,
Info,
Lightbulb,
AlertTriangle,
Plus,
Trash2,
Check,
Eye,
Edit3,
FileCode,
Tag,
Folder,
Sliders,
} from "lucide-react";
@@ -127,7 +119,7 @@ export default function DocForm({
const availableFolders = existingFolders?.[section] ?? [];
const baseInputCls =
"w-full px-3.5 py-2.5 bg-[var(--bg-surface)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-xl text-sm text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-2 focus:ring-[var(--brand)]/20 transition-all";
"w-full px-3.5 py-2 bg-[var(--bg-surface)] border border-[var(--border-color)] hover:border-[var(--text-muted)] rounded-lg text-sm text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--text-primary)] transition-colors";
function addCustomField(key = "", value = "") {
setCustomFields([...customFields, { key, value }]);
@@ -229,7 +221,7 @@ export default function DocForm({
return (
<form onSubmit={handleSubmit} className="space-y-6 max-w-4xl">
{error && (
<div className="p-4 rounded-xl border border-rose-500/30 bg-rose-500/10 text-rose-500 dark:text-rose-400 text-xs sm:text-sm font-medium animate-fade-in">
<div className="p-3.5 rounded-lg border border-rose-500/30 bg-rose-500/10 text-rose-500 dark:text-rose-400 text-xs font-medium animate-fade-in">
{error}
</div>
)}
@@ -244,7 +236,7 @@ export default function DocForm({
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="e.g. Exploiting Stack Alignment Issues in x86_64"
className={`${baseInputCls} text-base sm:text-lg font-semibold`}
className={`${baseInputCls} text-base font-semibold`}
required
/>
</div>
@@ -339,7 +331,7 @@ export default function DocForm({
type="text"
value={slug ?? ""}
readOnly
className="w-full px-3.5 py-2.5 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded-xl text-sm text-[var(--text-muted)] font-mono"
className="w-full px-3.5 py-2 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded-lg text-sm text-[var(--text-muted)] font-mono"
/>
) : (
<input
@@ -352,7 +344,7 @@ export default function DocForm({
/>
)}
<p className="text-[11px] text-[var(--text-dim)] mt-1.5 font-mono">
Full Slug: <span className="text-[var(--brand)] dark:text-[var(--accent)] font-semibold">{effectiveSlug || section}</span>
Full Slug: <span className="text-[var(--text-primary)] font-semibold">{effectiveSlug || section}</span>
</p>
</div>
</div>
@@ -386,26 +378,26 @@ export default function DocForm({
</div>
{/* Custom Metadata Fields */}
<div className="p-4 sm:p-5 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-2xl space-y-4 shadow-sm">
<div className="p-4 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-xl space-y-3.5 shadow-xs">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div>
<h3 className="text-xs font-semibold uppercase tracking-wider text-[var(--text-muted)] font-mono flex items-center gap-1.5">
<Sliders className="w-3.5 h-3.5 text-[var(--brand)] dark:text-[var(--accent)]" />
<span>Dynamic Technical Metadata</span>
<Sliders className="w-3.5 h-3.5 text-[var(--text-muted)]" />
<span>Technical Metadata</span>
</h3>
<p className="text-[11px] text-[var(--text-dim)] mt-0.5">
Custom fields (points, difficulty, event, solved) will render as styled badges in the document hero.
</p>
</div>
<div className="flex items-center gap-1.5 flex-wrap">
<div className="flex items-center gap-1 flex-wrap">
<span className="text-[10px] font-mono text-[var(--text-dim)] uppercase">Presets:</span>
{PRESET_FIELDS.map((preset) => (
<button
key={preset.key}
type="button"
onClick={() => addCustomField(preset.key, preset.value)}
className="px-2 py-0.5 text-[11px] bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md transition-colors font-mono cursor-pointer"
className="px-2 py-0.5 text-[10px] bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded transition-colors font-mono cursor-pointer"
>
+{preset.key}
</button>
@@ -432,7 +424,7 @@ export default function DocForm({
<button
type="button"
onClick={() => removeCustomField(i)}
className="p-2 text-rose-500 hover:text-rose-600 rounded-lg hover:bg-[var(--bg-elevated)] transition-colors cursor-pointer"
className="p-2 text-rose-500 hover:text-rose-600 rounded hover:bg-[var(--bg-elevated)] transition-colors cursor-pointer"
title="Remove field"
>
<Trash2 className="w-4 h-4" />
@@ -443,7 +435,7 @@ export default function DocForm({
<button
type="button"
onClick={() => addCustomField()}
className="inline-flex items-center gap-1.5 text-xs text-[var(--brand)] dark:text-[var(--accent)] hover:underline px-3 py-1.5 rounded-lg border border-[var(--brand)]/30 hover:border-[var(--brand)]/60 bg-[var(--brand)]/5 transition-colors font-medium cursor-pointer"
className="inline-flex items-center gap-1.5 text-xs text-[var(--text-primary)] hover:underline px-3 py-1 rounded-md border border-[var(--border-color)] bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] transition-colors font-medium cursor-pointer"
>
<Plus className="w-3.5 h-3.5" />
<span>Add Custom Field</span>
@@ -451,17 +443,17 @@ export default function DocForm({
</div>
{/* Markdown Body Editor */}
<div className="border border-[var(--border-color)] rounded-2xl overflow-hidden bg-[var(--bg-surface)] shadow-sm">
<div className="border border-[var(--border-color)] rounded-xl overflow-hidden bg-[var(--bg-surface)] shadow-xs">
{/* Editor Toolbar */}
<div className="flex items-center justify-between px-3 py-2 bg-[var(--bg-elevated)]/60 border-b border-[var(--border-color)] flex-wrap gap-2">
<div className="flex items-center justify-between px-3 py-2 bg-[var(--bg-elevated)] border-b border-[var(--border-color)] flex-wrap gap-2">
{/* Tabs */}
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => setActiveTab("write")}
className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-semibold transition-all cursor-pointer ${
className={`flex items-center gap-1.5 px-3 py-1 text-xs rounded font-semibold transition-all cursor-pointer ${
activeTab === "write"
? "bg-[var(--brand)] text-white shadow-xs"
? "bg-[var(--brand)] text-[var(--brand-fg)] shadow-xs"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
>
@@ -471,14 +463,14 @@ export default function DocForm({
<button
type="button"
onClick={() => setActiveTab("preview")}
className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-semibold transition-all cursor-pointer ${
className={`flex items-center gap-1.5 px-3 py-1 text-xs rounded font-semibold transition-all cursor-pointer ${
activeTab === "preview"
? "bg-[var(--brand)] text-white shadow-xs"
? "bg-[var(--brand)] text-[var(--brand-fg)] shadow-xs"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)]"
}`}
>
<Eye className="w-3.5 h-3.5" />
<span>Live Preview</span>
<span>Preview</span>
</button>
</div>
@@ -488,23 +480,23 @@ export default function DocForm({
<button
type="button"
onClick={() => insertMarkdown("**", "**")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)]"
title="Bold (**text**)"
className="p-1 hover:bg-[var(--bg-surface)] rounded hover:text-[var(--text-primary)]"
title="Bold"
>
<Bold className="w-3.5 h-3.5" />
</button>
<button
type="button"
onClick={() => insertMarkdown("*", "*")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)]"
title="Italic (*text*)"
className="p-1 hover:bg-[var(--bg-surface)] rounded hover:text-[var(--text-primary)]"
title="Italic"
>
<Italic className="w-3.5 h-3.5" />
</button>
<button
type="button"
onClick={() => insertMarkdown("## ")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)]"
className="p-1 hover:bg-[var(--bg-surface)] rounded hover:text-[var(--text-primary)]"
title="Heading 2"
>
<Heading2 className="w-3.5 h-3.5" />
@@ -512,7 +504,7 @@ export default function DocForm({
<button
type="button"
onClick={() => insertMarkdown("### ")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)]"
className="p-1 hover:bg-[var(--bg-surface)] rounded hover:text-[var(--text-primary)]"
title="Heading 3"
>
<Heading3 className="w-3.5 h-3.5" />
@@ -520,7 +512,7 @@ export default function DocForm({
<button
type="button"
onClick={() => insertMarkdown("```\n", "\n```")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)]"
className="p-1 hover:bg-[var(--bg-surface)] rounded hover:text-[var(--text-primary)]"
title="Code Block"
>
<Code className="w-3.5 h-3.5" />
@@ -528,27 +520,11 @@ export default function DocForm({
<button
type="button"
onClick={() => insertMarkdown("[", "](https://)")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)]"
className="p-1 hover:bg-[var(--bg-surface)] rounded hover:text-[var(--text-primary)]"
title="Link"
>
<LinkIcon className="w-3.5 h-3.5" />
</button>
<button
type="button"
onClick={() => insertMarkdown("> [!NOTE]\n> ")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)] text-indigo-500"
title="Note Callout"
>
<Info className="w-3.5 h-3.5" />
</button>
<button
type="button"
onClick={() => insertMarkdown("> [!TIP]\n> ")}
className="p-1.5 hover:bg-[var(--bg-surface)] rounded-md hover:text-[var(--text-primary)] text-emerald-500"
title="Tip Callout"
>
<Lightbulb className="w-3.5 h-3.5" />
</button>
</div>
)}
</div>
@@ -559,7 +535,7 @@ export default function DocForm({
ref={textareaRef}
value={body}
onChange={(e) => setBody(e.target.value)}
className="w-full h-96 p-4 sm:p-5 bg-transparent text-[var(--text-primary)] placeholder-[var(--text-dim)] font-mono text-sm focus:outline-none resize-y leading-relaxed"
className="w-full h-96 p-4 bg-transparent text-[var(--text-primary)] placeholder-[var(--text-dim)] font-mono text-sm focus:outline-none resize-y leading-relaxed"
placeholder="# Write your technical documentation or writeup here in Markdown..."
required
/>
@@ -568,7 +544,7 @@ export default function DocForm({
{body.trim() ? (
<Markdown source={body} />
) : (
<p className="text-xs text-[var(--text-dim)] italic">No content to preview yet.</p>
<p className="text-xs text-[var(--text-dim)] italic font-mono">No content to preview yet.</p>
)}
</div>
)}
@@ -579,18 +555,18 @@ export default function DocForm({
<button
type="submit"
disabled={loading}
className="px-6 py-2.5 bg-[var(--brand)] hover:bg-[var(--brand-hover)] text-white rounded-xl font-semibold text-sm transition-all shadow-md shadow-[var(--brand)]/20 hover:shadow-[var(--brand)]/35 disabled:opacity-50 flex items-center gap-2 cursor-pointer"
className="px-6 py-2.5 bg-[var(--brand)] hover:opacity-90 text-[var(--brand-fg)] rounded-lg font-semibold text-xs transition-all shadow-xs disabled:opacity-50 flex items-center gap-2 cursor-pointer font-mono uppercase tracking-wider"
>
{loading && (
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
<div className="w-3.5 h-3.5 border-2 border-current border-t-transparent rounded-full animate-spin" />
)}
<span>{loading ? "Persisting Document..." : mode === "create" ? "Publish to PostgreSQL" : "Save Changes"}</span>
<span>{loading ? "PERSISTING..." : mode === "create" ? "PUBLISH_DOCUMENT" : "SAVE_CHANGES"}</span>
</button>
<button
type="button"
onClick={() => router.back()}
className="px-4 py-2.5 border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-muted)] hover:text-[var(--text-primary)] bg-[var(--bg-surface)] rounded-xl text-sm transition-colors shadow-xs cursor-pointer"
className="px-4 py-2.5 border border-[var(--border-color)] hover:border-[var(--text-muted)] text-[var(--text-muted)] hover:text-[var(--text-primary)] bg-[var(--bg-surface)] rounded-lg text-xs transition-colors shadow-xs cursor-pointer font-mono"
>
Cancel
</button>
+27 -38
View File
@@ -7,16 +7,11 @@ import { DEFAULT_SECTIONS, type SectionConfig } from "@mcpedia/config/sections";
import ThemeToggle from "@/components/ThemeToggle";
import CommandMenu from "@/components/CommandMenu";
import {
Sparkles,
Plus,
Search,
Menu,
X,
Lock,
Layers,
FileText,
Activity,
Terminal,
} from "lucide-react";
export default function Header() {
@@ -37,45 +32,39 @@ export default function Header() {
return (
<header className="sticky top-0 z-40 border-b border-[var(--border-color)] glass-nav transition-all">
<div className="mx-auto max-w-7xl px-4 sm:px-6 h-15 flex items-center justify-between gap-3 sm:gap-4">
{/* Left: Brand Identity */}
<div className="mx-auto max-w-7xl px-4 sm:px-6 h-14 flex items-center justify-between gap-3 sm:gap-4">
{/* Brand */}
<div className="flex items-center gap-3">
<Link href="/" className="flex items-center gap-2.5 group">
<div className="relative flex items-center justify-center w-8 h-8 rounded-xl bg-gradient-to-br from-indigo-500 via-indigo-600 to-violet-700 text-white shadow-lg shadow-indigo-500/25 group-hover:scale-105 group-hover:shadow-indigo-500/40 transition-all">
<span className="font-mono font-bold text-sm tracking-tighter">M</span>
<span className="absolute -bottom-0.5 -right-0.5 w-2 h-2 rounded-full bg-emerald-400 ring-2 ring-[var(--bg-app)] animate-pulse" />
<div className="flex items-center justify-center w-7 h-7 rounded-md bg-[var(--brand)] text-[var(--brand-fg)] font-mono font-bold text-xs shadow-xs group-hover:opacity-90 transition-opacity">
M
</div>
<div className="flex flex-col">
<span className="font-bold text-[var(--text-primary)] text-base tracking-tight leading-none group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors">
<div className="flex items-center gap-2">
<span className="font-bold text-[var(--text-primary)] text-sm tracking-tight leading-none">
MCPedia
</span>
<span className="text-[10px] font-mono text-[var(--text-dim)] uppercase tracking-wider leading-tight mt-0.5 hidden sm:block">
AI Knowledge Hub
<span className="text-[10px] font-mono px-1.5 py-0.2 rounded bg-[var(--bg-elevated)] border border-[var(--border-color)] text-[var(--text-muted)] hidden sm:inline-block">
CORE::DB
</span>
</div>
</Link>
<div className="hidden xl:flex items-center gap-1.5 ml-2 px-2 py-0.5 rounded-full bg-[var(--bg-elevated)] border border-[var(--border-color)] text-[10px] font-mono text-[var(--text-dim)]">
<Activity className="w-3 h-3 text-emerald-500" />
<span>MCP :4021</span>
</div>
</div>
{/* Center: Desktop Dynamic Sections Nav */}
<nav className="hidden md:flex items-center gap-1 bg-[var(--bg-elevated)]/60 p-1 rounded-xl border border-[var(--border-color)]">
{/* Center: Desktop Sections Nav */}
<nav className="hidden md:flex items-center gap-1">
{sections.map((s) => {
const isActive = pathname === `/${s.id}` || pathname.startsWith(`/${s.id}/`);
return (
<Link
key={s.id}
href={`/${s.id}`}
className={`px-3 py-1 rounded-lg text-xs font-medium transition-all flex items-center gap-1.5 ${
className={`px-3 py-1 rounded-md text-xs font-medium transition-colors flex items-center gap-1.5 ${
isActive
? "text-[var(--text-primary)] bg-[var(--bg-surface)] shadow-xs font-semibold border border-[var(--border-color)]"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-surface)]/50"
? "text-[var(--text-primary)] bg-[var(--bg-elevated)] border border-[var(--border-color)] font-semibold"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
}`}
>
<span className="text-xs">{s.icon}</span>
<span>{s.icon}</span>
<span>{s.label}</span>
</Link>
);
@@ -88,7 +77,7 @@ export default function Header() {
<Link
href="/search"
className="md:hidden p-2 text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-lg bg-[var(--bg-surface)] border border-[var(--border-color)] transition-colors"
className="md:hidden p-1.5 text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md bg-[var(--bg-surface)] border border-[var(--border-color)] transition-colors"
title="Search knowledge base"
>
<Search className="w-4 h-4" />
@@ -96,7 +85,7 @@ export default function Header() {
<Link
href="/create"
className="hidden sm:inline-flex items-center gap-1.5 px-3 py-1.5 bg-[var(--brand)] hover:bg-[var(--brand-hover)] text-white text-xs font-medium rounded-lg shadow-md shadow-[var(--brand)]/20 hover:shadow-[var(--brand)]/35 transition-all cursor-pointer"
className="hidden sm:inline-flex items-center gap-1.5 px-3 py-1.5 bg-[var(--brand)] hover:opacity-90 text-[var(--brand-fg)] text-xs font-semibold rounded-md shadow-xs transition-all cursor-pointer"
>
<Plus className="w-3.5 h-3.5" />
<span>New Doc</span>
@@ -104,7 +93,7 @@ export default function Header() {
<Link
href="/login"
className={`p-1.5 sm:px-2.5 sm:py-1.5 text-xs rounded-lg transition-colors flex items-center gap-1.5 ${
className={`p-1.5 sm:px-2.5 sm:py-1.5 text-xs rounded-md transition-colors flex items-center gap-1.5 ${
pathname === "/login"
? "text-[var(--text-primary)] bg-[var(--bg-elevated)] font-semibold border border-[var(--border-color)]"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
@@ -119,10 +108,10 @@ export default function Header() {
<ThemeToggle />
</div>
{/* Mobile menu hamburger toggle */}
{/* Mobile menu toggle */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden p-2 text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-lg hover:bg-[var(--bg-elevated)] transition-colors"
className="md:hidden p-1.5 text-[var(--text-muted)] hover:text-[var(--text-primary)] rounded-md hover:bg-[var(--bg-elevated)] transition-colors"
aria-label="Toggle navigation"
>
{mobileMenuOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
@@ -132,9 +121,9 @@ export default function Header() {
{/* Mobile Drawer */}
{mobileMenuOpen && (
<div className="md:hidden border-t border-[var(--border-color)] bg-[var(--bg-surface)] px-4 py-4 space-y-2 animate-fade-in shadow-xl">
<div className="text-[11px] font-mono uppercase tracking-wider text-[var(--text-dim)] px-2 mb-1">
Sections
<div className="md:hidden border-t border-[var(--border-color)] bg-[var(--bg-surface)] px-4 py-4 space-y-2 animate-fade-in shadow-lg">
<div className="text-[10px] font-mono uppercase tracking-wider text-[var(--text-muted)] px-1 mb-1">
Knowledge Sections
</div>
<div className="grid grid-cols-2 gap-1.5">
{sections.map((s) => {
@@ -144,10 +133,10 @@ export default function Header() {
key={s.id}
href={`/${s.id}`}
onClick={() => setMobileMenuOpen(false)}
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium transition-colors ${
className={`flex items-center gap-2 px-3 py-2 rounded-md text-xs font-medium transition-colors ${
isActive
? "bg-[var(--brand)]/15 text-[var(--brand)] dark:text-[var(--accent)] border border-[var(--brand)]/30 font-semibold"
: "text-[var(--text-secondary)] hover:bg-[var(--bg-elevated)] hover:text-[var(--text-primary)] border border-transparent"
? "bg-[var(--bg-elevated)] text-[var(--text-primary)] border border-[var(--border-color)] font-semibold"
: "text-[var(--text-secondary)] hover:bg-[var(--bg-elevated)] hover:text-[var(--text-primary)]"
}`}
>
<span>{s.icon}</span>
@@ -161,7 +150,7 @@ export default function Header() {
<Link
href="/search"
onClick={() => setMobileMenuOpen(false)}
className="flex-1 flex items-center justify-center gap-1.5 py-2 text-xs bg-[var(--bg-elevated)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-lg font-medium border border-[var(--border-color)]"
className="flex-1 flex items-center justify-center gap-1.5 py-2 text-xs bg-[var(--bg-elevated)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-md font-medium border border-[var(--border-color)]"
>
<Search className="w-3.5 h-3.5" />
<span>Search</span>
@@ -169,7 +158,7 @@ export default function Header() {
<Link
href="/create"
onClick={() => setMobileMenuOpen(false)}
className="flex-1 flex items-center justify-center gap-1.5 py-2 text-xs bg-[var(--brand)] text-white rounded-lg font-medium shadow-sm"
className="flex-1 flex items-center justify-center gap-1.5 py-2 text-xs bg-[var(--brand)] text-[var(--brand-fg)] rounded-md font-semibold shadow-xs"
>
<Plus className="w-3.5 h-3.5" />
<span>Create Doc</span>
+75 -78
View File
@@ -9,12 +9,9 @@ import {
Sparkles,
Search,
Globe,
Terminal,
Server,
ArrowRight,
CheckCircle2,
FileCode2,
ShieldCheck,
} from "lucide-react";
interface NodeDetail {
@@ -33,7 +30,7 @@ const NODES: NodeDetail[] = [
{
id: "ai-agents",
title: "AI Coding Agents",
badge: "MCP Consumer",
badge: "MCP::CLIENT",
type: "client",
icon: Sparkles,
desc: "Claude Desktop, Cursor, Antigravity, and Zed connect via Model Context Protocol to read and write technical knowledge directly.",
@@ -52,10 +49,10 @@ const NODES: NodeDetail[] = [
{
id: "web-ui",
title: "Human Web Interface",
badge: "Next.js 16 + React 19",
badge: "UI::NEXTJS",
type: "client",
icon: Globe,
desc: "Fast, accessible, and responsive developer web interface with dark/light themes, keyboard shortcuts, and instant search.",
desc: "Fast, accessible developer web interface with dark/light themes, keyboard shortcuts, and instant search.",
metrics: [
{ label: "Framework", value: "Next.js 16.3" },
{ label: "Styling", value: "Tailwind CSS v4" },
@@ -71,10 +68,10 @@ const NODES: NodeDetail[] = [
{
id: "mcp-server",
title: "MCP Server Gateway",
badge: "Streamable HTTP :4021",
badge: "PORT::4021",
type: "protocol",
icon: Server,
desc: "High-throughput streamable Model Context Protocol server exposing tool endpoints and resource URIs.",
desc: "Streamable Model Context Protocol server exposing tool endpoints and resource URIs.",
metrics: [
{ label: "Port", value: ":4021" },
{ label: "Protocol", value: "MCP 2024-11-05" },
@@ -90,7 +87,7 @@ const NODES: NodeDetail[] = [
{
id: "core-engine",
title: "@mcpedia/core",
badge: "Single Source of Truth",
badge: "CORE::PACKAGE",
type: "core",
icon: Layers,
desc: "Unified business logic layer shared across Web, MCP Server, REST API, and Background Workers.",
@@ -109,10 +106,10 @@ const NODES: NodeDetail[] = [
{
id: "search-engine",
title: "Multi-Modal Search Engine",
badge: "RRF Algorithm",
badge: "SEARCH::RRF",
type: "core",
icon: Search,
desc: "Reciprocal Rank Fusion uniting Postgres Full-Text Search tsvectors with OpenAI/OpenRouter vector embeddings.",
desc: "Reciprocal Rank Fusion uniting Postgres Full-Text Search tsvectors with vector embeddings.",
metrics: [
{ label: "FTS Algorithm", value: "tsvector + GIN" },
{ label: "Vector Search", value: "Cosine Similarity" },
@@ -128,7 +125,7 @@ const NODES: NodeDetail[] = [
{
id: "postgres-db",
title: "PostgreSQL 16 + pgvector",
badge: "Primary Storage",
badge: "DB::POSTGRES",
type: "storage",
icon: Database,
desc: "ACID-compliant relational database storing document bodies, revision trees, vector embeddings, and full-text indexes.",
@@ -153,38 +150,38 @@ export default function InteractiveTopology() {
const ActiveIcon = activeNode.icon;
return (
<div className="bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-2xl overflow-hidden shadow-sm">
<div className="bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-xl overflow-hidden shadow-xs">
{/* Header Bar */}
<div className="p-5 sm:p-6 border-b border-[var(--border-color)] bg-[var(--bg-elevated)]/50 flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div className="p-4 sm:p-5 border-b border-[var(--border-color)] bg-[var(--bg-elevated)] flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div>
<div className="flex items-center gap-2 mb-1.5">
<span className="w-2 h-2 rounded-full bg-emerald-500 animate-pulse" />
<span className="text-[11px] font-mono uppercase tracking-wider text-[var(--text-muted)]">
Interactive System Topology
<div className="flex items-center gap-2 mb-1">
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500" />
<span className="text-[10px] font-mono uppercase tracking-wider text-[var(--text-muted)]">
Architecture Schematic
</span>
</div>
<h3 className="text-xl font-bold text-[var(--text-primary)] tracking-tight">
How MCPedia Unifies Humans & AI Agents
<h3 className="text-base sm:text-lg font-bold text-[var(--text-primary)] tracking-tight">
System Topology: Single Core Architecture
</h3>
</div>
<div className="flex items-center gap-2">
<span className="text-xs font-mono text-[var(--text-muted)] bg-[var(--bg-surface)] border border-[var(--border-color)] px-2.5 py-1 rounded-md">
Click any node below to inspect
<span className="text-[11px] font-mono text-[var(--text-muted)] bg-[var(--bg-surface)] border border-[var(--border-color)] px-2 py-1 rounded">
Click nodes to inspect
</span>
</div>
</div>
{/* Grid Layout: Visual Interactive Map & Inspector */}
{/* Grid Layout */}
<div className="grid grid-cols-1 lg:grid-cols-12">
{/* Visual Map Column (Left) */}
<div className="lg:col-span-7 p-6 border-b lg:border-b-0 lg:border-r border-[var(--border-color)] bg-[var(--bg-app)]/50 flex flex-col justify-between space-y-6">
<div className="lg:col-span-7 p-5 border-b lg:border-b-0 lg:border-r border-[var(--border-color)] bg-[var(--bg-app)] flex flex-col justify-between space-y-5">
{/* Layer 1: Clients */}
<div>
<span className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-dim)] mb-2 block">
<span className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-muted)] mb-2 block">
1. Ingestion & Access Layer
</span>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2.5">
{NODES.filter((n) => n.type === "client").map((node) => {
const Icon = node.icon;
const isSelected = activeNodeId === node.id;
@@ -193,21 +190,21 @@ export default function InteractiveTopology() {
key={node.id}
onClick={() => setActiveNodeId(node.id)}
type="button"
className={`p-3.5 rounded-xl border text-left transition-all cursor-pointer ${
className={`p-3 rounded-lg border text-left transition-all cursor-pointer ${
isSelected
? "bg-[var(--brand)]/15 border-[var(--brand)] shadow-md shadow-[var(--brand)]/10 ring-1 ring-[var(--brand)]"
: "bg-[var(--bg-surface)] border-[var(--border-color)] hover:border-[var(--brand)]/50 hover:bg-[var(--bg-elevated)]"
? "bg-[var(--bg-surface)] border-[var(--text-primary)] shadow-xs ring-1 ring-[var(--text-primary)]"
: "bg-[var(--bg-surface)] border-[var(--border-color)] hover:border-[var(--text-muted)]"
}`}
>
<div className="flex items-center justify-between mb-2">
<div className={`p-2 rounded-lg ${isSelected ? "bg-[var(--brand)] text-white" : "bg-[var(--bg-elevated)] text-[var(--text-secondary)]"}`}>
<Icon className="w-4 h-4" />
<div className={`p-1.5 rounded ${isSelected ? "bg-[var(--brand)] text-[var(--brand-fg)]" : "bg-[var(--bg-elevated)] text-[var(--text-muted)]"}`}>
<Icon className="w-3.5 h-3.5" />
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-[var(--bg-elevated)] text-[var(--text-muted)] border border-[var(--border-color)]">
<span className="text-[10px] font-mono px-1.5 py-0.2 rounded bg-[var(--bg-elevated)] text-[var(--text-muted)] border border-[var(--border-color)]">
{node.badge}
</span>
</div>
<div className="font-semibold text-sm text-[var(--text-primary)]">{node.title}</div>
<div className="font-semibold text-xs text-[var(--text-primary)]">{node.title}</div>
</button>
);
})}
@@ -216,17 +213,17 @@ export default function InteractiveTopology() {
{/* Flow Connector Arrow */}
<div className="flex justify-center text-[var(--text-dim)]">
<span className="text-xs font-mono px-3 py-0.5 rounded-full bg-[var(--bg-surface)] border border-[var(--border-color)]">
↓ Secure Protocol & Gateway
<span className="text-[10px] font-mono px-2.5 py-0.5 rounded bg-[var(--bg-surface)] border border-[var(--border-color)]">
↓ Protocol Gateway
</span>
</div>
{/* Layer 2: Protocol & Core Services */}
<div>
<span className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-dim)] mb-2 block">
2. Gateway & Intelligence Core
<span className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-muted)] mb-2 block">
2. Core Engine & Protocol Layer
</span>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2.5">
{NODES.filter((n) => n.type === "protocol" || n.type === "core").map((node) => {
const Icon = node.icon;
const isSelected = activeNodeId === node.id;
@@ -235,18 +232,18 @@ export default function InteractiveTopology() {
key={node.id}
onClick={() => setActiveNodeId(node.id)}
type="button"
className={`p-3.5 rounded-xl border text-left transition-all cursor-pointer ${
className={`p-3 rounded-lg border text-left transition-all cursor-pointer ${
isSelected
? "bg-[var(--brand)]/15 border-[var(--brand)] shadow-md shadow-[var(--brand)]/10 ring-1 ring-[var(--brand)]"
: "bg-[var(--bg-surface)] border-[var(--border-color)] hover:border-[var(--brand)]/50 hover:bg-[var(--bg-elevated)]"
? "bg-[var(--bg-surface)] border-[var(--text-primary)] shadow-xs ring-1 ring-[var(--text-primary)]"
: "bg-[var(--bg-surface)] border-[var(--border-color)] hover:border-[var(--text-muted)]"
}`}
>
<div className="flex items-center justify-between mb-2">
<div className={`p-2 rounded-lg ${isSelected ? "bg-[var(--brand)] text-white" : "bg-[var(--bg-elevated)] text-[var(--text-secondary)]"}`}>
<Icon className="w-4 h-4" />
<div className={`p-1.5 rounded ${isSelected ? "bg-[var(--brand)] text-[var(--brand-fg)]" : "bg-[var(--bg-elevated)] text-[var(--text-muted)]"}`}>
<Icon className="w-3.5 h-3.5" />
</div>
</div>
<div className="font-semibold text-xs text-[var(--text-primary)] leading-tight mb-1 truncate">
<div className="font-semibold text-xs text-[var(--text-primary)] leading-tight mb-0.5 truncate">
{node.title}
</div>
<div className="text-[10px] text-[var(--text-muted)] font-mono truncate">
@@ -260,15 +257,15 @@ export default function InteractiveTopology() {
{/* Flow Connector Arrow */}
<div className="flex justify-center text-[var(--text-dim)]">
<span className="text-xs font-mono px-3 py-0.5 rounded-full bg-[var(--bg-surface)] border border-[var(--border-color)]">
↓ Persistent Layer (ACID + Vectors)
<span className="text-[10px] font-mono px-2.5 py-0.5 rounded bg-[var(--bg-surface)] border border-[var(--border-color)]">
↓ Persistent Storage Layer
</span>
</div>
{/* Layer 3: Database & Vector Storage */}
{/* Layer 3: Storage */}
<div>
<span className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-dim)] mb-2 block">
3. Source of Truth Storage
<span className="text-[10px] font-mono uppercase tracking-widest text-[var(--text-muted)] mb-2 block">
3. Database (Source of Truth)
</span>
{NODES.filter((n) => n.type === "storage").map((node) => {
const Icon = node.icon;
@@ -278,23 +275,23 @@ export default function InteractiveTopology() {
key={node.id}
onClick={() => setActiveNodeId(node.id)}
type="button"
className={`w-full p-3.5 rounded-xl border text-left transition-all cursor-pointer ${
className={`w-full p-3 rounded-lg border text-left transition-all cursor-pointer ${
isSelected
? "bg-[var(--brand)]/15 border-[var(--brand)] shadow-md shadow-[var(--brand)]/10 ring-1 ring-[var(--brand)]"
: "bg-[var(--bg-surface)] border-[var(--border-color)] hover:border-[var(--brand)]/50 hover:bg-[var(--bg-elevated)]"
? "bg-[var(--bg-surface)] border-[var(--text-primary)] shadow-xs ring-1 ring-[var(--text-primary)]"
: "bg-[var(--bg-surface)] border-[var(--border-color)] hover:border-[var(--text-muted)]"
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className={`p-2 rounded-lg ${isSelected ? "bg-[var(--brand)] text-white" : "bg-[var(--bg-elevated)] text-[var(--text-secondary)]"}`}>
<div className={`p-1.5 rounded ${isSelected ? "bg-[var(--brand)] text-[var(--brand-fg)]" : "bg-[var(--bg-elevated)] text-[var(--text-muted)]"}`}>
<Icon className="w-4 h-4" />
</div>
<div>
<div className="font-semibold text-sm text-[var(--text-primary)]">{node.title}</div>
<div className="text-xs text-[var(--text-muted)]">Source of truth for all content & embeddings</div>
<div className="font-semibold text-xs text-[var(--text-primary)]">{node.title}</div>
<div className="text-[11px] text-[var(--text-muted)]">Source of truth for all content & embeddings</div>
</div>
</div>
<span className="text-[10px] font-mono px-2 py-0.5 rounded bg-[var(--bg-elevated)] text-[var(--text-muted)] border border-[var(--border-color)]">
<span className="text-[10px] font-mono px-1.5 py-0.2 rounded bg-[var(--bg-elevated)] text-[var(--text-muted)] border border-[var(--border-color)]">
{node.badge}
</span>
</div>
@@ -305,40 +302,40 @@ export default function InteractiveTopology() {
</div>
{/* Node Inspector Details Column (Right) */}
<div className="lg:col-span-5 p-6 bg-[var(--bg-surface)] flex flex-col justify-between space-y-6 animate-fade-in">
<div className="lg:col-span-5 p-5 bg-[var(--bg-surface)] flex flex-col justify-between space-y-5 animate-fade-in">
<div>
{/* Inspector Header */}
<div className="flex items-start justify-between gap-4 mb-4">
<div className="flex items-center gap-3">
<div className="w-11 h-11 rounded-xl bg-[var(--brand)]/15 border border-[var(--brand)]/30 text-[var(--brand)] dark:text-[var(--accent)] flex items-center justify-center">
<ActiveIcon className="w-5 h-5" />
<div className="flex items-start justify-between gap-3 mb-3">
<div className="flex items-center gap-2.5">
<div className="w-9 h-9 rounded-lg bg-[var(--bg-elevated)] border border-[var(--border-color)] text-[var(--text-primary)] flex items-center justify-center">
<ActiveIcon className="w-4 h-4" />
</div>
<div>
<h4 className="text-lg font-bold text-[var(--text-primary)]">{activeNode.title}</h4>
<span className="text-xs font-mono text-[var(--brand)] dark:text-[var(--accent)]">
<h4 className="text-sm font-bold text-[var(--text-primary)]">{activeNode.title}</h4>
<span className="text-[10px] font-mono text-[var(--text-muted)]">
{activeNode.badge}
</span>
</div>
</div>
</div>
<p className="text-xs sm:text-sm text-[var(--text-muted)] leading-relaxed mb-6">
<p className="text-xs text-[var(--text-secondary)] leading-relaxed mb-5">
{activeNode.desc}
</p>
{/* Telemetry Metrics */}
<div className="mb-6">
<span className="text-[11px] font-semibold uppercase tracking-wider text-[var(--text-dim)] font-mono mb-2.5 block">
Telemetry & Architecture
<div className="mb-5">
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--text-muted)] font-mono mb-2 block">
Specifications
</span>
<div className="space-y-2">
<div className="space-y-1.5">
{activeNode.metrics.map((m) => (
<div
key={m.label}
className="flex items-center justify-between p-2.5 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded-lg text-xs"
className="flex items-center justify-between p-2 bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded text-xs"
>
<span className="text-[var(--text-muted)]">{m.label}</span>
<span className="font-mono font-medium text-[var(--text-primary)]">{m.value}</span>
<span className="text-[var(--text-muted)] text-[11px]">{m.label}</span>
<span className="font-mono font-medium text-[var(--text-primary)] text-[11px]">{m.value}</span>
</div>
))}
</div>
@@ -346,13 +343,13 @@ export default function InteractiveTopology() {
{/* Key Capabilities */}
<div>
<span className="text-[11px] font-semibold uppercase tracking-wider text-[var(--text-dim)] font-mono mb-2.5 block">
Key Capabilities
<span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--text-muted)] font-mono mb-2 block">
Capabilities
</span>
<ul className="space-y-2">
<ul className="space-y-1.5">
{activeNode.features.map((feat, idx) => (
<li key={idx} className="flex items-start gap-2 text-xs text-[var(--text-secondary)]">
<CheckCircle2 className="w-3.5 h-3.5 text-emerald-500 shrink-0 mt-0.5" />
<span className="text-emerald-500 shrink-0 font-mono text-[11px]">✓</span>
<span>{feat}</span>
</li>
))}
@@ -362,12 +359,12 @@ export default function InteractiveTopology() {
{/* Link to Documentation */}
{activeNode.docLink && (
<div className="pt-4 border-t border-[var(--border-color)]">
<div className="pt-3 border-t border-[var(--border-color)]">
<Link
href={activeNode.docLink}
className="inline-flex items-center justify-center gap-2 w-full py-2.5 bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] text-xs font-semibold text-[var(--text-primary)] rounded-xl transition-all shadow-xs group"
className="inline-flex items-center justify-center gap-1.5 w-full py-2 bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-xs font-semibold text-[var(--text-primary)] rounded-lg transition-colors group"
>
<span>Read technical writeup in {activeNode.title}</span>
<span>Read documentation</span>
<ArrowRight className="w-3.5 h-3.5 group-hover:translate-x-0.5 transition-transform" />
</Link>
</div>
+17 -21
View File
@@ -6,11 +6,8 @@ import {
Sparkles,
Terminal,
Server,
Code2,
Copy,
Check,
CheckCircle2,
Cpu,
Laptop,
} from "lucide-react";
@@ -54,10 +51,10 @@ const CONFIGS = [
},
{
id: "http",
label: "Streamable HTTP Endpoint",
label: "Streamable HTTP (SSE)",
icon: Server,
lang: "json",
filename: "Streamable HTTP (SSE) Client Config",
filename: "Streamable HTTP Client Config",
content: `{
"mcpServers": {
"mcpedia-remote": {
@@ -74,11 +71,11 @@ const CONFIGS = [
label: "CLI & Scripts",
icon: Terminal,
lang: "bash",
filename: "Terminal / MCP Client Subprocess",
content: `# Ask questions over semantic knowledge
filename: "Terminal Subprocess / REPL",
content: `# Semantic question answering via MCP client
bun run apps/mcp-client/src/ask.ts "How does ret2win stack alignment work in x86_64?"
# Run interactive REPL with native tool calls
# Interactive REPL with tool discovery
bun run apps/mcp-client/src/index.ts`,
},
];
@@ -100,9 +97,9 @@ ${current.content}
}
return (
<div className="bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-2xl overflow-hidden shadow-sm">
<div className="bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-xl overflow-hidden shadow-xs">
{/* Header bar with tabs */}
<div className="flex items-center justify-between px-4 sm:px-6 py-3 bg-[var(--bg-elevated)]/60 border-b border-[var(--border-color)] flex-wrap gap-2">
<div className="flex items-center justify-between px-4 py-2.5 bg-[var(--bg-elevated)] border-b border-[var(--border-color)] flex-wrap gap-2">
<div className="flex items-center gap-1.5 flex-wrap">
{CONFIGS.map((c) => {
const Icon = c.icon;
@@ -112,9 +109,9 @@ ${current.content}
key={c.id}
onClick={() => setActiveTab(c.id)}
type="button"
className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-xl font-semibold transition-all cursor-pointer ${
className={`flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md font-medium transition-all cursor-pointer ${
isSelected
? "bg-[var(--brand)] text-white shadow-md shadow-[var(--brand)]/20"
? "bg-[var(--brand)] text-[var(--brand-fg)] font-semibold shadow-xs"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-surface)]"
}`}
>
@@ -128,12 +125,12 @@ ${current.content}
<button
onClick={handleCopyAll}
type="button"
className="flex items-center gap-1.5 px-3 py-1 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-lg transition-all shadow-xs cursor-pointer font-medium"
className="flex items-center gap-1.5 px-2.5 py-1 text-xs bg-[var(--bg-surface)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-primary)] rounded-md transition-colors shadow-xs cursor-pointer font-medium"
>
{copied ? (
<>
<Check className="w-3.5 h-3.5 text-emerald-500" />
<span className="text-emerald-500">Copied Config</span>
<span className="text-emerald-500">Copied</span>
</>
) : (
<>
@@ -144,15 +141,15 @@ ${current.content}
</button>
</div>
{/* Code preview via Markdown syntax highlighter */}
{/* Code preview */}
<div className="p-4 sm:p-5 [&>.prose>.group]:my-0 [&>.prose]:max-w-none">
<Markdown source={markdownSnippet} />
</div>
{/* Footer Info */}
<div className="px-4 sm:px-6 py-3 bg-[var(--bg-elevated)]/40 border-t border-[var(--border-color)] text-xs text-[var(--text-dim)] flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div className="px-4 py-2.5 bg-[var(--bg-elevated)] border-t border-[var(--border-color)] text-xs text-[var(--text-dim)] flex flex-col sm:flex-row sm:items-center justify-between gap-2">
<div className="flex items-center gap-2">
<span className="font-mono uppercase text-[10px] text-[var(--text-muted)] font-semibold">
<span className="font-mono uppercase text-[10px] text-[var(--text-muted)]">
Target File:
</span>
<span className="font-mono text-[var(--text-secondary)] text-[11px]">
@@ -160,10 +157,9 @@ ${current.content}
</span>
</div>
<div className="flex items-center gap-3 text-[11px]">
<span className="flex items-center gap-1 text-emerald-600 dark:text-emerald-400 font-medium">
<CheckCircle2 className="w-3.5 h-3.5" />
<span>13 MCP Tools Ready</span>
<div className="flex items-center gap-3 text-[11px] font-mono">
<span className="flex items-center gap-1 text-[var(--text-muted)]">
<span>[ 13_TOOLS_ACTIVE ]</span>
</span>
</div>
</div>
+23 -25
View File
@@ -20,8 +20,6 @@ import {
Copy,
Check,
ArrowLeft,
Filter,
ListOrdered,
BookOpen,
} from "lucide-react";
@@ -124,20 +122,20 @@ export default function PdfExportView({
return (
<div className="pb-16 print:pb-0">
{/* Export Control Toolbar (Screen Only — Hidden in Print) */}
<aside className="no-print export-toolbar sticky top-16 z-30 mb-8 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-2xl p-5 shadow-md">
<aside className="no-print export-toolbar sticky top-16 z-30 mb-8 bg-[var(--bg-surface)] border border-[var(--border-color)] rounded-xl p-4 sm:p-5 shadow-xs">
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
{/* Left info & Back button */}
<div className="flex items-center gap-3.5">
<div className="flex items-center gap-3">
<Link
href={backHref}
className="inline-flex items-center gap-1.5 px-3.5 py-2 text-xs font-semibold bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-xl text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-all shadow-xs"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] rounded-md text-[var(--text-secondary)] hover:text-[var(--text-primary)] transition-colors shadow-xs"
>
<ArrowLeft className="w-3.5 h-3.5" />
<span>Back</span>
</Link>
<div>
<div className="flex items-center gap-2">
<span className="text-xs font-mono text-[var(--brand)] dark:text-[var(--accent)] font-bold uppercase">
<span className="text-xs font-mono text-[var(--text-primary)] font-bold uppercase">
{sectionMeta.label}
</span>
<span className="text-xs text-[var(--text-dim)]">·</span>
@@ -158,7 +156,7 @@ export default function PdfExportView({
<button
onClick={handleCopyMarkdown}
type="button"
className="inline-flex items-center gap-1.5 px-3.5 py-2 text-xs font-semibold bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-xl transition-all shadow-xs cursor-pointer"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-md transition-colors shadow-xs cursor-pointer"
title="Copy compiled markdown document"
>
{copiedMd ? (
@@ -169,7 +167,7 @@ export default function PdfExportView({
) : (
<>
<Copy className="w-3.5 h-3.5" />
<span>Copy Markdown</span>
<span>Copy MD</span>
</>
)}
</button>
@@ -178,7 +176,7 @@ export default function PdfExportView({
<button
onClick={handleDownloadMarkdown}
type="button"
className="inline-flex items-center gap-1.5 px-3.5 py-2 text-xs font-semibold bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-xl transition-all shadow-xs cursor-pointer"
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs font-semibold bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-md transition-colors shadow-xs cursor-pointer"
title="Download compiled .md file"
>
<FileDown className="w-3.5 h-3.5" />
@@ -189,24 +187,24 @@ export default function PdfExportView({
<button
onClick={handlePrint}
type="button"
className="inline-flex items-center gap-2 px-4 py-2 text-xs font-bold bg-slate-900 hover:bg-slate-800 dark:bg-white dark:hover:bg-slate-100 text-white dark:text-slate-900 rounded-xl shadow-md transition-all cursor-pointer hover:shadow-lg"
className="inline-flex items-center gap-2 px-3.5 py-1.5 text-xs font-bold bg-[var(--brand)] hover:opacity-90 text-[var(--brand-fg)] rounded-md shadow-xs transition-all cursor-pointer"
title="Open browser print dialog to save as PDF"
>
<Printer className="w-4 h-4" />
<span>Print / Save as PDF</span>
<span>Print / Save PDF</span>
</button>
</div>
</div>
{/* Options & Filters Bar */}
<div className="mt-4 pt-3.5 border-t border-[var(--border-color)] flex flex-col sm:flex-row sm:items-center justify-between gap-3 text-xs">
<div className="mt-3.5 pt-3 border-t border-[var(--border-color)] flex flex-col sm:flex-row sm:items-center justify-between gap-3 text-xs">
{/* Sort Order Selector */}
<div className="flex items-center gap-2">
<span className="text-[var(--text-muted)] font-medium font-mono text-[11px] uppercase">Sort by:</span>
<span className="text-[var(--text-muted)] font-mono text-[10px] uppercase">Sort:</span>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as ExportSortOption)}
className="bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded-lg px-3 py-1 text-xs text-[var(--text-primary)] focus:outline-none focus:border-[var(--brand)]"
className="bg-[var(--bg-elevated)] border border-[var(--border-color)] rounded px-2.5 py-1 text-xs text-[var(--text-primary)] focus:outline-none"
>
<option value="category_points">Category & Points (Standard)</option>
<option value="points_desc">Points (Highest First)</option>
@@ -225,7 +223,7 @@ export default function PdfExportView({
type="checkbox"
checked={showTOC}
onChange={(e) => setShowTOC(e.target.checked)}
className="rounded border-[var(--border-color)] text-indigo-600"
className="rounded border-[var(--border-color)]"
/>
<span>Table of Contents</span>
</label>
@@ -236,7 +234,7 @@ export default function PdfExportView({
type="checkbox"
checked={pageBreaks}
onChange={(e) => setPageBreaks(e.target.checked)}
className="rounded border-[var(--border-color)] text-indigo-600"
className="rounded border-[var(--border-color)]"
/>
<span>Page Break per Challenge</span>
</label>
@@ -246,13 +244,13 @@ export default function PdfExportView({
{/* Category filter pills */}
{allCategories.length > 1 && (
<div className="mt-3 pt-3 border-t border-[var(--border-color)] flex items-center gap-1.5 flex-wrap text-xs">
<span className="text-[var(--text-muted)] font-mono text-[11px] uppercase mr-1">Filter:</span>
<span className="text-[var(--text-muted)] font-mono text-[10px] uppercase mr-1">Filter:</span>
<button
onClick={() => setSelectedCategory("all")}
type="button"
className={`px-3 py-1 rounded-lg text-xs font-mono transition-all cursor-pointer ${
className={`px-2.5 py-0.5 rounded text-xs font-mono transition-all cursor-pointer ${
selectedCategory === "all"
? "bg-slate-900 dark:bg-white text-white dark:text-slate-900 font-bold"
? "bg-[var(--brand)] text-[var(--brand-fg)] font-bold"
: "bg-[var(--bg-elevated)] text-[var(--text-muted)] hover:text-[var(--text-primary)] border border-[var(--border-color)]"
}`}
>
@@ -267,9 +265,9 @@ export default function PdfExportView({
key={cat}
onClick={() => setSelectedCategory(cat)}
type="button"
className={`px-3 py-1 rounded-lg text-xs font-mono transition-all cursor-pointer ${
className={`px-2.5 py-0.5 rounded text-xs font-mono transition-all cursor-pointer ${
selectedCategory.toLowerCase() === cat.toLowerCase()
? "bg-slate-900 dark:bg-white text-white dark:text-slate-900 font-bold"
? "bg-[var(--brand)] text-[var(--brand-fg)] font-bold"
: "bg-[var(--bg-elevated)] text-[var(--text-muted)] hover:text-[var(--text-primary)] border border-[var(--border-color)]"
}`}
>
@@ -282,7 +280,7 @@ export default function PdfExportView({
</aside>
{/* Writeup Document Flow */}
<main className="print-container bg-white text-slate-900 max-w-4xl mx-auto px-4 sm:px-8 py-6 print:p-0 print:max-w-none rounded-2xl border border-[var(--border-color)] print:border-none shadow-sm print:shadow-none">
<main className="print-container bg-white text-slate-900 max-w-4xl mx-auto px-4 sm:px-8 py-6 print:p-0 print:max-w-none rounded-xl border border-[var(--border-color)] print:border-none shadow-xs print:shadow-none">
{/* Document Header */}
<header className="mb-8 pb-4 border-b border-slate-300">
<h1 className="text-3xl sm:text-4xl font-extrabold text-slate-950 tracking-tight mb-2">
@@ -300,8 +298,8 @@ export default function PdfExportView({
{/* Table of Contents */}
{showTOC && filteredAndSortedDocs.length > 1 && (
<section className="toc-section mb-10 pb-6 border-b border-slate-200">
<h2 className="text-sm font-bold uppercase font-mono text-slate-700 mb-3 tracking-wider flex items-center gap-1.5">
<BookOpen className="w-4 h-4" />
<h2 className="text-xs font-bold uppercase font-mono text-slate-700 mb-3 tracking-wider flex items-center gap-1.5">
<BookOpen className="w-3.5 h-3.5" />
<span>Table of Contents</span>
</h2>
<ol className="list-decimal list-inside space-y-1.5 text-xs text-slate-800">
@@ -327,7 +325,7 @@ export default function PdfExportView({
</section>
)}
{/* Challenges / Writeups Flow */}
{/* Challenges Flow */}
<div className="challenges-flow space-y-10 print:space-y-0">
{filteredAndSortedDocs.map((doc, idx) => {
const chNum = idx + 1;
+17 -18
View File
@@ -12,7 +12,6 @@ import {
Search,
X,
Plus,
Compass,
} from "lucide-react";
interface Doc {
@@ -114,11 +113,11 @@ function TreeNodeItem({
return (
<li className={`select-none ${depth > 0 ? "ml-3.5 pl-2 border-l border-[var(--border-color)]" : ""}`}>
<div
className={`group flex items-center justify-between gap-1 text-xs py-1.5 px-2 rounded-lg transition-all ${
className={`group flex items-center justify-between gap-1 text-xs py-1.5 px-2 rounded-md transition-all ${
isActive
? "text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/15 font-semibold border border-[var(--brand)]/30 shadow-xs"
? "text-[var(--text-primary)] bg-[var(--bg-elevated)] font-semibold border border-[var(--border-color)] shadow-xs"
: isParentOfActive
? "text-[var(--text-primary)] bg-[var(--bg-elevated)]"
? "text-[var(--text-primary)] bg-[var(--bg-elevated)]/60"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
}`}
>
@@ -127,12 +126,12 @@ function TreeNodeItem({
className="flex items-center gap-2 min-w-0 flex-1 py-0.5"
title={node.title || node.name}
>
<span className="shrink-0 text-[var(--text-dim)] group-hover:text-[var(--brand)] dark:group-hover:text-[var(--accent)] transition-colors">
<span className="shrink-0 text-[var(--text-dim)] group-hover:text-[var(--text-primary)] transition-colors">
{isFolder ? (
isCollapsed ? (
<Folder className="w-3.5 h-3.5" />
) : (
<FolderOpen className="w-3.5 h-3.5 text-[var(--brand)] dark:text-[var(--accent)]" />
<FolderOpen className="w-3.5 h-3.5 text-[var(--text-primary)]" />
)
) : (
<FileText className="w-3.5 h-3.5" />
@@ -153,7 +152,7 @@ function TreeNodeItem({
e.stopPropagation();
toggleCollapse(node.slug);
}}
className="p-1 text-[var(--text-dim)] hover:text-[var(--text-primary)] rounded hover:bg-[var(--bg-elevated-hover)] transition-transform"
className="p-1 text-[var(--text-dim)] hover:text-[var(--text-primary)] rounded hover:bg-[var(--bg-elevated-hover)] transition-transform cursor-pointer"
aria-label={isCollapsed ? "Expand folder" : "Collapse folder"}
>
<ChevronRight
@@ -233,14 +232,14 @@ export default function Sidebar() {
type="text"
value={filterText}
onChange={(e) => setFilterText(e.target.value)}
placeholder="Filter knowledge tree..."
className="w-full pl-8 pr-7 py-1.5 bg-[var(--bg-surface)] border border-[var(--border-color)] hover:border-[var(--brand)] rounded-lg text-xs text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--brand)] focus:ring-1 focus:ring-[var(--brand)] transition-all"
placeholder="Filter index..."
className="w-full pl-8 pr-7 py-1.5 bg-[var(--bg-surface)] border border-[var(--border-color)] hover:border-[var(--text-muted)] rounded-md text-xs text-[var(--text-primary)] placeholder-[var(--text-dim)] focus:outline-none focus:border-[var(--text-primary)] transition-all"
/>
<Search className="w-3.5 h-3.5 text-[var(--text-dim)] absolute left-2.5 top-2.5 pointer-events-none" />
{filterText && (
<button
onClick={() => setFilterText("")}
className="absolute right-2 top-2 p-0.5 text-[var(--text-dim)] hover:text-[var(--text-primary)] rounded hover:bg-[var(--bg-elevated)]"
className="absolute right-2 top-2 p-0.5 text-[var(--text-dim)] hover:text-[var(--text-primary)] rounded"
aria-label="Clear filter"
>
<X className="w-3.5 h-3.5" />
@@ -250,7 +249,7 @@ export default function Sidebar() {
</div>
{/* Summary count */}
<div className="flex items-center justify-between px-1 mb-2 text-[10px] text-[var(--text-dim)] font-mono uppercase tracking-wider">
<div className="flex items-center justify-between px-1 mb-2 text-[10px] text-[var(--text-muted)] font-mono uppercase tracking-wider">
<span>Knowledge Index</span>
<span>{filteredDocs.length} items</span>
</div>
@@ -268,9 +267,9 @@ export default function Sidebar() {
<div key={id} className="border-b border-[var(--border-color)] pb-3 last:border-0">
<Link
href={`/${id}`}
className={`flex items-center justify-between gap-1.5 px-2 py-1.5 mb-1 rounded-lg text-xs font-semibold uppercase tracking-wider transition-colors ${
className={`flex items-center justify-between gap-1.5 px-2 py-1 mb-1 rounded-md text-xs font-semibold uppercase tracking-wider transition-colors ${
isSectionActive
? "text-[var(--brand)] dark:text-[var(--accent)] bg-[var(--brand)]/10"
? "text-[var(--text-primary)] bg-[var(--bg-elevated)]"
: "text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-elevated)]"
}`}
>
@@ -278,7 +277,7 @@ export default function Sidebar() {
<span className="text-xs">{icon}</span>
<span>{label}</span>
</div>
<span className="text-[10px] text-[var(--text-dim)] font-normal font-mono bg-[var(--bg-surface)] px-1.5 py-0.5 rounded border border-[var(--border-color)]">
<span className="text-[10px] text-[var(--text-muted)] font-normal font-mono bg-[var(--bg-surface)] px-1.5 py-0.2 rounded border border-[var(--border-color)]">
{sectionDocs.length}
</span>
</Link>
@@ -300,7 +299,7 @@ export default function Sidebar() {
})}
{filteredDocs.length === 0 && (
<div className="text-center py-8 text-xs text-[var(--text-dim)]">
<div className="text-center py-8 text-xs text-[var(--text-dim)] font-mono">
No matching documents
</div>
)}
@@ -310,10 +309,10 @@ export default function Sidebar() {
<div className="pt-3 border-t border-[var(--border-color)] mt-auto">
<Link
href="/create"
className="flex items-center justify-center gap-2 w-full py-2 text-xs bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--brand)] text-[var(--text-secondary)] hover:text-[var(--text-primary)] rounded-lg transition-all font-medium shadow-xs group"
className="flex items-center justify-center gap-2 w-full py-2 text-xs bg-[var(--bg-elevated)] hover:bg-[var(--bg-elevated-hover)] border border-[var(--border-color)] hover:border-[var(--text-muted)] text-[var(--text-primary)] rounded-md transition-all font-medium shadow-xs"
>
<Plus className="w-3.5 h-3.5 text-[var(--brand)] dark:text-[var(--accent)] group-hover:scale-110 transition-transform" />
<span>Create New Document</span>
<Plus className="w-3.5 h-3.5 text-[var(--text-muted)]" />
<span>Create Document</span>
</Link>
</div>
</nav>