feat: Initial code commit

Add project code, including a basic markdown browser and some utility
functions with associated tests
This commit is contained in:
Skyler Grey 2024-05-17 00:06:25 +00:00
parent 26b25a6cb6
commit 9e8658caea
Signed by: minion
GPG key ID: F27E3E5922772E7A
11 changed files with 1899 additions and 0 deletions

9
public/favicon.svg Normal file
View file

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

124
src/components/NavBar.astro Normal file
View file

@ -0,0 +1,124 @@
---
import { getCollection } from "astro:content";
import { relativePagePaths } from "../lib/pagePaths";
type Props = { currentPage: string };
const { currentPage } = Astro.props;
const wikiPages = await getCollection("wiki");
const pages = relativePagePaths(wikiPages, currentPage);
---
<style>
.navbar {
/* background: var(--bg-navbar); */
background: #ccc;
padding: .1em 0em;
height: 100%;
}
.navbar > ul {
display: flex;
flex-direction: column;
padding: 0;
}
.navbar > ul > li {
/* background: var(--bg-navbar-item); */
list-style: none;
text-transform: capitalize;
}
.navbar > ul > li:hover {
/* background: var(--bg-navbar-accent); */
background: aquamarine;
}
.active {
/* background: var(--bg-navbar-active); */
background: #eee;
}
.next {
margin-left: 1em;
}
.navbar > ul > li > a {
padding: .25em .5em;
display: block;
}
.separator {
height: .125em;
/* background: var(--bg-navbar-line); */
background: #f00;
margin: .125em 0em;
}
</style>
<nav class="navbar">
<ul>
{
pages.parentDirectory ?
<li>
<a
href={"/" + pages.parentDirectory.id}
>
{pages.parentDirectory.data.title.replaceAll("-", " ")}/
</a>
</li><div class="separator" /> : null
}
{
pages.siblingDirectories.map(page =>
<li>
<a
href={"/" + page.id}
>
{page.data.title.replaceAll("-", " ")}/
</a>
</li>
)
}
{
pages.siblingPages.map(page =>
<li>
<a
href={"/" + page.id}
>
{page.data.title}
</a>
</li>
)
}
{
pages.siblingDirectories.length || pages.siblingPages.length ?
<div class="separator" />
: null
}
{
<li>
<a class="active"
href={"/" + pages.currentPage.id}
>
{pages.childPages.length || pages.childDirectories.length ? `${pages.currentPage.data.title.replaceAll("-", " ")}/` : pages.currentPage.data.title}
</a>
</li>
}
{
pages.childDirectories.map(page =>
<li>
<a class="next"
href={"/" + page.id}
>
{page.data.title.replaceAll("-", " ")}/
</a>
</li>
)
}
{
pages.childPages.map(page =>
<li>
<a class="next"
href={"/" + page.id}
>
{page.data.title}
</a>
</li>
)
}
</ul>
</nav>

View file

@ -0,0 +1,81 @@
---
import { relativePagePaths } from "../lib/pagePaths";
import { getCollection } from "astro:content";
type Props = { path: string }
const { path } = Astro.props;
const wikiPages = await getCollection("wiki");
const pages = relativePagePaths(wikiPages, path);
---
<style>
.navpage {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%
}
.navpage > ul {
display: flex;
flex-direction: column;
padding: 0;
}
.navpage > ul > li {
/* background: var(--bg-navpage-item); */
list-style: none;
text-transform: capitalize;
}
.navpage > ul > li:hover {
/* background: var(--bg-navpage-accent); */
background: aquamarine;
}
.active {
/* background: var(--bg-navpage-active); */
background: #eee;
}
.next {
margin-left: 1em;
}
.navpage > ul > li > a {
padding: .25em .5em;
display: block;
}
.separator {
height: .125em;
/* background: var(--bg-navpage-line); */
background: #f00;
margin: .125em 0em;
}
</style>
<nav class="navpage">
<ul>
{
pages.childDirectories.map(page =>
<li>
<a class="next"
href={"/" + page.id}
>
{page.data.title.replaceAll("-", " ")}/
</a>
</li>
)
}
{
pages.childPages.map(page =>
<li>
<a class="next"
href={"/" + page.id}
>
{page.data.title}
</a>
</li>
)
}
</ul>
</nav>

View file

@ -0,0 +1,16 @@
---
import "../style/content.css";
import { type CollectionEntry } from 'astro:content';
type Props = { post: CollectionEntry<'wiki'> };
const { post } = Astro.props;
const { Content } = await post.render();
---
<div class="contents">
<Content />
</div>

2
src/env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

1384
src/lib/pagePaths.test.ts Normal file

File diff suppressed because it is too large Load diff

163
src/lib/pagePaths.ts Normal file
View file

@ -0,0 +1,163 @@
import type { CollectionEntry } from "astro:content";
import { parse, join, sep } from "node:path";
export interface PageLinkData {
id: string;
data: { title: string; };
}
type AllPathInformation = Map<string, CollectionEntry<"wiki"> | null>;
export interface Paths {
siblingPages: PageLinkData[];
siblingDirectories: PageLinkData[];
childPages: PageLinkData[];
childDirectories: PageLinkData[];
parentDirectory: PageLinkData | null;
currentPage: PageLinkData;
};
export function relativePagePaths(wikiEntries: PageLinkData[], currentPath: string): Paths {
let currentPage: PageLinkData | undefined;
let parentDirectory: PageLinkData | undefined | null;
const siblingPages: Map<string, PageLinkData> = new Map();
const childPages: Map<string, PageLinkData> = new Map();
const currentPathParsed = parse(currentPath);
const currentPathExtensionless = join(currentPathParsed.dir, currentPathParsed.name);
const childDirectoryPaths: Set<string> = new Set();
const siblingDirectoryPaths: Set<string> = new Set();
for (const entry of wikiEntries) {
const pagePathParsed = parse(entry.id);
const pagePathExtensionless = join(pagePathParsed.dir, pagePathParsed.name);
if (pagePathExtensionless === currentPathExtensionless) {
currentPage = entry
continue;
}
const isInCurrentDirectory = pagePathParsed.dir === currentPathParsed.dir;
if (isInCurrentDirectory) {
siblingPages.set(pagePathExtensionless, entry);
continue;
}
const isDirectChild = pagePathParsed.dir === currentPathExtensionless;
if (isDirectChild) {
childPages.set(pagePathExtensionless, entry);
continue;
}
const isIndirectChild = pagePathParsed.dir.startsWith(currentPathExtensionless + sep);
if (isIndirectChild) {
const nextPathSeparator = pagePathParsed.dir.indexOf(sep, currentPathExtensionless.length + 1);
if (nextPathSeparator === -1) {
childDirectoryPaths.add(pagePathParsed.dir);
continue;
}
childDirectoryPaths.add(pagePathParsed.dir.slice(0, nextPathSeparator));
continue;
}
const isIndirectInCurrentDirectory = currentPathParsed.dir === "" || pagePathParsed.dir.startsWith(currentPathParsed.dir + sep);
if (isIndirectInCurrentDirectory) {
const nextPathSeparator = pagePathParsed.dir.indexOf(sep, currentPathParsed.dir.length + 1);
if (nextPathSeparator === -1) {
siblingDirectoryPaths.add(pagePathParsed.dir);
continue;
}
siblingDirectoryPaths.add(pagePathParsed.dir.slice(0, nextPathSeparator));
continue;
}
const isParentDirectory = pagePathExtensionless === currentPathParsed.dir;
if (isParentDirectory) {
parentDirectory = entry;
}
}
const childDirectories: PageLinkData[] = [];
for (const childDirectoryPath of childDirectoryPaths.values()) {
const childDirectoryPage = childPages.get(childDirectoryPath);
if (childDirectoryPage) {
childDirectories.push(childDirectoryPage);
childPages.delete(childDirectoryPath);
continue;
}
const childDirectoryPathParsed = parse(childDirectoryPath);
childDirectories.push({
id: childDirectoryPath,
data: { title: childDirectoryPathParsed.name }
});
}
const siblingDirectories: PageLinkData[] = [];
for (const siblingDirectoryPath of siblingDirectoryPaths.values()) {
const siblingDirectoryPage = siblingPages.get(siblingDirectoryPath);
if (siblingDirectoryPage) {
siblingDirectories.push(siblingDirectoryPage);
siblingPages.delete(siblingDirectoryPath);
continue;
}
const siblingDirectoryPathParsed = parse(siblingDirectoryPath);
siblingDirectories.push({
id: siblingDirectoryPath,
data: { title: siblingDirectoryPathParsed.name }
});
}
if (currentPage === undefined) {
currentPage = {
id: currentPath,
data: { title: currentPathParsed.name }
};
}
if (parentDirectory === undefined) {
if (currentPathParsed.dir) {
const parentDirectoryPathParsed = parse(currentPathParsed.dir);
parentDirectory = {
id: currentPathParsed.dir,
data: { title: parentDirectoryPathParsed.name }
};
} else {
parentDirectory = null;
}
}
return {
siblingPages: Array.from(siblingPages.values()),
childPages: Array.from(childPages.values()),
siblingDirectories,
childDirectories,
currentPage,
parentDirectory,
}
}
export function allPageAndDirectoryPaths(wikiEntries: CollectionEntry<"wiki">[]): AllPathInformation {
const pathInformation: Map<string, CollectionEntry<"wiki"> | null> = new Map();
for (const entry of wikiEntries) {
pathInformation.set(entry.id, entry);
let parsedEntryPath = parse(entry.id);
while (parsedEntryPath.dir) {
pathInformation.set(parsedEntryPath.dir, null);
parsedEntryPath = parse(parsedEntryPath.dir);
}
}
return pathInformation;
}

