diff --git a/.gitattributes b/.gitattributes index d73961c..d04ab78 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ *.pdf filter=lfs diff=lfs merge=lfs -text *.mp4 filter=lfs diff=lfs merge=lfs -text +*.mov filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text *.gif filter=lfs diff=lfs merge=lfs -text diff --git a/build/markdownPlugin.js b/build/markdownPlugin.js index 3fa79ef..4db9f11 100644 --- a/build/markdownPlugin.js +++ b/build/markdownPlugin.js @@ -10,8 +10,8 @@ export const markdownPlugin = { const { data, content } = matter(code); const filename = path.basename(id, ".md"); const html = markdownToHtml(filename, content); - const htmlString = JSON.stringify({ html }); - return `export default ${htmlString};`; + return `export const html = ${JSON.stringify(html)}; + export const data = ${JSON.stringify(data)};`; } }, }; diff --git a/build/markdownToHtml.js b/build/markdownToHtml.js index 5ffd8f5..b3e691d 100644 --- a/build/markdownToHtml.js +++ b/build/markdownToHtml.js @@ -1,4 +1,6 @@ import MarkdownIt from "markdown-it"; +// import markdownItLatex from "markdown-it-latex"; +import markdownLatex from "markdown-it-latex2img"; const md = new MarkdownIt({ html: true, @@ -6,28 +8,29 @@ const md = new MarkdownIt({ linkify: true, }); -const mediaSrc = (folderName, fileName) => { - return `/posts/${folderName}/${fileName}`; -}; +md.use( + markdownLatex, + // {style: "width: 200%; height: 200%;",} +); + +// const mediaSrc = (folderName, fileName) => { +// return `/posts/${folderName}/${fileName}`; +// }; -// Customize Markdown-to-HTML mapping here -// md.renderer.rules.paragraph_open = () => '

'; md.renderer.rules.code_block = (tokens, idx, options, env, self) => { console.log("tokens", tokens); return `${tokens[idx].content}`; }; md.renderer.rules.image = (tokens, idx, options, env, self) => { - // console.log('env', env) const token = tokens[idx]; const src = token.attrGet("src"); const alt = token.content; const postName = env.postName; - const formattedSrc = mediaSrc(postName, src); + const formattedSrc = `/posts/${postName}/${src}`; - if (src.endsWith(".mp4")) { + if (src.endsWith(".mp4") || src.endsWith(".mov")) { return ``; } diff --git a/package.json b/package.json index 0fc8f76..00cbdf5 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@vercel/analytics": "^1.2.2", "gray-matter": "^4.0.3", "markdown-it": "^14.1.0", + "markdown-it-latex2img": "^0.0.6", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.22.3" diff --git a/src/App.tsx b/src/App.tsx index ed14069..022c0ff 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,7 +17,6 @@ ReactDOM.createRoot(document.getElementById("root")!).render(); function App() { - return ( diff --git a/src/components/Default.tsx b/src/components/Default.tsx index 01db0b4..38fe221 100644 --- a/src/components/Default.tsx +++ b/src/components/Default.tsx @@ -47,6 +47,8 @@ export function Default() {

