diff --git a/site/package-lock.json b/site/package-lock.json index 94c540b..6fafd98 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -9,13 +9,16 @@ "version": "0.1.0", "dependencies": { "@astrojs/markdown-remark": "7.3.2", + "animejs": "4.5.0", "astro": "7.3.8" }, "devDependencies": { "@astrojs/check": "0.9.10", "@playwright/test": "1.64.0", "@types/node": "24.19.1", + "@types/opentype.js": "1.3.10", "cheerio": "1.2.0", + "opentype.js": "2.0.0", "typescript": "~5.9.3" }, "engines": { @@ -1833,6 +1836,13 @@ "undici-types": ">=7.24.0 <7.24.7" } }, + "node_modules/@types/opentype.js": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/opentype.js/-/opentype.js-1.3.10.tgz", + "integrity": "sha512-F67EFyk6j02okHz5JCgata3ZRAcZi9GLnzmkHw/rzJq3OCc8/ZVdoKrxMTYjcQP6IYHGBz2cav1cpzkOkPiPCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2025,6 +2035,28 @@ "am-i-vibing": "dist/cli.mjs" } }, + "node_modules/animejs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/animejs/-/animejs-4.5.0.tgz", + "integrity": "sha512-NQimYX+lz8WaXonGS9zVVoviCIAjONeJayxacUditaivYLqyXgGjFKjuyl0aUUhFKm5MriX9ty1TGovNzoJQWA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/juliangarnier" + }, + "peerDependencies": { + "@types/three": ">=0.150.0", + "three": ">=0.150.0" + }, + "peerDependenciesMeta": { + "@types/three": { + "optional": true + }, + "three": { + "optional": true + } + } + }, "node_modules/ansi-regex": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", @@ -4986,6 +5018,16 @@ "regex-recursion": "^6.0.2" } }, + "node_modules/opentype.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-2.0.0.tgz", + "integrity": "sha512-kCyjv6xdDY1W/jLWZ/L3QhhTlKUqDZMQ5+Jdlw12b3dXkKNpYBqqlMMj0YDQPShWFTMwgZI1hG14kN3XUDSg/A==", + "dev": true, + "license": "MIT", + "bin": { + "ot": "bin/ot" + } + }, "node_modules/p-limit": { "version": "7.3.3", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.3.tgz", diff --git a/site/package.json b/site/package.json index 0bdc656..83e62b1 100644 --- a/site/package.json +++ b/site/package.json @@ -18,13 +18,16 @@ }, "dependencies": { "@astrojs/markdown-remark": "7.3.2", + "animejs": "4.5.0", "astro": "7.3.8" }, "devDependencies": { "@astrojs/check": "0.9.10", "@playwright/test": "1.64.0", "@types/node": "24.19.1", + "@types/opentype.js": "1.3.10", "cheerio": "1.2.0", + "opentype.js": "2.0.0", "typescript": "~5.9.3" } } diff --git a/site/playwright.config.ts b/site/playwright.config.ts index 510f5f9..39851d3 100644 --- a/site/playwright.config.ts +++ b/site/playwright.config.ts @@ -2,10 +2,11 @@ import './plugins/environment.mjs'; import { defineConfig } from '@playwright/test'; const base = process.env.SITE_BASE?.replace(/\/$/, '') || ''; +const port = process.env.SITE_TEST_PORT || '4322'; export default defineConfig({ testDir: './tests/browser', use: { - baseURL: `http://127.0.0.1:4322${base}/`, + baseURL: `http://127.0.0.1:${port}${base}/`, javaScriptEnabled: false, launchOptions: { executablePath: process.env.CHROME_PATH }, }, @@ -14,8 +15,8 @@ export default defineConfig({ { name: 'mobile', use: { viewport: { width: 390, height: 844 } } }, ], webServer: { - command: 'npm run preview -- --host 127.0.0.1 --port 4322 --ignore-lock', - url: `http://127.0.0.1:4322${base}/`, + command: `npm run preview -- --host 127.0.0.1 --port ${port} --ignore-lock`, + url: `http://127.0.0.1:${port}${base}/`, reuseExistingServer: false, }, }); diff --git a/site/public/fonts/EBGaramond-Bold.woff2 b/site/public/fonts/EBGaramond-Bold.woff2 new file mode 100644 index 0000000..a437c8d Binary files /dev/null and b/site/public/fonts/EBGaramond-Bold.woff2 differ diff --git a/site/public/fonts/EBGaramond-BoldItalic.woff2 b/site/public/fonts/EBGaramond-BoldItalic.woff2 new file mode 100644 index 0000000..e3fe0f5 Binary files /dev/null and b/site/public/fonts/EBGaramond-BoldItalic.woff2 differ diff --git a/site/public/fonts/EBGaramond-Italic.woff2 b/site/public/fonts/EBGaramond-Italic.woff2 new file mode 100644 index 0000000..da98825 Binary files /dev/null and b/site/public/fonts/EBGaramond-Italic.woff2 differ diff --git a/site/public/fonts/EBGaramond-OFL.txt b/site/public/fonts/EBGaramond-OFL.txt new file mode 100644 index 0000000..0375b80 --- /dev/null +++ b/site/public/fonts/EBGaramond-OFL.txt @@ -0,0 +1,94 @@ +Copyright 2017 The EB Garamond Project Authors (https://github.com/octaviopardo/EBGaramond12) +Copyright 2025 Deborah Khodanovich (RCS citation glyphs) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/site/public/fonts/EBGaramond-Regular.ttf b/site/public/fonts/EBGaramond-Regular.ttf new file mode 100644 index 0000000..8194251 Binary files /dev/null and b/site/public/fonts/EBGaramond-Regular.ttf differ diff --git a/site/public/fonts/EBGaramond-Regular.woff2 b/site/public/fonts/EBGaramond-Regular.woff2 new file mode 100644 index 0000000..c51dbd3 Binary files /dev/null and b/site/public/fonts/EBGaramond-Regular.woff2 differ diff --git a/site/public/fonts/LythMono-Bold.woff2 b/site/public/fonts/LythMono-Bold.woff2 new file mode 100644 index 0000000..368ba3d Binary files /dev/null and b/site/public/fonts/LythMono-Bold.woff2 differ diff --git a/site/public/fonts/LythMono-LICENSE.md b/site/public/fonts/LythMono-LICENSE.md new file mode 100644 index 0000000..8b14e75 --- /dev/null +++ b/site/public/fonts/LythMono-LICENSE.md @@ -0,0 +1,112 @@ +Copyright (c) 2015-2026, Renzhi Li (aka. Belleve Invis, belleve@typeof.net) + +Build configuration (c) 2026, Yuri Turov + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +-------------------------- + + +SIL Open Font License v1.1 +==================================================== + + +Preamble +---------- + +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + + +Definitions +------------- + +`"Font Software"` refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +`"Reserved Font Name"` refers to any names specified as such after the +copyright statement(s). + +`"Original Version"` refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +`"Modified Version"` refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +`"Author"` refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + + +Permission & Conditions +------------------------ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1. Neither the Font Software nor any of its individual components, + in Original or Modified Versions, may be sold by itself. + +2. Original or Modified Versions of the Font Software may be bundled, + redistributed and/or sold with any software, provided that each copy + contains the above copyright notice and this license. These can be + included either as stand-alone text files, human-readable headers or + in the appropriate machine-readable metadata fields within text or + binary files as long as those fields can be easily viewed by the user. + +3. No Modified Version of the Font Software may use the Reserved Font + Name(s) unless explicit written permission is granted by the corresponding + Copyright Holder. This restriction only applies to the primary font name as + presented to the users. + +4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font + Software shall not be used to promote, endorse or advertise any + Modified Version, except to acknowledge the contribution(s) of the + Copyright Holder(s) and the Author(s) or with their explicit written + permission. + +5. The Font Software, modified or unmodified, in part or in whole, + must be distributed entirely under this license, and must not be + distributed under any other license. The requirement for fonts to + remain under this license does not apply to any document created + using the Font Software. + + + +Termination +----------- + +This license becomes null and void if any of the above conditions are +not met. + + + DISCLAIMER + + THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE + COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL + DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM + OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/site/public/fonts/LythMono-Regular.ttf b/site/public/fonts/LythMono-Regular.ttf new file mode 100644 index 0000000..fb484f1 Binary files /dev/null and b/site/public/fonts/LythMono-Regular.ttf differ diff --git a/site/public/fonts/LythMono-Regular.woff2 b/site/public/fonts/LythMono-Regular.woff2 new file mode 100644 index 0000000..c67e9c2 Binary files /dev/null and b/site/public/fonts/LythMono-Regular.woff2 differ diff --git a/site/public/fonts/README.md b/site/public/fonts/README.md new file mode 100644 index 0000000..ad4c28b --- /dev/null +++ b/site/public/fonts/README.md @@ -0,0 +1,104 @@ +# Self-hosted fonts + +These are unmodified binaries from official upstream sources. Only the faces +used by the site are retained: Lyth Mono regular/bold, EB Garamond regular/bold +and their italics, plus each family's regular TTF for build-time outline +extraction with `opentype.js`. Browsers use WOFF2, not the TTFs. No Nerd, Term, +Square, Round, variable, light, or medium faces are included. + +## Lyth Mono + +- Repository: +- Selected non-prerelease release: **v0.10.0**, published 2026-02-06. +- Pinned tag commit: `71d1cf4f325663705ab41ee1908427ea435b2e5d`. +- Pinned Iosevka source submodule: `a90e82cf6f37cc9b82e47151024c86cfe0693874`. +- Official release archive: + +- Archive SHA-256, checked against the GitHub release asset digest: + `06b8c05671572e8cba61bf494404d3b973df348c607cd33f3153a374247d6607`. +- Extracted only `LythMono/WOFF2/LythMono-Regular.woff2`, + `LythMono/WOFF2/LythMono-Bold.woff2`, and + `LythMono/TTF/LythMono-Regular.ttf` (hinted versions). The archive is not + retained. +- License: `LythMono-LICENSE.md`, copied from `LICENSE.md` at the pinned commit; + SIL Open Font License 1.1, including upstream copyright notices. +- Internal family: **Lyth Mono**. Regular full name: **Lyth Mono**; + PostScript name: **Lyth-Mono**. Bold PostScript name: **Lyth-Mono-Bold**. + Regular TTF version: `Version 0.10.0; ttfautohint (v1.8.4)`. + +Regular TTF metrics (font units): units/em 1000; hhea ascender 925, descender +-195, line gap 50; OS/2 typo ascender 925, descender -245, line gap 0; +Windows ascent/descent 925/245; cap height 720; x-height 540. ASCII `0`, `W`, +`i`, `p`, and the four box glyphs below all advance **604** units (0.604em; +8.456px at 14px). This is not a 0.6em font. Glyph count: 12174. + +| Character | Unicode | Glyph ID | Ink bounds `(xMin, yMin, xMax, yMax)` | +| --- | --- | --- | --- | +| │ | U+2502 | 7535 | (264, -245, 340, 925) | +| ├ | U+251C | 7561 | (264, -245, 604, 925) | +| └ | U+2514 | 7553 | (264, 306, 604, 925) | +| ─ | U+2500 | 7533 | (0, 306, 604, 374) | + +These are real, nonempty outlines verified with `opentype.js`, not fallback +characters. The horizontal stroke's center is y=340, and vertical strokes +reach the font's 925/-245 line extents. Explicit line-height and vertical +alignment matter when joining animated strokes to text. + +## EB Garamond + +- Official source: +- Pinned commit: `106a4a6d377987459ae5e68673a4570f13b957fb`. +- This is the upstream revision recorded by Google Fonts in + `ofl/ebgaramond/METADATA.pb` at distribution commit + `f8c1d3d6cc75e30d77130bdcbfbff27e3b6233fe`. +- No published GitHub release is available at the source repository. Files + were downloaded directly from that pinned source commit, not a third-party + font CDN: `https://raw.githubusercontent.com/octaviopardo/EBGaramond12/106a4a6d377987459ae5e68673a4570f13b957fb/`. +- Source paths: `fonts/webfonts/EBGaramond-{Regular,Bold,Italic,BoldItalic}.woff2` + and `fonts/ttf/EBGaramond-Regular.ttf`. +- License: `EBGaramond-OFL.txt`, copied from `OFL.txt` at the pinned commit; + SIL Open Font License 1.1, including upstream copyright notices. +- Internal family: **EB Garamond**. Regular full name: **EB Garamond Regular**; + PostScript name: **EBGaramond-Regular**. Other included PostScript names: + **EBGaramond-Bold**, **EBGaramond-Italic**, **EBGaramond-BoldItalic**. +- The static regular TTF reports `Version 1.002; ttfautohint + (v1.8.4.16-eb64)`. Google Fonts' newer variable version is not included; + the static faces above are deliberately used as shipped by upstream. + +Regular TTF metrics (font units): units/em 1000; hhea and OS/2 typo ascender +1007, descender -298, line gap 0; Windows ascent/descent 1047/390; cap height +650; x-height 400. Advances: `0` 480, `W` 916, `i` 245, `p` 519. Glyph count: +3247. It does not contain the four box-drawing characters above. + +## Japanese / CJK fallback + +Neither regular TTF maps any codepoints in Hiragana (U+3040–309F), Katakana +(U+30A0–30FF), CJK Unified Ideographs (U+4E00–9FFF), or Extension A +(U+3400–4DBF). `あ`, `ア`, `漢`, `日`, and `本` all map to glyph 0 in both. +Do not use their `.notdef` outlines for Japanese glyph extraction. + +The proportional CSS stack uses installed `Noto Serif JP`, `Hiragino Mincho +ProN`, `Yu Mincho` / `YuMincho`, then generic `serif`. The mono stack retains +system monospace choices and installed `Noto Sans Mono CJK JP`. These are +fallback names only: no additional CJK font is downloaded or requested from +an external provider. Japanese metrics depend on the user's installed font. + +## Build-time outlines + +`src/lib/landing-glyphs.mjs` uses `opentype.js@2.0.0` during Astro rendering. +It reads `public/fonts/LythMono-Regular.ttf` from the site working directory, +extracts box glyphs directly through cmap (no programming ligatures or GSUB +shaping), and normalizes the vertical cell to the 24px tree row. Direct glyph +lookup also avoids Lyth Mono's contextual GSUB lookups that OpenType.js 2.0.0 +does not support. The 0.604em advance and glyph stroke width are retained. + +The resulting SVG paths are emitted into static HTML; neither the parser nor +the TTF is fetched by the browser. Anime.js controls the shared tree transform +and reveals the persistent trunk through a mask; there is no switch back to +HTML box-drawing text on completion. Labels and normal content remain text. + +## Integrity + +`SHA256SUMS` records every font and upstream license in this directory. Verify +from this directory with `sha256sum --check SHA256SUMS`. Individual hashes +pin the exact binaries even if upstream tags or release assets change. diff --git a/site/public/fonts/SHA256SUMS b/site/public/fonts/SHA256SUMS new file mode 100644 index 0000000..8ff8b62 --- /dev/null +++ b/site/public/fonts/SHA256SUMS @@ -0,0 +1,10 @@ +eba110c35ad2631cc85c5610bdaa119c80965606c2ccdafa0cae8390e21cfe12 EBGaramond-Bold.woff2 +e7ee9bcf70f45e96230688b30db409c5fd4b90e8ad105cdeb694796f49287606 EBGaramond-BoldItalic.woff2 +b83f7e6b6b82cd7ed609448eafb846125635380de1672c7a270796d0cf5fdb12 EBGaramond-Italic.woff2 +058611bd968817d532cedeab6acaa055e882d365a88523df00e7917ad7a0f704 EBGaramond-OFL.txt +2028dc06d3c130b4761693481436a32a8e35ed500bf58c25c53de004106125b8 EBGaramond-Regular.ttf +bef2aa73867d2d024a2afeea30b4222bb2995d0e48dbd9eb2af04d0efe84428d EBGaramond-Regular.woff2 +cfb84c57ed40f45fc264738d06f102f7465a22c695850c067da26aaa3aaaca68 LythMono-Bold.woff2 +81b8afed5a258c059368f5df783ac2697d0d5062c6acefe2a8f70708a583f68e LythMono-LICENSE.md +025a4739a1d0ecdb5454461cba8e46e23c2b70316f432eca07efc823ce9490ef LythMono-Regular.ttf +c498b6b28219d259176bd70feff9975b5a0b65fa549b5bc7959a2138ef1c66a0 LythMono-Regular.woff2 diff --git a/site/src/components/Landing.astro b/site/src/components/Landing.astro index 3d7bf68..66283e7 100644 --- a/site/src/components/Landing.astro +++ b/site/src/components/Landing.astro @@ -3,10 +3,12 @@ import { objects, initialObject, childrenOf, treePrefix, objectSignature, interf import { href } from '../lib/book'; import '../styles/landing.css'; import BoxFrame from './BoxFrame.astro'; +import TreePrefix from './TreePrefix.astro'; +import { trunkPath, stemWidth, branchY, branchThickness } from '../lib/landing-glyphs.mjs'; +const trunkLength = (objects.findLastIndex((object) => object.depth === 2) - 1) * 24 + branchY + branchThickness / 2; ---