97
src/pages/[...slug].astro Normal file
View file

@ -0,0 +1,97 @@
---
import { type CollectionEntry, getCollection, getEntry } from 'astro:content';
import NavBar from '../components/NavBar.astro';
import Renderer from '../components/Renderer.astro';
import NavPage from '../components/NavPage.astro';
import "../style/globals.css"
import { allPageAndDirectoryPaths } from "../lib/pagePaths";
import { parse } from "node:path"
export async function getStaticPaths() {
const wikiPages = await getCollection('wiki');
const paths = allPageAndDirectoryPaths(wikiPages);
return [
{
params: { slug: undefined },
props: { path: "home", name: "Home", post: await getEntry("wiki", "home") }
},
...Array.from(paths.entries()).flatMap(([key, post]) => {
if (!post) {
return [{
params: { slug: key },
props: { path: key, name: parse(key).name }
}]
} else {
return [
{
params: { slug: post.slug },
props: { path: post.id, name: post.data.title, post },
},
{
params: { slug: post.slug + ".md" },
props: { path: post.id, name: post.data.title, post },
}
]
}
})
// ...pages.flatMap((post: CollectionEntry<'wiki'> | undefined) => {
// if (!post) {
// return [{
// params: { slug: }
// }]
// } else {
// return [
// {
// params: { slug: post.slug },
// props: { path: post.id, name: post.data.title, post },
// },
// {
// params: { slug: post.slug + ".md" },
// props: { path: post.id, name: post.data.title, post },
// }
// ]
// }
// })
];
}
type Props = {
post?: CollectionEntry<'wiki'>
path: string;
name: string;
};
const { post, path, name } = Astro.props;
---
<head>
<meta charset="utf-8">
<title>Aux Docs - {name}</title>
</head>
<style>
.box {
display: flex;
flex-direction: row;
margin: 0;
height: 100vh;
}
.nav-pane {
display: flex;
flex-direction: column;
min-width: 16em;
width: fit-content;
}
</style>
<body class="box">
<div class="nav-pane">
<NavBar currentPage={path} />
</div>
{
post ? (
<Renderer post={post} />
) : (
<NavPage path={path} />
)
}
</body>

1
src/reset.d.ts vendored Normal file
View file

@ -0,0 +1 @@
import "@total-typescript/ts-reset";

15
src/style/content.css Normal file
View file

@ -0,0 +1,15 @@
.contents table {
border: 1px solid black;
border-collapse: collapse;
}
.contents td {
padding: 1em;
border: 1px solid black;
}
.contents {
overflow: scroll;
height: 100vh;
padding: 0em 2em;
}

7
src/style/globals.css Normal file
View file

@ -0,0 +1,7 @@
a:visited {
color: blue;
}
.contents {
width: 100%;
}