Writing

    +
  1. Scoped Propagators: A programming model for spatial canvases
  2. Objects as Reference: Toward Robust First Principles of Digital Organization
  3. diff --git a/src/components/Post.tsx b/src/components/Post.tsx index 0642af1..ef4e118 100644 --- a/src/components/Post.tsx +++ b/src/components/Post.tsx @@ -1,31 +1,44 @@ -import React from 'react'; +import { calcReadingTime } from '@/utils/readingTime'; +import { useEffect, useState } 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); + const [post, setPost] = useState<{ html: string, data: Record } | null>(null); + const [isLoading, setIsLoading] = useState(true); - React.useEffect(() => { + useEffect(() => { import(`../posts/${slug}.md`) .then((module) => { - setPost(module.default); + setPost({ html: module.html, data: module.data }); + setIsLoading(false); }) .catch((error) => { console.error('Failed to load post:', error); - setPost(null); + setIsLoading(false); }); }, [slug]); - if (!post) { - return
    Loading...
    ; + + if (isLoading) { + return
    hold on...
    ; } + if (!post) { + return
    post not found :(
    ; + } + + document.title = post.data.title; + return (
    Orion Reed
    -

    {post.title}

    +
    +

    {post.data.title}

    + {calcReadingTime(post.html)} +
    ); diff --git a/src/css/style.css b/src/css/style.css index e475795..414dc33 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -55,13 +55,18 @@ i { } code { - background-color: #38424c; + background-color: #e4e9ee; width: 100%; - color: white; + color: #38424c; padding: 0.2em 0.4em; border-radius: 4px; } +b, +strong { + font-variation-settings: "wght" 600; +} + blockquote { margin: -1em; padding: 1em; @@ -157,12 +162,18 @@ p a { ol, ul { padding-left: 0; + margin-top: 0; font-size: 1rem; & li::marker { color: rgba(0, 0, 0, 0.322); } } +img { + display: block; + margin: 0 auto; +} + @media (max-width: 600px) { main { padding: 2em; @@ -175,6 +186,33 @@ ul { } } +/* Some conditional spacing */ +table:not(:has(+ p)) { + margin-bottom: 2em; +} + +p:has(+ ul) { + margin-bottom: 0.5em; +} +p:has(+ ol) { + margin-bottom: 0.5em; +} + +.loading { + font-family: "Recursive"; + font-variation-settings: "CASL" 1; + font-size: 1rem; + text-align: center; + position: absolute; + top: 40%; + left: 50%; + transform: translate(-50%, -50%); + background-color: #f1f1f1; + border: 1px solid #c0c9d1; + padding: 0.5em; + border-radius: 4px; +} + /* CANVAS SHENANIGANS */ #toggle-physics, #toggle-canvas { diff --git a/src/posts/scoped-propagators.md b/src/posts/scoped-propagators.md new file mode 100644 index 0000000..8ab578a --- /dev/null +++ b/src/posts/scoped-propagators.md @@ -0,0 +1,77 @@ +--- +title: 'Scoped Propagators' +--- + +Scoped propagators are ... canvas environment implementation ... liveness ... debugging ... quick DIY tools ... lorem pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. + +An early work-in-progress + +As you can see in the examples above, scoped propagators can be used to add interactivity (e.g. buttons and sliders), constrain behaviour (layout constraints), add dynamic behaviour (lerp smoothing), create small utilitites, like homebrewed tools, and can combine these abilities to create more complex systems (a shark game with score counter, on/off switch, constrained joystick controls, and follower behaviour) all with 9 quite terse arrows. + +#### NOTES/PHRASES: +- liveness/interactivity +- diagrammatic specification +- pure functions +- without escape +- adding behaviour in-situ, with the same tools and affordances as the rest of the environment (its just arrows) +- bridging between systems (governance system example) + +## Definition +A scoped propagator is formed of a function which takes a *source* and *target* node and returns an updated *target* node, and a scope which defines some subset of events to listen to. We could express this more succinctly, though just as informally: + +$$ +{ +f(a, b) \mapsto b' +} +$$ +$$ +{ +S \subseteq \text{Events} +} +$$ + +For debugging purposes to allow side effects in propagators, allowing arbitrary JS execution. + +Below are the four event scopes which are currently implemented. While I found these appropriate and useful for adding liveness to a canvas environment, part of the value of *scopes* is that they can be domain-specific. + +| Scope | Conditions | +|----------|----------| +| change | Properties of the source node change | +| click | A source node is clicked | +| tick | A tick (frame render) event fires | +| geo | A node changes whose bounds overlap the target | + +## Applications +... + +#### Debugging +... inspection video + +#### Automation +reducing repetetive tasks, etc. +... prop update tool, multiple versions DAG + +## Prior Work +Scoped Propagators are related to [**propagator networks**](https://dspace.mit.edu/handle/1721.1/54635) but differ in three key ways: +- propagation happens along *edges* instead of *nodes* +- stateful cells are replaced with schematised nodes, and +- propagation is limited to a subset of events. + +Propagator networks have several advantages as a general-purpose model... + +## Future Work & Open Questions +- function reuse, access to scope +- handling of side effects +- handling cycles +- SISO is easy, but what about MIMO? +- application to other graph-shaped systems, like graph databases + +vids: +![intro](intro.mp4) +![buttons](buttons.mp4) +![constraints](constraints.mp4) +![lerp](lerp.mp4) +![tools](tools.mp4) +![inspection](inspection.mp4) +![game](game.mp4) +![bridging systems](bridging.mov) \ No newline at end of file diff --git a/src/posts/scoped-propagators/bridging.mov b/src/posts/scoped-propagators/bridging.mov new file mode 100644 index 0000000..6ee10ad --- /dev/null +++ b/src/posts/scoped-propagators/bridging.mov @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8a5fea015bcf937fbce5b0a233067e31059fb2e4d0f32f6471395fb82c6407c +size 1708542 diff --git a/src/posts/scoped-propagators/buttons.mp4 b/src/posts/scoped-propagators/buttons.mp4 new file mode 100644 index 0000000..bf91bd3 --- /dev/null +++ b/src/posts/scoped-propagators/buttons.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8763ad95ef6e31e4c66f9298d0ad22296edd83ab2fd6d7aa1549c8845521c932 +size 61302 diff --git a/src/posts/scoped-propagators/constraints.mp4 b/src/posts/scoped-propagators/constraints.mp4 new file mode 100644 index 0000000..99c3e98 --- /dev/null +++ b/src/posts/scoped-propagators/constraints.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbc1493d124f92abe9c35534b4b6dca4a8081c570f4e3f07c4d9559d60dea3eb +size 87493 diff --git a/src/posts/scoped-propagators/examples.mp4 b/src/posts/scoped-propagators/examples.mp4 new file mode 100644 index 0000000..d15e5cf --- /dev/null +++ b/src/posts/scoped-propagators/examples.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55e5c2ce64c027a808951ad11f8757f3a8d7d739639a72517a4b39c35aad815d +size 21393177 diff --git a/src/posts/scoped-propagators/game.mp4 b/src/posts/scoped-propagators/game.mp4 new file mode 100644 index 0000000..682ae88 --- /dev/null +++ b/src/posts/scoped-propagators/game.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8f8886b7e1b88e4df52295e293fa890843a5e514efeb71249e6f3a1a88fc40f +size 149309 diff --git a/src/posts/scoped-propagators/inspection.mp4 b/src/posts/scoped-propagators/inspection.mp4 new file mode 100644 index 0000000..958f002 --- /dev/null +++ b/src/posts/scoped-propagators/inspection.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f50fe4800e2a5957e0b96aa18121ea3dc40f514162914e9f771497e4cc54d1f +size 224988 diff --git a/src/posts/scoped-propagators/intro.mp4 b/src/posts/scoped-propagators/intro.mp4 new file mode 100644 index 0000000..03d89ab --- /dev/null +++ b/src/posts/scoped-propagators/intro.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29771060ae2ddb4c8056b4a68e24da75066e96b0a9264ab5577efba66de34379 +size 131411 diff --git a/src/posts/scoped-propagators/lerp.mp4 b/src/posts/scoped-propagators/lerp.mp4 new file mode 100644 index 0000000..cad9b1c --- /dev/null +++ b/src/posts/scoped-propagators/lerp.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3725438bab72654d31e56303f4694cb481da03ed52668a3235c0510769e43ef2 +size 213429 diff --git a/src/posts/test/paintbrush.mp4 b/src/posts/scoped-propagators/paintbrush.mp4 similarity index 100% rename from src/posts/test/paintbrush.mp4 rename to src/posts/scoped-propagators/paintbrush.mp4 diff --git a/src/posts/scoped-propagators/tools.mp4 b/src/posts/scoped-propagators/tools.mp4 new file mode 100644 index 0000000..dc6afe5 --- /dev/null +++ b/src/posts/scoped-propagators/tools.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c4030b83ec1ae36a9475a1165dc791f8d651da70cae91c1ab90c50a54d5bbd3 +size 127229 diff --git a/src/posts/test.md b/src/posts/test.md deleted file mode 100644 index 9779918..0000000 --- a/src/posts/test.md +++ /dev/null @@ -1,45 +0,0 @@ -# Scoped Propagators -this is some text under the title - -Lorem *ipsum dolor sit amet*, **consectetur adipiscing elit**. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - -## Subheading Example -Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. [Here us a link](https://google.com). nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. - -### Sub-subheading Example -Phasellus et lorem id felis nonummy placerat. Fusce aliquam vestibulum ipsum. Integer id sapien. Praesent id justo in neque elementum ultrices. Sed vel lectus. Donec odio tempus molestie, porttitor ut, iaculis quis, sem. Phasellus rhoncus. Aenean id metus id velit ullamcorper pulvinar. Vestibulum fermentum tortor id mi. Pellentesque ipsum. Nulla non arcu lacinia neque faucibus fringilla. Nullam sit amet magna in magna gravida vehicula. Mauris tincidunt sem sed arcu. Nunc posuere. Nam aliquam sem et tortor. Quisque sed augue a felis congue aliquam. Aliquam erat volutpat. Integer rutrum, orci vestibulum ullamcorper ultricies, lacus quam ultricies odio, vitae placerat pede sem sit amet enim. Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc. - -> This is a blockquote. It should be rendered in a special way to highlight the quoted text. -> -> — Author Name - -A code block: -```ts -console.log('hello world'); -``` - -**Bold text example** and *italic text example*. - -here is an image: -![paintbrush](paintbrush.mp4) - -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 - -## Another Subheading -Lorem *ipsum dolor sit amet*, **consectetur adipiscing elit**. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - -## Table Example - -| Column 1 | Column 2 | Column 3 | -|----------|----------|----------| -| Row 1 | Data 1 | Data 2 | -| Row 2 | Data 3 | Data 4 | -| Row 3 | Data 5 | Data 6 | - -### Another Sub-subheading -Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst. - -Phasellus et lorem id felis nonummy placerat. Fusce aliquam vestibulum ipsum. Integer id sapien. Praesent id justo in neque elementum ultrices. Sed vel lectus. Donec odio tempus molestie, porttitor ut, iaculis quis, sem. Phasellus rhoncus. Aenean id metus id velit ullamcorper pulvinar. Vestibulum fermentum tortor id mi. Pellentesque ipsum. Nulla non arcu lacinia neque faucibus fringilla. Nullam sit amet magna in magna gravida vehicula. Mauris tincidunt sem sed arcu. Nunc posuere. Nam aliquam sem et tortor. Quisque sed augue a felis congue aliquam. Aliquam erat volutpat. Integer rutrum, orci vestibulum ullamcorper ultricies, lacus quam ultricies odio, vitae placerat pede sem sit amet enim. Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc. \ No newline at end of file diff --git a/src/utils/readingTime.ts b/src/utils/readingTime.ts new file mode 100644 index 0000000..bcb32a3 --- /dev/null +++ b/src/utils/readingTime.ts @@ -0,0 +1,9 @@ +export const calcReadingTime = (text: string): string => { + if (!text) return "∞ min read"; + + const wordsPerMinute = 200; + const wordCount = text.split(/\s+/).length; + const minutes = Math.ceil(wordCount / wordsPerMinute); + + return `${minutes} min read`; +}; \ No newline at end of file