aboutsummaryrefslogtreecommitdiff
path: root/pages
diff options
context:
space:
mode:
Diffstat (limited to 'pages')
-rw-r--r--pages/_app.tsx34
-rw-r--r--pages/index.tsx101
-rw-r--r--pages/post/[id].tsx216
-rw-r--r--pages/search.tsx170
4 files changed, 0 insertions, 521 deletions
diff --git a/pages/_app.tsx b/pages/_app.tsx
deleted file mode 100644
index a6c53d3..0000000
--- a/pages/_app.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import Head from 'next/head';
-import { useEffect, useState } from 'react';
-
-import '../styles/button.css';
-import '../styles/card.css';
-import '../styles/code.css';
-import '../styles/globals.css';
-import '../styles/image.css';
-import '../styles/layout.css';
-import '../styles/navbar.css';
-import '../styles/print.css';
-import '../styles/search.css';
-import '../styles/tags.css';
-import '../styles/theme.css';
-
-export default function Blog({ Component, pageProps }) {
- var [dark, setDark] = useState(false);
- useEffect(() => {
- let colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)');
- setDark(!!colorSchemeQueryList.matches);
- colorSchemeQueryList.addEventListener('change', e => setDark(!!e.matches));
- }, []);
-
- return <>
- <Head>
- <html lang='en-US' />
- <link rel='preload' as='style' href='/font/font.css' onLoad={() => this.rel = 'stylesheet'} />
- <meta property='og:url' content='https://blog.pipeframe.xyz' />
- <meta property='og:type' content='website' />
- <meta name='theme-color' content={dark ? '#0D0C1A' : '#EFE9F4'} />
- </Head>
- <Component {...pageProps} />
- </>;
-}
diff --git a/pages/index.tsx b/pages/index.tsx
deleted file mode 100644
index 634950d..0000000
--- a/pages/index.tsx
+++ /dev/null
@@ -1,101 +0,0 @@
-import Head from 'next/head';
-import Button from '../components/button';
-import PostCard from '../components/card';
-import Chapters, { chapter } from '../components/chapters';
-import Navbar, { MobileNavbar, NavbarItem } from '../components/navbar';
-import Seperator from '../components/seperator';
-import { ArticleMeta, getStaticProps as getBlogPage, RenderedArticle } from './post/[id]';
-import { PostsInfo } from './search';
-
-import { useEffect, useState } from 'react';
-
-// edit this to change the post displayed on the home page and the pinned posts
-var posts = ['index', 'software'];
-
-export default function Home(props: {
- posts: Array<{
- props: {
- content: string;
- meta: ArticleMeta;
- };
- }>;
-}) {
- var [posts, setPosts] = useState<PostsInfo>({ posts: [], valid_tags: [] });
-
- useEffect(() => {
- (async () => {
- var posts = await fetch('/posts.json');
- var postsJson: PostsInfo = await posts.json();
- setPosts(postsJson);
- })();
- }, []);
-
- return <div>
- <Head>
- <title>Loek's Blog</title>
- <meta property='og:site_name' content="Loek's blog" />
- <meta property='og:title' content="Loek's excruciatingly interesting blog" />
- <meta property='og:description' content='This is my personal blog website' />
- </Head>
- <div className='centeredPage'>
- <div className='titleWrapper'>
- <h1>{props.posts[0].props.meta.title}</h1>
- </div>
- <div className='navAreaWrapper'>
- <div className='sticky'>
- <Navbar page='home' />
- <NavbarItem title='Pinned posts:' classList={['pinned']} />
- <Chapters
- chapters={[
- ...props.posts.slice(1).map(post => {
- return {
- children: post.props.meta.chapters,
- name: post.props.meta.title,
- sectionLink: '/post/' + post.props.meta.id,
- } as chapter;
- }),
- ]}
- />
- </div>
- </div>
- <MobileNavbar />
- <div className='contentWrapper'>
- {props.posts.map((post, index) => {
- return <>
- {index != 0 && <h1>{post.props.meta.title}</h1>}
- <RenderedArticle content={post.props.content} />
- {index + 1 != props.posts.length && <Seperator />}
- {index == 0 && <>
- <h2>Recent posts</h2>
- <div className='recentPosts'>
- {posts.posts.sort((a, b) => (
- new Date(a.date).getTime() -
- new Date(b.date).getTime()
- )).reverse().slice(0, 4).map(post => {
- return <PostCard post={post} />;
- })}
- </div>
-
- <div>
- <Button text='Go to all posts' href='/search' />
- </div>
- <Seperator />
- </>}
- </>;
- })}
- </div>
- </div>
- </div>;
-}
-
-export function getStaticProps() {
- var postsContent = [];
-
- posts.forEach(id => {
- postsContent.push(getBlogPage({ params: { id } }));
- });
-
- var staticProps = { props: { posts: postsContent } };
-
- return staticProps;
-}
diff --git a/pages/post/[id].tsx b/pages/post/[id].tsx
deleted file mode 100644
index 35a41e6..0000000
--- a/pages/post/[id].tsx
+++ /dev/null
@@ -1,216 +0,0 @@
-import Head from 'next/head';
-import { readdirSync, readFileSync } from 'fs';
-import { join } from 'path';
-import { CSSProperties, ReactNode, useState } from 'react';
-import ReactMarkdown from 'react-markdown';
-import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
-import rehypeRaw from 'rehype-raw';
-import gfm from 'remark-gfm';
-
-import Chapters, { chapter } from '../../components/chapters';
-import Image from '../../components/image';
-import Navbar, { MobileNavbar } from '../../components/navbar';
-import Seperator from '../../components/seperator';
-import Tags from '../../components/tag';
-
-export interface ArticleMeta {
- title?: string;
- subtitle?: string;
- author?: string;
- tags?: Array<string>;
- date?: string;
- chapters?: Array<chapter>;
- cover?: string;
- id?: string;
-}
-
-var headingLevel = (input: string) => input?.match(/^[#]+/)[0]?.length || 0;
-
-var sectionID = (input: string) =>
- input
- .replace(/[()\[\]{}!@#$%^&*<>?,./\;':"\\|=+]/g, '')
- .replace(/\s/g, '-')
- .toLowerCase();
-
-function Heading(props: {
- children?: ReactNode;
- level?: number;
-}) {
- var HeadingTag = 'h' + props.level as keyof JSX.IntrinsicElements;
- return <HeadingTag id={sectionID(props.children[0])} children={props.children} />;
-}
-
-function Code(props: {
- className?: string;
- children?: ReactNode;
-}) {
- var language = /language-(\w+)/.exec(props.className || '');
- if (!language) return <code children={props.children} className={props.className} />;
- return <SyntaxHighlighter
- language={language[1]}
- children={props.children.toString().trim()}
- useInlineStyles={false}
- PreTag='div'
- style={{}}
- />;
-}
-
-export function RenderedArticle(props: { content: string; }) {
- return <ReactMarkdown
- rehypePlugins={[rehypeRaw]}
- remarkPlugins={[gfm]}
- children={props.content}
- components={{
- img: ({ node, ...props }) => <Image src={props.src as string} alt={props.alt as string} />,
- hr: Seperator,
-
- h1: Heading, // TODO: fix this garbage
- h2: Heading,
- h3: Heading,
- h4: Heading,
- h5: Heading,
- h6: Heading,
-
- code: Code,
- }}
- />;
-}
-
-var collapsed = false;
-function toggle() {
- collapsed = !collapsed;
- document.documentElement.style.setProperty('--collapse-horizontal-gap', Number(collapsed).toString());
-}
-
-export default function Post(props: {
- content: string;
- meta: ArticleMeta;
-}) {
- return <div>
- <Head>
- <title>{props.meta.title} - Loek's Blog</title>
- <meta property='og:site_name' content={props.meta.date} />
- <meta property='og:title' content={props.meta.title} />
- <meta property='og:description' content={props.meta.subtitle} />
- </Head>
- <div className='centeredPage'>
- <div className='titleWrapper'>
- <h1 onClick={toggle}>{props.meta.title}</h1>
- <p className='subtile'>{props.meta.subtitle}</p>
- {props.meta.tags && <Tags tags={props.meta.tags} />}
- </div>
- <div className='navAreaWrapper'>
- <div className='sticky'>
- <Navbar />
- <Chapters chapters={props.meta.chapters} />
- </div>
- </div>
- <MobileNavbar />
- <div className='contentWrapper'>
- <RenderedArticle content={props.content} />
- </div>
- </div>
- </div>;
-}
-
-var parseTag = {
- 'title': (val: string) => val,
- 'subtitle': (val: string) => val,
- 'author': (val: string) => val,
- 'cover': (val: string) => val,
- 'tags': (val: string) => val.split(',').map(i => i.trim()),
- 'date': (val: string) => new Date(val).toDateString(),
-};
-
-function parseMeta(file: Array<string>): ArticleMeta {
- var meta: ArticleMeta = {};
-
- file.forEach(line => {
- if (!line.startsWith('[meta]: ')) return;
- var tags = line.match(/\[meta\]:\s+\<(.+?)\>\s+\((.+?)\)/);
- if (!tags || !tags[1] || !tags[2]) return;
- if (!parseTag.hasOwnProperty(tags[1])) return;
- meta[tags[1]] = parseTag[tags[1]](tags[2]);
- });
-
- return meta;
-}
-
-function parseToCRecursive(headings: Array<string>): Array<chapter> {
- interface WIPchapter extends chapter {
- unparsedChildren?: Array<string>;
- }
- var children: Array<WIPchapter> = [];
-
- var lowestLevel = headingLevel(headings[0]);
- var currentChildIndex = -1;
- for (var i in headings) {
- var localLevel = headingLevel(headings[i]);
- if (localLevel == lowestLevel) {
- var chapterName = headings[i].match(/^[#]+\s+(.+)/)[1];
- children.push({
- name: chapterName,
- sectionLink: '#' + sectionID(chapterName),
- unparsedChildren: [],
- });
- currentChildIndex += 1;
- } else {
- children[currentChildIndex].unparsedChildren.push(headings[i]);
- }
- }
-
- children.map(child => {
- child.children = parseToCRecursive(child.unparsedChildren);
- delete child.unparsedChildren;
-
- return child;
- });
-
- return children as Array<chapter>;
-}
-
-function parseToC(file: Array<string>): Array<chapter> {
- var fileAsStr = file.join('\n');
- fileAsStr = fileAsStr.replace(/```.*?```/gs, ''); // filter out code blocks from table of contents
- var fileAsArr = fileAsStr.split('\n');
- var chapterStrings = fileAsArr.filter(line => line.startsWith('#'));
- return parseToCRecursive(chapterStrings);
-}
-
-function preprocessor(fileContent: string) {
- var fileAsArr = fileContent.split('\n');
- var meta = parseMeta(fileAsArr);
- meta.chapters = parseToC(fileAsArr);
- var result = fileAsArr.join('\n').trim();
- return { meta, result };
-}
-
-export function getStaticProps(props: { params: { id: string; }; }) {
- var filename = join('posts/', props.params.id + '.md');
- var filecontent = readFileSync(filename).toString().trim();
-
- var parsed = preprocessor(filecontent);
- parsed.meta.id = props.params.id;
-
- return {
- props: {
- content: parsed.result,
- meta: parsed.meta,
- },
- };
-}
-
-export function getStaticPaths() {
- var files = readdirSync('posts').filter(f => f.endsWith('.md'));
-
- return {
- paths: files.map((f) => {
- return {
- params: {
- id: f.substr(0, f.length - 3),
- },
- };
- }),
- fallback: false,
- };
-}
diff --git a/pages/search.tsx b/pages/search.tsx
deleted file mode 100644
index 14f5e15..0000000
--- a/pages/search.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
-import Head from 'next/head';
-import Fuse from 'fuse.js';
-import { useEffect, useState } from 'react';
-
-import Navbar, { MobileNavbar } from '../components/navbar';
-import Tags from '../components/tag';
-
-import SearchOutlinedIcon from '@material-ui/icons/SearchOutlined';
-
-function SearchBar(props: { searchFunction: () => void; }) {
- return <div className='searchBar'>
- <input
- className='input'
- id='searchInput'
- placeholder='Search for posts...'
- onChange={() => props.searchFunction()}
- spellCheck='false'
- autoComplete='off'
- />
- <button className='button' onClick={() => props.searchFunction()}>
- <SearchOutlinedIcon />
- </button>
- </div>;
-}
-
-export interface Post {
- title: string;
- subtitle: string;
- author: string;
- date: string;
- id: string;
- cover: string;
- tags: Array<string>;
-}
-
-export interface PostsInfo {
- valid_tags: Array<string>;
- posts: Array<Post>;
-}
-
-function Post(props: { post: Post; }) {
- return <a className='post' href={'/post/' + props.post.id}>
- <b className='title'>{props.post.title}</b>
- {props.post.subtitle && <p className='subtitle'>{props.post.subtitle}</p>}
- <p className='authordate'>
- Written by {props.post.author} on {new Date(props.post.date).toLocaleString('en-us', {
- month: 'long',
- day: 'numeric',
- })}
- </p>
- <Tags tags={props.post.tags} />
- </a>;
-}
-
-function Posts(props: { posts: Array<Post>; }) {
- return <div className='searchResults'>
- {props.posts.map(post => <Post post={post} key={Math.random().toString()} />)}
- </div>;
-}
-
-function searchFilter(query: string, tags: Array<string>) {
- var output = {
- query: '',
- tags: [],
- };
-
- // remove string literals from tag matching
- var queryWithoutLiterals = query.replace(/\".+?\"/g, '');
-
- // find tags and remove them from the query
- tags.forEach(tag => {
- var index = queryWithoutLiterals.indexOf(tag);
- if (index == -1) return;
-
- // remove tag from query
- queryWithoutLiterals = queryWithoutLiterals.substr(0, index)
- + queryWithoutLiterals.substr(index + tag.length);
-
- output.tags.push(tag);
- });
-
- // add back in the string literals (janky just gets pasted on end)
- output.query = queryWithoutLiterals + ' '
- + (query.match(/\".+?\"/g)
- ?.map(r => r.substr(1, r.length - 2))
- .join(' ') || '');
- return output;
-}
-
-export default function SearchPage() {
- var [posts, setPosts] = useState<PostsInfo>({ posts: [], valid_tags: [] });
- var [query, setQuery] = useState('-');
- var [visiblePosts, setVisiblePosts] = useState<Array<Post>>([]);
-
- var fuse = new Fuse(posts.posts, {
- keys: [
- 'title',
- 'subtitle',
- 'author',
- 'date',
- 'id',
- 'tags',
- ],
- isCaseSensitive: false,
- });
-
- useEffect(() => {
- (async () => {
- var query = new URLSearchParams(window.location.search).get('q') || '';
- if (query) {
- (document.getElementById('searchInput') as HTMLInputElement).value = query;
- }
-
- var posts = await fetch('/posts.json');
- var postsJson: PostsInfo = await posts.json();
- setPosts(postsJson);
- setQuery(query);
- })();
- }, []);
-
- useEffect(() => {
- var search = searchFilter(query, posts.valid_tags);
-
- if (search.query.length == 0) {
- var results = posts.posts;
- } else {
- var fuseSearch = fuse.search(search.query);
- var results = fuseSearch.map(res => res.item);
- }
-
- results = results.filter(result => {
- for (var i in search.tags) {
- if (!result.tags.includes(search.tags[i])) {
- return false;
- }
- }
- return true;
- });
- results = results.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
- setVisiblePosts(results);
- }, [query]);
-
- return <div>
- <Head>
- <title>Search - Loek's Blog</title>
- <meta property='og:site_name' content="Loek's blog" />
- <meta property='og:title' content="Loek's excruciatingly interesting blog" />
- <meta property='og:description' content='This is my personal blog website' />
- </Head>
- <div className='centeredPage'>
- <div className='titleWrapper'>
- <h1>Search for posts</h1>
- </div>
- <div className='navAreaWrapper'>
- <div className='sticky'>
- <Navbar page='search' />
- </div>
- </div>
- <MobileNavbar />
- <div className='contentWrapper'>
- <SearchBar
- searchFunction={() => {
- setTimeout(() => setQuery((document.getElementById('searchInput') as HTMLInputElement).value));
- }}
- />
- <Posts posts={visiblePosts} />
- </div>
- </div>
- </div>;
-}