├── .github
├── FUNDING.yml
└── workflows
│ ├── dreamhost-preview-static.yaml
│ └── dreamhost-static.yaml
├── .gitignore
├── CNAME
├── LICENSE
├── README.md
├── index.html
├── package.json
├── pnpm-lock.yaml
├── prettier.config.js
├── public
├── assets
│ ├── 1.mp3
│ ├── 2.mp3
│ ├── 3.mp3
│ ├── 4.mp3
│ ├── 5.mp3
│ ├── 6.mp3
│ ├── 7.mp3
│ ├── DepartureMono-1.346.zip
│ ├── DepartureMono-1.350.zip
│ ├── DepartureMono-1.420.zip
│ ├── DepartureMono-1.422.zip
│ ├── DepartureMono-1.500.otf
│ ├── DepartureMono-1.500.woff
│ ├── DepartureMono-1.500.woff2
│ ├── DepartureMono-1.500.zip
│ ├── DepartureMono-Regular.otf
│ ├── DepartureMono-Regular.woff
│ ├── DepartureMono-Regular.woff2
│ ├── LICENSE
│ ├── README.md
│ ├── android-chrome-192x192.png
│ ├── android-chrome-512x512.png
│ ├── apple-touch-icon.png
│ ├── badge.png
│ ├── badge.svg
│ ├── bag-tag.svg
│ ├── ball.png
│ ├── boarding-pass.svg
│ ├── brief.svg
│ ├── cm.json
│ ├── departure-og.png
│ ├── dm.otf
│ ├── dm.woff
│ ├── dm.woff2
│ ├── dot-matrix-printout-grey.svg
│ ├── dot-matrix-printout-white.svg
│ ├── favicon-16x16.png
│ ├── favicon.ico
│ ├── flight-deck.svg
│ ├── glyph-specimen-static.svg
│ ├── highlighter-outline.svg
│ ├── javascript.svg
│ ├── mercury-diagram.svg
│ ├── newspaper-clipping.svg
│ ├── pad.jpg
│ ├── paddle.png
│ ├── paperclip.svg
│ ├── planet.svg
│ ├── receipt.svg
│ ├── rocket.png
│ ├── rust.svg
│ ├── sql.svg
│ └── vostok.jpg
├── favicon-16x16.png
├── favicon.ico
└── site.webmanifest
├── scripts
├── assemble.ts
└── charlist.txt
├── src
├── components
│ ├── app.tsx
│ ├── cockpit.css
│ ├── cockpit.tsx
│ ├── code.css
│ ├── code.tsx
│ ├── countdown.css
│ ├── countdown.tsx
│ ├── deparkanoid.css
│ ├── derparkanoid.tsx
│ ├── editor.css
│ ├── editor.tsx
│ ├── editordata.ts
│ ├── footer.tsx
│ ├── fuzz.css
│ ├── fuzz.tsx
│ ├── header.css
│ ├── header.tsx
│ ├── keeb.css
│ ├── keeb.tsx
│ ├── printout.css
│ ├── printout.tsx
│ ├── select.css
│ ├── select.tsx
│ ├── sensor.css
│ ├── sensor.tsx
│ ├── tester.css
│ ├── tester.tsx
│ ├── typetest.css
│ └── typetest.tsx
├── glsl-canvas.d.ts
├── gtag.d.ts
├── index.css
├── index.tsx
├── lib
│ ├── charmap.json
│ ├── font.ts
│ └── utils.ts
├── reset.css
└── vite-env.d.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | buy_me_a_coffee: helenazhang
2 |
--------------------------------------------------------------------------------
/.github/workflows/dreamhost-preview-static.yaml:
--------------------------------------------------------------------------------
1 | name: Build and deploy to DreamHost via sshpass
2 |
3 | on:
4 | push
5 |
6 | jobs:
7 | deploy:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - name: Checkout
11 | uses: actions/checkout@v4
12 |
13 | - name: Install pnpm
14 | uses: pnpm/action-setup@v4
15 | with:
16 | run_install: false
17 |
18 | - name: Install Node.js
19 | uses: actions/setup-node@v4
20 | with:
21 | node-version: 20
22 | cache: 'pnpm'
23 |
24 | - name: Install dependencies
25 | run: pnpm install
26 |
27 | - name: Install sshpass
28 | run: sudo apt-get install -y sshpass
29 |
30 | - name: Add SSH Key to known_hosts
31 | env:
32 | KNOWN_HOSTS_ENTRY: ${{ secrets.KNOWN_HOSTS_ENTRY }}
33 | HOST: ${{ secrets.HOST }}
34 | run: |
35 | mkdir -p ~/.ssh
36 | echo "$KNOWN_HOSTS_ENTRY" >> ~/.ssh/known_hosts
37 | chmod 644 ~/.ssh/known_hosts
38 |
39 | - name: Build static site
40 | run: pnpm build
41 |
42 | - name: Deploy via rsync and sshpass
43 | env:
44 | USERNAME: ${{ secrets.USERNAME }}
45 | PASSWORD: ${{ secrets.PASSWORD }}
46 | HOST: ${{ secrets.HOST }}
47 | DEPLOY_PATH: preview.departuremono.com
48 | run: |
49 | sshpass -p "$PASSWORD" rsync -avz --delete ./dist/* $USERNAME@$HOST:$DEPLOY_PATH
50 |
--------------------------------------------------------------------------------
/.github/workflows/dreamhost-static.yaml:
--------------------------------------------------------------------------------
1 | name: Build and deploy to DreamHost via sshpass
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | deploy:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4
14 |
15 | - name: Install pnpm
16 | uses: pnpm/action-setup@v4
17 | with:
18 | run_install: false
19 |
20 | - name: Install Node.js
21 | uses: actions/setup-node@v4
22 | with:
23 | node-version: 20
24 | cache: 'pnpm'
25 |
26 | - name: Install dependencies
27 | run: pnpm install
28 |
29 | - name: Install sshpass
30 | run: sudo apt-get install -y sshpass
31 |
32 | - name: Add SSH Key to known_hosts
33 | env:
34 | KNOWN_HOSTS_ENTRY: ${{ secrets.KNOWN_HOSTS_ENTRY }}
35 | HOST: ${{ secrets.HOST }}
36 | run: |
37 | mkdir -p ~/.ssh
38 | echo "$KNOWN_HOSTS_ENTRY" >> ~/.ssh/known_hosts
39 | chmod 644 ~/.ssh/known_hosts
40 |
41 | - name: Build static site
42 | run: pnpm build
43 |
44 | - name: Deploy via rsync and sshpass
45 | env:
46 | USERNAME: ${{ secrets.USERNAME }}
47 | PASSWORD: ${{ secrets.PASSWORD }}
48 | HOST: ${{ secrets.HOST }}
49 | DEPLOY_PATH: departuremono.com
50 | run: |
51 | sshpass -p "$PASSWORD" rsync -avz --delete ./dist/* $USERNAME@$HOST:$DEPLOY_PATH
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | departuremono.com
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Helena Zhang & Tobias Fried
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Departure Mono
8 |
9 |
10 |
11 | A monospaced pixel font with a lo-fi technical vibe
12 |
13 | ## About
14 |
15 | Departure Mono is a monospaced pixel font inspired by the constraints of early command-line and graphical user interfaces, the tiny pixel fonts of the late 90s/early 00s, and sci-fi concepts from film and television.
16 |
17 | ## Installation
18 |
19 | ### Homebrew
20 |
21 | For macOS users, the [Homebrew](https://brew.sh) package manager can be used to install and upgrade the font using the [`font-departure-mono`](https://formulae.brew.sh/cask/font-departure-mono#default) formula:
22 |
23 | ```sh
24 | brew install font-departure-mono
25 | ```
26 |
27 | ### Nix
28 |
29 | If you're using the [Nix](https://nixos.org) package manager or NixOS, you can install font using its canonical name: [`departure-mono`](https://search.nixos.org/packages?channel=unstable&show=departure-mono&from=0&size=50&sort=relevance&type=packages&query=departure-mono):
30 |
31 | ```sh
32 | # Here's an example on how to install it using `nix profile`
33 | nix profile install github:NixOS/nixpkgs#departure-mono
34 | # Here's an example on how to install it using `nix-env`
35 | nix-env -iA nixos.departure-mono
36 | ```
37 |
38 | ### NixOS
39 |
40 | For [NixOS](https://nixos.org) users, you can add the font to your system configuration by including it in `fonts.packages`:
41 |
42 | ```nix
43 | {
44 | fonts.packages = [
45 | pkgs.departure-mono
46 | ];
47 | }
48 | ```
49 |
50 | ### Manual
51 |
52 | Download the latest release from the [releases](https://github.com/rektdeckard/departure-mono/releases/latest) page.
53 |
54 | ## Usage
55 |
56 | For pixel-perfect results, set the font size to increments of 11px.
57 |
58 | Experiment with tighter or wider tracking (letter-spacing).
59 |
60 | ## Licenses
61 |
62 | | Resource | License | Author |
63 | | -------- | ------- | -------- |
64 | | Font | [SIL OFL](https://github.com/rektdeckard/departure-mono/blob/main/public/assets/LICENSE?raw=true) | [Helena Zhang](https://helenazhang.com) |
65 | | Site | [MIT](https://github.com/rektdeckard/departure-mono/blob/main/LICENSE?raw=true) | [Helena Zhang](https://helenazhang.com), [Tobias Fried](https://tobiasfried.com) |
66 |
67 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Departure Mono
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "departure-mono",
3 | "private": true,
4 | "version": "1.500.0",
5 | "type": "module",
6 | "packageManager": "pnpm@10.6.3",
7 | "scripts": {
8 | "predev": "pnpm run assemble",
9 | "dev": "vite",
10 | "prebuild": "pnpm run assemble",
11 | "build": "tsc -b && vite build",
12 | "format": "prettier --write --ignore-unknown .",
13 | "prettier:check": "prettier --check --ignore-unknown .",
14 | "preview": "vite preview",
15 | "assemble": "pnpm dlx tsx ./scripts/assemble.ts"
16 | },
17 | "dependencies": {
18 | "@ark-ui/solid": "^3.6.2",
19 | "@codemirror/autocomplete": "^6.17.0",
20 | "@codemirror/commands": "^6.6.0",
21 | "@codemirror/lang-css": "^6.2.1",
22 | "@codemirror/lang-go": "^6.0.1",
23 | "@codemirror/lang-html": "^6.4.9",
24 | "@codemirror/lang-javascript": "^6.2.2",
25 | "@codemirror/lang-markdown": "^6.2.5",
26 | "@codemirror/lang-python": "^6.1.6",
27 | "@codemirror/lang-rust": "^6.0.1",
28 | "@codemirror/lang-sql": "^6.7.0",
29 | "@codemirror/language": "^6.10.2",
30 | "@codemirror/state": "^6.4.1",
31 | "@codemirror/view": "^6.28.6",
32 | "@lezer/highlight": "^1.2.0",
33 | "codemirror": "^6.0.1",
34 | "cva": "npm:class-variance-authority@^0.7.0",
35 | "fonteditor-core": "^2.4.1",
36 | "glslCanvas": "^0.2.6",
37 | "kdim": "^0.6.6",
38 | "p5": "^1.9.4",
39 | "solid-codemirror": "^2.3.1",
40 | "solid-js": "^1.8.18"
41 | },
42 | "devDependencies": {
43 | "@types/node": "^22.15.21",
44 | "@types/p5": "^1.7.6",
45 | "gh-pages": "^6.1.1",
46 | "prettier": "^3.3.3",
47 | "typescript": "^5.2.2",
48 | "vite": "^5.3.4",
49 | "vite-plugin-node-polyfills": "^0.22.0",
50 | "vite-plugin-solid": "^2.10.2"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | arrowParens: "always",
3 | semi: true,
4 | trailingComma: "all",
5 | singleQuote: false,
6 | };
7 |
--------------------------------------------------------------------------------
/public/assets/1.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/1.mp3
--------------------------------------------------------------------------------
/public/assets/2.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/2.mp3
--------------------------------------------------------------------------------
/public/assets/3.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/3.mp3
--------------------------------------------------------------------------------
/public/assets/4.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/4.mp3
--------------------------------------------------------------------------------
/public/assets/5.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/5.mp3
--------------------------------------------------------------------------------
/public/assets/6.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/6.mp3
--------------------------------------------------------------------------------
/public/assets/7.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/7.mp3
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.346.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.346.zip
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.350.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.350.zip
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.420.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.420.zip
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.422.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.422.zip
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.500.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.500.otf
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.500.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.500.woff
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.500.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.500.woff2
--------------------------------------------------------------------------------
/public/assets/DepartureMono-1.500.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-1.500.zip
--------------------------------------------------------------------------------
/public/assets/DepartureMono-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-Regular.otf
--------------------------------------------------------------------------------
/public/assets/DepartureMono-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-Regular.woff
--------------------------------------------------------------------------------
/public/assets/DepartureMono-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/DepartureMono-Regular.woff2
--------------------------------------------------------------------------------
/public/assets/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2022–2024 Helena Zhang (helenazhang.com).
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | https://openfontlicense.org
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/public/assets/README.md:
--------------------------------------------------------------------------------
1 | # Hello!
2 |
3 | Thanks for trying Departure Mono (departuremono.com), licensed under the SIL OFL. Send your questions and suggestions to hello@helenazhang.com or DM me on Twitter/X: @minor_axis. Donations much appreciated at: buymeacoffee.com/helenazhang. Enjoy!
4 |
5 | — Helena Zhang (helenazhang.com)
6 |
7 | # Font Information
8 |
9 | Version 1.500 features 1,186 glyphs, including support for:
10 |
11 | - Basic Latin, Latin-1, Latin Extended-A, and most Latinate languages
12 | - Cyrillic
13 | - Basic Greek
14 | - Small caps
15 | - Old-style numerals and fractions
16 | - Simple box-drawing characters and selected symbols
17 |
18 | # Usage
19 |
20 | For pixel-perfect results, set the font size to increments of 11px.
21 |
22 | Experiment with tighter or wider tracking (letter-spacing).
23 |
24 | # Changelog
25 |
26 | v1.500
27 | - 1,186 glyphs
28 | - Added box-drawing characters: ▁ ▂ ▃ ▅ ▆ ▇ ▔ ▏ ▎ ▍ ▌ ▋ ▊ ▉ ▐ ▕ ▖ ▗ ▘ ▙ ▚ ▛ ▜ ▝ ▞ ▟ ■ □ ▪ ▫ ╦ ╖ ╓ ┰ ┒ ┧ ┎ ┟ ╁ ┯ ┑ ┩ ┍ ┡ ╇ ╤ ╕ ╒ ╍ ╏ ╻ ┳ ┓ ┏ ━ ╸ ╾ ┉ ┋ ╺ ┅ ┇ ╹ ┻ ┛ ╿ ┗ ┃ ╋ ┫ ┣ ╅ ┭ ┵ ┽ ┲ ┺ ╊ ╃ ╮ ╭ ╯ ╰ ╳ ╲ ╱ ╌ ╎ ╷ ╴ ╼ ┈ ┊ ╶ ┄ ┆ ╵ ╽ ╆ ┮ ┶ ┾ ┱ ┹ ╉ ╄ ╨ ╜ ╙ ╀ ┸ ┦ ┚ ┞ ┖ ╈ ┷ ┪ ┙ ┢ ┕ ╧ ╛ ╘ ╫ ╢ ╟ ╂ ┨ ┠ ┿ ┥ ┝ ╪ ╡ ╞ 🮂 🮃 🮄 🮅 🮆 🮇 🮈 🮉 🮊 🮋
29 | + Added brackets: ❰ ❮ ❱ ❯ ❬ ❭
30 |
31 | v1.422
32 | - 1,034 glyphs
33 | - Added stars: ★ ☆ ✦ ✧
34 | - Fixed bug: Ё and ё were missing accents
35 | - Adjusted: ₽ Д д Ы ы Ъ ъ
36 | - Serifed: Г г Ѓ ѓ Ґ ґ Ӷ ӷ
37 |
38 | v1.420
39 | - 1,030 glyphs
40 | - Added Cyrillic characters: А Б В Г Ѓ Ґ Ӷ Ғ Ҕ Д Е Ѐ Ё Ж З И Й Ѝ Ҋ К Ќ Л М Н О П Р С Т У Ў Ф Х Ч Ц Ш Щ Џ Ь Ы Ъ Љ Њ Ѕ Є Э І Ї Ј Ћ Ю Я Ђ Ѣ Ѵ Җ Ҙ Қ Ҟ Ҡ Ң Ҥ Ҧ Ԥ Ҩ Ҫ Ҭ Ү Ұ Ҳ Ҵ Ҷ Һ Ԧ Ҽ Ҿ Ӏ Ӂ Ӄ Ӆ Ӈ Ӊ Ӌ Ӎ Ӑ Ӓ Ӕ Ӗ Ӛ Ӝ Ӟ Ӡ Ӣ Ӥ Ӧ Ө Ӫ Ӭ Ӯ Ӱ Ӳ Ӵ Ӹ Ӽ Ԑ Ԓ Ԝ Ҍ Ҏ а б в г ѓ ґ ӷ ғ ҕ д е ѐ ё ж з и й ѝ ҋ к ќ л м н о п р с т у ў ф х ч ц ш щ џ ь ы ъ љ њ ѕ є э і ї ј ћ ю я ђ ѣ ѵ җ ҙ қ ҟ ҡ ң ҥ ҧ ԥ ҩ ҫ ҭ ү ұ ҳ ҵ ҷ һ ԧ ҽ ҿ ӏ ӂ ӄ ӆ ӈ ӊ ӌ ӎ ӑ ӓ ӕ ӗ ӛ ӝ ӟ ӡ ӣ ӥ ӧ ө ӫ ӭ ӯ ӱ ӳ ӵ ӹ ӽ ԑ ԓ ԝ ҍ ҏ
41 | - Added Greek characters: Ά Έ Ή Ί Ό Ύ Ώ Ϊ Ϋ ί ϊ ΐ ύ ϋ ΰ ό ώ ά έ ή ΄ ΅ ͺ
42 | - Added: № ∙
43 | - Adjusted: Ľ ť ĸ δ ι ʻ
44 |
45 | v1.350
46 | - 775 glyphs
47 | - Added fractions: ↉ ⅓ ⅔ ⅕ ⅖ ⅗ ⅘ ⅙ ⅚ ⅐ ⅑
48 | - Added a centered alternate for *
49 | - Adjusted old-style numerals 0, 1, 2 to be x-height
50 | - Adjusted ½ ¼ ¾ ⅛ ⅜ ⅝ ⅞ ‰ to fit in bounding box
51 | - Adjusted curly brackets to horizontally align with other brackets
52 | - Adjusted position of * and °
53 |
54 | v1.346
55 | - 763 glyphs
56 |
57 | # Thanks
58 |
59 | A big thank you to: Tobias Fried, Christine Lee, Daniel Stern, Kim Slawson, Parker McGowan, Alex Krivov, Karl Peterson, Alexander Zaytsev, Vadim Pleshkov, and Maxim Iorsh for their general feedback and testing across languages
--------------------------------------------------------------------------------
/public/assets/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/assets/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/android-chrome-512x512.png
--------------------------------------------------------------------------------
/public/assets/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/assets/badge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/badge.png
--------------------------------------------------------------------------------
/public/assets/bag-tag.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/assets/ball.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/ball.png
--------------------------------------------------------------------------------
/public/assets/departure-og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/departure-og.png
--------------------------------------------------------------------------------
/public/assets/dm.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/dm.otf
--------------------------------------------------------------------------------
/public/assets/dm.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/dm.woff
--------------------------------------------------------------------------------
/public/assets/dm.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/dm.woff2
--------------------------------------------------------------------------------
/public/assets/dot-matrix-printout-grey.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/public/assets/dot-matrix-printout-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/public/assets/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/favicon-16x16.png
--------------------------------------------------------------------------------
/public/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/favicon.ico
--------------------------------------------------------------------------------
/public/assets/glyph-specimen-static.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/public/assets/highlighter-outline.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/assets/pad.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/pad.jpg
--------------------------------------------------------------------------------
/public/assets/paddle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/paddle.png
--------------------------------------------------------------------------------
/public/assets/paperclip.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/assets/planet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/assets/rocket.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/rocket.png
--------------------------------------------------------------------------------
/public/assets/sql.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/public/assets/vostok.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/assets/vostok.jpg
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rektdeckard/departure-mono/75152a3f1e6dacdd248a6c397c97dbf27e33eea0/public/favicon.ico
--------------------------------------------------------------------------------
/public/site.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Departure Mono: A MONOSPACED PIXEL FONT WITH A LO-FI TECHNICAL VIBE",
3 | "short_name": "Departure Mono",
4 | "icons": [
5 | {
6 | "src": "/assets/android-chrome-192x192.png",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | },
10 | {
11 | "src": "/assets/android-chrome-512x512.png",
12 | "sizes": "512x512",
13 | "type": "image/png"
14 | }
15 | ],
16 | "theme_color": "#eeeeee",
17 | "background_color": "#eeeeee",
18 | "display": "standalone"
19 | }
20 |
--------------------------------------------------------------------------------
/scripts/assemble.ts:
--------------------------------------------------------------------------------
1 | import fs from "node:fs";
2 |
3 | type CharEntry = {
4 | code: number;
5 | feat?: string[];
6 | };
7 |
8 | (function main() {
9 | const charlist = fs.readFileSync("./scripts/charlist.txt", "utf-8");
10 |
11 | const charmap = charlist.split(/\n\n+/).reduce((acc, section) => {
12 | const [name, ...chars] = section.split(/\n/).map((line) => line.trim());
13 | acc[name] = chars.filter(Boolean).map(parseCharWithOptionalFeatures);
14 | return acc;
15 | }, {});
16 |
17 | fs.writeFileSync("src/lib/charmap.json", JSON.stringify(charmap, null, 2));
18 | })();
19 |
20 | function parseCharWithOptionalFeatures(line: string): CharEntry {
21 | const parts = line.split(".");
22 | const code = parseInt(parts[0], 16);
23 | if (isNaN(code)) {
24 | throw new Error(`Invalid code point in entry: ${line}`);
25 | }
26 |
27 | const entry: CharEntry = { code };
28 | if (parts.length > 1) {
29 | entry.feat = parts.slice(1).map((f) => f.trim());
30 | }
31 |
32 | return entry;
33 | }
34 |
--------------------------------------------------------------------------------
/scripts/charlist.txt:
--------------------------------------------------------------------------------
1 | Basic Latin
2 | 0041
3 | 0042
4 | 0043
5 | 0044
6 | 0045
7 | 0046
8 | 0047
9 | 0048
10 | 0049
11 | 004A
12 | 004B
13 | 004C
14 | 004D
15 | 004E
16 | 004F
17 | 0050
18 | 0051
19 | 0052
20 | 0053
21 | 0054
22 | 0055
23 | 0056
24 | 0057
25 | 0058
26 | 0059
27 | 005A
28 | 0061
29 | 0062
30 | 0063
31 | 0064
32 | 0065
33 | 0066
34 | 0067
35 | 0068
36 | 0069
37 | 006A
38 | 006B
39 | 006C
40 | 006D
41 | 006E
42 | 006F
43 | 0070
44 | 0071
45 | 0072
46 | 0073
47 | 0074
48 | 0075
49 | 0076
50 | 0077
51 | 0078
52 | 0079
53 | 007A
54 | 0061.sc
55 | 0062.sc
56 | 0063.sc
57 | 0064.sc
58 | 0065.sc
59 | 0066.sc
60 | 0067.sc
61 | 0068.sc
62 | 0069.sc
63 | 006A.sc
64 | 006B.sc
65 | 006C.sc
66 | 006D.sc
67 | 006E.sc
68 | 006F.sc
69 | 0070.sc
70 | 0071.sc
71 | 0072.sc
72 | 0073.sc
73 | 0074.sc
74 | 0075.sc
75 | 0076.sc
76 | 0077.sc
77 | 0078.sc
78 | 0079.sc
79 | 007A.sc
80 |
81 | Extended Latin
82 | 00C1
83 | 0102
84 | 01CD
85 | 00C2
86 | 00C4
87 | 1EA0
88 | 00C0
89 | 0100
90 | 0104
91 | 00C5
92 | 01FA
93 | 00C3
94 | 00C6
95 | 01FC
96 | 1E02
97 | 0181
98 | 0106
99 | 010C
100 | 00C7
101 | 0108
102 | 010A
103 | 010E
104 | 1E10
105 | 0110
106 | 1E0A
107 | 1E0C
108 | 018A
109 | 0189
110 | 00D0
111 | 00C9
112 | 0114
113 | 011A
114 | 00CA
115 | 00CB
116 | 0116
117 | 1EB8
118 | 00C8
119 | 0112
120 | 0118
121 | 0190
122 | 1EBC
123 | 018E
124 | 018F
125 | 1E1E
126 | 0191
127 | 011E
128 | 01E6
129 | 011C
130 | 0122
131 | 0120
132 | 0193
133 | 1E20
134 | 0126
135 | 1E2A
136 | 021E
137 | 0124
138 | 1E24
139 | 0132
140 | 00CD
141 | 012C
142 | 01CF
143 | 00CE
144 | 00CF
145 | 0130
146 | 1ECA
147 | 00CC
148 | 012A
149 | 012E
150 | 0197
151 | 0128
152 | 0134
153 | 0136
154 | 1E32
155 | 0198
156 | 0139
157 | 013D
158 | 013B
159 | 013F
160 | 1E36
161 | 0141
162 | 1E3E
163 | 1E40
164 | 0143
165 | 0147
166 | 0145
167 | 1E44
168 | 01F8
169 | 019D
170 | 00D1
171 | 014A
172 | 00D3
173 | 014E
174 | 01D1
175 | 00D4
176 | 00D6
177 | 1ECC
178 | 00D2
179 | 0150
180 | 014C
181 | 01EA
182 | 0186
183 | 00D8
184 | 01FE
185 | 00D5
186 | 0152
187 | 1E56
188 | 00DE
189 | 0154
190 | 0158
191 | 0156
192 | 1E5A
193 | 015A
194 | 0160
195 | 015E
196 | 015C
197 | 0218
198 | 1E60
199 | 1E62
200 | 1E9E
201 | 0166
202 | 0164
203 | 0162
204 | 021A
205 | 1E6A
206 | 1E6C
207 | 00DA
208 | 0244
209 | 016C
210 | 01D3
211 | 00DB
212 | 00DC
213 | 1EE4
214 | 00D9
215 | 01AF
216 | 0170
217 | 016A
218 | 0172
219 | 016E
220 | 0168
221 | 0194
222 | 01B2
223 | 1E7C
224 | 1E82
225 | 0174
226 | 1E84
227 | 1E80
228 | 00DD
229 | 0176
230 | 0178
231 | 1E8E
232 | 1EF2
233 | 01B3
234 | 0232
235 | 1EF8
236 | 0179
237 | 017D
238 | 017B
239 | 1E92
240 | A78B
241 | 00E1
242 | 0103
243 | 01CE
244 | 00E2
245 | 00E4
246 | 1EA1
247 | 00E0
248 | 0101
249 | 0105
250 | 00E5
251 | 01FB
252 | 00E3
253 | 00E6
254 | 01FD
255 | 1E03
256 | 0253
257 | 0107
258 | 010D
259 | 00E7
260 | 0109
261 | 010B
262 | 010F
263 | 1E11
264 | 0111
265 | 1E0B
266 | 1E0D
267 | 0257
268 | 0256
269 | 00F0
270 | 00E9
271 | 0115
272 | 011B
273 | 00EA
274 | 00EB
275 | 0117
276 | 1EB9
277 | 00E8
278 | 0113
279 | 0119
280 | 025B
281 | 1EBD
282 | 01DD
283 | 0259
284 | 1E1F
285 | 0263
286 | 011F
287 | 01E7
288 | 011D
289 | 0123
290 | 0121
291 | 0260
292 | 0294
293 | 1E21
294 | 0127
295 | 1E2B
296 | 021F
297 | 0125
298 | 1E25
299 | 0131
300 | 00ED
301 | 012D
302 | 01D0
303 | 00EE
304 | 00EF
305 | 1ECB
306 | 00EC
307 | 012B
308 | 012F
309 | 0268
310 | 0129
311 | 0133
312 | 0237
313 | 0135
314 | 0137
315 | 1E33
316 | 0138
317 | 0199
318 | 013A
319 | 013E
320 | 013C
321 | 0140
322 | 1E37
323 | 1E9B
324 | 0142
325 | 1E3F
326 | 1E41
327 | 0144
328 | 0148
329 | 0146
330 | 1E45
331 | 01F9
332 | 0272
333 | 00F1
334 | 014B
335 | 00F3
336 | 014F
337 | 01D2
338 | 00F4
339 | 00F6
340 | 1ECD
341 | 00F2
342 | 0151
343 | 014D
344 | 01EB
345 | 0254
346 | 00F8
347 | 01FF
348 | 00F5
349 | 0153
350 | 1E57
351 | 00FE
352 | 0155
353 | 0159
354 | 0157
355 | 1E5B
356 | 027C
357 | 015B
358 | 0161
359 | 015F
360 | 015D
361 | 0219
362 | 1E61
363 | 1E63
364 | 00DF
365 | 017F
366 | 0167
367 | 0165
368 | 0163
369 | 021B
370 | 1E6B
371 | 1E6D
372 | 00FA
373 | 0289
374 | 016D
375 | 01D4
376 | 00FB
377 | 00FC
378 | 1EE5
379 | 00F9
380 | 01B0
381 | 0171
382 | 016B
383 | 0173
384 | 016F
385 | 0169
386 | 028B
387 | 1E7D
388 | 1E83
389 | 0175
390 | 1E85
391 | 1E81
392 | 00FD
393 | 0177
394 | 00FF
395 | 1E8F
396 | 1EF3
397 | 01B4
398 | 0233
399 | 1EF9
400 | 017A
401 | 017E
402 | 017C
403 | 1E93
404 | A78C
405 | 01C2
406 | 01C0
407 | 01C1
408 | 01C3
409 |
410 | Cyrillic
411 | 0410
412 | 0411
413 | 0412
414 | 0413
415 | 0403
416 | 0490
417 | 04F6
418 | 0492
419 | 0494
420 | 0414
421 | 0415
422 | 0400
423 | 0401
424 | 0416
425 | 0417
426 | 0418
427 | 0419
428 | 040D
429 | 048A
430 | 041A
431 | 040C
432 | 041B
433 | 041C
434 | 041D
435 | 041E
436 | 041F
437 | 0420
438 | 0421
439 | 0422
440 | 0423
441 | 040E
442 | 0424
443 | 0425
444 | 0426
445 | 0427
446 | 0428
447 | 0429
448 | 040F
449 | 042C
450 | 042B
451 | 042A
452 | 0409
453 | 040A
454 | 0405
455 | 0404
456 | 042D
457 | 0406
458 | 0407
459 | 0408
460 | 040B
461 | 042E
462 | 042F
463 | 0402
464 | 0462
465 | 0474
466 | 0496
467 | 0498
468 | 049A
469 | 049E
470 | 04A0
471 | 04A2
472 | 04A4
473 | 04A6
474 | 0524
475 | 04A8
476 | 04AA
477 | 04AC
478 | 04AE
479 | 04B0
480 | 04B2
481 | 04B4
482 | 04B6
483 | 04BA
484 | 0526
485 | 04BC
486 | 04BE
487 | 04C0
488 | 04C1
489 | 04C3
490 | 04C5
491 | 04C7
492 | 04C9
493 | 04CB
494 | 04CD
495 | 04D0
496 | 04D2
497 | 04D4
498 | 04D6
499 | 04DC
500 | 04DE
501 | 04E0
502 | 04E2
503 | 04E4
504 | 04E6
505 | 04E8
506 | 04EA
507 | 04EC
508 | 04EE
509 | 04F0
510 | 04F2
511 | 04F4
512 | 04F8
513 | 04FC
514 | 0510
515 | 0512
516 | 051C
517 | 048C
518 | 048E
519 | 0430
520 | 0431
521 | 0432
522 | 0433
523 | 0453
524 | 0491
525 | 04F7
526 | 0493
527 | 0495
528 | 0434
529 | 0435
530 | 0450
531 | 0451
532 | 0436
533 | 0437
534 | 0438
535 | 0439
536 | 045D
537 | 048B
538 | 043A
539 | 045C
540 | 043B
541 | 043C
542 | 043D
543 | 043E
544 | 043F
545 | 0440
546 | 0441
547 | 0442
548 | 0443
549 | 045E
550 | 0444
551 | 0445
552 | 0446
553 | 0447
554 | 0448
555 | 0449
556 | 045F
557 | 044C
558 | 044B
559 | 044A
560 | 0459
561 | 045A
562 | 0455
563 | 0454
564 | 044D
565 | 0456
566 | 0457
567 | 0458
568 | 045B
569 | 044E
570 | 044F
571 | 0452
572 | 0463
573 | 0475
574 | 0497
575 | 0499
576 | 049B
577 | 049F
578 | 04A1
579 | 04A3
580 | 04A5
581 | 04A7
582 | 0525
583 | 04A9
584 | 04AB
585 | 04AD
586 | 04AF
587 | 04B1
588 | 04B3
589 | 04B5
590 | 04B7
591 | 04BB
592 | 0527
593 | 04BD
594 | 04BF
595 | 04CF
596 | 04C2
597 | 04C4
598 | 04C6
599 | 04C8
600 | 04CA
601 | 04CC
602 | 04CE
603 | 04D1
604 | 04D3
605 | 04D5
606 | 04D7
607 | 04DD
608 | 04DF
609 | 04E1
610 | 04E3
611 | 04E5
612 | 04E7
613 | 04E9
614 | 04EB
615 | 04ED
616 | 04EF
617 | 04F1
618 | 04F3
619 | 04F5
620 | 04F9
621 | 04FD
622 | 0511
623 | 0513
624 | 051D
625 | 048D
626 | 048F
627 |
628 | Greek
629 | 0391
630 | 0392
631 | 0393
632 | 0394
633 | 0395
634 | 0396
635 | 0397
636 | 0398
637 | 0399
638 | 039A
639 | 039B
640 | 039C
641 | 039D
642 | 039E
643 | 039F
644 | 03A0
645 | 03A1
646 | 03A3
647 | 03A4
648 | 03A5
649 | 03A6
650 | 03A7
651 | 03A8
652 | 03A9
653 | 0386
654 | 0388
655 | 0389
656 | 038A
657 | 038C
658 | 038E
659 | 038F
660 | 03AA
661 | 03AB
662 | 03B1
663 | 03B2
664 | 03B3
665 | 03B4
666 | 03B5
667 | 03B6
668 | 03B7
669 | 03B8
670 | 03B9
671 | 03BA
672 | 03BB
673 | 03BC
674 | 03BD
675 | 03BE
676 | 03BF
677 | 03C0
678 | 03C1
679 | 03C2
680 | 03C3
681 | 03C4
682 | 03C5
683 | 03C6
684 | 03C7
685 | 03C8
686 | 03C9
687 | 03AF
688 | 03CA
689 | 0390
690 | 03CD
691 | 03CB
692 | 03B0
693 | 03CC
694 | 03CE
695 | 03AC
696 | 03AD
697 | 03AE
698 | 037A
699 |
700 | Punctuation, Symbols
701 | 00AA
702 | 00BA
703 | 002E
704 | 002C
705 | 003A
706 | 003B
707 | 2026
708 | 0021
709 | 00A1
710 | 003F
711 | 00BF
712 | 00B7
713 | 2022
714 | 002A
715 | 204A
716 | 2027
717 | 0023
718 | 002F
719 | 005C
720 | 002D
721 | 2013
722 | 2014
723 | 2E17
724 | 2015
725 | 2010
726 | 005F
727 | 0028
728 | 0029
729 | 007B
730 | 007D
731 | 005B
732 | 005D
733 | 201A
734 | 201E
735 | 201C
736 | 201D
737 | 2018
738 | 2019
739 | 00AB
740 | 00BB
741 | 2039
742 | 203A
743 | 0022
744 | 0027
745 | 0040
746 | 0026
747 | 00B6
748 | 00A7
749 | 00A9
750 | 00AE
751 | 2122
752 | 00B0
753 | 2032
754 | 2033
755 | 007C
756 | 00A6
757 | 2020
758 | 2021
759 | 2116
760 | 2325
761 | 2318
762 |
763 | Numerals, Math, Currency
764 | 0030
765 | 0031
766 | 0032
767 | 0033
768 | 0034
769 | 0035
770 | 0036
771 | 0037
772 | 0038
773 | 0039
774 | 0030.osf
775 | 0031.osf
776 | 0032.osf
777 | 0033.osf
778 | 0034.osf
779 | 0035.osf
780 | 0036.osf
781 | 0037.osf
782 | 0038.osf
783 | 0039.osf
784 | 2044
785 | 00BD
786 | 2189
787 | 2153
788 | 2154
789 | 00BC
790 | 00BE
791 | 2155
792 | 2156
793 | 2157
794 | 2158
795 | 2159
796 | 215A
797 | 2150
798 | 215B
799 | 215C
800 | 215D
801 | 215E
802 | 2151
803 | 2080
804 | 2081
805 | 2082
806 | 2083
807 | 2084
808 | 2085
809 | 2086
810 | 2087
811 | 2088
812 | 2089
813 | 2070
814 | 00B9
815 | 00B2
816 | 00B3
817 | 2074
818 | 2075
819 | 2076
820 | 2077
821 | 2078
822 | 2079
823 | 0192
824 | 0E3F
825 | 2113
826 | 212E
827 | 20BF
828 | 00A2
829 | 00A4
830 | 0024
831 | 20AC
832 | 20A3
833 | 20B4
834 | 20BA
835 | 20BD
836 | 20B9
837 | 20AA
838 | 00A3
839 | 20B8
840 | 20AE
841 | 20A9
842 | 00A5
843 | 2219
844 | 2052
845 | 002B
846 | 2212
847 | 00D7
848 | 00F7
849 | 003D
850 | 2260
851 | 003E
852 | 003C
853 | 2265
854 | 2264
855 | 00B1
856 | 2248
857 | 007E
858 | 00AC
859 | 005E
860 | 221E
861 | 222B
862 | 2126
863 | 2206
864 | 220F
865 | 2211
866 | 221A
867 | 2202
868 | 00B5
869 | 0025
870 | 2030
871 | 27E8
872 | 27E9
873 |
874 | Graphical
875 | 2770
876 | 276E
877 | 2771
878 | 276F
879 | 276C
880 | 276D
881 | 2669
882 | 266A
883 | 266B
884 | 266C
885 | 2605
886 | 2606
887 | 2660
888 | 2663
889 | 2665
890 | 2666
891 | 2726
892 | 2727
893 | 2191
894 | 2197
895 | 2192
896 | 2198
897 | 2193
898 | 2199
899 | 2190
900 | 2196
901 | 2195
902 | 21B0
903 | 21B1
904 | 21B2
905 | 21B3
906 | 21B4
907 | 21B5
908 | 2581
909 | 2582
910 | 2583
911 | 2584
912 | 2585
913 | 2586
914 | 2587
915 | 2588
916 | 2580
917 | 2594
918 | 258F
919 | 258E
920 | 258D
921 | 258C
922 | 258B
923 | 258A
924 | 2589
925 | 2590
926 | 2595
927 | 2596
928 | 2597
929 | 2598
930 | 2599
931 | 259A
932 | 259B
933 | 259C
934 | 259D
935 | 259E
936 | 259F
937 | 2591
938 | 2592
939 | 2593
940 | 25CC
941 | 25CA
942 | 25A0
943 | 25A1
944 | 25AA
945 | 25AB
946 | 25B2
947 | 25BC
948 | 25B3
949 | 25B7
950 | 25BD
951 | 25C1
952 | 25BA
953 | 25C4
954 | 2566
955 | 2557
956 | 2554
957 | 2550
958 | 2569
959 | 255D
960 | 255A
961 | 2551
962 | 256C
963 | 2563
964 | 2560
965 | 2565
966 | 2556
967 | 2553
968 | 2530
969 | 2512
970 | 2527
971 | 250E
972 | 251F
973 | 2541
974 | 252F
975 | 2511
976 | 2529
977 | 250D
978 | 2521
979 | 2547
980 | 2564
981 | 2555
982 | 2552
983 | 254D
984 | 254F
985 | 257B
986 | 2533
987 | 2513
988 | 250F
989 | 2501
990 | 2578
991 | 257E
992 | 2509
993 | 250B
994 | 257A
995 | 2505
996 | 2507
997 | 2579
998 | 253B
999 | 251B
1000 | 257F
1001 | 2517
1002 | 2503
1003 | 254B
1004 | 252B
1005 | 2523
1006 | 2545
1007 | 252D
1008 | 2535
1009 | 253D
1010 | 2532
1011 | 253A
1012 | 254A
1013 | 2543
1014 | 256E
1015 | 256D
1016 | 256F
1017 | 2570
1018 | 2573
1019 | 2572
1020 | 2571
1021 | 254C
1022 | 254E
1023 | 2577
1024 | 252C
1025 | 2510
1026 | 250C
1027 | 2500
1028 | 2574
1029 | 257C
1030 | 2508
1031 | 250A
1032 | 2576
1033 | 2504
1034 | 2506
1035 | 2575
1036 | 257D
1037 | 2534
1038 | 2518
1039 | 2514
1040 | 2502
1041 | 253C
1042 | 2524
1043 | 251C
1044 | 2546
1045 | 252E
1046 | 2536
1047 | 253E
1048 | 2531
1049 | 2539
1050 | 2549
1051 | 2544
1052 | 2568
1053 | 255C
1054 | 2559
1055 | 2540
1056 | 2538
1057 | 2526
1058 | 251A
1059 | 251E
1060 | 2516
1061 | 2548
1062 | 2537
1063 | 252A
1064 | 2519
1065 | 2522
1066 | 2515
1067 | 2567
1068 | 255B
1069 | 2558
1070 | 256B
1071 | 2562
1072 | 255F
1073 | 2542
1074 | 2528
1075 | 2520
1076 | 253F
1077 | 2525
1078 | 251D
1079 | 256A
1080 | 2561
1081 | 255E
1082 | 1FB82
1083 | 1FB83
1084 | 1FB84
1085 | 1FB85
1086 | 1FB86
1087 | 1FB87
1088 | 1FB88
1089 | 1FB89
1090 | 1FB8A
1091 | 1FB8B
1092 |
--------------------------------------------------------------------------------
/src/components/app.tsx:
--------------------------------------------------------------------------------
1 | import { Suspense } from "solid-js";
2 |
3 | import { Header } from "./header";
4 | import { Cockpit } from "./cockpit";
5 | import { Keeb } from "./keeb";
6 | import { Tester } from "./tester";
7 | import { Deparkanoid } from "./derparkanoid";
8 | import { Code } from "./code";
9 | import { Footer } from "./footer";
10 |
11 | export function App() {
12 | return (
13 | <>
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/cockpit.css:
--------------------------------------------------------------------------------
1 | #transition {
2 | position: relative;
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | height: 1800px;
7 | transition:
8 | color 400ms ease,
9 | background-color 400ms ease;
10 | --back: color-mix(in lch, var(--enamel), var(--carbon) var(--t));
11 | background-color: var(--back);
12 | color: color-mix(in lch, var(--smoke), var(--amber) var(--t));
13 | padding-top: 100px;
14 | }
15 |
16 | #cockpit {
17 | position: sticky;
18 | top: 10vh;
19 | height: 75vh;
20 | margin: 120px 0;
21 | font-size: 14px;
22 | }
23 |
24 | #cockpit foreignObject {
25 | line-height: 1.1;
26 | }
27 |
28 | #tend {
29 | position: absolute;
30 | bottom: 200px;
31 | }
32 |
33 | @media screen and (max-width: 1115px) {
34 | #cockpit {
35 | margin-top: 0;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/components/code.css:
--------------------------------------------------------------------------------
1 | #code {
2 | position: relative;
3 | width: 100%;
4 | }
5 |
6 | #code .comment {
7 | position: absolute;
8 | top: 1199px;
9 | left: 798px;
10 | width: 182px;
11 | color: var(--mud);
12 | }
13 |
14 | #apollo2 {
15 | position: absolute;
16 | top: -28px;
17 | left: -364px;
18 | user-select: none;
19 | }
20 |
21 | #flow {
22 | position: absolute;
23 | left: -259px;
24 | top: 267px;
25 | }
26 |
27 | #sensor {
28 | position: absolute;
29 | top: 330px;
30 | left: 342px;
31 | color: var(--accent);
32 | font-size: 220px;
33 | }
34 |
35 | #mercury-diagram {
36 | position: absolute;
37 | top: 127px;
38 | left: -505px;
39 | }
40 |
41 | #mission-report {
42 | position: relative;
43 | margin-top: 1375px;
44 | left: 296px;
45 | }
46 |
47 | #code-rust {
48 | position: absolute;
49 | top: 198px;
50 | left: 798px;
51 | }
52 |
53 | #code-js {
54 | position: absolute;
55 | top: 673px;
56 | left: -40px;
57 | }
58 |
59 | #code-sql {
60 | position: absolute;
61 | bottom: -31px;
62 | left: -3px;
63 | }
64 |
65 | @media screen and (max-width: 1115px) {
66 | #mission-report {
67 | left: 87px;
68 | margin-top: 1316px;
69 | }
70 |
71 | #mercury-diagram {
72 | top: 88px;
73 | left: -642px;
74 | }
75 |
76 | #sensor {
77 | left: 44px;
78 | top: 286px;
79 | }
80 |
81 | #code-rust {
82 | top: 210px;
83 | left: 618px;
84 | }
85 |
86 | #code-js {
87 | left: -284px;
88 | }
89 |
90 | #code-sql {
91 | left: 326px;
92 | }
93 |
94 | #code .comment {
95 | top: 1180px;
96 | left: unset;
97 | right: 176px;
98 | }
99 | }
100 |
101 | @media screen and (max-width: 767px) {
102 | #diagram1 {
103 | gap: 114px;
104 | }
105 |
106 | #mercury-diagram {
107 | left: -953px;
108 | }
109 |
110 | #code-rust {
111 | top: 88px;
112 | left: 135px;
113 | }
114 |
115 | #code-sql {
116 | left: 102px;
117 | bottom: 56px;
118 | }
119 |
120 | #sensor {
121 | left: 75px;
122 | top: 970px;
123 | }
124 |
125 | #code .comment {
126 | top: 1454px;
127 | }
128 |
129 | #mission-report {
130 | left: 0;
131 | margin: 1572px 0 320px;
132 | }
133 |
134 | #mission-report pre {
135 | user-select: none;
136 | pointer-events: none;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/components/code.tsx:
--------------------------------------------------------------------------------
1 | import { Printout } from "./printout";
2 | import { Sensor } from "./sensor";
3 | import "./code.css";
4 |
5 | export function Code() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
19 | {altMissionReport}
20 |
21 |
22 |
23 | );
24 | }
25 |
26 | const altMissionReport = `\
27 | # MISSION REPORT 75X9389 1/17
28 |
29 | SUBJECT: ANOMALOUS ENERGY SIGNATURES FROM KERBEROS 5
30 | EARTH DATE: NOV 20, 2057
31 | MISSION DIRECTOR: E. KERNING
32 |
33 | ---
34 |
35 | ## OVERVIEW
36 |
37 | During the exploration of Kerberos 5, our instruments detected unexplained energy bursts
38 | emanating from the object's polar region. These signatures do not match any known natural
39 | phenomenon and warrant further investigation.
40 |
41 | ### OBSERVATIONS
42 |
43 | Surface Composition Analysis
44 |
45 | ┌────────────────────┬──────────────┬────────────────────────────────────────────┐
46 | │ Element/Compound │ Conc. (ppm) │ Notes │
47 | ├────────────────────┼──────────────┼────────────────────────────────────────────┤
48 | │ Methane (CH₄) │ 1450 │ Higher than anticipated │
49 | │ Ethane (C₂H₆) │ 720 │ Consistent with previous observations │
50 | │ Complex Organics │ 350 │ Potential biological activity │
51 | │ Water Ice (H₂O) │ 2100 │ Abundant, suggesting subsurface ocean │
52 | │ Tholins │ 1100 │ Common in Kerberos 5's atmosphere │
53 | └────────────────────┴──────────────┴────────────────────────────────────────────┘
54 |
55 | Energy Burst Analysis
56 |
57 | a) Frequency: Energy bursts occur at irregular intervals, averaging one event every 37.6
58 | Earth hours.
59 | b) Duration: Each burst lasts between 3.2 to 8.7 seconds.
60 | c) Energy Output: Estimated at 10^15 joules per event, far exceeding any known geologic
61 | process for an object of this size.
62 | d) Spectral Analysis: Shows an unusual combination of gamma rays, radio waves, and
63 | neutrino emissions.
64 |
65 | ### CHARACTERISTICS
66 |
67 | a) Directionality: Energy appears to be focused, rather than omnidirectional.
68 | b) Pattern: While irregular, there are hints of an underlying pattern in the timing of
69 | the bursts.
70 | c) Origin: Triangulation places the source approximately 5.3 km beneath the surface of
71 | the polar ice cap.
72 |
73 | ### HYPOTHESES
74 |
75 | a) Natural Phenomenon: Possibly a new form of cryovolcanic activity involving exotic ice
76 | compositions.
77 | b) Artificial Source: The regularity and energy output could suggest an artificial origin,
78 | potentially from an unknown civilization.
79 | c) Quantum Fluctuations: Interaction between normal matter and theorized exotic matter in
80 | Kerberos 5's core.
81 | d) Instrumental Error: While unlikely, we cannot completely rule out malfunction in our
82 | detection equipment.
83 |
84 | ### IMPLICATIONS
85 |
86 | If confirmed, these energy signatures could represent:
87 | a) A new class of astrophysical phenomena.
88 | b) Evidence of advanced technology or non-terrestrial intelligence.
89 | c) A novel energy source with potential technological applications.
90 |
91 | ### RECOMMENDATIONS
92 |
93 | a) Deploy specialized probes to the polar region for close-range observations.
94 | b) Establish a long-term monitoring station on Kerberos 5.
95 | c) Assemble a multidisciplinary team to analyze the data and develop further hypotheses.
96 | d) Prepare a follow-up mission with equipment designed specifically to study these energy
97 | signatures.
98 |
99 | ### SECURITY CONSIDERATIONS
100 |
101 | Given the potential implications of this discovery, it is
102 | recommended that this information be classified at the highest level until further analysis
103 | can be conducted.
104 | `;
105 |
106 | const extras = {
107 | download: `\
108 | Suspended ───► Queued ───► Connecting ────► Transferring ───► Transferred
109 | │ ▲ │ │ │
110 | │ │ │ │ │ (complete)
111 | └─────────────┼────────────┼────────────────┼────┐ ▼
112 | │ │ │ │ Acknowledged
113 | │ ▼ ▼ │
114 | │ Transient Error ─────► Error │
115 | │ │ │ │ (cancel)
116 | │ └───────┬────────┴────┤
117 | │ │ │
118 | │ (resume) │ │
119 | └────────────────────┘ └──► Cancelled \
120 | `,
121 | flow: `\
122 | ┌───────────────────────────────┐
123 | │random │
124 | └┬─────────────┬─────────────┬─┬┘
125 | ┌▽───────────┐┌▽────────────┐│┌▽─────────────┐
126 | │distribution││seed_sequence│││nonsecure_base│
127 | └┬───────────┘└┬───┬───────┬┘│└┬┬────────────┘
128 | │ │ ┌│───────│─│─│┘
129 | │ ┌───────────┘ ││ │ │┌┘
130 | │ │┌─────────────▽▽┐┌─────▽─▽▽┐
131 | │ ││salted_seed_seq││pool_urbg│
132 | │ │└┬──────────────┘└┬────────┘
133 | │┌▽─▽────────────────▽┐
134 | ││seed_material │
135 | │└┬───────────────────┘
136 | ┌▽─▽────┐
137 | │strings│
138 | └───────┘ \
139 | `,
140 | airline: `\
141 | │ Flight │ Airline │ Destination │ Departure Time │ Gate │ Status │
142 | ├─────────┼──────────────────┼──────────────────────┼─────────────────┼───────┼────────────┤
143 | │ DL123 │ Delta Air Lines │ LAX (Los Angeles) │ 08:00 │ 22 │ On Time │
144 | │ BA456 │ British Airways │ JFK (New York) │ 10:30 │ 5 │ Boarding │
145 | │ LH789 │ Lufthansa │ JFK (New York) │ 13:45 │ Z23 │ Delayed │
146 | │ AF321 │ Air France │ SFO (San Francisco) │ 09:15 │ 12 │ On Time │
147 | │ UA567 │ United Airlines │ LGA (New York) │ 11:20 │ C8 │ Departed │
148 | │ QF678 │ Qantas │ LHR (London) │ 20:00 │ 17 │ On Time │
149 | │ EK432 │ Emirates │ JFK (New York) │ 14:50 │ A7 │ Boarding │
150 | │ SQ890 │ Singapore Ai... │ LAX (Los Angeles) │ 19:30 │ B12 │ On Time │
151 | │ AA234 │ American Air... │ ORD (Chicago) │ 07:45 │ 15 │ Departed │
152 | │ CX879 │ Cathay Pacific │ LAX (Los Angeles) │ 22:10 │ 9 │ On Time │
153 | `,
154 | };
155 | void extras;
156 |
--------------------------------------------------------------------------------
/src/components/countdown.css:
--------------------------------------------------------------------------------
1 | #countdown {
2 | position: relative;
3 | height: 896px;
4 | background-image: url("/assets/pad.jpg");
5 | background-size: cover;
6 | background-position: center;
7 | background-size: calc(100% + var(--scroll) * 3);
8 |
9 | display: grid;
10 | place-items: center;
11 | }
12 |
13 | #count {
14 | font-size: 440px;
15 | color: var(--flux);
16 | z-index: 1;
17 | }
18 |
19 | #countdown > canvas {
20 | position: absolute;
21 | inset: 0;
22 | width: 100%;
23 | height: 100%;
24 | opacity: min(90%, calc((var(--scroll) - 12%) * 11));
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/countdown.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect } from "solid-js";
2 | import GlslCanvas from "glslCanvas";
3 | import { Timer } from "../lib/utils";
4 | import "./countdown.css";
5 |
6 | export function Countdown() {
7 | let containerRef: HTMLDivElement;
8 | let countRef: HTMLSpanElement;
9 | let canvasRef: HTMLCanvasElement;
10 | const timer = new Timer(9);
11 | const [remaining] = timer.signal;
12 |
13 | createEffect(() => {
14 | window.addEventListener(
15 | "scroll",
16 | (_e) => {
17 | document.documentElement.style.setProperty(
18 | "--scroll",
19 | `${(window.scrollY / (document.body.offsetHeight - window.innerHeight)) * 100}%`,
20 | );
21 | },
22 | false,
23 | );
24 |
25 | const observer = new IntersectionObserver(
26 | ([entry]) => {
27 | if (entry.isIntersecting) {
28 | timer.start();
29 | }
30 | },
31 | { threshold: 0.4 },
32 | );
33 | observer.observe(countRef);
34 | });
35 |
36 | createEffect(() => {
37 | canvasRef.width = window.innerWidth;
38 | canvasRef.height = containerRef.clientHeight;
39 |
40 | const sandbox = new GlslCanvas(canvasRef);
41 | sandbox.load(`\
42 | // Author @patriciogv - 2015
43 | // http://patriciogonzalezvivo.com
44 |
45 | // Modified @rektdeckard - 2024
46 | // https://tobiasfried.com
47 |
48 | #ifdef GL_ES
49 | precision mediump float;
50 | #endif
51 |
52 | uniform vec2 u_resolution;
53 | uniform vec2 u_mouse;
54 | uniform float u_time;
55 |
56 | float random (vec2 st) {
57 | return fract(u_time * 5.0 + sin(dot(st.xy,
58 | vec2(12.9898,78.233)))*
59 | 43758.5453123);
60 | }
61 |
62 | // Value noise by Inigo Quilez - iq/2013
63 | // https://www.shadertoy.com/view/lsf3WH
64 | float noise(vec2 st) {
65 | vec2 i = floor(st);
66 | vec2 f = fract(st);
67 | vec2 u = f*f*(3.0-2.0*f);
68 | return mix(mix(random(i + vec2(0.0,0.0)),
69 | random(i + vec2(1.0,0.0)), u.x),
70 | mix(random(i + vec2(0.0,1.0)),
71 | random(i + vec2(1.0,1.0)), u.x), u.y);
72 | }
73 |
74 | mat2 rotate2d(float angle){
75 | return mat2(cos(angle),-sin(angle),
76 | sin(angle),cos(angle));
77 | }
78 |
79 | float lines(in vec2 pos, float b){
80 | float scale = u_mouse.x/u_mouse.y;
81 | pos *= scale;
82 | return smoothstep(0.0,
83 | .5+b*.5,
84 | abs((sin(pos.x*3.1415)+b*2.0))*.5);
85 | }
86 |
87 | void main() {
88 | vec2 st = gl_FragCoord.xy/u_resolution.xy;
89 |
90 | float rnd = random(st);
91 | st.y *= u_resolution.y/u_resolution.x;
92 |
93 | vec2 pos = st.yx*vec2(10.,3.);
94 | float pattern = pos.x;
95 |
96 | // Add noise
97 | pos = rotate2d(noise(pos)) * pos;
98 |
99 | // Draw lines
100 | pattern = lines(pos,0.5);
101 |
102 | gl_FragColor = vec4(vec3(rnd),1.0) + vec4(vec3(pattern),0.1) / 4.0;
103 | }
104 | `);
105 | });
106 |
107 | function reset() {
108 | timer.reset();
109 | timer.start();
110 | }
111 |
112 | return (
113 |
114 |
115 |
116 | {remaining()}
117 |
118 |
119 |
120 | );
121 | }
122 |
--------------------------------------------------------------------------------
/src/components/deparkanoid.css:
--------------------------------------------------------------------------------
1 | #deparkanoid {
2 | position: relative;
3 | margin: 380px 0 100px;
4 | }
5 |
6 | #deparkanoid .comment {
7 | position: absolute;
8 | top: -146px;
9 | left: 154px;
10 | width: 302px;
11 | color: var(--mud);
12 | }
13 |
14 | @media screen and (max-width: 1115px) {
15 | #deparkanoid {
16 | margin: 286px 0 100px;
17 | }
18 |
19 | #deparkanoid .comment {
20 | left: 93px;
21 | }
22 | }
23 |
24 | @media screen and (max-width: 767px) {
25 | #deparkanoid {
26 | display: none;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/editor.css:
--------------------------------------------------------------------------------
1 | .editor {
2 | position: relative;
3 | width: 100%;
4 | max-width: 824px;
5 | }
6 |
7 | .cm-editor .cm-content {
8 | font-family: "Departure Mono";
9 | }
10 |
--------------------------------------------------------------------------------
/src/components/editor.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect } from "solid-js";
2 | import { EditorView } from "codemirror";
3 | import {
4 | autocompletion,
5 | completionKeymap,
6 | closeBrackets,
7 | closeBracketsKeymap,
8 | } from "@codemirror/autocomplete";
9 | import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
10 | import { bracketMatching, indentOnInput } from "@codemirror/language";
11 | import { EditorState, Compartment, Extension } from "@codemirror/state";
12 | import {
13 | drawSelection,
14 | dropCursor,
15 | highlightSpecialChars,
16 | keymap,
17 | } from "@codemirror/view";
18 |
19 | import { Language, Snippets, theme, syntax } from "./editordata";
20 | import { Select } from "./select";
21 | import "./editor.css";
22 |
23 | const DEFAULT_EXTENSIONS: Extension = [
24 | history(),
25 | highlightSpecialChars(),
26 | drawSelection(),
27 | dropCursor(),
28 | EditorState.allowMultipleSelections.of(true),
29 | EditorView.lineWrapping,
30 | indentOnInput(),
31 | bracketMatching(),
32 | closeBrackets(),
33 | autocompletion(),
34 | keymap.of([
35 | ...closeBracketsKeymap,
36 | ...defaultKeymap,
37 | ...historyKeymap,
38 | ...completionKeymap,
39 | ]),
40 | theme,
41 | syntax,
42 | ];
43 |
44 | export type EditorProps = {
45 | initialValue?: string;
46 | initialLanguage?: Language;
47 | };
48 |
49 | export function Editor(props: EditorProps) {
50 | let ref: HTMLDivElement;
51 | let view: EditorView;
52 | const initialLang = props.initialLanguage ?? Language.RUST;
53 | const initialDoc = props.initialValue ?? Snippets[initialLang].doc;
54 |
55 | const langs = new Compartment(),
56 | tabSize = new Compartment();
57 | const state = EditorState.create({
58 | doc: initialDoc,
59 | extensions: [
60 | DEFAULT_EXTENSIONS,
61 | langs.of(Snippets[initialLang].syntax),
62 | tabSize.of(EditorState.tabSize.of(4)),
63 | ],
64 | });
65 |
66 | createEffect(() => {
67 | view = new EditorView({
68 | state,
69 | parent: ref,
70 | });
71 | });
72 |
73 | function selectLanguage(lang: Language) {
74 | view.setState(
75 | EditorState.create({
76 | doc: Snippets[lang].doc,
77 | extensions: [
78 | DEFAULT_EXTENSIONS,
79 | tabSize.of(EditorState.tabSize.of(4)),
80 | Snippets[lang].syntax,
81 | ],
82 | }),
83 | );
84 | }
85 |
86 | return (
87 |
88 |
({
91 | value,
92 | label: value,
93 | }))}
94 | value={[initialLang]}
95 | onValueChange={(v) => selectLanguage(v.items[0].value)}
96 | />
97 |
98 |
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/src/components/editordata.ts:
--------------------------------------------------------------------------------
1 | import { Extension } from "@codemirror/state";
2 | import { EditorView } from "@codemirror/view";
3 | import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
4 | import { tags as t } from "@lezer/highlight";
5 | import { go } from "@codemirror/lang-go";
6 | // import { html } from "@codemirror/lang-html";
7 | import { javascript } from "@codemirror/lang-javascript";
8 | import { markdown } from "@codemirror/lang-markdown";
9 | // import { python } from "@codemirror/lang-python";
10 | import { rust } from "@codemirror/lang-rust";
11 | import { sql } from "@codemirror/lang-sql";
12 |
13 | export enum Language {
14 | GO = "go",
15 | JAVASCRIPT = "javascript",
16 | MARKDOWN = "markdown",
17 | RUST = "rust",
18 | SQL = "sql",
19 | TYPESCRIPT = "typescript",
20 | }
21 |
22 | export const theme = EditorView.theme({
23 | "&": {
24 | color: "var(--cement)",
25 | backgroundColor: "var(--carbon)",
26 | },
27 | ".cm-content": { caretColor: "var(--cement)" },
28 | ".cm-cursor, .cm-dropCursor": { borderLeftColor: "var(--cement)" },
29 | "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
30 | {
31 | backgroundColor: "var(--ash)",
32 | },
33 | });
34 |
35 | export const syntax = syntaxHighlighting(
36 | HighlightStyle.define([
37 | { tag: t.keyword, color: "var(--pumpkin)" },
38 | {
39 | tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
40 | color: "var(--cement)",
41 | },
42 | {
43 | tag: [t.function(t.variableName), t.labelName],
44 | color: "var(--enamel)",
45 | },
46 | // {
47 | // tag: [t.color, t.constant(t.name), t.standard(t.name)],
48 | // color: "var(--flux)",
49 | // },
50 | // {
51 | // tag: [t.definition(t.name), t.separator],
52 | // color: "var(--clay)",
53 | // },
54 | {
55 | tag: [
56 | t.typeName,
57 | t.className,
58 | t.number,
59 | t.changed,
60 | t.annotation,
61 | t.modifier,
62 | t.self,
63 | t.namespace,
64 | ],
65 | color: "var(--amber)",
66 | },
67 | // {
68 | // tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)],
69 | // color: "var(--ash)",
70 | // },
71 | {
72 | tag: [t.meta, t.comment],
73 | color: "var(--clay)",
74 | },
75 | {
76 | tag: t.strong,
77 | fontWeight: "bold",
78 | },
79 | {
80 | tag: t.emphasis,
81 | fontStyle: "italic",
82 | },
83 | {
84 | tag: t.strikethrough,
85 | textDecoration: "line-through",
86 | },
87 | {
88 | tag: t.link,
89 | color: "var(--flux)",
90 | textDecoration: "underline",
91 | },
92 | {
93 | tag: t.heading,
94 | fontWeight: "bold",
95 | color: "var(--amber)",
96 | },
97 | {
98 | tag: [t.atom, t.bool, t.special(t.variableName)],
99 | color: "var(--enamel)",
100 | },
101 | {
102 | tag: [t.processingInstruction, t.string, t.inserted],
103 | color: "var(--flux)",
104 | },
105 | {
106 | tag: t.invalid,
107 | color: "var(--foam)",
108 | },
109 | ]),
110 | );
111 |
112 | export const goSnippet = `\
113 | package comlink
114 |
115 | import (
116 | "errors"
117 | "sync"
118 | "time"
119 | )
120 |
121 | const MAX_PROCESS_TIME = 90 * time.Microsecond
122 |
123 | type MessageBus[D any] struct {
124 | mu sync.Mutex
125 | updata []D
126 | downdata []D
127 | }
128 |
129 | func (bus *MessageBus[D]) Drain(sink func(D) error) error {
130 | timeout := time.After(MAX_PROCESS_TIME)
131 | result := make(chan error, len(bus.downdata))
132 | done := make(chan error)
133 |
134 | bus.mu.Lock()
135 | defer bus.mu.Unlock()
136 |
137 | for i, msg := range bus.downdata {
138 | go func(i int, msg D) {
139 | err := sink(msg)
140 | bus.downdata = append(bus.downdata[:i], bus.downdata[i+1:]...)
141 | result <- err
142 | }(i, msg)
143 | }
144 | go func() {
145 | for range len(bus.downdata) {
146 | if err := <-result; err != nil {
147 | done <- err
148 | }
149 | }
150 | done <- nil
151 | }()
152 |
153 | select {
154 | case <-timeout:
155 | return errors.New("timed out draining downdata bus")
156 | case err := <-done:
157 | return err
158 | }
159 | }`;
160 |
161 | export const jsSnippet = `\
162 | function shortestPath(source, target) {
163 | if (!source || !target) return [];
164 | if (source === target) return [source];
165 |
166 | const queue = [source];
167 | const visited = { [source]: true };
168 | const predecessor = {};
169 | let tail = 0;
170 |
171 | while (tail < queue.length) {
172 | // Pop vertex off queue
173 | let last = queue[tail++];
174 | let neighbors = nodeMap[last];
175 |
176 | if (neighbors) {
177 | for (let neighbor of neighbors) {
178 | if (visited[neighbor]) continue;
179 |
180 | visited[neighbor] = true;
181 | if (neighbor === target) {
182 | // Check if path is complete. If so, backtrack!
183 | const path = [neighbor];
184 | while (last !== source) {
185 | path.push(last);
186 | last = predecessor[last];
187 | }
188 | path.push(last);
189 | path.reverse();
190 | return path;
191 | }
192 | predecessor[neighbor] = last;
193 | queue.push(neighbor);
194 | }
195 | }
196 | }
197 | };`;
198 |
199 | export const mdSnippet = `\
200 | # MISSION REPORT
201 |
202 | ## SUBJECT: ASTRONAUT'S DISCOVERY ON SATURN'S MOON TITAN
203 |
204 | DATE: JULY 20, 2031
205 | MISSION NAME: TITAN EXPLORER I
206 | LEAD ASTRONAUT: COMMANDER JANE DOE
207 |
208 | ---
209 |
210 | ## EXECUTIVE SUMMARY
211 |
212 | On July 20, 2031, during the Titan Explorer I mission, Commander Jane Doe made an unprecedented and shocking discovery on Saturn's moon Titan. This report documents the significant findings and initial analyses of the geological features near the landing site. The data collected provides groundbreaking insights into the potential for extraterrestrial life and the geological history of Titan.
213 |
214 | ---
215 |
216 | ## DISCOVERY OVERVIEW
217 |
218 | Commander Jane Doe reported the presence of unusual geological formations and organic compounds near the designated landing site. The initial analysis suggests these formations may have biological origins, indicating the possible existence of primitive life forms on Titan.
219 |
220 | ---
221 |
222 | ## GEOLOGICAL FEATURES AND MEASUREMENTS
223 |
224 | ### LANDING SITE COORDINATES
225 | Latitude: 22.3°N
226 | Longitude: 135.5°W
227 |
228 | ### TABLE 1: SURFACE COMPOSITION ANALYSIS
229 | ┌────────────────────┬─────────────────────┬────────────────────────────────────────────┐
230 | │ Element/Compound │ Concentration (ppm) │ Notes │
231 | ├────────────────────┼─────────────────────┼────────────────────────────────────────────┤
232 | │ Methane (CH₄) │ 1450 │ Higher than anticipated │
233 | │ Ethane (C₂H₆) │ 720 │ Consistent with previous observations │
234 | │ Complex Organics │ 350 │ Indicates potential biological activity │
235 | │ Water Ice (H₂O) │ 2100 │ Abundant, suggesting subsurface ocean │
236 | │ Tholins │ 1100 │ Common in Titan's atmosphere │
237 | └────────────────────┴─────────────────────┴────────────────────────────────────────────┘
238 |
239 | ### TABLE 2: GEOLOGICAL FEATURE DIMENSIONS
240 | ┌─────────────────────┬──────────┬───────────┬──────────────────────────────────────────┐
241 | │ Feature │ Dia. (m) │ Depth (m) │ Description │
242 | ├─────────────────────┼──────────┼───────────┼──────────────────────────────────────────┤
243 | │ Methane Lake │ 300 │ 50 │ Smooth, reflective surface │
244 | │ Organic Ridge │ 150 │ 20 │ Composed of unknown organic material │
245 | │ Ice Volcano │ 200 │ 75 │ Erupting with a mix of water and ammonia │
246 | │ Cryovolcanic Plain │ 500 │ 10 │ Flat area with cryovolcanic deposits │
247 | └─────────────────────┴──────────┴───────────┴──────────────────────────────────────────┘
248 |
249 | ---
250 |
251 | ## DETAILED OBSERVATIONS
252 |
253 | ### METHANE LAKE
254 | The methane lake observed near the landing site exhibits a highly reflective surface, indicating a smooth, liquid state. The presence of such a large, stable liquid methane body is critical for understanding Titan's hydrological cycle and potential for life.
255 |
256 | ### ORGANIC RIDGE
257 | The organic ridge, spanning 150 meters in diameter, is composed of complex organic materials not previously observed on Titan. Preliminary spectroscopic analysis suggests the presence of amino acids and other precursors to life.
258 |
259 | ### ICE VOLCANO
260 | The ice volcano, approximately 200 meters in diameter and 75 meters deep, is actively erupting with a mixture of water and ammonia. This finding supports the hypothesis of a subsurface ocean and ongoing cryovolcanic activity.
261 |
262 | ### CRYOVOLCANIC PLAIN
263 | The cryovolcanic plain is characterized by an expansive, flat area covered with cryovolcanic deposits. This feature provides evidence of recent geological activity and potential subsurface habitability.
264 |
265 | ---
266 |
267 | ## CONCLUSIONS AND RECOMMENDATIONS
268 |
269 | The discovery of complex organics and geological activity on Titan suggests the potential for primitive life forms and a dynamic, evolving environment. It is recommended that future missions focus on in-depth analysis of the organic ridge and methane lake to explore the possibility of extant life. Additionally, a subsurface probe should be deployed to investigate the composition and habitability of Titan's subsurface ocean.
270 |
271 | ---
272 |
273 | ## ATTACHMENTS
274 |
275 | - Spectroscopic Analysis Data
276 | - High-Resolution Images of Geological Features
277 | - Detailed Logs from Commander Jane Doe
278 |
279 | ---
280 |
281 | **SIGNED:**
282 |
283 | Commander Jane Doe
284 | Lead Astronaut, Titan Explorer I
285 | NESA
286 |
287 | ---
288 |
289 | **END OF REPORT**`;
290 |
291 | export const rsSnippet = `\
292 | use crate::biometrics::Monitor;
293 | use crate::telemetry::Telemetry;
294 |
295 | #[derive(Default)]
296 | pub struct Consumables {
297 | pub v_ox: f32,
298 | pub v_aq: f32,
299 | pub delta_ox: f32,
300 | pub delta_aq: f32,
301 | pub suit_pressure: usize,
302 | }
303 |
304 | pub struct LifeSupport<'t> {
305 | pub bios: &'t Monitor,
306 | pub telemetry: &'t Telemetry,
307 | consumables: Consumables,
308 | }
309 |
310 | impl <'t> LifeSupport<'t> {
311 | pub fn new_with_intruments(bios: &'t Monitor, telemetry: &'t Telemetry) -> Self {
312 | LifeSupport {
313 | bios,
314 | telemetry,
315 | consumables: Default::default()
316 | }
317 | }
318 |
319 | pub fn eva_remaining(&self) -> std::time::Duration {
320 | const UPSILON: u64 = 41;
321 | let mut t = std::time::Duration::from_secs(UPSILON);
322 | t *= (self.consumables.v_ox - self.consumables.delta_ox.powi(4)) as u32;
323 | t * self.consumables.v_aq.powf(self.consumables.delta_aq).floor() as u32
324 | }
325 | }`;
326 |
327 | export const Snippets: Record = {
328 | [Language.GO]: {
329 | syntax: go(),
330 | doc: goSnippet,
331 | },
332 | [Language.JAVASCRIPT]: {
333 | syntax: javascript(),
334 | doc: jsSnippet,
335 | },
336 | [Language.MARKDOWN]: {
337 | syntax: markdown(),
338 | doc: mdSnippet,
339 | },
340 | [Language.RUST]: {
341 | syntax: rust(),
342 | doc: rsSnippet,
343 | },
344 | [Language.SQL]: {
345 | syntax: sql(),
346 | doc: `\
347 | -- Summarize largest and most prominent space missions from Bloc Gamma
348 | WITH gamma_bloc_missions AS (
349 | SELECT
350 | m.id,
351 | m.name AS mission_name,
352 | m.launch_date,
353 | s.name AS spacecraft_name,
354 | COUNT(ma.astronaut_id) AS astronaut_count
355 | FROM
356 | missions m
357 | JOIN
358 | spacecraft s ON m.spacecraft_id = s.id
359 | JOIN
360 | mission_astronauts ma ON m.id = ma.mission_id
361 | WHERE
362 | m.launch_date BETWEEN '2029-01-01' AND '2032-12-31' -- all dates UTC
363 | GROUP BY
364 | m.id, m.name, m.launch_date, s.name
365 | ORDER BY
366 | astronaut_count DESC,
367 | m.launch_date DESC
368 | LIMIT 5
369 | )
370 | SELECT
371 | mission_name,
372 | launch_date,
373 | spacecraft_name,
374 | astronaut_count
375 | FROM
376 | gamma_bloc_missions;
377 | `,
378 | },
379 | [Language.TYPESCRIPT]: {
380 | syntax: javascript({ typescript: true }),
381 | doc: `\
382 | const GRAVITATIONAL_CONSTANT: number = 9.81; // m/s^2, Earth's gravitational constant
383 | const EARTH_RADIUS: number = 6371; // km, average radius of the Earth
384 |
385 | function dynamicPressure(speed: number, altitude: number): number {
386 | return 0.5 * (speed ** 2);
387 | }
388 |
389 | function reentryAngle(
390 | speed: number,
391 | azimuth: number,
392 | altitude: number,
393 | mass: number,
394 | ): number {
395 | const az = degToRad(azimuth);
396 | const dp = dynamicPressure(speed, altitude);
397 | const fc = (speed ** 2) / (EARTH_RADIUS + altitude);
398 | const fg = GRAVITATIONAL_CONSTANT * mass / Math.pow(EARTH_RADIUS + altitude, 2);
399 | const ft = fc - fg;
400 |
401 | const angle = Math.atan2(dp, ft);
402 | return radToDeg(angle);
403 | }
404 |
405 | const speed = 7800; // m/s, typical re-entry speed
406 | const azimuth = 45; // degrees
407 | const altitude = 120; // km
408 | const mass = 2000; // kg, mass of the spacecraft
409 |
410 | gimbalEngines(reentryAngle(speed, azimuth, altitude, mass));
411 | `,
412 | },
413 | };
414 |
415 | export const missionReport = `\
416 | # MISSION REPORT 2/2
417 |
418 | ## SUBJECT: FINDINGS FROM ORION NEBULA
419 |
420 | DATE: JULY 20, 2031
421 | MISSION NAME: TITAN EXPLORER I
422 | MISSION COMMANDER: E. KERNING
423 |
424 | ---
425 |
426 |
427 | ## GEOLOGICAL FEATURES AND MEASUREMENTS
428 |
429 | ### LANDING SITE COORDINATES
430 | Latitude: 22.3°N
431 | Longitude: 135.5°W
432 |
433 | ### TABLE 1: SURFACE COMPOSITION ANALYSIS
434 | ┌────────────────────┬─────────────────────┬───────────────────────────────┐
435 | │ Element/Compound │ Concentration (ppm) │ Notes │
436 | ├────────────────────┼─────────────────────┼───────────────────────────────┤
437 | │ Methane (CH₄) │ 1450 │ Higher than anticipated │
438 | │ Ethane (C₂H₆) │ 720 │ Consistent with previous │
439 | │ Complex Organics │ 350 │ Indicates potential biolog │
440 | │ Water Ice (H₂O) │ 2100 │ Abundant, suggesting... │
441 | │ Tholins │ 1100 │ Common in Titan's rocky... │
442 | └────────────────────┴─────────────────────┴───────────────────────────────┘
443 |
444 | ### TABLE 2: GEOLOGICAL FEATURE DIMENSIONS
445 | ┌─────────────────────┬──────────┬───────────┬─────────────────────────────┐
446 | │ Feature │ Dia. (m) │ Depth (m) │ Description │
447 | ├─────────────────────┼──────────┼───────────┼─────────────────────────────┤
448 | │ Methane Lake │ 300 │ 50 │ Smooth, reflective surface │
449 | │ Organic Ridge │ 150 │ 20 │ Composed of unknown orga... │
450 | │ Ice Volcano │ 200 │ 75 │ Erupting with a mix of w... │
451 | │ Cryovolcanic Plain │ 500 │ 10 │ Flat area with cryovolca... │
452 | └─────────────────────┴──────────┴───────────┴─────────────────────────────┘
453 |
454 | ---
455 |
456 | ## DETAILED OBSERVATIONS
457 |
458 | ### METHANE LAKE
459 | The methane lake observed near the landing site exhibits a highly reflective surface, indicating a smooth, liquid state. The presence of such a large, stable liquid methane body is critical for understanding Titan's hydrological cycle and potential for life.
460 |
461 | ### ORGANIC RIDGE
462 | `;
463 |
--------------------------------------------------------------------------------
/src/components/footer.tsx:
--------------------------------------------------------------------------------
1 | export function Footer() {
2 | return (
3 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/src/components/fuzz.css:
--------------------------------------------------------------------------------
1 | #fuzz {
2 | position: fixed;
3 | display: grid;
4 | place-items: center;
5 | inset: 0;
6 | opacity: var(--fuzz);
7 | pointer-events: none;
8 | z-index: 1;
9 | }
10 |
11 | #fuzz > canvas {
12 | position: fixed;
13 | top: 0;
14 | left: 0;
15 | right: 0;
16 | height: 100vh;
17 | }
18 |
19 | #count {
20 | font-size: 440px;
21 | color: var(--flux);
22 | z-index: 1;
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/fuzz.tsx:
--------------------------------------------------------------------------------
1 | import { createEffect } from "solid-js";
2 | import GlslCanvas from "glslCanvas";
3 | import { Timer } from "../lib/utils";
4 | import "./fuzz.css";
5 |
6 | export function Fuzz() {
7 | let containerRef: HTMLDivElement;
8 | let countRef: HTMLSpanElement;
9 | let canvasRef: HTMLCanvasElement;
10 | const timer = new Timer(9);
11 | const [remaining] = timer.signal;
12 |
13 | createEffect(() => {
14 | const observer = new IntersectionObserver(
15 | ([entry]) => {
16 | if (entry.isIntersecting) {
17 | timer.start();
18 | }
19 | },
20 | { threshold: 0.4 },
21 | );
22 | observer.observe(countRef);
23 | });
24 |
25 | createEffect(() => {
26 | canvasRef.width = window.innerWidth;
27 | canvasRef.height = containerRef.clientHeight;
28 |
29 | const sandbox = new GlslCanvas(canvasRef);
30 | sandbox.load(`\
31 | // Author @patriciogv - 2015
32 | // http://patriciogonzalezvivo.com
33 |
34 | // Modified @rektdeckard - 2024
35 | // https://tobiasfried.com
36 |
37 | #ifdef GL_ES
38 | precision mediump float;
39 | #endif
40 |
41 | uniform vec2 u_resolution;
42 | uniform vec2 u_mouse;
43 | uniform float u_time;
44 |
45 | float random (vec2 st) {
46 | return fract(u_time * 5.0 + sin(dot(st.xy,
47 | vec2(12.9898,78.233)))*
48 | 43758.5453123);
49 | }
50 |
51 | // Value noise by Inigo Quilez - iq/2013
52 | // https://www.shadertoy.com/view/lsf3WH
53 | float noise(vec2 st) {
54 | vec2 i = floor(st);
55 | vec2 f = fract(st);
56 | vec2 u = f*f*(3.0-2.0*f);
57 | return mix(mix(random(i + vec2(0.0,0.0)),
58 | random(i + vec2(1.0,0.0)), u.x),
59 | mix(random(i + vec2(0.0,1.0)),
60 | random(i + vec2(1.0,1.0)), u.x), u.y);
61 | }
62 |
63 | mat2 rotate2d(float angle){
64 | return mat2(cos(angle),-sin(angle),
65 | sin(angle),cos(angle));
66 | }
67 |
68 | float lines(in vec2 pos, float b){
69 | float scale = u_mouse.x/u_mouse.y;
70 | pos *= scale;
71 | return smoothstep(0.0,
72 | .5+b*.5,
73 | abs((sin(pos.x*3.1415)+b*2.0))*.5);
74 | }
75 |
76 | void main() {
77 | vec2 st = gl_FragCoord.xy/u_resolution.xy;
78 |
79 | float rnd = random(st);
80 | st.y *= u_resolution.y/u_resolution.x;
81 |
82 | vec2 pos = st.yx*vec2(10.,3.);
83 | float pattern = pos.x;
84 |
85 | // Add noise
86 | pos = rotate2d(noise(pos)) * pos;
87 |
88 | // Draw lines
89 | pattern = lines(pos,0.5);
90 |
91 | gl_FragColor = vec4(vec3(rnd),1.0) + vec4(vec3(pattern),0.1) / 4.0;
92 | }
93 | `);
94 | });
95 |
96 | function reset() {
97 | timer.reset();
98 | timer.start();
99 | }
100 |
101 | return (
102 |
103 |
104 | {remaining()}
105 |
106 |
107 |
108 | );
109 | }
110 |
--------------------------------------------------------------------------------
/src/components/header.css:
--------------------------------------------------------------------------------
1 | header {
2 | position: relative;
3 | padding: 137px 0 0;
4 | background-color: var(--bg);
5 |
6 | --time: 1000ms;
7 | --easing: cubic-bezier(1, 0.05, 0.48, 0.99);
8 | }
9 |
10 | #title {
11 | display: inline-flex;
12 | align-items: flex-start;
13 | gap: 11px;
14 | line-height: 1;
15 | }
16 |
17 | .headline {
18 | display: flex;
19 | align-items: flex-start;
20 | justify-content: space-between;
21 | flex-wrap: wrap;
22 | }
23 |
24 | #title > h1 {
25 | font-size: 88px;
26 | color: var(--soot);
27 | }
28 |
29 | #title > h1 > :not(#version) {
30 | background-color: var(--aluminum);
31 | display: inline-block;
32 | line-height: 1;
33 | white-space: pre;
34 | }
35 |
36 | #title > h1 > #title-space-dyn {
37 | display: inline-block;
38 | }
39 | #title > h1 > #title-space-sta {
40 | display: none;
41 | }
42 |
43 | a {
44 | color: currentColor;
45 | transition: color 150ms var(--flick);
46 | }
47 |
48 | a:hover {
49 | color: var(--accent);
50 | }
51 |
52 | menu {
53 | display: flex;
54 | flex-direction: column;
55 | align-items: flex-start;
56 | gap: 6px;
57 | margin: 8px 22px 0 0;
58 | color: var(--dark);
59 | }
60 |
61 | menu > a {
62 | text-decoration: none;
63 | font-size: 16.5px;
64 | line-height: 21px;
65 | transition: background-color 75ms var(--flick);
66 | }
67 |
68 | menu > a:hover {
69 | color: unset;
70 | background-color: var(--foam);
71 | }
72 |
73 | sup {
74 | font-size: 0.6em;
75 | vertical-align: top;
76 | margin-left: -0.6em;
77 | }
78 |
79 | #version {
80 | position: relative;
81 | top: 10px;
82 | left: 11px;
83 | font-size: 11px;
84 | vertical-align: top;
85 | }
86 |
87 | #departures {
88 | position: relative;
89 | left: -290px;
90 | font-size: 44px;
91 | line-height: 1.27;
92 | margin: 0 0 132px;
93 | }
94 |
95 | @keyframes blink {
96 | 0%,
97 | 100% {
98 | background-color: transparent;
99 | }
100 | 50% {
101 | background-color: var(--foam);
102 | }
103 | }
104 |
105 | #departures .blink {
106 | animation: blink 1s infinite steps(1);
107 | }
108 |
109 | #letter {
110 | position: relative;
111 | display: grid;
112 | grid-template-columns: 155px 1fr;
113 | gap: 187px;
114 | margin: 185px 0 492px 114px;
115 | }
116 |
117 | #letter .comment {
118 | width: 204px;
119 | }
120 |
121 | #brief {
122 | font-size: 16.5px;
123 | z-index: 1;
124 | }
125 |
126 | #brief pre {
127 | position: relative;
128 | top: -92px;
129 | padding: 88px 66px 0;
130 | line-height: 24.75px;
131 | }
132 |
133 | #newspaper-clipping {
134 | position: absolute;
135 | z-index: 1;
136 | left: 836px;
137 | }
138 |
139 | #paperclip {
140 | position: absolute;
141 | z-index: 1;
142 | top: -14px;
143 | left: 1045px;
144 | }
145 |
146 | #badge {
147 | position: absolute;
148 | top: 258px;
149 | left: -250px;
150 | transition: transform 600ms var(--easing);
151 | z-index: 1;
152 | }
153 |
154 | #badge:hover {
155 | transform: rotate(3deg) translate(100px, -12px);
156 | }
157 |
158 | #highlighter {
159 | position: absolute;
160 | top: 804px;
161 | left: 308px;
162 | }
163 |
164 | #planet {
165 | position: absolute;
166 | top: 152px;
167 | left: -768px;
168 | }
169 |
170 | #ephemera {
171 | position: relative;
172 | height: 956px;
173 | }
174 |
175 | #ephemera .comment {
176 | position: relative;
177 | left: 231px;
178 | width: 177px;
179 | }
180 |
181 | #boarding-pass {
182 | position: absolute;
183 | left: 342px;
184 | top: 133px;
185 | }
186 |
187 | #receipt {
188 | position: absolute;
189 | top: 217px;
190 | left: 84px;
191 | }
192 |
193 | #bag-tag {
194 | position: absolute;
195 | top: 396px;
196 | left: 904px;
197 | transform: rotate(270deg);
198 | }
199 |
200 | #ephemera-items {
201 | height: 736px;
202 | margin-top: 70px;
203 | }
204 |
205 | #ephemera-items > * {
206 | transition: transform var(--time) var(--easing);
207 | }
208 |
209 | #ephemera-items:hover {
210 | #boarding-pass {
211 | transform: translate(20px, -24px) rotate(2deg);
212 | }
213 |
214 | #receipt {
215 | transform: rotate(-3deg) translate(-96px, 12px);
216 | }
217 |
218 | #bag-tag {
219 | transform: translate(60px, 20px) rotate(275deg);
220 | }
221 | }
222 |
223 | #announce {
224 | position: relative;
225 | left: -170px;
226 | width: 1065px;
227 | font-size: 88px;
228 | line-height: 1;
229 | }
230 |
231 | .highlight {
232 | background-color: var(--foam);
233 | }
234 |
235 | @media screen and (max-width: 1115px) {
236 | header {
237 | padding: 132px 0 0;
238 | }
239 |
240 | #title > h1 > span {
241 | margin-bottom: 6px;
242 | }
243 |
244 | #title > h1 > #title-space-dyn {
245 | display: none;
246 | }
247 |
248 | #title > h1 > #title-space-sta {
249 | display: initial;
250 | }
251 |
252 | .headline {
253 | flex-direction: column;
254 | gap: 38px;
255 | line-height: 1.4;
256 | }
257 |
258 | menu {
259 | margin: 0;
260 | gap: 11px;
261 | }
262 |
263 | #letter {
264 | margin: 110px 0 335px 0;
265 | gap: 105px;
266 | }
267 |
268 | #newspaper-clipping {
269 | left: 612px;
270 | }
271 |
272 | #paperclip {
273 | left: 821px;
274 | }
275 |
276 | #badge {
277 | left: -194px;
278 | }
279 |
280 | #highlighter {
281 | top: 713px;
282 | left: 416px;
283 | }
284 |
285 | #departures {
286 | margin: 0 0 88px;
287 | }
288 |
289 | #ephemera {
290 | height: 883px;
291 | }
292 |
293 | #receipt {
294 | top: 204px;
295 | left: -159px;
296 | }
297 |
298 | #boarding-pass {
299 | top: 106px;
300 | left: 87px;
301 | }
302 |
303 | #bag-tag {
304 | top: 376px;
305 | left: 586px;
306 | }
307 | }
308 |
309 | @media screen and (max-width: 767px) {
310 | header {
311 | padding-top: 88px;
312 | }
313 |
314 | .headline {
315 | gap: 33px;
316 | }
317 |
318 | #title > h1 {
319 | font-size: 44px;
320 | width: 256px;
321 | }
322 |
323 | #title > h1 > span {
324 | margin-bottom: 2.5px;
325 | }
326 |
327 | #letter {
328 | display: block;
329 | margin: 88px 0 773px;
330 | }
331 |
332 | #brief {
333 | position: relative;
334 | left: -123px;
335 | }
336 |
337 | #badge {
338 | top: 374px;
339 | left: -76px;
340 | z-index: 1;
341 | pointer-events: none;
342 | }
343 |
344 | #newspaper-clipping {
345 | top: 551px;
346 | left: 156px;
347 | z-index: 2;
348 | }
349 |
350 | #paperclip {
351 | display: none;
352 | }
353 |
354 | #letter .comment {
355 | position: absolute;
356 | top: 1063px;
357 | left: 113px;
358 | }
359 |
360 | #highlighter {
361 | top: 1203px;
362 | left: 0px;
363 | }
364 |
365 | #departures {
366 | left: -100px;
367 | font-size: 22px;
368 | margin: 0 0 84px;
369 | }
370 |
371 | #ephemera {
372 | height: 894px;
373 | pointer-events: none;
374 | }
375 |
376 | #ephemera .comment {
377 | position: relative;
378 | left: 57px;
379 | }
380 |
381 | #boarding-pass {
382 | top: 117px;
383 | left: 57px;
384 | }
385 |
386 | #receipt {
387 | top: 215px;
388 | left: -286px;
389 | }
390 |
391 | #bag-tag {
392 | top: 421px;
393 | left: 292px;
394 | }
395 | }
396 |
--------------------------------------------------------------------------------
/src/components/header.tsx:
--------------------------------------------------------------------------------
1 | import { Random, Range } from "kdim";
2 | import { version } from "../../package.json";
3 | import { Printout } from "./printout";
4 | import { TypeTest } from "./typetest";
5 | import "./header.css";
6 | import { createEffect, createSignal } from "solid-js";
7 |
8 | const [MAJOR, MINOR] = version.split(".");
9 | const GITHUB_RELEASES_URL = "https://github.com/rektdeckard/departure-mono/releases/latest";
10 |
11 | export function Header() {
12 | return (
13 |
70 | );
71 | }
72 |
73 | const DEPARTURE = "DEPARTURE";
74 | const MONO = "MONO";
75 | const COGNATES: Record = {
76 | E: "3ΣΞ€Ǝ",
77 | A: "Λ",
78 | R: "2₹",
79 | T: "7",
80 | U: "Ʉ",
81 | " ": "_",
82 | O: "0",
83 | N: "Ɲ",
84 | } as const;
85 |
86 | function GlitchTitle() {
87 | const MIN_DELAY = 400;
88 | const MAX_DELAY = 2000;
89 | const GLITCH_CHANCE = 0.1;
90 | const GLITCH_DELAY = 30;
91 |
92 | const [departure, setDeparture] = createSignal(DEPARTURE);
93 | const [space, setSpace] = createSignal(" ");
94 | const [mono, setMono] = createSignal(MONO);
95 |
96 | function glitchWord(original: string, odds: number[]): string {
97 | const swaps = Random.sample(odds)!;
98 | if (swaps === 0) return original;
99 |
100 | const glitched = [...original];
101 | const opts =
102 | original.length === 1
103 | ? [0]
104 | : Random.permutation(Range.of(original.length));
105 | for (let i = 0; i < swaps; i++) {
106 | glitched[opts[i]] = Random.sample([
107 | ...(COGNATES[original[opts[i]]] ?? original[opts[i]]),
108 | ])!;
109 | }
110 |
111 | return glitched.join("");
112 | }
113 |
114 | createEffect(() => {
115 | (function d() {
116 | setDeparture(glitchWord(DEPARTURE, [0, 0, 0, 1, 1, 2, 2, 2, 3]));
117 | if (Math.random() < GLITCH_CHANCE) {
118 | const delay = Random.natural(MAX_DELAY - MIN_DELAY) + MIN_DELAY;
119 | const glitched = glitchWord(DEPARTURE, [2, 3, 4, 4, 5, 5, 5, 6, 6]);
120 | setTimeout(() => setDeparture(DEPARTURE), delay);
121 | setTimeout(() => setDeparture(glitched), delay + GLITCH_DELAY);
122 | setTimeout(() => setDeparture(DEPARTURE), delay + GLITCH_DELAY * 2);
123 | setTimeout(() => setDeparture(glitched), delay + GLITCH_DELAY * 3);
124 | setTimeout(d, delay + GLITCH_DELAY * 4);
125 | } else {
126 | setTimeout(d, Random.natural(MAX_DELAY - MIN_DELAY) + MIN_DELAY);
127 | }
128 | })();
129 | (function s() {
130 | setSpace(glitchWord(" ", [0, 1, 1]));
131 | setTimeout(s, Random.natural(MAX_DELAY - MIN_DELAY) + MIN_DELAY);
132 | })();
133 | (function m() {
134 | setMono(glitchWord(MONO, [0, 0, 0, 1, 1, 2, 2]));
135 | if (Math.random() < GLITCH_CHANCE) {
136 | const delay = Random.natural(MAX_DELAY - MIN_DELAY) + MIN_DELAY;
137 | const glitched = glitchWord(DEPARTURE, [1, 1, 2, 2, 3, 3]);
138 | setTimeout(() => setDeparture(DEPARTURE), delay);
139 | setTimeout(() => setDeparture(glitched), delay + GLITCH_DELAY);
140 | setTimeout(() => setDeparture(DEPARTURE), delay + GLITCH_DELAY * 2);
141 | setTimeout(() => setDeparture(glitched), delay + GLITCH_DELAY * 3);
142 | setTimeout(m, delay + GLITCH_DELAY * 4);
143 | } else {
144 | setTimeout(m, Random.natural(MAX_DELAY - MIN_DELAY) + MIN_DELAY);
145 | }
146 | })();
147 | });
148 |
149 | return (
150 |
151 | {departure()}
152 | {space()}
153 |
154 | {mono()}
155 |
156 | v{MAJOR}.{MINOR}
157 |
158 |
159 | );
160 | }
161 |
162 | const letter = (
163 |
164 |
165 | {`\
166 | To: From:
167 | DR. E. KERNING PROFESSOR H. J. FARNSWORTH
168 | MERCURY RESEARCH LABS FARNSWORTH INSTITUTE
169 | 3572 WILSHIRE BLVD #9732 88 ESSEX ST
170 | LOS ANGELES, CA 90010 NEW NEW YORK, NY 10002
171 |
172 |
173 |
174 | Dear Dr. Kerning,
175 |
176 | I trust this message finds you in good spirits. I am pleased to brief
177 | you on `}
178 | this critical scientific venture
179 | {` at the far reaches of our
180 | solar system. Your groundbreaking research and expertise in
181 | exoplanetary ecosystems uniquely qualifies you for this endeavor.
182 |
183 | `}
184 |
185 | Your primary objective will be to investigate an anomaly detected in
186 |
187 |
188 | the Kuiper belt.
189 | {` Initial readings suggest the presence of amino acids
190 | near newly discovered KBOs. Your team will deploy specialized
191 | equipment to collect data and analyze the phenomenon.
192 |
193 | Please ensure all preparations are completed according to the attached
194 | mission protocols. We would appreciate your prompt assessment of the
195 | included candidate profiles. Departure is set from Earth's orbit on
196 | the 15th of next month.
197 |
198 | I have full confidence in your ability to navigate the challenges of
199 | this mission with scientific rigor and ethical consideration. Your
200 | discoveries may unlock new chapters in our understanding of
201 | extraterrestrial life.
202 |
203 | Best regards,
204 | Professor Hubert J. FarnsworthFounder, Farnsworth Institute
205 | `}
206 |
207 |
208 | );
209 |
210 | void letter;
211 |
--------------------------------------------------------------------------------
/src/components/keeb.css:
--------------------------------------------------------------------------------
1 | #diagram1 {
2 | position: relative;
3 | display: flex;
4 | align-items: center;
5 | gap: 114px;
6 | padding-top: 198px;
7 | padding-bottom: 28px;
8 | color: var(--clay);
9 | }
10 |
11 | #diagram1 .comment {
12 | position: absolute;
13 | top: 132px;
14 | left: 228px;
15 | width: 212px;
16 | color: var(--mud);
17 | }
18 |
19 | #proto {
20 | margin-top: 60px;
21 | font-size: 11px;
22 | }
23 |
24 | #keeb {
25 | font-size: 16.5px;
26 | color: var(--clay);
27 | line-height: 1.2;
28 | }
29 |
30 | @media screen and (max-width: 1115px) {
31 | #diagram1 {
32 | gap: 73px;
33 | }
34 | }
35 |
36 | @media screen and (max-width: 767px) {
37 | #diagram1 {
38 | padding: 0;
39 | }
40 |
41 | #diagram1 .comment {
42 | top: -86px;
43 | left: 66px;
44 | }
45 |
46 | #proto {
47 | position: relative;
48 | font-size: 13px;
49 | left: -295px;
50 | }
51 |
52 | #keeb {
53 | position: relative;
54 | left: -345px;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/keeb.tsx:
--------------------------------------------------------------------------------
1 | import "./keeb.css";
2 |
3 | export function Keeb() {
4 | return (
5 |
6 |
13 |
14 | {`\
15 | 0 1 2 3
16 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
17 | ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
18 | │ Source Port │ Destination Port │
19 | ├───────────────────────────────┴───────────────────────────────┤
20 | │ Sequence Number │
21 | ├───────────────────────────────────────────────────────────────┤
22 | │ Acknowledgment Number │
23 | ├───────┬───────┬───────────────┬───────────────────────────────┤
24 | │ Offset│ Res. │ Flags │ Window │
25 | ├───────┴───────┴───────────────┼───────────────────────────────┤
26 | │ Checksum │ Urgent Pointer │
27 | ├───────────────────────────────┴───────────────┬───────────────┤
28 | │ Options │ Padding │
29 | └───────────────────────────────────────────────┴───────────────┘
30 | `}
31 |
32 |
33 | {`
34 | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬───────────┐
35 | │ esc │ f1 │ f2 │ f3 │ f4 │ f5 │ f6 │ f7 │ f8 │ f9 │ f10 │ f11 │ f12 │ power │
36 | ├─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼───────────┤
37 | │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │ │
38 | │ \` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ — │ = │ delete │
39 | ├─────┴─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┬─────┤
40 | │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │
41 | │ tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \\ │
42 | ├───────────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┼─────┴─────┤
43 | │ │ │ │ │ │ │ │ │ │ │ : │ “ │ │
44 | │ caps lock │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ enter │
45 | ├───────────┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴───────────┤
46 | │ │ │ │ │ │ │ │ │ < │ > │ ? │ │
47 | │ shift │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ shift │
48 | ├─────┬──────┬─┴───┬─┴───┬─┴─────┴─────┴─────┴─────┴─────┼─────┼─────┼─────┴┬──────┬──────┤
49 | │ │ │ │ │ │ │ ├──────┼──────┼──────┤
50 | │ fn │ ctrl │ opt │ cmd │ s p a c e │ cmd │ opt ├──────┼──────┼──────┤
51 | └─────┴──────┴─────┴─────┴───────────────────────────────┴─────┴─────┴──────┴──────┴──────┘
52 | `}
53 |
54 |
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/src/components/printout.css:
--------------------------------------------------------------------------------
1 | .printout {
2 | color: var(--carbon);
3 | }
4 |
5 | .printout > foreignObject > * {
6 | position: relative;
7 | padding: 88px 44px 0;
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/printout.tsx:
--------------------------------------------------------------------------------
1 | import { JSX, splitProps } from "solid-js";
2 | import "./printout.css";
3 |
4 | type PaperColor = "white" | "grey";
5 |
6 | const Colors: Record = {
7 | white: ["white", "var(--enamel)"],
8 | grey: ["var(--cement)", "var(--carbon)"],
9 | } as const;
10 |
11 | type PrintoutProps = JSX.SvgSVGAttributes & {
12 | color?: PaperColor;
13 | };
14 |
15 | export function Printout(props: PrintoutProps) {
16 | const [local, rest] = splitProps(props, ["class", "color", "children"]);
17 | return (
18 |
27 |
33 |
34 |
38 |
39 | {local.children}
40 |
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/select.css:
--------------------------------------------------------------------------------
1 | [data-scope="select"][data-part="root"] {
2 | }
3 |
4 | [data-scope="select"][data-part="root"][data-invalid] {
5 | }
6 |
7 | [data-scope="select"][data-part="root"][data-readonly] {
8 | }
9 |
10 | [data-scope="select"][data-part="trigger"] {
11 | display: flex;
12 | align-items: center;
13 | justify-content: space-between;
14 | gap: 1rem;
15 | min-width: 128px;
16 | height: 24px;
17 | background: var(--bg);
18 | border: 1px solid var(--fg);
19 | font-family: inherit;
20 | }
21 |
22 | [data-scope="select"][data-part="trigger"]:hover {
23 | color: var(--bg);
24 | background-color: var(--accent);
25 | }
26 |
27 | [data-scope="select"][data-part="content"] {
28 | background: var(--bg);
29 | border: 1px solid var(--fg);
30 | padding: 0.5rem;
31 | }
32 |
33 | [data-scope="select"][data-part="content"][data-state="open"] {
34 | }
35 |
36 | [data-scope="select"][data-part="content"][data-state="closed"] {
37 | }
38 |
39 | [data-scope="select"][data-part="item"] {
40 | display: grid;
41 | grid-template-columns: 0.7rem 1fr;
42 | gap: 0.5rem;
43 | padding: 0.2rem;
44 | }
45 |
46 | [data-scope="select"][data-part="item"]:hover {
47 | color: var(--bg);
48 | background-color: var(--accent);
49 | }
50 |
51 | [data-scope="select"][data-part="item"][data-state="checked"] {
52 | }
53 |
54 | [data-scope="select"][data-part="item-indicator"][data-state="unchecked"] {
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/select.tsx:
--------------------------------------------------------------------------------
1 | import { Index, Show, splitProps } from "solid-js";
2 | import { Portal } from "solid-js/web";
3 | import { Select as ArkSelect, type SelectRootProps } from "@ark-ui/solid";
4 | import "./select.css";
5 |
6 | export type SelectItem = { label: string; value: T };
7 |
8 | export type SelectProps = Omit>, "items"> & {
9 | label?: string;
10 | placeholder?: string;
11 | options: SelectItem[];
12 | };
13 |
14 | export function Select(props: SelectProps) {
15 | const [local, rest] = splitProps(props, ["label", "placeholder", "options"]);
16 |
17 | return (
18 |
19 |
20 | {local.label}
21 |
22 |
23 |
24 |
25 | ↕
26 |
27 |
28 |
29 |
30 |
31 |
32 | {(item) => (
33 |
34 |
35 | →
36 |
37 | {item().label}
38 |
39 | )}
40 |
41 |
42 |
43 |
44 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/src/components/sensor.css:
--------------------------------------------------------------------------------
1 | .sensor {
2 | color: var(--accent);
3 | font-size: 220px;
4 | }
5 |
--------------------------------------------------------------------------------
/src/components/sensor.tsx:
--------------------------------------------------------------------------------
1 | import { createSignal, createEffect } from "solid-js";
2 | import "./sensor.css";
3 |
4 | export function Sensor() {
5 | const [val, setVal] = createSignal(0);
6 | createEffect(() => {
7 | function scrollPercent() {
8 | const scroll = document.documentElement.scrollTop;
9 | const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
10 | setVal(100 - Math.round((scroll / height) * 100));
11 | }
12 |
13 | window.addEventListener("scroll", scrollPercent);
14 | return () => window.removeEventListener("scroll", scrollPercent);
15 | });
16 |
17 | return {val().toString().padStart(2, "0")} ;
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/tester.css:
--------------------------------------------------------------------------------
1 | #tester {
2 | position: relative;
3 | flex: 1;
4 | padding-top: 160px;
5 | cursor: cell;
6 | }
7 |
8 | #apollo1 {
9 | position: absolute;
10 | transform: rotate(90deg);
11 | top: 966px;
12 | left: -391px;
13 | user-select: none;
14 | }
15 |
16 | .split {
17 | display: grid;
18 | grid-template-columns: 600px 1fr;
19 | gap: 88px;
20 | position: relative;
21 | }
22 |
23 | #glyph-specimen-static {
24 | display: none;
25 | }
26 |
27 | #glyph-specimen {
28 | --notch: 48px;
29 | position: sticky;
30 | top: 44px;
31 | display: grid;
32 | grid-template-rows: 237px 80px 240px 80px 1fr;
33 |
34 | color: var(--carbon);
35 | font-size: 11px;
36 | width: 100%;
37 | min-width: 500px;
38 | height: 794px;
39 | z-index: 1;
40 | clip-path: polygon(
41 | 0 0,
42 | calc(100% - var(--notch)) 0,
43 | 100% var(--notch),
44 | 100% 100%,
45 | 0 100%,
46 | 0 0
47 | );
48 |
49 | /* --e: 1px; */
50 | /* --w: 48px; */
51 | /* background:
52 | linear-gradient(
53 | 0deg,
54 | transparent var(--e),
55 | var(--cement) var(--e),
56 | var(--cement) calc(var(--w) - var(--e) - 1px),
57 | transparent calc(var(--w) - var(--e) - 1px),
58 | transparent var(--w)
59 | ),
60 | linear-gradient(
61 | 90deg,
62 | transparent var(--e),
63 | var(--cement) var(--e),
64 | var(--cement) calc(var(--w) - var(--e) - 1px),
65 | transparent calc(var(--w) - var(--e) - 1px),
66 | transparent var(--w)
67 | ),
68 | linear-gradient(
69 | 0deg,
70 | transparent calc(var(--w) - 1px),
71 | currentColor calc(var(--w) - 1px),
72 | transparent var(--w)
73 | ),
74 | linear-gradient(
75 | 90deg,
76 | transparent calc(var(--w) - 1px),
77 | currentColor calc(var(--w) - 1px),
78 | transparent var(--w)
79 | ); */
80 | /* background-size: var(--w) var(--w); */
81 | /* background-position: 22px 39.5px; */
82 | background-color: var(--cement);
83 | }
84 |
85 | #glyph-specimen::before {
86 | position: absolute;
87 | content: "";
88 | width: 6px;
89 | height: 22px;
90 | border-radius: 2px;
91 | top: 176px;
92 | left: 22px;
93 | background-color: currentColor;
94 | }
95 |
96 | #glyph-specimen::after {
97 | position: absolute;
98 | content: "";
99 | width: 6px;
100 | height: 22px;
101 | border-radius: 2px;
102 | bottom: 176px;
103 | left: 22px;
104 | background-color: currentColor;
105 | }
106 |
107 | #big {
108 | position: absolute;
109 | top: 117.5px;
110 | left: calc(50% - 140px);
111 | font-size: 440px;
112 | }
113 |
114 | .anatomy {
115 | border-top: 1px solid;
116 | display: flex;
117 | align-items: flex-start;
118 | justify-content: space-between;
119 | padding: 6px 22px;
120 | }
121 |
122 | .specimen-details {
123 | display: flex;
124 | align-items: flex-start;
125 | justify-content: space-between;
126 | padding: 94px 22px 0;
127 | }
128 |
129 | .specimen-name {
130 | width: 180px;
131 | text-wrap: balance;
132 | }
133 |
134 | #glyph-list {
135 | display: flex;
136 | flex-wrap: wrap;
137 | }
138 |
139 | .segment-header {
140 | text-transform: uppercase;
141 | color: var(--clay);
142 | font-size: 11px;
143 | margin: 0 auto 16px 11px;
144 | padding-top: 66px;
145 | width: 100%;
146 | color: var(--mud);
147 | }
148 |
149 | .glyph-item {
150 | display: flex;
151 | flex-direction: column;
152 | gap: 1rem;
153 | align-items: center;
154 | font-size: 44px;
155 | padding: 2px;
156 | margin: 0 6px;
157 |
158 | --b: 2px; /* thickness of the border */
159 | --c: var(--accent); /* color of the border */
160 | --w: 12px; /* width of border */
161 |
162 | border: var(--b) solid transparent;
163 | }
164 |
165 | .glyph-item:hover,
166 | .glyph-item:focus-visible {
167 | outline: none;
168 | border: var(--b) solid #0000; /* space for the border */
169 | --_g: #0000 90deg, var(--c) 0;
170 | --_p: var(--w) var(--w) border-box no-repeat;
171 | background:
172 | conic-gradient(from 90deg at top var(--b) left var(--b), var(--_g)) 0 0 /
173 | var(--_p),
174 | conic-gradient(from 180deg at top var(--b) right var(--b), var(--_g)) 100% 0 /
175 | var(--_p),
176 | conic-gradient(from 0deg at bottom var(--b) left var(--b), var(--_g)) 0 100% /
177 | var(--_p),
178 | conic-gradient(from -90deg at bottom var(--b) right var(--b), var(--_g))
179 | 100% 100% / var(--_p);
180 | }
181 |
182 | .glyph-item .glyph-example {
183 | width: 1ch;
184 | padding: 4px;
185 | background-color: transparent;
186 | transition: background-color 150ms var(--flick);
187 | }
188 |
189 | .glyph-item[data-selected="true"] .glyph-example {
190 | color: var(--carbon);
191 | background-color: var(--accent);
192 | }
193 |
194 | .glyph-item[data-selected="true"] .glyph-detail {
195 | color: var(--accent);
196 | }
197 |
198 | .glyph-example {
199 | flex: 1;
200 | }
201 |
202 | .glyph-detail {
203 | display: block;
204 | font-size: 1rem;
205 | }
206 |
207 | @media screen and (max-width: 1115px) {
208 | .split {
209 | grid-template-columns: 378px 1fr;
210 | gap: 55px;
211 | }
212 |
213 | #glyph-specimen {
214 | min-width: unset;
215 | width: 378px;
216 | height: 580px;
217 | grid-template-rows: 188px 48px 144px 48px 1fr;
218 | }
219 |
220 | #glyph-specimen::before {
221 | top: 88px;
222 | }
223 |
224 | #glyph-specimen::after {
225 | bottom: 88px;
226 | }
227 |
228 | #big {
229 | font-size: 264px;
230 | top: 116px;
231 | left: 104.5px;
232 | }
233 |
234 | .specimen-details {
235 | padding: 50px 22px 0;
236 | }
237 |
238 | .glyph-item {
239 | font-size: 33px;
240 | }
241 | }
242 |
243 | @media screen and (max-width: 1115px) {
244 | #tester {
245 | padding-top: 105px;
246 | }
247 |
248 | .segment-header {
249 | padding-top: 46px;
250 | }
251 | }
252 |
253 | @media screen and (max-width: 767px) {
254 | #tester {
255 | padding-top: 83px;
256 | }
257 |
258 | .split {
259 | display: flex;
260 | flex-direction: column;
261 | gap: 44px;
262 | }
263 |
264 | #glyph-specimen {
265 | display: none;
266 | }
267 |
268 | #glyph-specimen-static {
269 | display: block;
270 | }
271 |
272 | #apollo1 {
273 | display: none;
274 | }
275 | }
276 |
277 | /******************************************************************************/
278 | /* These classes must map to font feature settings from Glyphs 3. */
279 | /******************************************************************************/
280 |
281 | .sc {
282 | font-variant: small-caps;
283 | }
284 |
285 | .osf {
286 | font-variant: oldstyle-nums;
287 | }
288 |
289 | .lf {
290 | font-variant: lining-nums;
291 | }
292 |
293 | .numr {
294 | font-variant: numerator;
295 | }
296 |
297 | .dnom {
298 | font-variant: denominator;
299 | }
300 |
301 | .loclNLD {
302 | font-feature-settings: "locl";
303 | }
304 |
305 | .ss01 {
306 | font-variant: stylistic;
307 | }
308 |
309 | .ss02 {
310 | font-variant: stylistic-2;
311 | }
312 |
--------------------------------------------------------------------------------
/src/components/tester.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createResource,
3 | createSignal,
4 | createMemo,
5 | For,
6 | createEffect,
7 | } from "solid-js";
8 | import { TTF } from "fonteditor-core";
9 |
10 | import cm from "../lib/charmap.json";
11 | import { font, charDb } from "../lib/font";
12 | import "./tester.css";
13 |
14 | type CharEntry = {
15 | code: number;
16 | feat?: string[];
17 | }
18 |
19 | type GlyphEntry = CharEntry & {
20 | glyf: TTF.Glyph;
21 | };
22 |
23 | enum Segment {
24 | BASIC_LATIN = "Basic Latin",
25 | EXTENDED_LATIN = "Extended Latin",
26 | CYRILLIC = "Cyrillic",
27 | GREEK_AND_COPTIC = "Greek",
28 | PUNCTUATION = "Punctuation, Symbols",
29 | NUMERALS = "Numerals, Math, Currency",
30 | GRAPHICAL = "Graphical",
31 | }
32 |
33 | const FONT_FEATURE_MAP = {
34 | sc: "small caps",
35 | osf: "old style figures",
36 | numr: "numerator",
37 | dnom: "denominator",
38 | loclNLD: "dutch localization",
39 | } as const;
40 |
41 | export function Tester() {
42 | const [fk] = createResource(font());
43 | const [cdb] = createResource(charDb());
44 | const gs = createMemo>(() => {
45 | const f = fk()?.get();
46 | if (!f) return {};
47 | return Object.entries(cm as Record).reduce>(
48 | (acc, [segment, chars]) => {
49 | const entries = chars
50 | .map((entry) => ({ ...entry, glyf: f.glyf[f.cmap[entry.code.toString()]] }))
51 | .filter((g) => {
52 | if (import.meta.env.DEV && !g.glyf) {
53 | console.warn(
54 | `Missing glyph for 0x${g.code.toString(16).padStart(4, "0")}`,
55 | );
56 | }
57 | return !!g.glyf;
58 | });
59 | acc[segment] = entries;
60 | return acc;
61 | },
62 | {},
63 | );
64 | });
65 |
66 | const [glyf, setGlyf] = createSignal(null);
67 | createEffect(() => {
68 | if (fk.state === "ready") {
69 | const ge = gs()[Segment.BASIC_LATIN]?.find((e) => e.code === 0x51)!;
70 | setGlyf(ge);
71 | }
72 | });
73 |
74 | function selectGlyph(ge: GlyphEntry) {
75 | setGlyf(ge);
76 | window.gtag("event", "specimen_select_glyph", {
77 | name: cdb()?.get(ge.code) ?? ge.glyf.name,
78 | unicode: ge.code,
79 | features: ge.feat,
80 | });
81 | }
82 |
83 | return (
84 |
85 |
86 | {apollo}
87 |
88 |
89 |
94 |
95 |
96 |
97 |
98 | {cdb()?.get(glyf()?.code!)}
99 |
100 | {glyf()?.feat?.map((ft) => FONT_FEATURE_MAP[ft as keyof typeof FONT_FEATURE_MAP]).filter(Boolean).join(", ").toUpperCase()}
101 |
102 |
103 | U+
104 | {glyf()?.code.toString(16).padStart(4, "0").toUpperCase() ??
105 | "0000"}
106 |
107 |
108 |
109 |
110 | ASCENDER /
111 | CAP HEIGHT
112 |
113 | 400
114 |
115 |
116 | X-HEIGHT
117 | 300
118 |
119 |
120 | BASELINE
121 | 0
122 |
123 |
124 | DESCENDER
125 | -100
126 |
127 |
128 | {String.fromCodePoint(glyf()?.code ?? 0)}
129 |
130 |
131 |
132 |
133 | {([segment, entries]) => (
134 | <>
135 |
136 |
137 | {(ge, _idx) => (
138 | selectGlyph(ge)}
142 | />
143 | )}
144 |
145 | >
146 | )}
147 |
148 |
149 |
150 |
151 | );
152 | }
153 |
154 | type GlyphItemProps = GlyphEntry & {
155 | selected: boolean;
156 | className?: string;
157 | onClick: (e: Event) => void;
158 | };
159 |
160 | function GlyphItem(props: GlyphItemProps) {
161 | const content = () => String.fromCodePoint(props.code);
162 |
163 | return (
164 | e.key === "Enter" && props.onClick(e)}
171 | >
172 | {content()}
173 |
174 | );
175 | }
176 |
177 | export const apollo = `\
178 | ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
179 | │ CLOCK │ │ COUNTER & │ │ S │
180 | ├──────────────────┤ ┌─────►│ INTERRUPT ├────────►│ ─── │
181 | │ │ │ │ PRIORITY │ │ MEMORY │
182 | │ OSCILLATOR AND │ │ ┌───┤ CIRCUITS │ │ ADDRESS │ ───────────────┐
183 | │ TIMING PULSES │ │ │ └──────────────────┘ ┌───►│ REGISTER │ / /│
184 | │ │ │ │ │ ├──────────────────┼──────┐ / / │◄──┐
185 | └────────┬─────────┘ │ │ │ │ MEMORY │ │ ┌──────────────┐ │ │
186 | │ │ │ ┌──────────────────┐ │◄──►│ BANK │ ├──►│ FIXED │ / │
187 | ├────────────┐ │ │ │ ADDRESSABLE │ │ │ REGISTERS │ │ │ MEMORY │ / │
188 | │ │ │ │ │ CENTRAL │ │ └──────────────────┘ ┌──┼───┤++++++++++++++│/ │
189 | ▼ │ │ │ │ REGISTERS │◄──►│ │ │ └──────────────┘ │
190 | ┌──────────────────┐ │ │ │ │ │ │ │ │ │
191 | │ SEQUENCE │ │ │ │ │ A │ │ ┌──────────────────┐ │ │ ───────────────┐ │
192 | │ GENERATOR │◄─┼──┼──┘ │ L •◄────┼────┼──┐ │ G │ │ │ / /│ │
193 | ├──────────────────┤ │ │ │ Q │ │ └─┼─►• ─── │◄──┘ │ / / │◄──┤
194 | │ │ │ │ ┌──►│ Z │ │ │ MEMORY │ │ ┌──────────────┐ │ │
195 | │ INSTRUCTION │ │ │ │ └──────────────────┘ │◄──►│ LOCAL │ └──►│ ERASABLE │ / │
196 | ┌─►│ MICROPROGRAM │ │ │ │ │ │ REGISTER │ │ MEMORY │ / │
197 | │ │ PULSES │ │ │ │ │ ┌►├──────────────────┤◄────────►│++++++++++++++│/ │
198 | │ │ │ │ │ │ ┌──────────────────┐ │ │ │ PARITY │ └──────────────┘ │
199 | │ └────────┬─────────┘ │ │ │ │ ARITHMETIC │ │ │ └──────────────────┘ │
200 | │ │ │ │ ├──►│ UNIT │◄──►│ │ │
201 | │ ├────────────┼──┼──┤ └──────────────────┘ │ └───────────┐ │
202 | │ │ │ │ │ │ │ │
203 | │ │ │ │ │ │ ┌─────────┴────────┐ │
204 | │ ┌────────┴─────────┐ │ │ │ ┌──────────────────┐ │ │ SPECIAL │ │
205 | │ │ MEMORY │ └──┼──┼──►│ │ │ │ GATING │ │
206 | │ │ TIMING │ │ │ │ │ │ └──────────────────┘ │
207 | │ └─────────────┬────┘ └──┼──►│ INPUT / │◄──►│ ▲ │
208 | │ │ │ │ OUTPUT │ │ │ │
209 | │ INPUTS ─────┼─────────────┼──►│ CHANNELS │ ├──────────────┘ │
210 | │ │ │ │ │ │ │
211 | │ OUTPUTS ◄────┼─────────────┼───┤ │ │ │
212 | │ │ │ └──────────────────┘ │ │
213 | │ │ │ │ │
214 | │ └─────────────┼───────────────────────────┼────────────────────────────────────────────────────────┘
215 | │ │ │
216 | │ │ ┌──────────────────┐ │
217 | │ │ │ SQ │ │
218 | │ └──►│ ──── │◄───┘
219 | │ │ INSTRUCTION │
220 | └──────────────────────────────────┤ DECODING │
221 | └──────────────────┘
222 | `;
223 |
--------------------------------------------------------------------------------
/src/components/typetest.css:
--------------------------------------------------------------------------------
1 | .type-samples {
2 | display: flex;
3 | flex-direction: column;
4 | gap: 66px;
5 | }
6 |
7 | .type-sample {
8 | display: flex;
9 | flex-direction: column;
10 | }
11 |
12 | .type-sample-header {
13 | display: flex;
14 | align-items: center;
15 | gap: 55px;
16 | color: var(--clay);
17 | }
18 |
19 | .type-sample-text {
20 | color-scheme: light;
21 | resize: none;
22 | border: none;
23 | color: inherit;
24 | background: transparent;
25 | font-family: inherit;
26 | width: 100%;
27 | }
28 |
29 | .type-sample-text:focus-visible {
30 | color: var(--bg);
31 | background: var(--dark);
32 | outline: none;
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/typetest.tsx:
--------------------------------------------------------------------------------
1 | import { For } from "solid-js";
2 | import "./typetest.css";
3 |
4 | const SAMPLES: TypeSampleProps[] = [
5 | {
6 | class: "hidden-small",
7 | copy: "ATTN: PASSENGERS QUIET IN THE CABIN",
8 | size: 121,
9 | tracking: -11,
10 | style: "gap: 3px; line-height: 1;",
11 | },
12 | {
13 | copy: "FLIGHT ATTENDANTS, PREPARE FOR TAKEOFF",
14 | size: 55,
15 | tracking: -5,
16 | style: "gap: 13px; line-height: 1;",
17 | },
18 | {
19 | copy: "DEPARTURE MONO IS A MONOSPACED PIXEL FONT INSPIRED BY THE CONSTRAINTS OF EARLY COMMAND-LINE AND GRAPHICAL USER INTERFACES, THE TINY PIXEL FONTS OF THE LATE 90s/EARLY 00s, AND SCI-FI CONCEPTS FROM FILM AND TELEVISION.",
20 | size: 22,
21 | style: "gap: 15px; max-width: 1124px;",
22 | },
23 | {
24 | copy: "Departure Mono is a monospaced pixel font inspired by the constraints of early command-line and graphical user interfaces, the tiny pixel fonts of the late 90s/early 00s, and sci-fi concepts from film and television.",
25 | size: 16.5,
26 | style: "gap: 16px; max-width: 895px;",
27 | },
28 | ];
29 |
30 | export function TypeTest() {
31 | return (
32 |
33 | {(props) => }
34 |
35 | );
36 | }
37 |
38 | type TypeSampleProps = {
39 | class?: string;
40 | style?: string;
41 | copy: string;
42 | size: number;
43 | tracking?: number;
44 | };
45 |
46 | function TypeSample(props: TypeSampleProps) {
47 | return (
48 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/src/glsl-canvas.d.ts:
--------------------------------------------------------------------------------
1 | declare module "glslCanvas" {
2 | export type TestResult = {
3 | wasValid: boolean;
4 | frag: string;
5 | vert: string;
6 | timeElapsedMs: number;
7 | };
8 |
9 | export default class GlslCanvas {
10 | constructor(canvas: HTMLCanvasElement, contextOptions?: {}, options?: {});
11 | destroy();
12 | load(fragString: string);
13 | load(fragString: string, vertString: string);
14 | test(
15 | callback: (r: TestResult) => void,
16 | fragString: string,
17 | vertString: string,
18 | );
19 | loadTexture(
20 | name: string,
21 | urlElementOrData: string | URL | ImageData,
22 | options: any,
23 | );
24 | uniform(method: string, type: string, name: string, ...value: any[]);
25 | uniformTexture(name: string, texture, options);
26 | setUniform(name: string, ...value: any[]);
27 | setUniforms(uniforms: any[]);
28 | refreshUniforms();
29 | setMouse(mouse: MouseEvent | { x: number; y: number });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/gtag.d.ts:
--------------------------------------------------------------------------------
1 | type TagType = "event";
2 |
3 | export declare global {
4 | interface Window {
5 | gtag(type: TagType, event: string, params: { [key: string]: any }): void;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Departure Mono";
3 | src:
4 | url("/assets/DepartureMono-Regular.woff2") format("woff2"),
5 | url("/assets/DepartureMono-Regular.woff") format("woff"),
6 | url("/assets/DepartureMono-Regular.otf") format("opentype");
7 | font-feature-settings: "locl";
8 | }
9 |
10 | :root {
11 | font-family: "Departure Mono", monospace;
12 | font-synthesis: none;
13 | text-rendering: optimizeLegibility;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 |
17 | color-scheme: light dark;
18 |
19 | font-size: 11px;
20 | --unit: 1em;
21 | /* --scroll: 0%; */
22 | /* --fuzz: 0; */
23 | --t: 0%;
24 | --flick: cubic-bezier(0.36, 2.09, 0.07, -1.52);
25 |
26 | --amber: #ffa133;
27 | --pumpkin: #e47b1a;
28 | --flux: #c8be50;
29 | --foam: #bccabb;
30 | --enamel: #eeeeee;
31 | --cement: #c0c0c0;
32 | --aluminum: #cccccc;
33 | --ash: #8e8e8e;
34 | --mud: #8a8a6f;
35 | --clay: #6c6c58;
36 | --smoke: #666666;
37 | --dark: #444444;
38 | --soot: #333333;
39 | --carbon: #222222;
40 | --black: #141414;
41 |
42 | --accent: var(--amber);
43 | --fg: var(--dark);
44 | --bg: var(--enamel);
45 |
46 | color: var(--fg);
47 | background-color: var(--carbon);
48 | /* color: hsl(30 1% min(88%, var(--scroll) * 8)); */
49 | /* background-color: hsl(30 1% calc(100% - min(88%, var(--scroll) * 8))); */
50 | }
51 |
52 | @media (prefers-color-scheme: dark) {
53 | }
54 |
55 | html,
56 | body {
57 | max-width: 100%;
58 | overflow-x: clip;
59 | }
60 |
61 | ::selection,
62 | .light ::selection {
63 | color: var(--enamel);
64 | background-color: var(--carbon);
65 | }
66 |
67 | ::spelling-error {
68 | text-decoration: none;
69 | }
70 |
71 | main {
72 | display: flex;
73 | flex-direction: column;
74 | align-items: stretch;
75 |
76 | --fg: var(--cement);
77 | --bg: var(--carbon);
78 |
79 | color: var(--fg);
80 | background-color: var(--bg);
81 | }
82 |
83 | main ::selection {
84 | color: var(--carbon);
85 | background-color: var(--cement);
86 | }
87 |
88 | section {
89 | display: grid;
90 | place-items: center;
91 | padding: 2rem;
92 | }
93 |
94 | footer {
95 | display: flex;
96 | align-items: center;
97 | justify-content: space-between;
98 | padding: 44px;
99 | background-color: var(--carbon);
100 | }
101 |
102 | h2 {
103 | font-size: 44px;
104 | line-height: 1.1;
105 | color: var(--soot);
106 | background-color: var(--ash);
107 | line-height: 1;
108 | z-index: 1;
109 | }
110 |
111 | .callout {
112 | color: var(--mud);
113 | border-left: 7px solid var(--mud);
114 | padding-left: 13px;
115 | font-size: 11px;
116 | line-height: 14px;
117 | padding: 8px 0 8px 12px;
118 | }
119 |
120 | .callout::selection {
121 | color: var(--carbon);
122 | background-color: var(--cement);
123 | }
124 |
125 | .inverse {
126 | color: var(--bg);
127 | background-color: var(--fg);
128 |
129 | ::selection {
130 | color: var(--cement);
131 | background-color: var(--clay);
132 | }
133 | }
134 |
135 | .maxwidth {
136 | max-width: 1440px;
137 | padding-inline: 44px;
138 | margin: 0 auto;
139 | }
140 |
141 | .comment {
142 | color: var(--clay);
143 | font-size: 11px;
144 | white-space: preserve;
145 | }
146 |
147 | .control {
148 | position: absolute;
149 | top: 1rem;
150 | right: 1rem;
151 | z-index: 1;
152 | }
153 |
154 | .diagram {
155 | color: var(--clay);
156 | }
157 |
158 | .lg {
159 | font-size: 1.4rem;
160 | }
161 |
162 | .accent {
163 | color: var(--accent);
164 | }
165 |
166 | @media screen and (max-width: 1115px) {
167 | footer {
168 | flex-direction: column;
169 | align-items: flex-start;
170 | gap: 44px;
171 | }
172 | }
173 |
174 | @media screen and (max-width: 767px) {
175 | .maxwidth {
176 | padding-inline: 16px;
177 | }
178 |
179 | .type-sample.hidden-small {
180 | display: none;
181 | }
182 |
183 | footer {
184 | gap: 22px;
185 | padding-bottom: 33px;
186 | }
187 |
188 | h2 {
189 | font-size: 33px;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | /* @refresh reload */
2 | import { render } from "solid-js/web";
3 |
4 | import "./reset.css";
5 | import "./index.css";
6 | import { App } from "./components/app";
7 |
8 | const root = document.getElementById("root");
9 | render(() => , root!);
10 |
--------------------------------------------------------------------------------
/src/lib/font.ts:
--------------------------------------------------------------------------------
1 | import { Font } from "fonteditor-core";
2 | import {
3 | fetchWithProgress,
4 | type FetchWithProgressOptions as Opts,
5 | } from "./utils";
6 |
7 | export function font(
8 | onProgress?: Opts["onProgress"],
9 | onDone?: Opts["onDone"],
10 | ) {
11 | return async function () {
12 | const buf = await fetchWithProgress("/assets/DepartureMono-Regular.otf", {
13 | responseType: "arraybuffer",
14 | onProgress,
15 | onDone,
16 | });
17 | return Font.create(buf, { type: "otf" });
18 | };
19 | }
20 |
21 | export function charDb() {
22 | return async function () {
23 | const json = await fetchWithProgress>(
24 | "/assets/cm.json",
25 | {
26 | responseType: "json",
27 | },
28 | );
29 | return new Map(
30 | Object.entries(json).map(([k, v]) => [parseInt(k, 16), v]),
31 | );
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { createSignal, type Signal } from "solid-js";
2 |
3 | export type FetchWithProgressOptions = {
4 | method?: string;
5 | responseType?: XMLHttpRequestResponseType;
6 | onProgress?: (pct: number) => void;
7 | onDone?: (res: D) => void;
8 | };
9 |
10 | export class Timer {
11 | seconds: number;
12 | signal: Signal;
13 | interval: NodeJS.Timeout | undefined;
14 |
15 | constructor(seconds: number) {
16 | this.seconds = seconds;
17 | this.signal = createSignal(seconds);
18 | }
19 |
20 | decrement() {
21 | this.signal[1]((s) => s - 1);
22 | }
23 |
24 | start() {
25 | if (this.interval) return;
26 | this.interval = setInterval(() => {
27 | const [s] = this.signal;
28 | if (s() > 0) {
29 | this.decrement();
30 | } else {
31 | this.stop();
32 | }
33 | }, 1000);
34 | }
35 |
36 | stop() {
37 | clearInterval(this.interval);
38 | this.interval = undefined;
39 | }
40 |
41 | reset() {
42 | stop();
43 | this.signal[1](this.seconds);
44 | }
45 | }
46 |
47 | export async function fetchWithProgress(
48 | url: string,
49 | opts?: FetchWithProgressOptions,
50 | ): Promise {
51 | return new Promise((resolve, reject) => {
52 | const xhr = new XMLHttpRequest();
53 |
54 | xhr.open(opts?.method ?? "GET", url, true);
55 | xhr.responseType = opts?.responseType ?? "blob";
56 |
57 | xhr.onprogress = (event) => {
58 | if (event.lengthComputable) {
59 | const percentComplete = (event.loaded / event.total) * 100;
60 | opts?.onProgress?.(percentComplete);
61 | }
62 | };
63 |
64 | xhr.onload = () => {
65 | if (xhr.status >= 200 && xhr.status < 300) {
66 | resolve(xhr.response);
67 | opts?.onDone?.(xhr.response);
68 | } else {
69 | reject(new Error(`HTTP error! status: ${xhr.status}`));
70 | }
71 | };
72 |
73 | xhr.onerror = () => {
74 | reject(new Error("Network error"));
75 | };
76 |
77 | xhr.send();
78 | });
79 | }
80 |
81 | export function getCSSVariable(name: string) {
82 | return getComputedStyle(document.documentElement)
83 | .getPropertyValue(name)
84 | .trim();
85 | }
86 |
87 | export const Colors = {
88 | fg: getCSSVariable("--fg"),
89 | bg: getCSSVariable("--bg"),
90 | accent: getCSSVariable("--accent"),
91 | amber: getCSSVariable("--amber"),
92 | pumpkin: getCSSVariable("--pumpkin"),
93 | flux: getCSSVariable("--flux"),
94 | foam: getCSSVariable("--foam"),
95 | enamel: getCSSVariable("--enamel"),
96 | cement: getCSSVariable("--cement"),
97 | ash: getCSSVariable("--ash"),
98 | clay: getCSSVariable("--clay"),
99 | smoke: getCSSVariable("--smoke"),
100 | dark: getCSSVariable("--dark"),
101 | carbon: getCSSVariable("--carbon"),
102 | black: getCSSVariable("--black"),
103 | } as const;
104 |
--------------------------------------------------------------------------------
/src/reset.css:
--------------------------------------------------------------------------------
1 | /* http://meyerweb.com/eric/tools/css/reset/
2 | v2.0 | 20110126
3 | License: none (public domain)
4 | */
5 |
6 | html,
7 | body,
8 | div,
9 | span,
10 | applet,
11 | object,
12 | iframe,
13 | h1,
14 | h2,
15 | h3,
16 | h4,
17 | h5,
18 | h6,
19 | p,
20 | blockquote,
21 | pre,
22 | a,
23 | abbr,
24 | acronym,
25 | address,
26 | big,
27 | cite,
28 | code,
29 | del,
30 | dfn,
31 | em,
32 | img,
33 | ins,
34 | kbd,
35 | q,
36 | s,
37 | samp,
38 | small,
39 | strike,
40 | strong,
41 | sub,
42 | sup,
43 | tt,
44 | var,
45 | b,
46 | u,
47 | i,
48 | center,
49 | dl,
50 | dt,
51 | dd,
52 | ol,
53 | ul,
54 | li,
55 | fieldset,
56 | form,
57 | label,
58 | legend,
59 | table,
60 | caption,
61 | tbody,
62 | tfoot,
63 | thead,
64 | tr,
65 | th,
66 | td,
67 | article,
68 | aside,
69 | canvas,
70 | details,
71 | embed,
72 | figure,
73 | figcaption,
74 | footer,
75 | header,
76 | hgroup,
77 | menu,
78 | nav,
79 | output,
80 | ruby,
81 | section,
82 | summary,
83 | time,
84 | mark,
85 | audio,
86 | video {
87 | margin: 0;
88 | padding: 0;
89 | border: 0;
90 | font-size: 100%;
91 | font: inherit;
92 | vertical-align: baseline;
93 | }
94 | /* HTML5 display-role reset for older browsers */
95 | article,
96 | aside,
97 | details,
98 | figcaption,
99 | figure,
100 | footer,
101 | header,
102 | hgroup,
103 | menu,
104 | nav,
105 | section {
106 | display: block;
107 | }
108 | ol,
109 | ul {
110 | list-style: none;
111 | }
112 | blockquote,
113 | q {
114 | quotes: none;
115 | }
116 | blockquote:before,
117 | blockquote:after,
118 | q:before,
119 | q:after {
120 | content: "";
121 | content: none;
122 | }
123 | table {
124 | border-collapse: collapse;
125 | border-spacing: 0;
126 | }
127 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
5 | "target": "ES2020",
6 | "useDefineForClassFields": true,
7 | "module": "ESNext",
8 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
9 | "skipLibCheck": true,
10 |
11 | /* Bundler mode */
12 | "moduleResolution": "bundler",
13 | "allowImportingTsExtensions": true,
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "moduleDetection": "force",
17 | "noEmit": true,
18 | "jsx": "preserve",
19 | "jsxImportSource": "solid-js",
20 |
21 | /* Linting */
22 | "strict": true,
23 | "noUnusedLocals": true,
24 | "noUnusedParameters": true,
25 | "noFallthroughCasesInSwitch": true
26 | },
27 | "include": ["src"]
28 | }
29 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "files": [],
3 | "references": [
4 | {
5 | "path": "./tsconfig.app.json"
6 | },
7 | {
8 | "path": "./tsconfig.node.json"
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
5 | "skipLibCheck": true,
6 | "module": "ESNext",
7 | "moduleResolution": "bundler",
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "noEmit": true
11 | },
12 | "include": ["vite.config.ts"]
13 | }
14 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite";
2 | import { nodePolyfills } from "vite-plugin-node-polyfills";
3 | import solid from "vite-plugin-solid";
4 |
5 | export default defineConfig({
6 | plugins: [nodePolyfills({ include: ["buffer", "stream"] }), solid()],
7 | });
8 |
--------------------------------------------------------------------------------