wiki base, middleware

This commit is contained in:
3zachm 2023-02-06 03:03:33 -08:00
parent db1c07a3cf
commit 22ba0c7cf6
16 changed files with 2203 additions and 263 deletions

View file

@ -72,7 +72,7 @@ function NavBar(props: NavBarProps) {
setActivePage("wiki");
}}
>
<ActiveLink href="/wiki/en" pageName="wiki">
<ActiveLink href="/wiki" pageName="wiki">
<WikiIcon />
</ActiveLink>
</m.div>

View file

@ -0,0 +1,18 @@
import mdStyles from "./markdown.module.css";
import RenderMarkdown from "./RenderMarkdown";
interface PageBodyProps {
children: string;
}
export default function PageBody({ children }: PageBodyProps) {
return (
<div className="prose prose-sm sm:prose lg:prose-lg xl:prose-xl">
<div className={mdStyles["markdown-body"]}>
<div className="text-left">
<RenderMarkdown>{children}</RenderMarkdown>
</div>
</div>
</div>
);
}

View file

@ -0,0 +1,31 @@
import Link from "next/link";
import ReactMarkdown from "react-markdown";
import rehypeHighlight from "rehype-highlight";
import rehypeRaw from "rehype-raw";
import rehypeSlug from "rehype-slug";
import remarkGfm from "remark-gfm";
interface RenderMarkdownProps {
children: string;
}
export default function RenderMarkdown({ children }: RenderMarkdownProps) {
return (
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeHighlight, rehypeSlug]}
// This doesnt work....
components={{
a: ({ node, ...props }) => {
return (
<Link legacyBehavior href={props.href as string}>
<a>{props.children ? props.children[0] : props.href}</a>
</Link>
);
},
}}
>
{children}
</ReactMarkdown>
);
}

View file

@ -0,0 +1,944 @@
/**
https://github.com/sindresorhus/github-markdown-css/blob/main/github-markdown-dark.css
**/
.markdown-body {
color-scheme: dark;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
margin: 0;
font-size: 16px;
line-height: 1.5;
word-wrap: break-word;
}
.markdown-body .octicon {
display: inline-block;
fill: currentColor;
vertical-align: text-bottom;
}
.markdown-body h1:hover .anchor .octicon-link:before,
.markdown-body h2:hover .anchor .octicon-link:before,
.markdown-body h3:hover .anchor .octicon-link:before,
.markdown-body h4:hover .anchor .octicon-link:before,
.markdown-body h5:hover .anchor .octicon-link:before,
.markdown-body h6:hover .anchor .octicon-link:before {
width: 16px;
height: 16px;
content: " ";
display: inline-block;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}
.markdown-body details,
.markdown-body figcaption,
.markdown-body figure {
display: block;
}
.markdown-body summary {
display: list-item;
}
.markdown-body [hidden] {
display: none !important;
}
.markdown-body a {
background-color: transparent;
color: #58a6ff;
text-decoration: none;
}
.markdown-body a:active,
.markdown-body a:hover {
outline-width: 0;
}
.markdown-body abbr[title] {
border-bottom: none;
text-decoration: underline dotted;
}
.markdown-body b,
.markdown-body strong {
font-weight: 600;
}
.markdown-body dfn {
font-style: italic;
}
.markdown-body h1 {
margin: 0.67em 0;
font-weight: 600;
padding-bottom: 0.3em;
font-size: 2em;
border-bottom: 1px solid #414141;
}
.markdown-body mark {
background-color: rgba(187, 128, 9, 0.15);
color: #c9d1d9;
}
.markdown-body small {
font-size: 90%;
}
.markdown-body sub,
.markdown-body sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
.markdown-body sub {
bottom: -0.25em;
}
.markdown-body sup {
top: -0.5em;
}
.markdown-body img {
border-style: none;
max-width: 100%;
box-sizing: content-box;
background-color: #0d1117;
}
.markdown-body code,
.markdown-body kbd,
.markdown-body pre,
.markdown-body samp {
font-family: monospace, monospace;
font-size: 1em;
}
.markdown-body figure {
margin: 1em 40px;
}
.markdown-body hr {
box-sizing: content-box;
overflow: hidden;
background: transparent;
border-bottom: 1px solid #414141;
height: 0.25em;
padding: 0;
margin: 24px 0;
background-color: #30363d;
border: 0;
}
.markdown-body input {
font: inherit;
margin: 0;
overflow: visible;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
.markdown-body [type="button"],
.markdown-body [type="reset"],
.markdown-body [type="submit"] {
-webkit-appearance: button;
}
.markdown-body [type="button"]::-moz-focus-inner,
.markdown-body [type="reset"]::-moz-focus-inner,
.markdown-body [type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
.markdown-body [type="button"]:-moz-focusring,
.markdown-body [type="reset"]:-moz-focusring,
.markdown-body [type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
.markdown-body [type="checkbox"],
.markdown-body [type="radio"] {
box-sizing: border-box;
padding: 0;
}
.markdown-body [type="number"]::-webkit-inner-spin-button,
.markdown-body [type="number"]::-webkit-outer-spin-button {
height: auto;
}
.markdown-body [type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
.markdown-body [type="search"]::-webkit-search-cancel-button,
.markdown-body [type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
.markdown-body ::-webkit-input-placeholder {
color: inherit;
opacity: 0.54;
}
.markdown-body ::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
.markdown-body a:hover {
text-decoration: underline;
}
.markdown-body hr::before {
display: table;
content: "";
}
.markdown-body hr::after {
display: table;
clear: both;
content: "";
}
.markdown-body table {
border-spacing: 0;
border-collapse: collapse;
display: block;
width: max-content;
max-width: 100%;
overflow: auto;
}
.markdown-body td,
.markdown-body th {
padding: 0;
}
.markdown-body details summary {
cursor: pointer;
}
.markdown-body details:not([open]) > *:not(summary) {
display: none !important;
}
.markdown-body kbd {
display: inline-block;
padding: 3px 5px;
font: 11px ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
line-height: 10px;
color: #c9d1d9;
vertical-align: middle;
background-color: #161b22;
border: solid 1px rgba(110, 118, 129, 0.4);
border-bottom-color: rgba(110, 118, 129, 0.4);
border-radius: 6px;
box-shadow: inset 0 -1px 0 rgba(110, 118, 129, 0.4);
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.markdown-body h2 {
font-weight: 600;
padding-bottom: 0.3em;
font-size: 1.5em;
border-bottom: 1px solid #414141;
}
.markdown-body h3 {
font-weight: 600;
font-size: 1.25em;
}
.markdown-body h4 {
font-weight: 600;
font-size: 1em;
}
.markdown-body h5 {
font-weight: 600;
font-size: 0.875em;
}
.markdown-body h6 {
font-weight: 600;
font-size: 0.85em;
color: #8b949e;
}
.markdown-body p {
margin-top: 0;
margin-bottom: 10px;
}
.markdown-body blockquote {
margin: 0;
padding: 0 1em;
color: #8b949e;
border-left: 0.25em solid #30363d;
}
.markdown-body ul,
.markdown-body ol {
margin-top: 0;
margin-bottom: 0;
padding-left: 2em;
}
.markdown-body ol ol,
.markdown-body ul ol {
list-style-type: lower-roman;
}
.markdown-body ul ul ol,
.markdown-body ul ol ol,
.markdown-body ol ul ol,
.markdown-body ol ol ol {
list-style-type: lower-alpha;
}
.markdown-body dd {
margin-left: 0;
}
.markdown-body tt,
.markdown-body code {
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
font-size: 12px;
}
.markdown-body pre {
margin-top: 0;
margin-bottom: 0;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas,
Liberation Mono, monospace;
font-size: 12px;
word-wrap: normal;
}
.markdown-body .octicon {
display: inline-block;
overflow: visible !important;
vertical-align: text-bottom;
fill: currentColor;
}
.markdown-body ::placeholder {
color: #484f58;
opacity: 1;
}
.markdown-body input::-webkit-outer-spin-button,
.markdown-body input::-webkit-inner-spin-button {
margin: 0;
-webkit-appearance: none;
appearance: none;
}
.markdown-body .pl-c {
color: #8b949e;
}
.markdown-body .pl-c1,
.markdown-body .pl-s .pl-v {
color: #79c0ff;
}
.markdown-body .pl-e,
.markdown-body .pl-en {
color: #d2a8ff;
}
.markdown-body .pl-smi,
.markdown-body .pl-s .pl-s1 {
color: #c9d1d9;
}
.markdown-body .pl-ent {
color: #7ee787;
}
.markdown-body .pl-k {
color: #ff7b72;
}
.markdown-body .pl-s,
.markdown-body .pl-pds,
.markdown-body .pl-s .pl-pse .pl-s1,
.markdown-body .pl-sr,
.markdown-body .pl-sr .pl-cce,
.markdown-body .pl-sr .pl-sre,
.markdown-body .pl-sr .pl-sra {
color: #a5d6ff;
}
.markdown-body .pl-v,
.markdown-body .pl-smw {
color: #ffa657;
}
.markdown-body .pl-bu {
color: #f85149;
}
.markdown-body .pl-ii {
color: #f0f6fc;
background-color: #8e1519;
}
.markdown-body .pl-c2 {
color: #f0f6fc;
background-color: #b62324;
}
.markdown-body .pl-sr .pl-cce {
font-weight: bold;
color: #7ee787;
}
.markdown-body .pl-ml {
color: #f2cc60;
}
.markdown-body .pl-mh,
.markdown-body .pl-mh .pl-en,
.markdown-body .pl-ms {
font-weight: bold;
color: #1f6feb;
}
.markdown-body .pl-mi {
font-style: italic;
color: #c9d1d9;
}
.markdown-body .pl-mb {
font-weight: bold;
color: #c9d1d9;
}
.markdown-body .pl-md {
color: #ffdcd7;
background-color: #67060c;
}
.markdown-body .pl-mi1 {
color: #aff5b4;
background-color: #033a16;
}
.markdown-body .pl-mc {
color: #ffdfb6;
background-color: #5a1e02;
}
.markdown-body .pl-mi2 {
color: #c9d1d9;
background-color: #1158c7;
}
.markdown-body .pl-mdr {
font-weight: bold;
color: #d2a8ff;
}
.markdown-body .pl-ba {
color: #8b949e;
}
.markdown-body .pl-sg {
color: #484f58;
}
.markdown-body .pl-corl {
text-decoration: underline;
color: #a5d6ff;
}
.markdown-body [data-catalyst] {
display: block;
}
.markdown-body g-emoji {
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 1em;
font-style: normal !important;
font-weight: 400;
line-height: 1;
vertical-align: -0.075em;
}
.markdown-body g-emoji img {
width: 1em;
height: 1em;
}
.markdown-body::before {
display: table;
content: "";
}
.markdown-body::after {
display: table;
clear: both;
content: "";
}
.markdown-body > *:first-child {
margin-top: 0 !important;
}
.markdown-body > *:last-child {
margin-bottom: 0 !important;
}
.markdown-body a:not([href]) {
color: inherit;
text-decoration: none;
}
.markdown-body .absent {
color: #f85149;
}
.markdown-body .anchor {
float: left;
padding-right: 4px;
margin-left: -20px;
line-height: 1;
}
.markdown-body .anchor:focus {
outline: none;
}
.markdown-body p,
.markdown-body blockquote,
.markdown-body ul,
.markdown-body ol,
.markdown-body dl,
.markdown-body table,
.markdown-body pre,
.markdown-body details {
margin-top: 0;
margin-bottom: 16px;
}
.markdown-body blockquote > :first-child {
margin-top: 0;
}
.markdown-body blockquote > :last-child {
margin-bottom: 0;
}
.markdown-body sup > a::before {
content: "[";
}
.markdown-body sup > a::after {
content: "]";
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #c9d1d9;
vertical-align: middle;
visibility: hidden;
}
.markdown-body h1:hover .anchor,
.markdown-body h2:hover .anchor,
.markdown-body h3:hover .anchor,
.markdown-body h4:hover .anchor,
.markdown-body h5:hover .anchor,
.markdown-body h6:hover .anchor {
text-decoration: none;
}
.markdown-body h1:hover .anchor .octicon-link,
.markdown-body h2:hover .anchor .octicon-link,
.markdown-body h3:hover .anchor .octicon-link,
.markdown-body h4:hover .anchor .octicon-link,
.markdown-body h5:hover .anchor .octicon-link,
.markdown-body h6:hover .anchor .octicon-link {
visibility: visible;
}
.markdown-body h1 tt,
.markdown-body h1 code,
.markdown-body h2 tt,
.markdown-body h2 code,
.markdown-body h3 tt,
.markdown-body h3 code,
.markdown-body h4 tt,
.markdown-body h4 code,
.markdown-body h5 tt,
.markdown-body h5 code,
.markdown-body h6 tt,
.markdown-body h6 code {
padding: 0 0.2em;
font-size: inherit;
}
.markdown-body ul.no-list,
.markdown-body ol.no-list {
padding: 0;
list-style-type: none;
}
.markdown-body ol[type="1"] {
list-style-type: decimal;
}
.markdown-body ol[type="a"] {
list-style-type: lower-alpha;
}
.markdown-body ol[type="i"] {
list-style-type: lower-roman;
}
.markdown-body div > ol:not([type]) {
list-style-type: decimal;
}
.markdown-body ul ul,
.markdown-body ul ol,
.markdown-body ol ol,
.markdown-body ol ul {
margin-top: 0;
margin-bottom: 0;
}
.markdown-body li > p {
margin-top: 16px;
}
.markdown-body li + li {
margin-top: 0.25em;
}
.markdown-body dl {
padding: 0;
}
.markdown-body dl dt {
padding: 0;
margin-top: 16px;
font-size: 1em;
font-style: italic;
font-weight: 600;
}
.markdown-body dl dd {
padding: 0 16px;
margin-bottom: 16px;
}
.markdown-body table th {
font-weight: 600;
}
.markdown-body table th,
.markdown-body table td {
padding: 6px 13px;
border: 1px solid #30363d;
}
.markdown-body table tr {
background-color: #00000060;
border-top: 1px solid #414141;
}
.markdown-body table tr:nth-child(2n) {
background-color: #0000003d;
}
.markdown-body table img {
background-color: transparent;
}
.markdown-body img[align="right"] {
padding-left: 20px;
}
.markdown-body img[align="left"] {
padding-right: 20px;
}
.markdown-body .emoji {
max-width: none;
vertical-align: text-top;
background-color: transparent;
}
.markdown-body span.frame {
display: block;
overflow: hidden;
}
.markdown-body span.frame > span {
display: block;
float: left;
width: auto;
padding: 7px;
margin: 13px 0 0;
overflow: hidden;
border: 1px solid #30363d;
}
.markdown-body span.frame span img {
display: block;
float: left;
}
.markdown-body span.frame span span {
display: block;
padding: 5px 0 0;
clear: both;
color: #c9d1d9;
}
.markdown-body span.align-center {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-center > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: center;
}
.markdown-body span.align-center span img {
margin: 0 auto;
text-align: center;
}
.markdown-body span.align-right {
display: block;
overflow: hidden;
clear: both;
}
.markdown-body span.align-right > span {
display: block;
margin: 13px 0 0;
overflow: hidden;
text-align: right;
}
.markdown-body span.align-right span img {
margin: 0;
text-align: right;
}
.markdown-body span.float-left {
display: block;
float: left;
margin-right: 13px;
overflow: hidden;
}
.markdown-body span.float-left span {
margin: 13px 0 0;
}
.markdown-body span.float-right {
display: block;
float: right;
margin-left: 13px;
overflow: hidden;
}
.markdown-body span.float-right > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: right;
}
.markdown-body code,
.markdown-body tt {
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
background-color: rgba(110, 118, 129, 0.4);
border-radius: 6px;
}
.markdown-body code br,
.markdown-body tt br {
display: none;
}
.markdown-body del code {
text-decoration: inherit;
}
.markdown-body pre code {
font-size: 100%;
}
.markdown-body pre > code {
padding: 0;
margin: 0;
word-break: normal;
white-space: pre;
background: transparent;
border: 0;
}
.markdown-body .highlight {
margin-bottom: 16px;
}
.markdown-body .highlight pre {
margin-bottom: 0;
word-break: normal;
}
.markdown-body .highlight pre,
.markdown-body pre {
padding: 16px;
overflow: auto;
font-size: 85%;
line-height: 1.45;
background-color: #00000060;
border-radius: 6px;
}
.markdown-body pre code,
.markdown-body pre tt {
display: inline;
max-width: auto;
padding: 0;
margin: 0;
overflow: visible;
line-height: inherit;
word-wrap: normal;
background-color: transparent;
border: 0;
}
.markdown-body .csv-data td,
.markdown-body .csv-data th {
padding: 5px;
overflow: hidden;
font-size: 12px;
line-height: 1;
text-align: left;
white-space: nowrap;
}
.markdown-body .csv-data .blob-num {
padding: 10px 8px 9px;
text-align: right;
background: #0d1117;
border: 0;
}
.markdown-body .csv-data tr {
border-top: 0;
}
.markdown-body .csv-data th {
font-weight: 600;
background: #161b22;
border-top: 0;
}
.markdown-body .footnotes {
font-size: 12px;
color: #8b949e;
border-top: 1px solid #30363d;
}
.markdown-body .footnotes ol {
padding-left: 16px;
}
.markdown-body .footnotes li {
position: relative;
}
.markdown-body .footnotes li:target::before {
position: absolute;
top: -8px;
right: -8px;
bottom: -8px;
left: -24px;
pointer-events: none;
content: "";
border: 2px solid #1f6feb;
border-radius: 6px;
}
.markdown-body .footnotes li:target {
color: #c9d1d9;
}
.markdown-body .footnotes .data-footnote-backref g-emoji {
font-family: monospace;
}
.markdown-body .task-list-item {
list-style-type: none;
}
.markdown-body .task-list-item label {
font-weight: 400;
}
.markdown-body .task-list-item.enabled label {
cursor: pointer;
}
.markdown-body .task-list-item + .task-list-item {
margin-top: 3px;
}
.markdown-body .task-list-item .handle {
display: none;
}
.markdown-body .task-list-item-checkbox {
margin: 0 0.2em 0.25em -1.6em;
vertical-align: middle;
}
.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {
margin: 0 -1.6em 0.25em 0.2em;
}
.markdown-body ::-webkit-calendar-picker-indicator {
filter: invert(50%);
}

8
interfaces/WikiPage.ts Normal file
View file

@ -0,0 +1,8 @@
export default interface WikiPage {
slug: string;
layout?: string;
content: string;
data: {
layout?: string;
};
}

View file

@ -32,7 +32,7 @@ function DashLayout(props: DashLayoutProps) {
const title = props.metaTags.title ?? "Dashboard - toffee";
return (
<m.div
className="bg-gradient-to-t from-zinc-900 to-[#3015457b]"
className="bg-zinc-900"
initial="initial"
animate="animate"
exit="exit"

70
lib/wiki/api.ts Normal file
View file

@ -0,0 +1,70 @@
import { default as pathlib } from "path";
import fg from "fast-glob";
import fs from "fs";
import matter from "gray-matter";
const wikiDir = pathlib.join(process.cwd(), "InvestWiki/wiki");
const getAllFiles = (path: string) => {
const files = fg.sync("**/*.md", {
cwd: path,
onlyFiles: true,
absolute: false,
});
return files;
};
function getAllWikiPaths() {
const files = getAllFiles(wikiDir);
// manipulate array entries, remove the .md extension and move it to the front of the string
const paths = files.map((file: string) => {
const path = file.replace(".md", "");
// move the last part of the path to the front
const pathParts = path.split("/");
const lastPart = pathParts.pop();
if (lastPart) {
pathParts.unshift(lastPart);
}
return pathParts.join("/");
});
return paths;
}
function getWikiPath(lang: string, path: string) {
const files = getAllFiles(wikiDir);
// filter the files to only include the ones in the path
const pageFiles = files.filter(
(file) => file.split("/").slice(0, -1).join("/") === path
);
let pagePath = "";
// do https://github.com/vercel/next.js/blob/canary/examples/blog-starter/lib/api.ts
if (pageFiles.length !== 0) {
// check if there is a file with the language code
const langFile = pageFiles.find((file) => file.includes(`${lang}.md`));
if (langFile) {
pagePath = langFile;
} else {
// otherwise, use the english file if it exists, otherwise return the first file
pagePath =
pageFiles.find((file) => file.includes("en.md")) ?? pageFiles[0];
}
}
return pagePath;
}
function getWikiContent(lang: string, path: string) {
// get the path to the file
const relativePath = getWikiPath(lang, path);
if (!relativePath) {
return null;
}
const filePath = pathlib.join(wikiDir, relativePath);
// read the file
const fileContents = fs.readFileSync(filePath, "utf8");
const { data, content } = matter(fileContents);
return { data, content };
}
export { getWikiPath, getAllWikiPaths, getWikiContent };

View file

@ -1,7 +0,0 @@
import { remark } from "remark";
import html from "remark-html";
export default async function convertMarkdown(markdown: string) {
const result = await remark().use(html).process(markdown);
return result.toString();
}

26
middleware.ts Normal file
View file

@ -0,0 +1,26 @@
import { NextRequest, NextResponse } from "next/server";
const PUBLIC_FILE = /\.(.*)$/;
export async function middleware(req: NextRequest) {
if (
req.nextUrl.pathname.startsWith("/_next") ||
req.nextUrl.pathname.includes("/api/") ||
PUBLIC_FILE.test(req.nextUrl.pathname)
) {
return;
}
// if path is /wiki/, redirect to /wiki/:locale
if (req.nextUrl.pathname === "/wiki") {
const language =
req.headers
.get("accept-language")
?.split(",")?.[0]
.split("-")?.[0]
.toLowerCase() || "en";
const redirUrl = req.nextUrl.clone();
redirUrl.pathname = redirUrl.pathname + `/${language}`;
return NextResponse.rewrite(redirUrl);
}
return;
}

View file

@ -11,6 +11,11 @@ const nextConfig = {
"cdn.frankerfacez.com",
],
},
i18n: {
// append/clean as needed
locales: ["en", "de", "fr", "es", "it", "pt", "ru", "zh", "ja", "ko"],
defaultLocale: "en",
},
};
module.exports = nextConfig;

997
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,7 @@
"chart.js": "^4.2.0",
"eslint": "8.28.0",
"eslint-config-next": "13.0.4",
"fast-glob": "^3.2.12",
"framer-motion": "^7.6.19",
"gray-matter": "^4.0.3",
"ioredis": "^5.2.5",
@ -20,8 +21,11 @@
"react": "18.2.0",
"react-chartjs-2": "^5.2.0",
"react-dom": "18.2.0",
"remark": "^14.0.2",
"remark-html": "^15.0.1",
"react-markdown": "^8.0.5",
"rehype-highlight": "^6.0.0",
"rehype-raw": "^6.1.1",
"rehype-slug": "^5.1.0",
"remark-gfm": "^3.0.1",
"sharp": "^0.31.2",
"typescript": "4.9.3"
},
@ -30,7 +34,6 @@
"@types/node": "18.11.9",
"@types/react": "18.0.25",
"@types/react-dom": "18.0.9",
"@types/recursive-readdir": "^2.2.1",
"autoprefixer": "^10.4.13",
"husky": "^8.0.2",
"lint-staged": "^13.0.3",

View file

@ -1,50 +0,0 @@
import DashLayout from "../../../layouts/DashLayout";
import Image from "next/image";
// markdown styles
import styles from "../../../styles/markdown.module.css";
interface WikiLandingPageProps {
children: React.ReactNode;
}
function WikiLandingPage(props: WikiLandingPageProps) {
return (
<div className="flex h-full w-full flex-col items-center justify-center p-3 font-plusJakarta">
<div className="inline-grid h-full w-full max-w-7xl grid-cols-10 rounded-2xl bg-zinc-800 bg-opacity-70 p-6">
<div className="col-span-10 text-center text-6xl font-semibold text-white">
<div className="flex flex-row items-center justify-center">
<Image
src="/img/emotes/peepoTalk.webp"
alt={"PeepoTalk"}
width={64}
height={64}
className="mr-3 mb-4"
/>
<div>
<span>the t</span>
<span className="text-orange-400">off</span>
<span>ee wiki</span>
</div>
</div>
<div className={styles.markdown}>
<div className="text-left"></div>
</div>
</div>
</div>
</div>
);
}
WikiLandingPage.getLayout = function getLayout(page: React.ReactNode) {
const metaTags = {
title: "Wiki",
description: "Wiki for toffee",
};
return (
<DashLayout metaTags={metaTags} navIcon="wiki">
{page}
</DashLayout>
);
};
export default WikiLandingPage;

View file

@ -0,0 +1,164 @@
import Image from "next/image";
import { getAllWikiPaths, getWikiContent } from "../../../lib/wiki/api";
import WikiPage from "../../../interfaces/WikiPage";
import DashLayout from "../../../layouts/DashLayout";
import Link from "next/link";
import { m } from "framer-motion";
import RenderMarkdown from "../../../components/wiki/RenderMarkdown";
import PageBody from "../../../components/wiki/PageBody";
import { ReactElement, useEffect, useState } from "react";
interface WikiLandingPageProps {
children: React.ReactNode;
page: WikiPage;
}
interface TableOfContentsItem {
id: string;
type: string;
text: string;
}
function WikiLandingPage(props: WikiLandingPageProps) {
const [wikiContent, setWikiContent] = useState<ReactElement>(<></>);
const [indexContent, setIndexContent] = useState<TableOfContentsItem[]>([]);
// needed for proper hydration due to replacing some elements
useEffect(() => {
setWikiContent(<PageBody>{props.page.content}</PageBody>);
}, [props.page.content]);
useEffect(() => {
const toc: TableOfContentsItem[] = [];
const headings = document.querySelectorAll("h2, h3");
// store the heading text, id, and type, keep order
headings.forEach((heading) => {
const id = heading.getAttribute("id");
const type = heading.tagName.toLowerCase();
const text = heading.textContent;
if (id && type && text) {
toc.push({ id, type, text });
}
});
setIndexContent(toc);
console.log(toc);
}, [wikiContent]);
return (
<div className="flex h-full w-full flex-col items-center justify-center p-3 font-plusJakarta ">
<div className="inline-grid h-full w-full max-w-screen-2xl grid-cols-10">
{/* Sidebar */}
<div className="col-span-2 hidden w-full flex-col items-center justify-center lg:block">
<div className="w-full rounded-tl-2xl rounded-bl-2xl border-r-2 border-orange-400 border-opacity-60 bg-zinc-800 bg-opacity-70 p-6 text-left text-6xl text-white">
<div className="text-2xl">Contents</div>
<div className="mt-4 text-left text-orange-400">
{indexContent.map((item) => {
return (
// increase indent based on heading level
<div
style={{
paddingLeft: `${(parseInt(item.type[1]) - 2) * 1.25}rem`,
}}
className="text-xl"
key={item.id}
>
<Link href={`#${item.id}`}>
<p className="mt-2 overflow-hidden overflow-ellipsis whitespace-nowrap hover:text-white">
{item.text}
</p>
</Link>
</div>
);
})}
</div>
</div>
</div>
<div className="col-span-10 rounded-2xl bg-zinc-800 bg-opacity-70 p-6 text-center text-6xl text-white lg:col-span-8 lg:rounded-tl-none">
<m.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3 }}
key={props.page.slug}
>
<div className="w-full px-3">{wikiContent}</div>
</m.div>
</div>
</div>
</div>
);
}
type Params = {
params: {
slug: string[];
};
};
export async function getStaticProps({ params }: Params) {
// only language
if (params.slug.length < 2) {
// if langauge is two letters, redirect to its home
if (params.slug[0].length === 2 && params.slug[0].match(/^[a-z]+$/i)) {
return {
redirect: {
destination: `/wiki/${params.slug[0]}/home`,
},
};
}
// else, 404 to prevemt building pointless pages (e.g. /wiki/this-is-not-a-language x 580258538 times)
return {
notFound: true,
};
}
// slug[0] = language
// slug[1...n] = page path
const lang = params.slug[0];
const path = params.slug.slice(1).join("/");
const pageData = getWikiContent(lang, path);
// if no content, 404
if (!pageData) {
return {
notFound: true,
};
}
return {
props: {
page: {
slug: params.slug,
content: pageData.content,
data: pageData.data,
},
},
};
}
export async function getStaticPaths() {
const paths = getAllWikiPaths();
return {
paths: paths.map((path) => {
return {
params: {
slug: path.split("/"),
},
};
}),
fallback: "blocking",
};
}
WikiLandingPage.getLayout = function getLayout(page: React.ReactNode) {
const metaTags = {
title: "Wiki - toffee",
description: "Wiki for toffee",
};
return (
<DashLayout metaTags={metaTags} navIcon="wiki">
{page}
</DashLayout>
);
};
export default WikiLandingPage;

View file

@ -51,3 +51,118 @@ body::-webkit-scrollbar-corner,
div::body::-webkit-scrollbar-corner {
background-color: transparent;
}
.hljs {
color: #c9d1d9;
background: #0d1117;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_ {
/* prettylights-syntax-keyword */
color: #ff7b72;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_ {
/* prettylights-syntax-entity */
color: #d2a8ff;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id {
/* prettylights-syntax-constant */
color: #79c0ff;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string {
/* prettylights-syntax-string */
color: #a5d6ff;
}
.hljs-built_in,
.hljs-symbol {
/* prettylights-syntax-variable */
color: #ffa657;
}
.hljs-comment,
.hljs-code,
.hljs-formula {
/* prettylights-syntax-comment */
color: #8b949e;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #7ee787;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #c9d1d9;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #1f6feb;
font-weight: bold;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #f2cc60;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #c9d1d9;
font-style: italic;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #c9d1d9;
font-weight: bold;
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #aff5b4;
background-color: #033a16;
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #ffdcd7;
background-color: #67060c;
}
.hljs-char.escape_,
.hljs-link,
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
}

View file

@ -1,18 +0,0 @@
.markdown {
@apply text-lg leading-relaxed;
}
.markdown p,
.markdown ul,
.markdown ol,
.markdown blockquote {
@apply my-6;
}
.markdown h2 {
@apply mt-12 mb-4 text-3xl leading-snug;
}
.markdown h3 {
@apply mt-8 mb-4 text-2xl leading-snug;
}