| author | |
| committer | |
| log | 35197e9d466b5eca56137fceccd0d061cf3bbb1c |
| tree | 840897c05de2275247afc8f82f215b120ed73ed7 |
| parent | f9994128fc914b0bf22191d8305a9d2e9f435dfc |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
9 files changed, 118 insertions(+), 26 deletions(-)
framework/font.ts+49-2| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { | 1 | export async function buildFonts(p: progress.Ref, fonts: sg.Font[], base: string) { |
| 2 | if (fonts.length === 0) return; | 2 | if (fonts.length === 0) return; |
| 3 | using node = p.start("build fonts"); | 3 | using node = p.start("build fonts"); |
| 4 | function getArgs(vars: sg.FontVars) { | 4 | function getArgs(vars: sg.FontVars) { |
| ... | @@ -22,7 +22,14 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { | ... | @@ -22,7 +22,14 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { |
| 22 | await Promise.all(fonts.map((font) => | 22 | await Promise.all(fonts.map((font) => |
| 23 | incr.work(node, async (io, font) => { | 23 | incr.work(node, async (io, font) => { |
| 24 | using node = io.start(font.name); | 24 | using node = io.start(font.name); |
| 25 | const baseFile = await fetchFont(node, font.name, font.sources); | 25 | let baseFile: string; |
| 26 | try { | ||
| 27 | baseFile = await fetchFont(node, font.name, font.sources); | ||
| 28 | } catch (err) { | ||
| 29 | using _ = node.start("fetch fallback"); | ||
| 30 | await fetchProductionFontFiles(node, io, font, base); | ||
| 31 | return; | ||
| 32 | } | ||
| 26 | await using baseBuffer = async.deferred(fs.readFile(baseFile)); | 33 | await using baseBuffer = async.deferred(fs.readFile(baseFile)); |
| 27 | await Promise.all(font.subsets.map(async (subset) => { | 34 | await Promise.all(font.subsets.map(async (subset) => { |
| 28 | const args = getArgs(subset.vars ?? {}); | 35 | const args = getArgs(subset.vars ?? {}); |
| ... | @@ -66,6 +73,44 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { | ... | @@ -66,6 +73,44 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { |
| 66 | )); | 73 | )); |
| 67 | } | 74 | } |
| 68 | 75 | ||
| 76 | async function fetchProductionFontFiles( | ||
| 77 | ref: progress.Ref, | ||
| 78 | io: incr.Io, | ||
| 79 | font: sg.Font, | ||
| 80 | base: string, | ||
| 81 | ) { | ||
| 82 | const fetchPromises = font.subsets.flatMap((subset) => { | ||
| 83 | return ["woff2", "ttf", "eot"].map(async (ext) => { | ||
| 84 | const assetPath = subset.asset.replace("[ext]", ext); | ||
| 85 | const cacheDir = path.join(os.tmpdir(), "sitegen-fonts", font.name); | ||
| 86 | fs.mkdirSync(cacheDir); | ||
| 87 | const cacheName = path.join(cacheDir, assetPath.replace(/[\/\\]/g, "_")); | ||
| 88 | |||
| 89 | if (!fs.existsSync(cacheName)) { | ||
| 90 | const url = new URL(assetPath, base).href; | ||
| 91 | try { | ||
| 92 | using _ = ref.start("fetch: " + assetPath); | ||
| 93 | const response = await fetch(url); | ||
| 94 | if (response.ok) { | ||
| 95 | await fs.writeMkdir(cacheName, new Uint8Array(await response.arrayBuffer())); | ||
| 96 | } else { | ||
| 97 | throw new Error(`Fetching fallback from ${url} failed: ${response.status} ${response.statusText}`); | ||
| 98 | } | ||
| 99 | } catch (err) { | ||
| 100 | throw new Error(`Fetching fallback from ${url} failed: ${error.message(err)}`); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | const buffer = await fs.readFile(cacheName); | ||
| 105 | await io.writeAsset({ | ||
| 106 | pathname: assetPath, | ||
| 107 | buffer: buffer, | ||
| 108 | }); | ||
| 109 | }); | ||
| 110 | }); | ||
| 111 | await Promise.all(fetchPromises); | ||
| 112 | } | ||
| 113 | |||
| 69 | export async function fetchFont(ref: progress.Ref, name: string, sources: string[]) { | 114 | export async function fetchFont(ref: progress.Ref, name: string, sources: string[]) { |
| 70 | using _ = ref.start("fetch"); | 115 | using _ = ref.start("fetch"); |
| 71 | const errs = []; | 116 | const errs = []; |
| ... | @@ -82,6 +127,7 @@ export async function fetchFont(ref: progress.Ref, name: string, sources: string | ... | @@ -82,6 +127,7 @@ export async function fetchFont(ref: progress.Ref, name: string, sources: string |
| 82 | cacheName, | 127 | cacheName, |
| 83 | new Uint8Array(await response.arrayBuffer()), | 128 | new Uint8Array(await response.arrayBuffer()), |
| 84 | ); | 129 | ); |
| 130 | return cacheName; | ||
| 85 | } else { | 131 | } else { |
| 86 | errs.push( | 132 | errs.push( |
| 87 | new Error( | 133 | new Error( |
| ... | @@ -129,6 +175,7 @@ export async function fetchFont(ref: progress.Ref, name: string, sources: string | ... | @@ -129,6 +175,7 @@ export async function fetchFont(ref: progress.Ref, name: string, sources: string |
| 129 | } | 175 | } |
| 130 | } | 176 | } |
| 131 | 177 | ||
| 178 | ASSERT(errs.length > 0); | ||
| 132 | throw new AggregateError(errs, `Failed to fetch the ${name} font`); | 179 | throw new AggregateError(errs, `Failed to fetch the ${name} font`); |
| 133 | } | 180 | } |
| 134 | 181 |
framework/generate.ts+1-1| ... | @@ -38,7 +38,7 @@ export async function generate(p: progress.Ref) { | ... | @@ -38,7 +38,7 @@ export async function generate(p: progress.Ref) { |
| 38 | ); | 38 | ); |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | const builtFonts = fonts.buildFonts(p, siteConfig.fonts); | 41 | const builtFonts = fonts.buildFonts(p, siteConfig.fonts, siteConfig.meta.base); |
| 42 | 42 | ||
| 43 | await loadMarkoCache(p); | 43 | await loadMarkoCache(p); |
| 44 | 44 |
framework/watch.ts+4-2| ... | @@ -10,7 +10,7 @@ process.on("beforeExit", () => { | ... | @@ -10,7 +10,7 @@ process.on("beforeExit", () => { |
| 10 | let watch: Watch; | 10 | let watch: Watch; |
| 11 | 11 | ||
| 12 | export async function main() { | 12 | export async function main() { |
| 13 | child_process.execSync("./node_modules/.bin/dprint fmt .", { stdio: "inherit" }); | 13 | child_process.execSync("./node_modules/.bin/dprint fmt --allow-no-files .", { stdio: "inherit" }); |
| 14 | // Catch up state by running a main build. | 14 | // Catch up state by running a main build. |
| 15 | if (!process.argv.includes("-f")) await incr.restore(); | 15 | if (!process.argv.includes("-f")) await incr.restore(); |
| 16 | watch = new Watch(rebuild); | 16 | watch = new Watch(rebuild); |
| ... | @@ -47,7 +47,9 @@ async function rebuild(files: string[]) { | ... | @@ -47,7 +47,9 @@ async function rebuild(files: string[]) { |
| 47 | try { | 47 | try { |
| 48 | const formattingFiles = files.filter(x => !x.endsWith(".mdo")); | 48 | const formattingFiles = files.filter(x => !x.endsWith(".mdo")); |
| 49 | if (formattingFiles.length > 0) { | 49 | if (formattingFiles.length > 0) { |
| 50 | child_process.execSync("./node_modules/.bin/dprint fmt " + formattingFiles.join(" "), { stdio: "inherit" }); | 50 | child_process.execSync("./node_modules/.bin/dprint fmt --allow-no-files " + formattingFiles.join(" "), { |
| 51 | stdio: "inherit", | ||
| 52 | }); | ||
| 51 | } | 53 | } |
| 52 | } catch (err) { | 54 | } catch (err) { |
| 53 | isRunning = false; | 55 | isRunning = false; |
package-lock.json+8-8| ... | @@ -16,7 +16,7 @@ | ... | @@ -16,7 +16,7 @@ |
| 16 | "harfbuzzjs": "0.10.1", | 16 | "harfbuzzjs": "0.10.1", |
| 17 | "hls.js": "1.6.5", | 17 | "hls.js": "1.6.5", |
| 18 | "hono": "4.12.8", | 18 | "hono": "4.12.8", |
| 19 | "marko": "5.38.33", | 19 | "marko": "5.38.38", |
| 20 | "msgpackr": "1.11.5", | 20 | "msgpackr": "1.11.5", |
| 21 | "playwright": "1.58.2", | 21 | "playwright": "1.58.2", |
| 22 | "prettier": "^3.8.1", | 22 | "prettier": "^3.8.1", |
| ... | @@ -694,9 +694,9 @@ | ... | @@ -694,9 +694,9 @@ |
| 694 | } | 694 | } |
| 695 | }, | 695 | }, |
| 696 | "node_modules/@marko/runtime-tags": { | 696 | "node_modules/@marko/runtime-tags": { |
| 697 | "version": "6.0.159", | 697 | "version": "6.0.169", |
| 698 | "resolved": "https://registry.npmjs.org/@marko/runtime-tags/-/runtime-tags-6.0.159.tgz", | 698 | "resolved": "https://registry.npmjs.org/@marko/runtime-tags/-/runtime-tags-6.0.169.tgz", |
| 699 | "integrity": "sha512-gtw2oiqLKb1mbC+gsupbXL/BVBshNH2maDbiJ8s4L6BSmFlxkSmqJgkl6l6aCvoXqMh7PRqRZArwpT5RN9/Tpw==", | 699 | "integrity": "sha512-PApCgxeIOGR71TPx+BVwlNV9L2ej61hkDcB1ZBkCLKaUwJMdA9/LWEPMXNEJWB0Vn172iP/kwGIsi9dEPTPo1A==", |
| 700 | "license": "MIT", | 700 | "license": "MIT", |
| 701 | "dependencies": { | 701 | "dependencies": { |
| 702 | "@marko/compiler": "^5.39.62", | 702 | "@marko/compiler": "^5.39.62", |
| ... | @@ -1974,13 +1974,13 @@ | ... | @@ -1974,13 +1974,13 @@ |
| 1974 | } | 1974 | } |
| 1975 | }, | 1975 | }, |
| 1976 | "node_modules/marko": { | 1976 | "node_modules/marko": { |
| 1977 | "version": "5.38.33", | 1977 | "version": "5.38.38", |
| 1978 | "resolved": "https://registry.npmjs.org/marko/-/marko-5.38.33.tgz", | 1978 | "resolved": "https://registry.npmjs.org/marko/-/marko-5.38.38.tgz", |
| 1979 | "integrity": "sha512-e056TGqKjTE9CyO76eSlYLKk7P9z9uu0VGXt1Z3OrceCz728SCP/UhTas55ru9sNqihZEJ+vwv4ApwVwUsbtNg==", | 1979 | "integrity": "sha512-dH5a6x/vcxb+fq/ZdWOTIuyz/Nf0kcdfWu9GwrGzl3AeM/FM2mAyp+szRssDmKYLw0VjxHiJTp3ddACSssVPPA==", |
| 1980 | "license": "MIT", | 1980 | "license": "MIT", |
| 1981 | "dependencies": { | 1981 | "dependencies": { |
| 1982 | "@marko/compiler": "^5.39.62", | 1982 | "@marko/compiler": "^5.39.62", |
| 1983 | "@marko/runtime-tags": "^6.0.159", | 1983 | "@marko/runtime-tags": "^6.0.169", |
| 1984 | "app-module-path": "^2.2.0", | 1984 | "app-module-path": "^2.2.0", |
| 1985 | "argly": "^1.2.0", | 1985 | "argly": "^1.2.0", |
| 1986 | "browser-refresh-client": "1.1.4", | 1986 | "browser-refresh-client": "1.1.4", |
package.json+1-1| ... | @@ -12,7 +12,7 @@ | ... | @@ -12,7 +12,7 @@ |
| 12 | "harfbuzzjs": "0.10.1", | 12 | "harfbuzzjs": "0.10.1", |
| 13 | "hls.js": "1.6.5", | 13 | "hls.js": "1.6.5", |
| 14 | "hono": "4.12.8", | 14 | "hono": "4.12.8", |
| 15 | "marko": "5.38.33", | 15 | "marko": "5.38.38", |
| 16 | "msgpackr": "1.11.5", | 16 | "msgpackr": "1.11.5", |
| 17 | "playwright": "1.58.2", | 17 | "playwright": "1.58.2", |
| 18 | "prettier": "^3.8.1", | 18 | "prettier": "^3.8.1", |
src/file-viewer/backend.ts+3-3| ... | @@ -163,13 +163,13 @@ app.get("/file/*", async (c, next) => { | ... | @@ -163,13 +163,13 @@ app.get("/file/*", async (c, next) => { |
| 163 | if (rangeHeader) { | 163 | if (rangeHeader) { |
| 164 | const ranges = http.parseRangeHeader(rangeHeader, file.size); | 164 | const ranges = http.parseRangeHeader(rangeHeader, file.size); |
| 165 | if (ranges?.[0] && ranges.length === 1) { | 165 | if (ranges?.[0] && ranges.length === 1) { |
| 166 | start = ranges[0][0]; | 166 | start = ranges[0].start; |
| 167 | end = ranges[0][1]; | 167 | end = ranges[0].end; |
| 168 | 168 | ||
| 169 | status = 206; | 169 | status = 206; |
| 170 | headers.set( | 170 | headers.set( |
| 171 | "Content-Range", | 171 | "Content-Range", |
| 172 | `bytes ${ranges[0][0]}-${ranges[0][1]}/${file.size}`, | 172 | `bytes ${ranges[0].start}-${ranges[0].end}/${file.size}`, |
| 173 | ); | 173 | ); |
| 174 | headers.delete("Content-Length"); | 174 | headers.delete("Content-Length"); |
| 175 | } else { | 175 | } else { |
src/pages/dream.mdo created+42| ... | @@ -0,0 +1,42 @@ | ||
| 1 | --- | ||
| 2 | meta: | ||
| 3 | title: paper clover's media license | ||
| 4 | --- | ||
| 5 | <div style='display:flex;align-items:center;justify-content:center;min-height:100dvh'> | ||
| 6 | <main style='max-width:666px;line-height:1.5;text-align:justify'> | ||
| 7 | |||
| 8 | [go back to the home page](/) | ||
| 9 | |||
| 10 | # my life's dream | ||
| 11 | |||
| 12 | if i could make art (music and videos) full time, forever, i would take that | ||
| 13 | life in a heartbeat, never looking back. this has happened once in may 2025 for | ||
| 14 | a month (an interlude between jobs). i called this **ART MONTH**, and it was the | ||
| 15 | best month of my life. i want to do it again, for longer: **ART DECADE**. or if it was possible, **ART | ||
| 16 | FOREVER**. the desire has been in me since age 16 or so. i don't want to work in | ||
| 17 | the music industry, and i don't want to be contingent on my music being | ||
| 18 | profitable. i want to make my art, the way i envision it. | ||
| 19 | |||
| 20 | currently, i have a stupid tech job, which i am exceptionally good at. too good | ||
| 21 | at it maybe. my knowledge and experience was built from doing crazy things in | ||
| 22 | school and is the reason my colleagues rely on me for accurate technical advice. | ||
| 23 | i'm the one to attempt and succeed with large, technically intricate projects. | ||
| 24 | because of this, i make a cozy amount of money. this is convenient because it | ||
| 25 | costs money to be alive. so, i'm saving and investing that money however | ||
| 26 | possible to reach the level i could "retire" and get **ART DECADE** going. | ||
| 27 | retire is the wrong word as i'll probably only work more. | ||
| 28 | |||
| 29 | until then, the art here is done in my free time, elegantly balancing the time i | ||
| 30 | spent on human connection, media consumption, and the art. i've learned so many | ||
| 31 | times over that ruining this balance really fucks your head up. | ||
| 32 | |||
| 33 | my one other dream is travel. not to places. but people. i love my friends with | ||
| 34 | all my heart, and i want to visit all of them, especially the ones that wouldn't | ||
| 35 | ever be able to travel to me. i do this a little now, but i only get so much | ||
| 36 | time off work. if i could go to everyone. | ||
| 37 | |||
| 38 | i will put in everything from my soul to make these two goals a reality. | ||
| 39 | eventually, however long it takes. | ||
| 40 | |||
| 41 | </main> | ||
| 42 | </div> | ||
src/pages/index.marko+9-6| ... | @@ -20,30 +20,33 @@ export const meta = { | ... | @@ -20,30 +20,33 @@ export const meta = { |
| 20 | i am a girl that is adorable and | 20 | i am a girl that is adorable and |
| 21 | <a class="custom cite" href="https://en.wikipedia.org/wiki/Life">alive</a>. | 21 | <a class="custom cite" href="https://en.wikipedia.org/wiki/Life">alive</a>. |
| 22 | </p> | 22 | </p> |
| 23 | <p>this site contains my art.</p> | 23 | <h2>art</h2> |
| 24 | <h2>posts</h2> | ||
| 25 | <ul class="section-list"> | 24 | <ul class="section-list"> |
| 26 | <li> | 25 | <li> |
| 27 | video collab: | 26 | video collab: |
| 28 | <a href="/history-of-japan-reanimated">history of japan reanimated</a> | 27 | <a href="/history-of-japan-reanimated">history of japan reanimated</a> |
| 29 | (2026-02-02) | 28 | (2026-02-02) |
| 30 | </li> | 29 | </li> |
| 30 | <li>song: <a href="/in-the-summer">in the summer</a> (2025-08-08)</li> | ||
| 31 | <li>song: <a href="/waterfalls">waterfalls</a> (2025-01-01)</li> | ||
| 32 | </ul> | ||
| 33 | <h2>webdev blog</h2> | ||
| 34 | <ul class="section-list"> | ||
| 31 | <li> | 35 | <li> |
| 32 | blog: | 36 | blog: |
| 33 | <a href="/blog/webdev/one-year-next-app-router"> | 37 | <a href="/blog/webdev/one-year-next-app-router"> |
| 34 | One Year with Next.js App Router — why we're moving on | 38 | everyone hates next.js |
| 35 | </a> | 39 | </a> |
| 36 | (2025-10-21) | 40 | (2025-10-21) |
| 37 | </li> | 41 | </li> |
| 38 | <li>song: <a href="/in-the-summer">in the summer</a> (2025-08-08)</li> | ||
| 39 | <li>song: <a href="/waterfalls">waterfalls</a> (2025-01-01)</li> | ||
| 40 | </ul> | 42 | </ul> |
| 41 | <h2>things</h2> | 43 | <h2>things</h2> |
| 42 | <ul class="section-list"> | 44 | <ul class="section-list"> |
| 43 | <li><a href="/q+a">questions and answers</a></li> | 45 | <li><a href="/q+a">questions and answers</a></li> |
| 44 | <li><a href="/log">recent activity</a></li> | 46 | <li><a href="/log">recent activity</a></li> |
| 45 | <li><a href="/file">file browser</a></li> | 47 | <li><a href="/file">file browser</a></li> |
| 46 | <li><a href="https://git.paperclover.net/clo">code forge</a></li> | 48 | <li><a href="https://git.paperclover.net/clo">my software</a></li> |
| 49 | <li><a href="/dream">my life's dream</a></li> | ||
| 47 | <li><a href="/subscribe">mailing list</a></li> | 50 | <li><a href="/subscribe">mailing list</a></li> |
| 48 | </ul> | 51 | </ul> |
| 49 | <h2>links</h2> | 52 | <h2>links</h2> |
src/pages/subscribe.marko+1-3| ... | @@ -19,9 +19,7 @@ export const meta = { | ... | @@ -19,9 +19,7 @@ export const meta = { |
| 19 | </code> | 19 | </code> |
| 20 | <p> | 20 | <p> |
| 21 | i read every message, so you can say whatever else you want and i will see | 21 | i read every message, so you can say whatever else you want and i will see |
| 22 | it. there won't be a confirmation email for subscribing. for those who are | 22 | it. there won't be a confirmation email for subscribing. |
| 23 | close friends with me, add your name and i'll add you to the 'friends' list, | ||
| 24 | where i send life updates in addition to new content. | ||
| 25 | </p> | 23 | </p> |
| 26 | <p>to unsubscribe, send an email asking to unsubscribe.</p> | 24 | <p>to unsubscribe, send an email asking to unsubscribe.</p> |
| 27 | <br><br><br> | 25 | <br><br><br> |