From 9cc7cdd7cda0ce1e9115374ed1fbad099d7e8430 Mon Sep 17 00:00:00 2001 From: "Builder.io" Date: Tue, 11 Nov 2025 18:00:24 +0000 Subject: [PATCH] Update InternalDocsLayout to Gitbook-style document page features cgen-bb86dd35ff284fe6bb1c385349c83dce --- .../internal-docs/InternalDocsLayout.tsx | 183 ++++++++++++------ 1 file changed, 129 insertions(+), 54 deletions(-) diff --git a/client/pages/internal-docs/InternalDocsLayout.tsx b/client/pages/internal-docs/InternalDocsLayout.tsx index 280d8b9d..831822c2 100644 --- a/client/pages/internal-docs/InternalDocsLayout.tsx +++ b/client/pages/internal-docs/InternalDocsLayout.tsx @@ -1,6 +1,6 @@ -import { useState } from "react"; +import React, { useEffect, useState } from "react"; import { Link, useLocation, Navigate } from "react-router-dom"; -import { Menu, X, ChevronRight, Lock, Home } from "lucide-react"; +import { Menu, X, ChevronRight, Lock, Home, ExternalLink } from "lucide-react"; import { useAuth } from "@/contexts/AuthContext"; interface NavSpace { @@ -151,15 +151,89 @@ interface InternalDocsLayoutProps { description?: string; } -export default function InternalDocsLayout({ - children, - title, - description, -}: InternalDocsLayoutProps) { +// Map routes -> source file path so "Edit this page" can link to the component in the repo +const SOURCE_MAP: Record = { + "/internal-docs": "code/client/pages/internal-docs/Space1Welcome.tsx", + "/internal-docs/axiom-model": "code/client/pages/internal-docs/Space1AxiomModel.tsx", + "/internal-docs/find-your-role": "code/client/pages/internal-docs/Space1FindYourRole.tsx", + "/internal-docs/code-of-conduct": "code/client/pages/internal-docs/Space2CodeOfConduct.tsx", + "/internal-docs/communication": "code/client/pages/internal-docs/Space2Communication.tsx", + "/internal-docs/meetings": "code/client/pages/internal-docs/Space2MeetingCadence.tsx", + "/internal-docs/brand": "code/client/pages/internal-docs/Space2BrandVoice.tsx", + "/internal-docs/tech-stack": "code/client/pages/internal-docs/Space2TechStack.tsx", + "/internal-docs/foundation-governance": "code/client/pages/internal-docs/Space3FoundationGovernance.tsx", + "/internal-docs/foundation-protocol": "code/client/pages/internal-docs/Space3OpenSourceProtocol.tsx", + "/internal-docs/foundation-programs": "code/client/pages/internal-docs/Space3CommunityPrograms.tsx", + "/internal-docs/corp-product": "code/client/pages/internal-docs/Space4ProductOps.tsx", + "/internal-docs/corp-blueprints": "code/client/pages/internal-docs/Space4CorpBlueprints.tsx", + "/internal-docs/corp-clients": "code/client/pages/internal-docs/Space4ClientOps.tsx", + "/internal-docs/corp-platform": "code/client/pages/internal-docs/Space4PlatformStrategy.tsx", + "/internal-docs/onboarding": "code/client/pages/internal-docs/Space5Onboarding.tsx", + "/internal-docs/finance": "code/client/pages/internal-docs/Space5Finance.tsx", +}; + +export default function InternalDocsLayout({ children, title, description }: InternalDocsLayoutProps) { const { user, loading } = useAuth(); const [sidebarOpen, setSidebarOpen] = useState(false); const location = useLocation(); + useEffect(() => { + // Add copy buttons to code blocks + const addCopyButtons = () => { + document.querySelectorAll("pre").forEach((pre) => { + if ((pre as HTMLElement).dataset.hasCopy === "true") return; + (pre as HTMLElement).dataset.hasCopy = "true"; + const btn = document.createElement("button"); + btn.className = "copy-code-btn absolute right-3 top-3 bg-slate-800/70 text-slate-200 text-xs px-2 py-1 rounded-md hover:bg-slate-800"; + btn.innerText = "Copy"; + btn.onclick = async () => { + const code = pre.querySelector("code")?.textContent || pre.textContent || ""; + try { + await navigator.clipboard.writeText(code); + btn.innerText = "Copied"; + setTimeout(() => (btn.innerText = "Copy"), 1500); + } catch { + btn.innerText = "Copy"; + } + }; + (pre as HTMLElement).style.position = "relative"; + (pre as HTMLElement).appendChild(btn); + }); + }; + + // Add heading anchors for h2/h3 + const addAnchors = () => { + const container = document.querySelector(".internal-docs-content"); + if (!container) return; + container.querySelectorAll("h2, h3").forEach((el) => { + if ((el as HTMLElement).querySelector(".heading-anchor")) return; + const text = (el.textContent || "").trim(); + const id = text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, ""); + (el as HTMLElement).id = id; + const anchor = document.createElement("a"); + anchor.className = "heading-anchor ml-2 text-slate-500 text-sm opacity-0 hover:opacity-100 transition-opacity"; + anchor.href = `#${id}`; + anchor.innerText = "#"; + (el as HTMLElement).appendChild(anchor); + }); + }; + + addCopyButtons(); + addAnchors(); + + const obs = new MutationObserver(() => { + addCopyButtons(); + addAnchors(); + }); + obs.observe(document.querySelector(".internal-docs-content") || document.body, { childList: true, subtree: true }); + return () => obs.disconnect(); + }, []); + + useEffect(() => { + // close sidebar on navigation (mobile) + setSidebarOpen(false); + }, [location.pathname]); + if (loading) { return (
@@ -177,6 +251,11 @@ export default function InternalDocsLayout({ const isCurrentPage = (path: string) => location.pathname === path; + const sourcePath = SOURCE_MAP[location.pathname]; + const editUrl = sourcePath + ? `https://github.com/AeThex-Corporation/aethex-forge/blob/main/${sourcePath}` + : undefined; + return (
{/* Sidebar */} @@ -187,11 +266,7 @@ export default function InternalDocsLayout({ > {/* Header */}
- setSidebarOpen(false)} - > + setSidebarOpen(false)}>
Internal Hub
@@ -199,10 +274,7 @@ export default function InternalDocsLayout({
- + Back to Site @@ -215,12 +287,8 @@ export default function InternalDocsLayout({
{space.emoji}
-
- {space.title} -
-
- {space.description} -
+
{space.title}
+
{space.description}
@@ -231,17 +299,11 @@ export default function InternalDocsLayout({ to={page.path} onClick={() => setSidebarOpen(false)} className={`block px-3 py-2 rounded-lg text-sm transition-all group ${ - isCurrentPage(page.path) - ? "bg-blue-600 text-white font-medium" - : "text-slate-400 hover:text-slate-200 hover:bg-slate-800" + isCurrentPage(page.path) ? "bg-blue-600 text-white font-medium" : "text-slate-400 hover:text-slate-200 hover:bg-slate-800" }`} >
{page.title}
- {page.description && ( -
- {page.description} -
- )} + {page.description &&
{page.description}
} ))}
@@ -260,47 +322,60 @@ export default function InternalDocsLayout({
{/* Mobile Header */}
- - + ← Back
-
- {title && ( -
-

{title}

- {description && ( -

{description}

+
+
+
+ {title && ( +
+

{title}

+ {description &&

{description}

} +
)}
- )} -
{children}
+
+ {editUrl && ( + + Edit this page + + )} +
+
+ +
{children}
{/* Footer */}
-
+

- © 2025 AeThex. This is an internal operations hub. Information - here is confidential and for authorized personnel only. + © 2025 AeThex. This is an internal operations hub. Information here is confidential and for authorized personnel only.

+ + {/* Inline styles for print and copy button */} +
); }