md posts
This commit is contained in:
parent
7948a458f9
commit
36bb02cf64
|
|
@ -14,10 +14,13 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dimforge/rapier2d": "^0.11.2",
|
"@dimforge/rapier2d": "^0.11.2",
|
||||||
"@tldraw/tldraw": "2.0.2",
|
"@tldraw/tldraw": "2.0.2",
|
||||||
|
"@types/markdown-it": "^14.1.1",
|
||||||
"@vercel/analytics": "^1.2.2",
|
"@vercel/analytics": "^1.2.2",
|
||||||
|
"gray-matter": "^4.0.3",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.22.3"
|
"react-router-dom": "^6.22.3",
|
||||||
|
"vite-plugin-md": "^0.21.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.4.1",
|
"@biomejs/biome": "1.4.1",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { useCanvas } from "@/hooks/useCanvas"
|
||||||
import { createShapes } from "@/utils";
|
import { createShapes } from "@/utils";
|
||||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||||
import { Contact } from "@/components/Contact";
|
import { Contact } from "@/components/Contact";
|
||||||
|
import { Post } from '@/components/Post';
|
||||||
inject();
|
inject();
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
|
ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
|
||||||
|
|
@ -23,6 +24,7 @@ function App() {
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
<Route path="/card/contact" element={<Contact />} />
|
<Route path="/card/contact" element={<Contact />} />
|
||||||
|
<Route path="/posts/:slug" element={<Post />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,8 @@ export function Default() {
|
||||||
<h1>My work</h1>
|
<h1>My work</h1>
|
||||||
<p>
|
<p>
|
||||||
Alongside my independent work I am a researcher at <a href="https://block.science/">Block Science</a> building{' '}
|
Alongside my independent work I am a researcher at <a href="https://block.science/">Block Science</a> building{' '}
|
||||||
<i>knowledge organisation infrastructure</i> and at <a href="https://economicspace.agency/">ECSA</a> working on{' '}
|
<i>knowledge organisation infrastructure</i> and an engineer-in-residence at <a href="https://tldraw.com">tldraw</a>. I am also part of the nascent <a href="https://libcomp.org/">Liberatory Computing</a>{' '}
|
||||||
<i>computational media</i>. I am also part of the nascent <a href="https://libcomp.org/">Liberatory Computing</a>{' '}
|
collective, occasional collaborator with <a href="https://economicspace.agency/">ECSA</a> and a co-organiser of the <a href="https://canvasprotocol.org/">OCWG</a>.
|
||||||
collective and a co-organiser of the <a href="https://canvasprotocol.org/">OCWG</a>.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>Get in touch</h1>
|
<h1>Get in touch</h1>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
export function Post() {
|
||||||
|
const { slug } = useParams<{ slug: string }>();
|
||||||
|
const [post, setPost] = React.useState<{ title: string, html: string } | null>(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
import(`../posts/${slug}.md`)
|
||||||
|
.then((module) => {
|
||||||
|
setPost(module.default);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Failed to load post:', error);
|
||||||
|
setPost(null);
|
||||||
|
});
|
||||||
|
}, [slug]);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<h1>{post.title}</h1>
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: post.html }} />
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# This is a title
|
||||||
|
this is some text under the title
|
||||||
|
|
||||||
|
here is an image:
|
||||||
|

|
||||||
|
|
||||||
|
requirements:
|
||||||
|
- basic html (headings, quotes, italics, bold...)
|
||||||
|
- images and videos (easy authoring, what are the vercel limits too?)
|
||||||
|
- static gen, we don't want SPA pop-in, we want 100% prerendered html
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import MarkdownIt from 'markdown-it';
|
||||||
|
|
||||||
|
const md = new MarkdownIt({
|
||||||
|
html: true,
|
||||||
|
breaks: true,
|
||||||
|
linkify: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Customize Markdown-to-HTML mapping here
|
||||||
|
md.renderer.rules.paragraph_open = () => '<p class="custom-paragraph">';
|
||||||
|
md.renderer.rules.image = (tokens, idx, options, env, self) => {
|
||||||
|
const token = tokens[idx];
|
||||||
|
const src = token.attrGet('src');
|
||||||
|
const alt = token.content;
|
||||||
|
return `<img src="${src}" alt="${alt}" class="custom-image" />`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function markdownToHtml(content: string): string {
|
||||||
|
return md.render(content);
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": ["vite.config.ts"]
|
"include": ["vite.config.ts", "src/utils/*"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,29 @@
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig, Plugin } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
import react from '@vitejs/plugin-react'
|
||||||
import wasm from "vite-plugin-wasm";
|
import wasm from "vite-plugin-wasm";
|
||||||
import topLevelAwait from "vite-plugin-top-level-await";
|
import topLevelAwait from "vite-plugin-top-level-await";
|
||||||
|
import matter from 'gray-matter';
|
||||||
|
import { markdownToHtml } from './src/utils/markdownToHtml';
|
||||||
|
|
||||||
|
const markdownPlugin: Plugin = {
|
||||||
|
name: 'markdown-plugin',
|
||||||
|
enforce: 'pre',
|
||||||
|
transform(code, id) {
|
||||||
|
if (id.endsWith('.md')) {
|
||||||
|
const { data, content } = matter(code);
|
||||||
|
const html = markdownToHtml(content);
|
||||||
|
const jsonContent = JSON.stringify({ ...data, html });
|
||||||
|
return `export default ${jsonContent};`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
wasm(),
|
wasm(),
|
||||||
topLevelAwait()
|
topLevelAwait(),
|
||||||
|
markdownPlugin
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
sourcemap: true, // Enable source maps for production
|
sourcemap: true, // Enable source maps for production
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue