diff --git a/framework/font.ts b/framework/font.ts index 8814b1aab17ac32dcb01165f2d91c0a5d9a48f66..c3935c292b4a7d7648ea0768b56470810f117189 100644 --- a/framework/font.ts +++ b/framework/font.ts @@ -1,4 +1,4 @@ -export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { +export async function buildFonts(p: progress.Ref, fonts: sg.Font[], base: string) { if (fonts.length === 0) return; using node = p.start("build fonts"); function getArgs(vars: sg.FontVars) { @@ -22,7 +22,14 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { await Promise.all(fonts.map((font) => incr.work(node, async (io, font) => { using node = io.start(font.name); - const baseFile = await fetchFont(node, font.name, font.sources); + let baseFile: string; + try { + baseFile = await fetchFont(node, font.name, font.sources); + } catch (err) { + using _ = node.start("fetch fallback"); + await fetchProductionFontFiles(node, io, font, base); + return; + } await using baseBuffer = async.deferred(fs.readFile(baseFile)); await Promise.all(font.subsets.map(async (subset) => { const args = getArgs(subset.vars ?? {}); @@ -66,6 +73,44 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { )); } +async function fetchProductionFontFiles( + ref: progress.Ref, + io: incr.Io, + font: sg.Font, + base: string, +) { + const fetchPromises = font.subsets.flatMap((subset) => { + return ["woff2", "ttf", "eot"].map(async (ext) => { + const assetPath = subset.asset.replace("[ext]", ext); + const cacheDir = path.join(os.tmpdir(), "sitegen-fonts", font.name); + fs.mkdirSync(cacheDir); + const cacheName = path.join(cacheDir, assetPath.replace(/[\/\\]/g, "_")); + + if (!fs.existsSync(cacheName)) { + const url = new URL(assetPath, base).href; + try { + using _ = ref.start("fetch: " + assetPath); + const response = await fetch(url); + if (response.ok) { + await fs.writeMkdir(cacheName, new Uint8Array(await response.arrayBuffer())); + } else { + throw new Error(`Fetching fallback from ${url} failed: ${response.status} ${response.statusText}`); + } + } catch (err) { + throw new Error(`Fetching fallback from ${url} failed: ${error.message(err)}`); + } + } + + const buffer = await fs.readFile(cacheName); + await io.writeAsset({ + pathname: assetPath, + buffer: buffer, + }); + }); + }); + await Promise.all(fetchPromises); +} + export async function fetchFont(ref: progress.Ref, name: string, sources: string[]) { using _ = ref.start("fetch"); const errs = []; @@ -82,6 +127,7 @@ export async function fetchFont(ref: progress.Ref, name: string, sources: string cacheName, new Uint8Array(await response.arrayBuffer()), ); + return cacheName; } else { errs.push( new Error( @@ -129,6 +175,7 @@ export async function fetchFont(ref: progress.Ref, name: string, sources: string } } + ASSERT(errs.length > 0); throw new AggregateError(errs, `Failed to fetch the ${name} font`); } diff --git a/framework/generate.ts b/framework/generate.ts index 844a0544bd8522a38735ff8745af14c18c08570b..d473e55b2df43164ec5341cef5f27c141c93a1e4 100644 --- a/framework/generate.ts +++ b/framework/generate.ts @@ -38,7 +38,7 @@ export async function generate(p: progress.Ref) { ); } - const builtFonts = fonts.buildFonts(p, siteConfig.fonts); + const builtFonts = fonts.buildFonts(p, siteConfig.fonts, siteConfig.meta.base); await loadMarkoCache(p); diff --git a/framework/watch.ts b/framework/watch.ts index c639d69a5655222e3b6575d3e23ede4c810b6a57..c2dcdb20ccf325bc9f3735949300fef3e5c548d5 100644 --- a/framework/watch.ts +++ b/framework/watch.ts @@ -10,7 +10,7 @@ process.on("beforeExit", () => { let watch: Watch; export async function main() { - child_process.execSync("./node_modules/.bin/dprint fmt .", { stdio: "inherit" }); + child_process.execSync("./node_modules/.bin/dprint fmt --allow-no-files .", { stdio: "inherit" }); // Catch up state by running a main build. if (!process.argv.includes("-f")) await incr.restore(); watch = new Watch(rebuild); @@ -47,7 +47,9 @@ async function rebuild(files: string[]) { try { const formattingFiles = files.filter(x => !x.endsWith(".mdo")); if (formattingFiles.length > 0) { - child_process.execSync("./node_modules/.bin/dprint fmt " + formattingFiles.join(" "), { stdio: "inherit" }); + child_process.execSync("./node_modules/.bin/dprint fmt --allow-no-files " + formattingFiles.join(" "), { + stdio: "inherit", + }); } } catch (err) { isRunning = false; diff --git a/package-lock.json b/package-lock.json index cb18fc6a5221f3ec0d7909d2f38e169097c187f9..6f0ca7c483219628ac9e1249a12400bf826a0b8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "harfbuzzjs": "0.10.1", "hls.js": "1.6.5", "hono": "4.12.8", - "marko": "5.38.33", + "marko": "5.38.38", "msgpackr": "1.11.5", "playwright": "1.58.2", "prettier": "^3.8.1", @@ -694,9 +694,9 @@ } }, "node_modules/@marko/runtime-tags": { - "version": "6.0.159", - "resolved": "https://registry.npmjs.org/@marko/runtime-tags/-/runtime-tags-6.0.159.tgz", - "integrity": "sha512-gtw2oiqLKb1mbC+gsupbXL/BVBshNH2maDbiJ8s4L6BSmFlxkSmqJgkl6l6aCvoXqMh7PRqRZArwpT5RN9/Tpw==", + "version": "6.0.169", + "resolved": "https://registry.npmjs.org/@marko/runtime-tags/-/runtime-tags-6.0.169.tgz", + "integrity": "sha512-PApCgxeIOGR71TPx+BVwlNV9L2ej61hkDcB1ZBkCLKaUwJMdA9/LWEPMXNEJWB0Vn172iP/kwGIsi9dEPTPo1A==", "license": "MIT", "dependencies": { "@marko/compiler": "^5.39.62", @@ -1974,13 +1974,13 @@ } }, "node_modules/marko": { - "version": "5.38.33", - "resolved": "https://registry.npmjs.org/marko/-/marko-5.38.33.tgz", - "integrity": "sha512-e056TGqKjTE9CyO76eSlYLKk7P9z9uu0VGXt1Z3OrceCz728SCP/UhTas55ru9sNqihZEJ+vwv4ApwVwUsbtNg==", + "version": "5.38.38", + "resolved": "https://registry.npmjs.org/marko/-/marko-5.38.38.tgz", + "integrity": "sha512-dH5a6x/vcxb+fq/ZdWOTIuyz/Nf0kcdfWu9GwrGzl3AeM/FM2mAyp+szRssDmKYLw0VjxHiJTp3ddACSssVPPA==", "license": "MIT", "dependencies": { "@marko/compiler": "^5.39.62", - "@marko/runtime-tags": "^6.0.159", + "@marko/runtime-tags": "^6.0.169", "app-module-path": "^2.2.0", "argly": "^1.2.0", "browser-refresh-client": "1.1.4", diff --git a/package.json b/package.json index 3e4dddc38106201f8016f350cf9c4413946505d7..c61f5b184b7ffdd5575a2e8884e19315358189bd 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "harfbuzzjs": "0.10.1", "hls.js": "1.6.5", "hono": "4.12.8", - "marko": "5.38.33", + "marko": "5.38.38", "msgpackr": "1.11.5", "playwright": "1.58.2", "prettier": "^3.8.1", diff --git a/src/file-viewer/backend.ts b/src/file-viewer/backend.ts index f1844d651f884892ce690847fa1048a983a8bc52..3d1620101321ed90b4eaea57b6d02a2ab8c5d455 100644 --- a/src/file-viewer/backend.ts +++ b/src/file-viewer/backend.ts @@ -163,13 +163,13 @@ app.get("/file/*", async (c, next) => { if (rangeHeader) { const ranges = http.parseRangeHeader(rangeHeader, file.size); if (ranges?.[0] && ranges.length === 1) { - start = ranges[0][0]; - end = ranges[0][1]; + start = ranges[0].start; + end = ranges[0].end; status = 206; headers.set( "Content-Range", - `bytes ${ranges[0][0]}-${ranges[0][1]}/${file.size}`, + `bytes ${ranges[0].start}-${ranges[0].end}/${file.size}`, ); headers.delete("Content-Length"); } else { diff --git a/src/pages/dream.mdo b/src/pages/dream.mdo new file mode 100644 index 0000000000000000000000000000000000000000..6b7981d9a728e4f924f355cc3e40738f137e9af5 --- /dev/null +++ b/src/pages/dream.mdo @@ -0,0 +1,42 @@ +--- +meta: + title: paper clover's media license +--- +
+
+ +[go back to the home page](/) + +# my life's dream + +if i could make art (music and videos) full time, forever, i would take that +life in a heartbeat, never looking back. this has happened once in may 2025 for +a month (an interlude between jobs). i called this **ART MONTH**, and it was the +best month of my life. i want to do it again, for longer: **ART DECADE**. or if it was possible, **ART +FOREVER**. the desire has been in me since age 16 or so. i don't want to work in +the music industry, and i don't want to be contingent on my music being +profitable. i want to make my art, the way i envision it. + +currently, i have a stupid tech job, which i am exceptionally good at. too good +at it maybe. my knowledge and experience was built from doing crazy things in +school and is the reason my colleagues rely on me for accurate technical advice. +i'm the one to attempt and succeed with large, technically intricate projects. +because of this, i make a cozy amount of money. this is convenient because it +costs money to be alive. so, i'm saving and investing that money however +possible to reach the level i could "retire" and get **ART DECADE** going. +retire is the wrong word as i'll probably only work more. + +until then, the art here is done in my free time, elegantly balancing the time i +spent on human connection, media consumption, and the art. i've learned so many +times over that ruining this balance really fucks your head up. + +my one other dream is travel. not to places. but people. i love my friends with +all my heart, and i want to visit all of them, especially the ones that wouldn't +ever be able to travel to me. i do this a little now, but i only get so much +time off work. if i could go to everyone. + +i will put in everything from my soul to make these two goals a reality. +eventually, however long it takes. + +
+
diff --git a/src/pages/index.marko b/src/pages/index.marko index 448626bfdf031548f45d473503bf822256cbcc62..a3505cebba22e26e742896c256925ac3bfff7143 100644 --- a/src/pages/index.marko +++ b/src/pages/index.marko @@ -20,30 +20,33 @@ export const meta = { i am a girl that is adorable and alive.

-

this site contains my art.

-

posts

+

art

+

webdev blog

+

things

links

diff --git a/src/pages/subscribe.marko b/src/pages/subscribe.marko index 56c99777ed12e756144f53d82461c50a6295b9f3..943927bd0af760e23b2831bbca3200443d07e888 100644 --- a/src/pages/subscribe.marko +++ b/src/pages/subscribe.marko @@ -19,9 +19,7 @@ export const meta = {

i read every message, so you can say whatever else you want and i will see - it. there won't be a confirmation email for subscribing. for those who are - close friends with me, add your name and i'll add you to the 'friends' list, - where i send life updates in addition to new content. + it. there won't be a confirmation email for subscribing.

to unsubscribe, send an email asking to unsubscribe.