@@ -0,0 +1,157 @@
1+import Head from "next/head";
2+import Link from "next/link";
3+import { useRouter } from "next/router";
4+import { useState } from "react";
5+import classnames from "classnames";
6+import { join } from "path";
7+import { getDocs, formatDate, buildPageTree, getPage } from "../../lib/lib";
8+import Markdown from "../../components/Markdown";
9+import ContentArea from "../../components/ContentArea";
10+import Sidebar from "../../components/Sidebar";
11+import markdownStyles from "../../styles/markdown.module.css";
12+import { decode } from "html-entities";
13+
14+const breadcrumbs = (posts, paths) => {
15+ const results = [
16+ <Link href="/">Urbit</Link>,
17+ <span className="px-1">/</span>,
18+ <Link href="/using">Operator's Manual</Link>,
19+ ];
20+ let thisLink = "/docs";
21+ for (const path of paths) {
22+ posts = posts.children[path];
23+ thisLink = join(thisLink, path);
24+ results.push(
25+ <span className="px-1">/</span>,
26+ <Link href={thisLink}>{posts.title}</Link>
27+ );
28+ }
29+ return results;
30+};
31+
32+const childPages = (thisLink, children, level = 0) => (
33+ <ul className="pl-1">
34+ {Object.entries(children).map(([childSlug, child]) => (
35+ <li>{pageTree(join(thisLink, childSlug), child, level)}</li>
36+ ))}
37+ </ul>
38+);
39+
40+const pageTree = (thisLink, tree, level = 0) => {
41+ const router = useRouter();
42+ const firstCrumb = "/" + router.asPath.split("/").slice(1, -1).join("/");
43+
44+ const includesThisPage = firstCrumb.includes(thisLink);
45+ const isThisPage = router.asPath === thisLink;
46+ const [isOpen, toggleTree] = useState(includesThisPage);
47+
48+ const activeClasses = classnames({
49+ hidden: !isOpen,
50+ });
51+
52+ const headingItemClasses = classnames({
53+ "pl-0 text-black text-base font-semibold hover:text-green leading-relaxed":
54+ level === 0,
55+ "pl-4 text-black text-base font-semibold hover:text-green": level === 1,
56+ "pl-8 text-black text-base hover:text-green": level === 2,
57+ });
58+
59+ const pageItemClasses = classnames({
60+ "pl-4 text-black text-base hover:text-green": level === 0,
61+ "pl-8 text-black text-base hover:text-green": level === 1,
62+ "pl-12 text-black text-base hover:text-green": level === 2,
63+ dot: isThisPage,
64+ });
65+
66+ return (
67+ <>
68+ <span onClick={() => toggleTree(!isOpen)}>
69+ <p className={`${headingItemClasses} cursor-pointer`}>{tree.title}</p>
70+ </span>
71+ <div className={activeClasses}>
72+ <ul className={"pl-1"}>
73+ {tree.pages.map(({ title, slug }) => {
74+ const href = join(thisLink, slug);
75+ const isSelected = router.asPath === href;
76+ const selectedClasses = classnames({
77+ dot: isSelected,
78+ "text-green": isSelected,
79+ });
80+ return (
81+ <li>
82+ <Link href={href}>
83+ <a
84+ className={`relative ${pageItemClasses} ${selectedClasses}`}
85+ >
86+ {title}
87+ </a>
88+ </Link>
89+ </li>
90+ );
91+ })}
92+ </ul>
93+ {childPages(thisLink, tree.children, level + 1)}
94+ </div>
95+ </>
96+ );
97+};
98+
99+export default function UsingLayout({ posts, data, params, search, markdown }) {
100+ return (
101+ <>
102+ <Head>
103+ <title>{data.title} / Operator's Manual / Urbit.org</title>
104+ </Head>
105+ <div className="flex w-screen h-screen min-h-screen w-screen">
106+ <Sidebar>{childPages("/docs", posts.children)}</Sidebar>
107+ <ContentArea
108+ breadcrumbs={breadcrumbs(posts, params.slug?.slice(0, -1) || "")}
109+ title={data.title}
110+ search={search}
111+ section={"Operator's Manual"}
112+ >
113+ <div className={markdownStyles["markdown"]}>
114+ <article
115+ dangerouslySetInnerHTML={{ __html: decode(markdown) }}
116+ ></article>
117+ </div>
118+ </ContentArea>
119+ </div>
120+ </>
121+ );
122+}
123+
124+export async function getStaticProps({ params }) {
125+ const posts = buildPageTree(join(process.cwd(), "content/using"), "weight");
126+
127+ const { data, content } = getPage(
128+ join(process.cwd(), "content/using", params.slug?.join("/") || "/")
129+ );
130+
131+ const markdown = await Markdown({ post: { content: content } });
132+
133+ return { props: { posts, data, markdown, params } };
134+}
135+
136+export async function getStaticPaths() {
137+ const posts = buildPageTree(join(process.cwd(), "content/using"), "weight");
138+ const slugs = [];
139+
140+ const allHrefs = (thisLink, tree) => {
141+ slugs.push(thisLink, ...tree.pages.map((e) => join(thisLink, e.slug)));
142+ allHrefsChildren(thisLink, tree.children);
143+ };
144+
145+ const allHrefsChildren = (thisLink, children) => {
146+ Object.entries(children).map(([childSlug, child]) => {
147+ allHrefs(join(thisLink, childSlug), child);
148+ });
149+ };
150+
151+ allHrefs("/using", posts);
152+
153+ return {
154+ paths: slugs,
155+ fallback: false,
156+ };
157+}