├── .github └── workflows │ └── build.yml ├── .gitignore ├── CNAME ├── LICENSE.md ├── build.sh ├── index.html ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── public ├── buttons │ ├── 88x31.png │ ├── PoweredByNEXTSTEP.gif │ ├── VisitNeXT.gif │ ├── agplv3-with-text-162x68.webp │ ├── any-browser.webp │ ├── arc.gif │ ├── arch.gif │ ├── button.gif │ ├── ce88x31.webp │ ├── circular-88x31.gif │ ├── dlbadge.webp │ ├── dreamland-new.webp │ ├── eightyeightthirtyone.webp │ ├── firefox.gif │ ├── foxmossbutton.webp │ ├── freemusicnow.webp │ ├── hg88x31.webp │ ├── lucida-2.gif │ ├── mariokart.webp │ ├── melankorin.gif │ ├── necoarc-88x31.webp │ ├── omada.gif │ ├── simpleanalytics.svg │ ├── thinlqd.webp │ └── wearr.gif ├── coolthings │ ├── AppleInternal │ │ ├── AB-Arcade.png │ │ ├── AB-DemoEnd.png │ │ ├── AB-Icon.png │ │ ├── AB-Menu.png │ │ ├── HDI.jpeg │ │ ├── Pricing-appleconnect.png │ │ ├── Pricing-icon.png │ │ ├── Pricing-loginalert.png │ │ ├── Pricing-services.png │ │ ├── angrybirds.html │ │ ├── hditestagent.html │ │ ├── index.html │ │ └── pricing.html │ └── index.html ├── epic.ogg ├── favicon.ico ├── fonts │ ├── body │ │ ├── AdwaitaSans-Italic.ttf │ │ ├── AdwaitaSans-Italic.woff │ │ ├── AdwaitaSans-Italic.woff2 │ │ ├── AdwaitaSans-Regular.ttf │ │ ├── AdwaitaSans-Regular.woff │ │ └── AdwaitaSans-Regular.woff2 │ ├── display │ │ ├── InterTight-Italic-VariableFont_wght.ttf │ │ ├── InterTight-Italic-VariableFont_wght.woff │ │ ├── InterTight-Italic-VariableFont_wght.woff2 │ │ ├── InterTight-VariableFont_wght.ttf │ │ ├── InterTight-VariableFont_wght.woff │ │ └── InterTight-VariableFont_wght.woff2 │ ├── mono │ │ ├── AdwaitaMono-Bold.ttf │ │ ├── AdwaitaMono-Bold.woff │ │ ├── AdwaitaMono-Bold.woff2 │ │ ├── AdwaitaMono-BoldItalic.ttf │ │ ├── AdwaitaMono-BoldItalic.woff │ │ ├── AdwaitaMono-BoldItalic.woff2 │ │ ├── AdwaitaMono-Italic.ttf │ │ ├── AdwaitaMono-Italic.woff │ │ ├── AdwaitaMono-Italic.woff2 │ │ ├── AdwaitaMono-Regular.ttf │ │ ├── AdwaitaMono-Regular.woff │ │ └── AdwaitaMono-Regular.woff2 │ └── serif │ │ ├── AppleGaramond Bd.ttf │ │ ├── AppleGaramond BdIt.ttf │ │ ├── AppleGaramond Bk.ttf │ │ ├── AppleGaramond BkIt.ttf │ │ ├── AppleGaramond Lt.ttf │ │ └── AppleGaramond LtIt.ttf ├── index.css ├── kawaii.webp ├── legacy.css ├── legacy.html ├── main.css ├── modern-normalize.css ├── oneko-ctp.gif ├── oneko.gif ├── oneko.js ├── proj-thumbnails │ ├── SSC2024_Social_Static_16x9.jpg │ ├── Twitter Banner.png │ ├── anura.webp │ ├── appcommander.webp │ ├── celeste.webp │ ├── cowabunga.webp │ ├── dssos.webp │ ├── mandelapro.webp │ ├── picasso.webp │ └── placeholder.jpg ├── projects │ └── index.html ├── style.css ├── styles.css ├── tools │ ├── index.html │ ├── jailbreakme │ │ ├── _ │ │ │ └── iPhone1,x_3.1.3.pdf │ │ ├── detector.js │ │ ├── index.html │ │ ├── slider.css │ │ ├── slider.js │ │ ├── star.css │ │ ├── star.js │ │ ├── sunspider-3dcube.js │ │ ├── test.html │ │ ├── ui_normal.css │ │ └── wallpaper-retina.jpg │ └── mememaker │ │ ├── index.html │ │ ├── script.js │ │ └── style.css └── typography.css ├── src ├── 3DSite │ ├── 3DFloor.tsx │ ├── ClickWall.tsx │ ├── Screen.tsx │ ├── ThreeDeeApp.tsx │ └── ThreeDeeInfo.tsx ├── App.tsx ├── CommonCSS.tsx ├── Cursor.tsx ├── DarkReaderWarning.tsx ├── Footer.tsx ├── IsMobile.ts ├── LargeProjectView.tsx ├── LatestToot.tsx ├── N64.tsx ├── Navigation.tsx ├── Project.ts ├── ProjectCard.tsx ├── Projects.ts ├── SiteContent.tsx ├── Status.ts ├── Themes.tsx ├── Utils.ts └── vite-env.d.ts ├── tsconfig.json └── vite.config.ts /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy (dreamland.js) 2 | 3 | on: 4 | # Runs on pushes targeting the default branch 5 | push: 6 | branches: 7 | - master 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-24.04 31 | steps: 32 | - name: Install pnpm 33 | uses: pnpm/action-setup@v4 34 | with: 35 | version: 10 36 | - name: Checkout 37 | uses: actions/checkout@v4 38 | - name: Setup Pages 39 | uses: actions/configure-pages@v5 40 | - name: Install Dependencies 41 | run: 'pnpm i' 42 | - name: 'Build' 43 | run: 'pnpm build' 44 | - name: 'Fix router' 45 | run: 'cp dist/index.html dist/404.html' 46 | - name: Upload artifact 47 | uses: actions/upload-pages-artifact@v3 48 | with: 49 | path: 'dist' 50 | - name: Deploy to GitHub Pages 51 | id: deployment 52 | uses: actions/deploy-pages@v4 53 | -------------------------------------------------------------------------------- /.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 | 26 | .vite 27 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | bomberfish.ca -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm run build 4 | 5 | for f in $(ls -d dist/assets/*.js); do 6 | echo "Appending licence info to $f" 7 | echo -e "// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0\n$(cat $f)" > $f 8 | sed -i "\$i\\// @license-end" $f 9 | done 10 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BomberFish 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 102 | 117 | 132 |
133 | 134 | 135 | 142 | 152 | 153 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bomberfish-website", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "devDependencies": { 12 | "terser": "^5.39.0", 13 | "typescript": "^5.8.3", 14 | "vite": "^6.3.4", 15 | "vite-plugin-minify": "^1.5.2" 16 | }, 17 | "dependencies": { 18 | "@vitejs/plugin-legacy": "^6.1.1", 19 | "dreamland": "^0.0.24", 20 | "lightningcss": "^1.29.3", 21 | "vite-plugin-dreamland": "^1.2.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/buttons/88x31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/88x31.png -------------------------------------------------------------------------------- /public/buttons/PoweredByNEXTSTEP.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/PoweredByNEXTSTEP.gif -------------------------------------------------------------------------------- /public/buttons/VisitNeXT.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/VisitNeXT.gif -------------------------------------------------------------------------------- /public/buttons/agplv3-with-text-162x68.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/agplv3-with-text-162x68.webp -------------------------------------------------------------------------------- /public/buttons/any-browser.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/any-browser.webp -------------------------------------------------------------------------------- /public/buttons/arc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/arc.gif -------------------------------------------------------------------------------- /public/buttons/arch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/arch.gif -------------------------------------------------------------------------------- /public/buttons/button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/button.gif -------------------------------------------------------------------------------- /public/buttons/ce88x31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/ce88x31.webp -------------------------------------------------------------------------------- /public/buttons/circular-88x31.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/circular-88x31.gif -------------------------------------------------------------------------------- /public/buttons/dlbadge.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/dlbadge.webp -------------------------------------------------------------------------------- /public/buttons/dreamland-new.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/dreamland-new.webp -------------------------------------------------------------------------------- /public/buttons/eightyeightthirtyone.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/eightyeightthirtyone.webp -------------------------------------------------------------------------------- /public/buttons/firefox.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/firefox.gif -------------------------------------------------------------------------------- /public/buttons/foxmossbutton.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/foxmossbutton.webp -------------------------------------------------------------------------------- /public/buttons/freemusicnow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/freemusicnow.webp -------------------------------------------------------------------------------- /public/buttons/hg88x31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/hg88x31.webp -------------------------------------------------------------------------------- /public/buttons/lucida-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/lucida-2.gif -------------------------------------------------------------------------------- /public/buttons/mariokart.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/mariokart.webp -------------------------------------------------------------------------------- /public/buttons/melankorin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/melankorin.gif -------------------------------------------------------------------------------- /public/buttons/necoarc-88x31.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/necoarc-88x31.webp -------------------------------------------------------------------------------- /public/buttons/omada.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/omada.gif -------------------------------------------------------------------------------- /public/buttons/thinlqd.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/thinlqd.webp -------------------------------------------------------------------------------- /public/buttons/wearr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/buttons/wearr.gif -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/AB-Arcade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/AB-Arcade.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/AB-DemoEnd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/AB-DemoEnd.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/AB-Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/AB-Icon.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/AB-Menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/AB-Menu.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/HDI.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/HDI.jpeg -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/Pricing-appleconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/Pricing-appleconnect.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/Pricing-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/Pricing-icon.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/Pricing-loginalert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/Pricing-loginalert.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/Pricing-services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/coolthings/AppleInternal/Pricing-services.png -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/angrybirds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Angry Birds Reloaded - bomberfish.ca 11 | 12 | 13 |

Angry Birds Reloaded Retail Demo (macOS)

14 | ❮ Go Back

15 | 🏛️ Download 16 |

Bundle ID: com.rovio.abreloaded.retaildemo

17 |

Latest known version: 1.0.13805 (13805)

18 |

Latest leaked version: 1.0.13805 (13805)

19 |

Signed by: Rovio Entertainment Oyj (D3S84ERWQK)

20 |

Non-standard/private entitlements: None

21 |

Created on: October 25, 2022 00:56:14

22 |

Notes: Demo ends after ~15 levels

23 |

24 |

AngryBirdsReloaded.app Icon

25 | App Icon for AngryBirdsReloadedRetailDemo 26 |

Apple Arcade splash screen

27 | Apple Arcade splash screen 28 |

Main Menu (notice the demo text)

29 | AngryBirdsReloaded main menu 30 |

Thanks for playing screen

31 | AngryBirdsReloadedRetailDemo demo ended screen 32 | 33 | -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/hditestagent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Pricing - bomberfish.ca 11 | 12 | 13 |

HDITestAgent

14 | ❮ Go Back

15 |

Download unavailable

16 |

Bundle ID: Unknown

17 |

Latest known version: Unknown

18 |

Latest leaked version: Unknown

19 |

Signed by: Unknown

20 |

Non-standard/private entitlements: Unknown

21 |

Created on: Unknown

22 |

Notes: Unknown if any version has been leaked. Perhaps check the 45GB dump on the IA?

23 |

24 |

Images

25 |

HDITestAgent's icon on an Apple Store employee's MacBook

26 | HDITestAgent's icon on an Apple Store employee's MacBook 27 | 28 | -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Apple Internal - bomberfish.ca 11 | 12 | 13 |

Apple Internal Software

14 | ❮ Go Back

15 | Pricing.app (macOS)
16 | Angry Birds Reloaded Retail Demo (macOS)
17 | HDITestAgent
18 | 19 | -------------------------------------------------------------------------------- /public/coolthings/AppleInternal/pricing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Pricing - bomberfish.ca 11 | 12 | 13 |

Pricing.app (macOS)

14 | ❮ Go Back

15 | 🏛️ Download 16 |

Bundle ID: com.apple.ist.windward-mac

17 |

Latest known version: 2.1.4 (275)

18 |

Latest leaked version: 2.1.4 (275)

19 |

Signed by: Apple Inc. - Retail IS&T Mac (8GHZHQX4WV)

20 |

Non-standard/private entitlements: com.apple.private.mobilestoredemo.enabledemo

21 |

Created on: October 25, 2022 00:14:25

22 |

Notes: Requires AppleConnect Account

23 |

24 |

Images

25 |

Pricing.app Icon

26 | App Icon for Pricing 27 |

Welcome screen

28 | Welcome screen 29 |

AppleConnect login

30 | AppleConnect login page 31 |

"You need to sign in to use the app" prompt

32 | Sign-in error prompt 33 | 34 | -------------------------------------------------------------------------------- /public/coolthings/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | coolthings - bomberfish.ca 11 | 12 | 13 |

Cool Things I found

14 | ❮ Go Back

15 | Apple Internal software 16 | 17 | -------------------------------------------------------------------------------- /public/epic.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/epic.ogg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/favicon.ico -------------------------------------------------------------------------------- /public/fonts/body/AdwaitaSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/body/AdwaitaSans-Italic.ttf -------------------------------------------------------------------------------- /public/fonts/body/AdwaitaSans-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/body/AdwaitaSans-Italic.woff -------------------------------------------------------------------------------- /public/fonts/body/AdwaitaSans-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/body/AdwaitaSans-Italic.woff2 -------------------------------------------------------------------------------- /public/fonts/body/AdwaitaSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/body/AdwaitaSans-Regular.ttf -------------------------------------------------------------------------------- /public/fonts/body/AdwaitaSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/body/AdwaitaSans-Regular.woff -------------------------------------------------------------------------------- /public/fonts/body/AdwaitaSans-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/body/AdwaitaSans-Regular.woff2 -------------------------------------------------------------------------------- /public/fonts/display/InterTight-Italic-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/display/InterTight-Italic-VariableFont_wght.ttf -------------------------------------------------------------------------------- /public/fonts/display/InterTight-Italic-VariableFont_wght.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/display/InterTight-Italic-VariableFont_wght.woff -------------------------------------------------------------------------------- /public/fonts/display/InterTight-Italic-VariableFont_wght.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/display/InterTight-Italic-VariableFont_wght.woff2 -------------------------------------------------------------------------------- /public/fonts/display/InterTight-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/display/InterTight-VariableFont_wght.ttf -------------------------------------------------------------------------------- /public/fonts/display/InterTight-VariableFont_wght.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/display/InterTight-VariableFont_wght.woff -------------------------------------------------------------------------------- /public/fonts/display/InterTight-VariableFont_wght.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/display/InterTight-VariableFont_wght.woff2 -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Bold.ttf -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Bold.woff -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Bold.woff2 -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-BoldItalic.ttf -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-BoldItalic.woff -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-BoldItalic.woff2 -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Italic.ttf -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Italic.woff -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Italic.woff2 -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Regular.ttf -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Regular.woff -------------------------------------------------------------------------------- /public/fonts/mono/AdwaitaMono-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/mono/AdwaitaMono-Regular.woff2 -------------------------------------------------------------------------------- /public/fonts/serif/AppleGaramond Bd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/serif/AppleGaramond Bd.ttf -------------------------------------------------------------------------------- /public/fonts/serif/AppleGaramond BdIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/serif/AppleGaramond BdIt.ttf -------------------------------------------------------------------------------- /public/fonts/serif/AppleGaramond Bk.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/serif/AppleGaramond Bk.ttf -------------------------------------------------------------------------------- /public/fonts/serif/AppleGaramond BkIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/serif/AppleGaramond BkIt.ttf -------------------------------------------------------------------------------- /public/fonts/serif/AppleGaramond Lt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/serif/AppleGaramond Lt.ttf -------------------------------------------------------------------------------- /public/fonts/serif/AppleGaramond LtIt.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/fonts/serif/AppleGaramond LtIt.ttf -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | legacy.css -------------------------------------------------------------------------------- /public/kawaii.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/kawaii.webp -------------------------------------------------------------------------------- /public/legacy.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Prefixed by https://autoprefixer.github.io 3 | * PostCSS: v8.4.14, 4 | * Autoprefixer: v10.4.7 5 | * Browsers: last 999 version 6 | */ 7 | 8 | @import url('/typography.css'); 9 | 10 | body { 11 | background: #000000; 12 | color: #fbf9fb; 13 | font-family: var(--font-body), sans-serif; 14 | } 15 | 16 | h1, 17 | h2, 18 | h3, 19 | h4 { 20 | font-family: var(--font-serif), serif; 21 | } 22 | 23 | nav { 24 | background: #110f11; 25 | position: fixed; 26 | top: 0; 27 | left: 0; 28 | right: 0; 29 | z-index: 100; 30 | padding: 1em 0; 31 | width: 100vw; 32 | height: 2.5rem; 33 | margin: 0; 34 | padding: 5px; 35 | display: -webkit-box; 36 | display: -webkit-flex; 37 | display: -moz-box; 38 | display: -ms-flexbox; 39 | display: flex; 40 | -webkit-box-orient: horizontal; 41 | -webkit-box-direction: normal; 42 | -webkit-flex-direction: row; 43 | -moz-box-orient: horizontal; 44 | -moz-box-direction: normal; 45 | -ms-flex-direction: row; 46 | flex-direction: row; 47 | -webkit-box-align: center; 48 | -webkit-align-items: center; 49 | -moz-box-align: center; 50 | -ms-flex-align: center; 51 | align-items: center; 52 | } 53 | 54 | nav h2 { 55 | justify-self: flex-start; 56 | } 57 | 58 | nav span { 59 | justify-self: flex-end; 60 | margin-left: auto; 61 | padding-right: 1em; 62 | } 63 | 64 | main { 65 | margin-top: 2rem; 66 | padding: 1em; 67 | } 68 | 69 | img.circle { 70 | -webkit-border-radius: 100%; 71 | -moz-border-radius: 100%; 72 | border-radius: 100%; 73 | display: inline; 74 | margin-left: 1em; 75 | margin-right: 0.5em; 76 | -webkit-transition: -webkit-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 77 | transition: -webkit-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 78 | -o-transition: -o-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 79 | -moz-transition: transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 80 | -moz-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 81 | transition: transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 82 | transition: transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 83 | -webkit-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 84 | -moz-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 85 | -o-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 86 | -webkit-transform: rotate(0deg); 87 | -moz-transform: rotate(0deg); 88 | -ms-transform: rotate(0deg); 89 | -o-transform: rotate(0deg); 90 | transform: rotate(0deg); 91 | background: black; 92 | padding: 3px; 93 | } 94 | 95 | a, 96 | a:visited:hover { 97 | color: #eba6ff; 98 | } 99 | 100 | a:visited { 101 | color: #cba6f7; 102 | } 103 | 104 | @media (prefers-color-scheme: light) { 105 | body { 106 | background: #eff1f5; 107 | color: #4c4f69; 108 | } 109 | 110 | a, 111 | a:visited:hover { 112 | color: #8839ef; 113 | } 114 | 115 | a:visited { 116 | color: #a16be6; 117 | } 118 | 119 | nav { 120 | background: #e6e9ef; 121 | } 122 | } 123 | 124 | h0 { 125 | font-size: 3em; 126 | font-weight: 700; 127 | margin: 0.67em 0; 128 | display: block; 129 | } 130 | 131 | * { 132 | outline-color: #eba6ff; 133 | } 134 | -------------------------------------------------------------------------------- /public/legacy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | BomberFish 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 26 |
27 |

About me

28 |

29 | I'm a 15 year old (aspiring) software developer from Canada, who 30 | loves tinkering with whatever devices I find. 31 |

32 |

Things on this site

33 | 38 |

Contact

39 | 57 |
58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /public/main.css: -------------------------------------------------------------------------------- 1 | *:not(ul>li>ul, ul>li>ul>li, img, article section *) { 2 | transition: background-color .3s linear, color .1s linear, box-shadow .3s linear; 3 | } 4 | 5 | * { 6 | scrollbar-width: thin; 7 | scrollbar-color: var(--surface0) var(--crust); 8 | } 9 | 10 | ::selection { 11 | background: var(--accent); 12 | color: var(--base); 13 | } 14 | 15 | img::selection { 16 | background: color-mix(in srgb, var(--accent), transparent 40%); 17 | border: 1px solid var(--accent); 18 | } 19 | 20 | ::-webkit-scrollbar, 21 | *::-webkit-scrollbar { 22 | width: 6px; 23 | height: 6px; 24 | } 25 | 26 | ::-webkit-scrollbar-track, 27 | *::-webkit-scrollbar-track { 28 | background: var(--crust) !important; 29 | } 30 | 31 | ::-webkit-scrollbar-track-piece, 32 | *::-webkit-scrollbar-track-piece { 33 | background: var(--crust) !important; 34 | } 35 | 36 | ::-webkit-scrollbar-thumb, 37 | *::-webkit-scrollbar-thumb { 38 | background: var(--surface0) !important; 39 | border-radius: 9999px !important; 40 | margin: 0 2px !important; 41 | transition: background 0.2s !important; 42 | } 43 | 44 | ::-webkit-scrollbar-thumb:hover, 45 | *::-webkit-scrollbar-thumb:hover { 46 | background: var(--surface2) !important; 47 | transition: background 0.2s !important; 48 | } 49 | 50 | ::-webkit-scrollbar-button, 51 | *::-webkit-scrollbar-button, 52 | ::-webkit-scrollbar-corner, 53 | *::-webkit-scrollbar-corner { 54 | display: none !important; 55 | background: transparent !important; 56 | } 57 | 58 | :root { 59 | --crust: #000000; 60 | --base: #0f0e0f; 61 | --mantle: #050405; 62 | --text: #fffaff; 63 | --accent: #eba6ff; 64 | --overlay1: #8a888a; 65 | --surface2: #3d3a3d; 66 | --surface0: #1b181b; 67 | --subtext0: #a6a4a6; 68 | } 69 | 70 | :root { 71 | --perspective: 1000px; 72 | --gridsize: 50px; 73 | --bgmoveX: -19px; 74 | --bgmoveY: -19px; 75 | --bgscale: 1; 76 | } 77 | 78 | li::marker { 79 | color: var(--accent); 80 | } 81 | 82 | .no-js t { 83 | font-weight: 500; 84 | font-size: 1.2rem; 85 | } 86 | 87 | html, 88 | body { 89 | font-family: var(--font-body); 90 | font-size: 100%; 91 | margin: 0; 92 | padding: 0; 93 | } 94 | 95 | body { 96 | display: flex; 97 | justify-content: center; 98 | flex-direction: column; 99 | background: #0f0e0f; 100 | color: #fffaff; 101 | background: var(--crust); 102 | color: var(--text); 103 | overflow-x: hidden; 104 | } 105 | 106 | /* * { 107 | cursor: none!important; 108 | } */ 109 | 110 | body { 111 | color-scheme: dark; 112 | } 113 | 114 | body.Latte { 115 | color-scheme: light; 116 | } 117 | 118 | html:has(body.cool), 119 | body.cool { 120 | display: initial; 121 | padding: 0; 122 | overflow: hidden; 123 | width: 100%; 124 | height: 100%; 125 | background: transparent; 126 | } 127 | 128 | body, 129 | body.cool { 130 | background: var(--mantle); 131 | background-image: radial-gradient(var(--surface0) calc(var(--bgscale) * var(--bgscale) * 1.5px), 132 | transparent 0); 133 | background-size: calc(20px * var(--bgscale)) calc(20px * var(--bgscale)); 134 | background-position: var(--bgmoveX) var(--bgmoveY); 135 | } 136 | 137 | /* 138 | body:not(.cool) { 139 | transition: 0.4s background-position ease 140 | } */ 141 | 142 | #oneko { 143 | z-index: 99999; 144 | } 145 | 146 | .secretEffect { 147 | border: 1px solid var(--overlay1); 148 | border-radius: 2rem; 149 | transition: transform 3s cubic-bezier(0.3, 0, 0.6, 1); 150 | transform-style: preserve-3d; 151 | perspective: 1000px; 152 | transform: rotate3d(1, 1, 1, 360deg); 153 | animation: scale 3s 1; 154 | transform-origin: 50vw 50vh; 155 | } 156 | 157 | @keyframes scale { 158 | 0% { 159 | scale: 1; 160 | } 161 | 162 | 50% { 163 | scale: 0.5; 164 | } 165 | 166 | 100% { 167 | scale: 1; 168 | } 169 | } 170 | 171 | @media (orientation: portrait) { 172 | 173 | main { 174 | display: initial !important; 175 | width: 100vw; 176 | } 177 | 178 | body { 179 | background: var(--base) 180 | } 181 | 182 | #darkreader-warning { 183 | width: 100vw; 184 | border-radius: 0; 185 | } 186 | } 187 | 188 | 189 | 190 | body.Latte .card:hover, 191 | body.Latte .card:focus, 192 | body.Latte .card:focus-visible { 193 | box-shadow: 0 0 20px rgba(24, 24, 37, 0.3); 194 | } 195 | 196 | body { 197 | line-height: 1.5; 198 | } 199 | 200 | #oneko { 201 | opacity: 0; 202 | background-position: -115.5px -115.5px; 203 | transition: 0.5s opacity; 204 | } 205 | -------------------------------------------------------------------------------- /public/modern-normalize.css: -------------------------------------------------------------------------------- 1 | /*! modern-normalize v2.0.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ 2 | *,::after,::before{box-sizing:border-box}html{font-family:system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';line-height:1.15;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4}body{margin:0}hr{height:0;color:inherit}abbr[title]{text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}table{text-indent:0;border-color:inherit}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type='button'],[type='reset'],[type='submit'],button{-webkit-appearance:button}::-moz-focus-inner{border-style:none;padding:0}:-moz-focusring{outline:1px dotted ButtonText}:-moz-ui-invalid{box-shadow:none}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type='search']{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item} -------------------------------------------------------------------------------- /public/oneko-ctp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/oneko-ctp.gif -------------------------------------------------------------------------------- /public/oneko.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/oneko.gif -------------------------------------------------------------------------------- /public/oneko.js: -------------------------------------------------------------------------------- 1 | // oneko.js: https://github.com/adryd325/oneko.js 2 | // @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat 3 | 4 | var _eventHandlers = {}; // somewhere global 5 | 6 | const addListener = (node, event, handler, capture = false) => { 7 | if (!(event in _eventHandlers)) { 8 | _eventHandlers[event] = []; 9 | } 10 | // here we track the events and their nodes (note that we cannot 11 | // use node as Object keys, as they'd get coerced into a string 12 | _eventHandlers[event].push({ 13 | node: node, 14 | handler: handler, 15 | capture: capture, 16 | }); 17 | node.addEventListener(event, handler, capture); 18 | }; 19 | 20 | const removeAllListeners = (targetNode, event) => { 21 | // check if _eventHandlers[event] is defined 22 | if (_eventHandlers[event]) { 23 | // remove listeners from the matching nodes 24 | _eventHandlers[event] 25 | .filter(({ node }) => node === targetNode) 26 | .forEach(({ node, handler, capture }) => 27 | node.removeEventListener(event, handler, capture) 28 | ); 29 | 30 | // update _eventHandlers global 31 | _eventHandlers[event] = _eventHandlers[event].filter( 32 | ({ node }) => node !== targetNode 33 | ); 34 | } 35 | }; 36 | 37 | const mobileRE = 38 | /(android|bb\d+|meego).+mobile|armv7l|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|samsungbrowser.*mobile|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i; 39 | const notMobileRE = /CrOS/; 40 | 41 | const tabletRE = /android|ipad|playbook|silk/i; 42 | 43 | function isMobile(opts) { 44 | if (!opts) opts = {}; 45 | let ua = opts.ua; 46 | if (!ua && typeof navigator !== "undefined") ua = navigator.userAgent; 47 | if (ua && ua.headers && typeof ua.headers["user-agent"] === "string") { 48 | ua = ua.headers["user-agent"]; 49 | } 50 | if (typeof ua !== "string") return false; 51 | 52 | let result = 53 | (mobileRE.test(ua) && !notMobileRE.test(ua)) || 54 | (!!opts.tablet && tabletRE.test(ua)); 55 | 56 | if ( 57 | !result && 58 | opts.tablet && 59 | opts.featureDetect && 60 | navigator && 61 | navigator.maxTouchPoints > 1 && 62 | ua.indexOf("Macintosh") !== -1 && 63 | ua.indexOf("Safari") !== -1 64 | ) { 65 | result = true; 66 | } 67 | 68 | return result; 69 | } 70 | 71 | (function oneko() { 72 | if (isMobile({ tablet: true })) { 73 | return; 74 | } 75 | console.log("ONEKO INIT"); 76 | const nekoRem = 2.75; 77 | const isReducedMotion = 78 | window.matchMedia(`(prefers-reduced-motion: reduce)`) === true || 79 | window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true; 80 | 81 | if (isReducedMotion) return; 82 | 83 | const nekoEl = document.createElement("div"); 84 | 85 | function convertRemToPixels(rem) { 86 | return ( 87 | rem * parseFloat(getComputedStyle(document.documentElement).fontSize) 88 | ); 89 | } 90 | 91 | let i = convertRemToPixels(3.5); 92 | let j = convertRemToPixels(2); 93 | 94 | let nekoPosX = i + j; 95 | let nekoPosY = i + 32; 96 | 97 | let mousePosX = i + j; 98 | let mousePosY = i + 32; 99 | 100 | let frameCount = 0; 101 | let idleTime = 0; 102 | let idleAnimation = null; 103 | let idleAnimationFrame = 0; 104 | 105 | const nekoSpeed = (16 * nekoRem) / 3; 106 | const spriteSets = { 107 | idle: [[-3, -3]], 108 | alert: [[-7, -3]], 109 | scratchSelf: [ 110 | [-5, 0], 111 | [-6, 0], 112 | [-7, 0], 113 | ], 114 | scratchWallN: [ 115 | [0, 0], 116 | [0, -1], 117 | ], 118 | scratchWallS: [ 119 | [-7, -1], 120 | [-6, -2], 121 | ], 122 | scratchWallE: [ 123 | [-2, -2], 124 | [-2, -3], 125 | ], 126 | scratchWallW: [ 127 | [-4, 0], 128 | [-4, -1], 129 | ], 130 | tired: [[-3, -2]], 131 | sleeping: [ 132 | [-2, 0], 133 | [-2, -1], 134 | ], 135 | N: [ 136 | [-1, -2], 137 | [-1, -3], 138 | ], 139 | NE: [ 140 | [0, -2], 141 | [0, -3], 142 | ], 143 | E: [ 144 | [-3, 0], 145 | [-3, -1], 146 | ], 147 | SE: [ 148 | [-5, -1], 149 | [-5, -2], 150 | ], 151 | S: [ 152 | [-6, -3], 153 | [-7, -2], 154 | ], 155 | SW: [ 156 | [-5, -3], 157 | [-6, -1], 158 | ], 159 | W: [ 160 | [-4, -2], 161 | [-4, -3], 162 | ], 163 | NW: [ 164 | [-1, 0], 165 | [-1, -1], 166 | ], 167 | }; 168 | 169 | function init() { 170 | nekoEl.style.transition = "0.5s opacity"; 171 | nekoEl.id = "oneko"; 172 | nekoEl.ariaHidden = true; 173 | nekoEl.style.width = `${nekoRem}rem`; 174 | nekoEl.style.height = `${nekoRem}rem`; 175 | nekoEl.style.backgroundSize = `${convertRemToPixels(nekoRem) * 8}px ${ 176 | convertRemToPixels(nekoRem) * 4 177 | }px`; 178 | nekoEl.style.position = "fixed"; 179 | nekoEl.style.pointerEvents = "none"; 180 | nekoEl.style.imageRendering = "pixelated"; 181 | nekoEl.style.left = `${nekoPosX - 16}px`; 182 | nekoEl.style.top = `${nekoPosY - 16}px`; 183 | nekoEl.style.zIndex = Number.MAX_VALUE; 184 | setTimeout(() => { 185 | nekoEl.style.opacity = 1; 186 | }, 2); 187 | 188 | let nekoFile = "./oneko-ctp.gif"; 189 | const curScript = document.currentScript; 190 | if (curScript && curScript.dataset.cat) { 191 | nekoFile = curScript.dataset.cat; 192 | } 193 | nekoEl.style.backgroundImage = `url(${nekoFile})`; 194 | 195 | document.body.appendChild(nekoEl); 196 | 197 | document.addEventListener("mousemove", function (event) { 198 | if (!document.getElementById("oneko")) { 199 | init(); 200 | } 201 | mousePosX = event.clientX; 202 | mousePosY = event.clientY; 203 | }); 204 | 205 | window.requestAnimationFrame(onAnimationFrame); 206 | } 207 | 208 | let lastFrameTimestamp; 209 | 210 | function onAnimationFrame(timestamp) { 211 | // Stops execution if the neko element is removed from DOM 212 | if (!nekoEl.isConnected) { 213 | return; 214 | } 215 | if (!lastFrameTimestamp) { 216 | lastFrameTimestamp = timestamp; 217 | } 218 | if (timestamp - lastFrameTimestamp > 100) { 219 | lastFrameTimestamp = timestamp; 220 | frame(); 221 | } 222 | window.requestAnimationFrame(onAnimationFrame); 223 | } 224 | 225 | function setSprite(name, frame) { 226 | const sprite = spriteSets[name][frame % spriteSets[name].length]; 227 | nekoEl.style.backgroundPosition = `${ 228 | sprite[0] * convertRemToPixels(nekoRem) 229 | }px ${sprite[1] * convertRemToPixels(nekoRem)}px`; 230 | } 231 | 232 | function resetIdleAnimation() { 233 | idleAnimation = null; 234 | idleAnimationFrame = 0; 235 | } 236 | 237 | function idle() { 238 | idleTime += 1; 239 | 240 | // every ~ 12 seconds 241 | if ( 242 | idleTime > 6 && 243 | Math.floor(Math.random() * 200) == 0 && 244 | idleAnimation == null 245 | ) { 246 | let avalibleIdleAnimations = ["sleeping", "scratchSelf"]; 247 | if (nekoPosX < 16 * nekoRem - 20) { 248 | avalibleIdleAnimations.push("scratchWallW"); 249 | } 250 | if (nekoPosY < 16 * nekoRem - 20) { 251 | avalibleIdleAnimations.push("scratchWallN"); 252 | } 253 | if (nekoPosX > window.innerWidth - (16 * nekoRem - 20)) { 254 | avalibleIdleAnimations.push("scratchWallE"); 255 | } 256 | if (nekoPosY > window.innerHeight - (16 * nekoRem - 20)) { 257 | avalibleIdleAnimations.push("scratchWallS"); 258 | } 259 | console.log(avalibleIdleAnimations); 260 | idleAnimation = 261 | avalibleIdleAnimations[ 262 | Math.floor(Math.random() * avalibleIdleAnimations.length) 263 | ]; 264 | } 265 | 266 | switch (idleAnimation) { 267 | case "sleeping": 268 | if (idleAnimationFrame < 8) { 269 | setSprite("tired", 0); 270 | break; 271 | } 272 | setSprite("sleeping", Math.floor(idleAnimationFrame / 4)); 273 | removeAllListeners(nekoEl, "click"); 274 | addListener(nekoEl, "click", () => { 275 | resetIdleAnimation(); 276 | }); 277 | if (idleAnimationFrame > 192) { 278 | resetIdleAnimation(); 279 | } 280 | break; 281 | case "scratchWallN": 282 | case "scratchWallS": 283 | case "scratchWallE": 284 | case "scratchWallW": 285 | case "scratchSelf": 286 | setSprite(idleAnimation, idleAnimationFrame); 287 | if (idleAnimationFrame > 9) { 288 | resetIdleAnimation(); 289 | } 290 | break; 291 | default: 292 | setSprite("idle", 0); 293 | return; 294 | } 295 | idleAnimationFrame += 1; 296 | } 297 | 298 | function frame() { 299 | frameCount += 1; 300 | const diffX = nekoPosX - mousePosX; 301 | const diffY = nekoPosY - mousePosY; 302 | const distance = Math.sqrt(diffX ** 2 + diffY ** 2); 303 | 304 | if (distance < nekoSpeed || distance < 48) { 305 | idle(); 306 | return; 307 | } 308 | 309 | idleAnimation = null; 310 | idleAnimationFrame = 0; 311 | 312 | if (idleTime > 1) { 313 | setSprite("alert", 0); 314 | // count down after being alerted before moving 315 | idleTime = Math.min(idleTime, 3.5); 316 | idleTime -= 1; 317 | return; 318 | } 319 | 320 | let direction; 321 | direction = diffY / distance > 0.5 ? "N" : ""; 322 | direction += diffY / distance < -0.5 ? "S" : ""; 323 | direction += diffX / distance > 0.5 ? "W" : ""; 324 | direction += diffX / distance < -0.5 ? "E" : ""; 325 | setSprite(direction, frameCount); 326 | 327 | nekoPosX -= (diffX / distance) * nekoSpeed; 328 | nekoPosY -= (diffY / distance) * nekoSpeed; 329 | 330 | nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16); 331 | nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16); 332 | 333 | nekoEl.style.left = `${nekoPosX - 16}px`; 334 | nekoEl.style.top = `${nekoPosY - 16}px`; 335 | } 336 | 337 | init(); 338 | })(); 339 | 340 | // @license-end 341 | -------------------------------------------------------------------------------- /public/proj-thumbnails/SSC2024_Social_Static_16x9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/SSC2024_Social_Static_16x9.jpg -------------------------------------------------------------------------------- /public/proj-thumbnails/Twitter Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/Twitter Banner.png -------------------------------------------------------------------------------- /public/proj-thumbnails/anura.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/anura.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/appcommander.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/appcommander.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/celeste.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/celeste.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/cowabunga.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/cowabunga.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/dssos.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/dssos.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/mandelapro.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/mandelapro.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/picasso.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/picasso.webp -------------------------------------------------------------------------------- /public/proj-thumbnails/placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/proj-thumbnails/placeholder.jpg -------------------------------------------------------------------------------- /public/projects/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Home - bomberfish.ca 11 | 12 | 13 |

My Projects

14 | ❮ Go Back

15 |

MacDirtyCow Apps

16 | Whitelist – Un-blacklist all enterprise-signed apps
17 | COMING SOON: AppCommander – A swiss army knife for your iPhone apps.
18 | ControlConfig – Customize your Control Center. Collab with sneakyf1shy.
19 | Freeload – Remove the 3-app limit on free Developer Accounts.
20 | Mandela Rewritten
21 | Mandela - Customization app
22 | Cowabunga (Various Contributions)
23 |

Other iOS apps

24 | InstaSpring - Instantly respring your device.
25 | COMING SOON: Harmonize - Tune your instrument
26 |

Other Projects

27 | This website (on GitHub)
28 | Orphan Destroyer: Weird Discord Bot
29 | Simple fraction reducer in python. Made for a math assessment.
30 | 3D Test in Godot. Uses gyro on mobile I think.
31 | 32 | -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | legacy.css -------------------------------------------------------------------------------- /public/styles.css: -------------------------------------------------------------------------------- 1 | legacy.css -------------------------------------------------------------------------------- /public/tools/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 17 | 18 | Games - bomberfish.ca 19 | 20 | 21 |

Tools

22 | <- Back

23 | Random Number Generator
25 | Simple Meme Generator
26 | JailbreakMe 2.0 (3.1.2-4.0.1)
28 | TrollGuide - TrollStore Easy Install
30 | IPA Installer test
31 | Signed IPAs for popular jailbreak apps
34 | SnapchatTweakInstaller - Install Snapchat++ on iPhone
37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/_/iPhone1,x_3.1.3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/tools/jailbreakme/_/iPhone1,x_3.1.3.pdf -------------------------------------------------------------------------------- /public/tools/jailbreakme/detector.js: -------------------------------------------------------------------------------- 1 | var agent = navigator.userAgent; 2 | var index = agent.indexOf("OS "); 3 | firmware = agent.slice(index + "OS ".length); 4 | firmware = firmware.slice(0, firmware.indexOf(" ")); 5 | firmware = firmware.replace(/_/g, "."); 6 | var ssi = getSunSpiderInterval(); 7 | window.location = '#' + ssi; 8 | window.page = './_/iPhone1,x_3.1.3.pdf' 9 | _ = new Image(window.page); -------------------------------------------------------------------------------- /public/tools/jailbreakme/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | JailbreakMe 9 | 10 | 11 | 12 |
13 |

JailbreakMe

14 |

by comex (et al.)

15 |
16 |
17 |
18 |
19 |
20 |
JailbreakMe 2.0
21 |
Jailbreak to get tweaks and apps Apple won't allow in the App Store.
Free, legal, safe.
22 |
You should sync with iTunes before using this tool.
23 |
24 | 25 | ❮ Back
26 | More Info ❯ 27 |
28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 |
slide to jailbreak
36 |
37 |
38 |
39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/slider.css: -------------------------------------------------------------------------------- 1 | #unlock1, #unlock2 { 2 | -webkit-border-radius: 13px; 3 | } 4 | 5 | #unlock1 { 6 | border: 1px solid rgba(30, 30, 30, 0.88); 7 | margin-top: 22px; 8 | width: 282px; 9 | margin-left: auto; 10 | margin-right:auto; 11 | } 12 | 13 | #unlock2 { 14 | width: 282px; 15 | height: 50px; 16 | border: 1px solid rgba(100, 100, 100, 0.88); 17 | background-color: rgba(0, 0, 0, 0.77); 18 | position: relative; 19 | } 20 | 21 | #unlock_text { 22 | position: absolute; 23 | 24 | text-align: center; 25 | left: 90px; 26 | top: 12px; 27 | 28 | color: #ffffff; 29 | 30 | font-size: 22px; 31 | font-family: Helvetica; 32 | 33 | -webkit-animation-name: slide; 34 | -webkit-animation-duration: 0.5s; 35 | -webkit-animation-iteration-count: infinite; 36 | -webkit-mask-image: -webkit-gradient(linear, left bottom, right bottom, from(rgba(0,0,0,0.5)), to(rgba(0,0,0,0.5))); 37 | } 38 | 39 | #arrow { 40 | margin-top: 2px; 41 | margin-left: 0px; 42 | position: absolute; 43 | } 44 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/slider.js: -------------------------------------------------------------------------------- 1 | /* slider */ 2 | var step; 3 | var unlock4 = document.getElementById('unlock_text'); 4 | var unlock1 = document.getElementById('unlock1'); 5 | var ival = null; 6 | 7 | // returns a -webkit-gradient string for an infinite gradient 8 | // there is probably a simpler way to do this. 9 | function get_gradient(bits) { 10 | var last = -10000; 11 | var last_alpha = -10000; 12 | var ret = '-webkit-gradient(linear, left bottom, right bottom, '; 13 | var stops = ''; 14 | var on = false; 15 | //console.log('bits='+bits); 16 | for(var i = 0; i < bits.length; i += 2) { 17 | var pos = bits[i]; 18 | var alpha = bits[i+1]; 19 | if(!on && pos >= 0) { 20 | var from = (alpha * (0 - last_pos) - last_alpha * (0 - pos)) / (pos - last_pos); 21 | ret += 'from(rgba(0,0,0,' + from + ')), '; 22 | on = true; 23 | } 24 | if(on) { 25 | if(pos >= 1) { 26 | // last_alpha + ((alpha - last_alpha) / (pos - last_pos)) * (1 - last_pos); 27 | // [1/[pos - last_pos]](last_alpha*pos - last_alpha*last_pos + alpha - alpha*last_pos - last_alpha + last_alpha*last_pos) 28 | // [1/[pos - last_pos]](last_alpha*(pos - 1) + alpha(1 - last_pos)) 29 | 30 | var to = (alpha * (1 - last_pos) - last_alpha * (1 - pos)) / (pos - last_pos); 31 | ret += 'to(rgba(0,0,0,' + to + '))'; 32 | ret += stops; 33 | break; 34 | } 35 | stops += ', color-stop(' + pos + ', rgba(0,0,0,' + alpha + '))'; 36 | } 37 | last_pos = pos; 38 | last_alpha = alpha; 39 | } 40 | return ret + ')'; 41 | } 42 | function turn_on() { 43 | if(ival) return; 44 | step = -0.15; 45 | ival = setInterval(window.stepp = function() { 46 | step = (step + 0.05) % 1.55; 47 | var wleft = step - 0.15; 48 | var wright = step; 49 | var gleft = wleft - 0.2; 50 | var gright = wright + 0.2; 51 | var s = get_gradient([-1000, 0.5, gleft, 0.5, wleft, 0.9, wright, 0.9, gright, 0.5, 1000, 0.5]); 52 | //console.log('step='+step+' s='+s); 53 | unlock4.style.WebkitMaskImage = s; 54 | }, 50); 55 | } 56 | function turn_off() { 57 | if(!ival) return; 58 | clearInterval(ival); 59 | ival = null; 60 | unlock4.style.WebkitMaskImage = ''; 61 | } 62 | 63 | var left = 0; 64 | function set_left(left_) { 65 | left = left_; 66 | slider.style.left = left_ + 'px'; 67 | unlock4.style.opacity = 1.0 - (left / 40); 68 | } 69 | 70 | var startX = null, startLeft, maxLeft; 71 | 72 | var vmismatch = 0; 73 | 74 | slider.ontouchstart = function(e) { 75 | startX = e.targetTouches[0].clientX; 76 | startLeft = left; 77 | turn_off(); 78 | slider.style.WebkitTransitionProperty = ''; 79 | slider.style.WebkitTransitionDuration = '0s'; 80 | unlock4.style.WebkitTransitionProperty = ''; 81 | unlock4.style.WebkitTransitionDuration = '0s'; 82 | maxLeft = slider.parentNode.clientWidth - slider.clientWidth - 5; 83 | return false; 84 | } 85 | slider.ontouchmove = function(e) { 86 | var diff = e.targetTouches[0].clientX - startX; 87 | if(diff < 0) diff = 0; 88 | else if(diff >= maxLeft) diff = maxLeft; 89 | set_left(diff + startLeft); 90 | } 91 | window.ontouchend = function() { 92 | if(startX == null) return; 93 | startX = null; 94 | if(maxLeft - left < 15) { 95 | jailbreak(); 96 | return false; 97 | } 98 | turn_on(); 99 | unlock4.style.WebkitTransitionProperty = 'opacity'; 100 | unlock4.style.WebkitTransitionDuration = '0.5s'; 101 | var left_ = left; 102 | set_left(0); 103 | slider.style.WebkitTransform = 'translateX('+left_+'px)'; 104 | setTimeout(function() { 105 | slider.style.WebkitTransitionProperty = '-webkit-transform'; 106 | slider.style.WebkitTransitionDuration = '0.5s'; 107 | slider.style.WebkitTransform = 'translateX(0px)'; 108 | }, 0); 109 | return false; 110 | } 111 | 112 | set_left(0); 113 | turn_on(); 114 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/star.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | 6 | background: #000; 7 | background-repeat: no-repeat; 8 | 9 | font-family: "Helvetica", sans-serif; 10 | -webkit-user-select: none; 11 | } 12 | 13 | /* iphone portrait */ 14 | @media screen and (min-width: 320px) and (max-width: 320px) { 15 | body { 16 | height: 416px; 17 | background-image: url('wallpaper-iphone.jpg'); 18 | } 19 | } 20 | 21 | /* iphone landscape */ 22 | @media screen and (min-width: 480px) and (max-width: 480px){ 23 | body { 24 | height: 269px; 25 | background-image: url('wallpaper-iphone.jpg'); 26 | } 27 | } 28 | 29 | /* ipad landscape */ 30 | @media screen and (min-width: 1024px) and (max-width: 1024px) { 31 | body { 32 | height: 691px; 33 | background-image: url('wallpaper-ipad.jpg'); 34 | } 35 | } 36 | 37 | /* ipad portrait */ 38 | @media screen and (min-width: 768px) and (max-width: 768px) { 39 | body { 40 | height: 947px; 41 | background-image: url('wallpaper-ipad.jpg'); 42 | } 43 | } 44 | 45 | /* retina! */ 46 | @media screen and (-webkit-min-device-pixel-ratio: 2) { 47 | body { 48 | font-family: "Helvetica Neue", sans-serif; 49 | background-image: url('wallpaper-retina.jpg'); 50 | background-size: 480px 480px; 51 | } 52 | } 53 | 54 | ::selection { background: transparent; } 55 | ::-moz-selection { background: transparent; } 56 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/star.js: -------------------------------------------------------------------------------- 1 | var onetext = '
Oops...
It looks like the installer crashed last time you tried to jailbreak. :(
It might work if you try again.
'; 2 | var twotext = '
It worked!
Tap the Cydia icon to get started with your jailbreak.
(If you restored from a backup, you might be seeing this even though you\'re not jailbroken yet.)
'; 3 | var toooldtext = '
JailbreakMe
Version too old. You need to upgrade using iTunes before you can use this site.
'; 4 | var toonewtext = '
Welp.
Version too new. You need to downgrade to 4.0.1/3.2.1 or earlier (which may be impossible, explanation) before you can use this site.
'; 5 | 6 | function add_animations(elem) { 7 | elem.style.webkitTransitionProperty = "-webkit-transform, opacity"; 8 | elem.style.webkitTransitionDuration = '0.4s, 0.4s'; 9 | } 10 | 11 | function get_progress() { 12 | var then = 0; 13 | var then_progress = 0; 14 | var matches = document.cookie.match(/progress=[0-9]_[0-9\.]+/g); 15 | if(matches) { 16 | for(var i = 0; i < matches.length; i++) { 17 | var m = matches[i]; 18 | var t = parseInt(m.substring(11)); 19 | if(t > then) { 20 | then = t; 21 | then_progress = parseInt(m.substring(9, 10)); 22 | } 23 | } 24 | } 25 | return then_progress; 26 | } 27 | 28 | var my_progress = 0; 29 | window.onload = function() { 30 | //return; // XXX 31 | if(vmismatch == -1) { 32 | document.getElementById('texts').innerHTML = toooldtext; 33 | return; 34 | } else if(vmismatch == 1) { 35 | document.getElementById('texts').innerHTML = toonewtext; 36 | return; 37 | } 38 | var prog = get_progress(); 39 | if(prog == 1) { 40 | document.getElementById('texts').innerHTML = onetext; 41 | } else if(prog == 2) { 42 | document.getElementById('texts').innerHTML = twotext; 43 | } 44 | } 45 | 46 | /*function show_x1() { 47 | document.getElementById('podtext').innerHTML = pt; 48 | document.documentElement.style.WebkitTransitionProperty = '-webkit-transform'; 49 | document.documentElement.style.WebkitTransitionDuration = '0.2s'; 50 | document.documentElement.style.WebkitTransitionTimingFunction = 'linear'; 51 | document.documentElement.style.WebkitTransform = 'translateY(-60px)'; 52 | var pod = document.getElementById('pod'); 53 | pod.style.display = 'block'; 54 | } 55 | 56 | show_x1();*/ 57 | 58 | 59 | function jailbreak() { 60 | var middle = document.getElementsByClassName("middle_wrapper")[0]; 61 | add_animations(middle); 62 | middle.style.opacity = '0'; 63 | 64 | var toolbar = document.getElementsByClassName("tool_bar")[0]; 65 | add_animations(toolbar); 66 | toolbar.style.opacity = '0'; 67 | toolbar.style.webkitTransform = 'translateY(96px)'; 68 | 69 | var topbar = document.getElementsByClassName("top_bar")[0]; 70 | add_animations(topbar); 71 | topbar.style.opacity = '0'; 72 | topbar.style.webkitTransform = 'translateY(-96px)'; 73 | 74 | jailbreak_real(); 75 | } 76 | 77 | //setTimeout(jailbreak, 300); 78 | 79 | function jailbreak_real() { 80 | document.cookie = 'progress=1_' + (new Date().getTime()/1000) + ';domain=wrxck.github.io/jailbreakme;path=/;expires=Sat, 01 Feb 2020 05:00:00 GMT'; 81 | 82 | if(!window.page) { 83 | alert('There was no page... ' + navigator.userAgent); 84 | } 85 | //alert(page);// + " with the interval: " + _sunSpiderInterval); 86 | 87 | var i = document.createElement("iframe"); 88 | i.setAttribute("src", page); 89 | i.style.position = 'absolute'; 90 | i.style.opacity = '0.000001'; 91 | i.style.width = '100px'; 92 | i.style.height = '100px'; 93 | i.style.zIndex = '-9999'; 94 | 95 | document.body.appendChild(i); 96 | 97 | pival = setInterval(function() { 98 | var prog = get_progress(); 99 | if(prog == 2) { 100 | clearInterval(pival); 101 | window.location = 'index.html'; 102 | } else if(prog == 3) { 103 | clearInterval(pival); 104 | window.location = window.location; 105 | } 106 | }, 500); 107 | } 108 | 109 | var old = window.orientation; 110 | function ooc(e) { 111 | if (old != window.orientation) 112 | window.scrollTo(0, 1); 113 | 114 | old = window.orientation; 115 | } 116 | 117 | function loaded() { 118 | setTimeout(function() { 119 | window.scrollTo(0, 1); 120 | }, 10); 121 | } 122 | 123 | window.addEventListener('load', function (e) { loaded(); setInterval(ooc, 100) }, false); 124 | window.addEventListener('onorientationchange', ooc, false); 125 | 126 | 127 | document.addEventListener('touchmove', function(e) { 128 | e.preventDefault(); 129 | }, false); 130 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/sunspider-3dcube.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2007 Apple Inc. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | */ 25 | var _sunSpiderInterval = 0; 26 | function getSunSpiderInterval() { 27 | if(_sunSpiderInterval)return _sunSpiderInterval; 28 | var _sunSpiderStartDate=new Date,Q=[],MTrans=[],MQube=[],I=[],Origin={},Testing={},LoopTimer,DisplArea={};DisplArea.Width=300;DisplArea.Height=300;function DrawLine(a,c){var d=a.V[0],b=c.V[0],e=a.V[1],f=c.V[1],g=Math.abs(b-d),h=Math.abs(f-e),m=d,n=e,k,j,l;if(b>=d)b=d=1;else b=d=-1;if(f>=e)f=e=1;else f=e=-1;if(g>=h){f=d=0;k=g;j=g/2;l=h;g=g}else{e=b=0;k=h;j=h/2;l=g;g=h}g=Math.round(Q.LastPx+g);for(h=Q.LastPx;h=k){j-=k;m+=d;n+=e}m+=b;n+=f}Q.LastPx=g} 29 | function CalcCross(a,c){var d=[];d[0]=a[1]*c[2]-a[2]*c[1];d[1]=a[2]*c[0]-a[0]*c[2];d[2]=a[0]*c[1]-a[1]*c[0];return d}function CalcNormal(a,c,d){for(var b=[],e=[],f=0;f<3;f++){b[f]=a[f]-c[f];e[f]=d[f]-c[f]}b=CalcCross(b,e);a=Math.sqrt(b[0]*b[0]+b[1]*b[1]+b[2]*b[2]);for(f=0;f<3;f++)b[f]/=a;b[3]=1;return b}function CreateP(a,c,d){this.V=[a,c,d,1]} 30 | function MMulti(a,c){for(var d=[[],[],[],[]],b=0,e=0;b<4;b++)for(e=0;e<4;e++)d[b][e]=a[b][0]*c[0][e]+a[b][1]*c[1][e]+a[b][2]*c[2][e]+a[b][3]*c[3][e];return d}function VMulti(a,c){for(var d=[],b=0;b<4;b++)d[b]=a[b][0]*c[0]+a[b][1]*c[1]+a[b][2]*c[2]+a[b][3]*c[3];return d}function VMulti2(a,c){for(var d=[],b=0;b<3;b++)d[b]=a[b][0]*c[0]+a[b][1]*c[1]+a[b][2]*c[2];return d}function MAdd(a,c){for(var d=[[],[],[],[]],b=0,e=0;b<4;b++)for(e=0;e<4;e++)d[b][e]=a[b][e]+c[b][e];return d} 31 | function Translate(a,c,d,b){return MMulti([[1,0,0,c],[0,1,0,d],[0,0,1,b],[0,0,0,1]],a)}function RotateX(a,c){var d=c;d*=Math.PI/180;var b=Math.cos(d);d=Math.sin(d);return MMulti([[1,0,0,0],[0,b,-d,0],[0,d,b,0],[0,0,0,1]],a)}function RotateY(a,c){var d=c;d*=Math.PI/180;var b=Math.cos(d);d=Math.sin(d);return MMulti([[b,0,d,0],[0,1,0,0],[-d,0,b,0],[0,0,0,1]],a)}function RotateZ(a,c){var d=c;d*=Math.PI/180;var b=Math.cos(d);d=Math.sin(d);return MMulti([[b,-d,0,0],[d,b,0,0],[0,0,1,0],[0,0,0,1]],a)} 32 | function DrawQube(){var a=[],c=5;for(Q.LastPx=0;c>-1;c--)a[c]=VMulti2(MQube,Q.Normal[c]);if(a[0][2]<0){if(!Q.Line[0]){DrawLine(Q[0],Q[1]);Q.Line[0]=true}if(!Q.Line[1]){DrawLine(Q[1],Q[2]);Q.Line[1]=true}if(!Q.Line[2]){DrawLine(Q[2],Q[3]);Q.Line[2]=true}if(!Q.Line[3]){DrawLine(Q[3],Q[0]);Q.Line[3]=true}}if(a[1][2]<0){if(!Q.Line[2]){DrawLine(Q[3],Q[2]);Q.Line[2]=true}if(!Q.Line[9]){DrawLine(Q[2],Q[6]);Q.Line[9]=true}if(!Q.Line[6]){DrawLine(Q[6],Q[7]);Q.Line[6]=true}if(!Q.Line[10]){DrawLine(Q[7],Q[3]); 33 | Q.Line[10]=true}}if(a[2][2]<0){if(!Q.Line[4]){DrawLine(Q[4],Q[5]);Q.Line[4]=true}if(!Q.Line[5]){DrawLine(Q[5],Q[6]);Q.Line[5]=true}if(!Q.Line[6]){DrawLine(Q[6],Q[7]);Q.Line[6]=true}if(!Q.Line[7]){DrawLine(Q[7],Q[4]);Q.Line[7]=true}}if(a[3][2]<0){if(!Q.Line[4]){DrawLine(Q[4],Q[5]);Q.Line[4]=true}if(!Q.Line[8]){DrawLine(Q[5],Q[1]);Q.Line[8]=true}if(!Q.Line[0]){DrawLine(Q[1],Q[0]);Q.Line[0]=true}if(!Q.Line[11]){DrawLine(Q[0],Q[4]);Q.Line[11]=true}}if(a[4][2]<0){if(!Q.Line[11]){DrawLine(Q[4],Q[0]);Q.Line[11]= 34 | true}if(!Q.Line[3]){DrawLine(Q[0],Q[3]);Q.Line[3]=true}if(!Q.Line[10]){DrawLine(Q[3],Q[7]);Q.Line[10]=true}if(!Q.Line[7]){DrawLine(Q[7],Q[4]);Q.Line[7]=true}}if(a[5][2]<0){if(!Q.Line[8]){DrawLine(Q[1],Q[5]);Q.Line[8]=true}if(!Q.Line[5]){DrawLine(Q[5],Q[6]);Q.Line[5]=true}if(!Q.Line[9]){DrawLine(Q[6],Q[2]);Q.Line[9]=true}if(!Q.Line[1]){DrawLine(Q[2],Q[1]);Q.Line[1]=true}}Q.Line=[false,false,false,false,false,false,false,false,false,false,false,false];Q.LastPx=0} 35 | function Loop(){if(!(Testing.LoopCount>Testing.LoopMax)){for(var a=String(Testing.LoopCount);a.length<3;)a="0"+a;MTrans=Translate(I,-Q[8].V[0],-Q[8].V[1],-Q[8].V[2]);MTrans=RotateX(MTrans,1);MTrans=RotateY(MTrans,3);MTrans=RotateZ(MTrans,5);MTrans=Translate(MTrans,Q[8].V[0],Q[8].V[1],Q[8].V[2]);MQube=MMulti(MTrans,MQube);for(a=8;a>-1;a--)Q[a].V=VMulti(MTrans,Q[a].V);DrawQube();Testing.LoopCount++;Loop()}} 36 | function Init(a){Origin.V=[150,150,20,1];Testing.LoopCount=0;Testing.LoopMax=50;Testing.TimeMax=0;Testing.TimeAvg=0;Testing.TimeMin=0;Testing.TimeTemp=0;Testing.TimeTotal=0;Testing.Init=false;MTrans=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];MQube=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];I=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];Q[0]=new CreateP(-a,-a,a);Q[1]=new CreateP(-a,a,a);Q[2]=new CreateP(a,a,a);Q[3]=new CreateP(a,-a,a);Q[4]=new CreateP(-a,-a,-a);Q[5]=new CreateP(-a,a,-a);Q[6]=new CreateP(a, 37 | a,-a);Q[7]=new CreateP(a,-a,-a);Q[8]=new CreateP(0,0,0);Q.Edge=[[0,1,2],[3,2,6],[7,6,5],[4,5,1],[4,0,3],[1,5,6]];Q.Normal=[];for(var c=0;c 2 | 3 | 4 | 5 | JailbreakMe 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/ui_normal.css: -------------------------------------------------------------------------------- 1 | .top_bar { 2 | width: 100%; 3 | position: absolute; 4 | top: 0; 5 | height: 96px; 6 | 7 | text-align: center; 8 | background: -webkit-gradient(linear, left top, left bottom, from(#222222), to(#000), color-stop(0.5, rgba(21, 21, 21, 0.7)), color-stop(0.5, rgba(0, 0, 0, 0.7))); 9 | border-bottom: 1px solid #343434; 10 | } 11 | 12 | .top_bar h1 { 13 | margin-top: 5px; 14 | font-size: 52px; 15 | 16 | font-weight: lighter; 17 | 18 | color: #f0f0f0; 19 | text-shadow: #000 0px -2px 0px; 20 | } 21 | 22 | .top_bar h2 { 23 | opacity: 1; 24 | margin-top: -2.20em; 25 | font-weight: normal; 26 | color: #fff; 27 | font-size: 16px; 28 | text-shadow: #000 0px -2px 0px; 29 | } 30 | 31 | .middle_wrapper { 32 | position: absolute; 33 | top: 96px; 34 | width: 100%; 35 | } 36 | 37 | .bubble { 38 | margin-top: 27px; 39 | margin-right: auto; 40 | margin-left: auto; 41 | 42 | /*background-color: rgba(0, 20, 70, 0.796875);*/ 43 | background-color: rgba(55, 55, 55, 0.815624); 44 | border: 3px solid rgba(190, 196, 208, 0.937500); 45 | border-radius: 11px; 46 | 47 | /* fix apple's stupid ipad bug */ 48 | -webkit-border-top-left-radius: 11px; 49 | -webkit-border-top-right-radius: 11px; 50 | -webkit-border-bottom-left-radius: 11px; 51 | -webkit-border-bottom-right-radius: 11px; 52 | 53 | -webkit-box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.4); 54 | text-shadow: #000 0px -1px 0px; 55 | width: 262px; 56 | height: 177px; 57 | 58 | font-size: 16px; 59 | text-align: center; 60 | color: white; 61 | position: relative; 62 | 63 | padding: 0 5px; 64 | 65 | } 66 | 67 | .slider { 68 | width: 59px; 69 | height: 44px; 70 | border-radius: 10px; 71 | -webkit-border-radius: 10px; 72 | -webkit-border-top-left-radius: 10px; 73 | -webkit-border-top-right-radius: 10px; 74 | -webkit-border-bottom-left-radius: 10px; 75 | -webkit-border-bottom-right-radius: 10px; 76 | margin-top: 2px; 77 | margin-left: 2px; 78 | border: 1px solid #ccc; 79 | -webkit-box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.4); 80 | background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#919191)); 81 | position: relative; 82 | /*-webkit-transform: scale(10);*/ 83 | } 84 | .arrow { 85 | position: absolute; 86 | left: 16px; 87 | top: 12px; 88 | } 89 | .shadow { 90 | -webkit-border-radius: 0px; 91 | -webkit-border-bottom-right-radius: 10px; 92 | -webkit-border-bottom-left-radius: 10px; 93 | -moz-border-radius-bottomright: 10px; 94 | -moz-border-radius-bottomleft: 10px; 95 | background-color: rgba(255, 255, 255, 0.4); 96 | position: absolute; 97 | left: 50%; 98 | margin-left: -10px; 99 | width: 20px; 100 | height: 25px; 101 | -webkit-transform: scaleX(13.6) /* 272/20 */ 102 | } 103 | .ttext { 104 | margin-top: 5px; 105 | } 106 | .ttitle { 107 | font-weight: bold; 108 | margin-top: 7px; 109 | margin-bottom: 5px; 110 | } 111 | 112 | .tbottom { 113 | font-weight: bold; 114 | position: absolute; 115 | bottom: 14px; 116 | } 117 | 118 | .ttop { 119 | padding-top: 5px; 120 | } 121 | 122 | .bubble a { 123 | display: block; 124 | width: 100%; 125 | 126 | text-decoration: none; 127 | color: white; 128 | } 129 | 130 | .tool_bar { 131 | position: absolute; 132 | top: 325px; 133 | height: 96px; 134 | width: 100%; 135 | 136 | background: -webkit-gradient(linear, left top, left bottom, from(rgba(34, 34, 34, 0.7)), to(rgba(0, 0, 0, 0.7)), color-stop(0.5, rgba(21, 21, 21, 0.7)), color-stop(0.5, rgba(0, 0, 0, 0.7))); 137 | border-top: 1px solid #343434; 138 | } 139 | 140 | /*#pod { 141 | position: fixed; 142 | top: 416px; 143 | display: none; 144 | height: 60px; 145 | width: 100%; 146 | background: -webkit-gradient(linear, left top, left bottom, from(#777), to(#777), color-stop(0.5, #444)); 147 | } 148 | #podtext { 149 | margin: 5px; 150 | color: #eee; 151 | text-shadow: #000 0px -1px 0px; 152 | } */ 153 | 154 | 155 | /* landscape iphone */ 156 | @media screen and (min-width: 480px) and (max-width: 480px) { 157 | .tool_bar { 158 | top: 195px; 159 | /*height: 20px;*/ 160 | } 161 | #by { 162 | padding-left: 5px; 163 | } 164 | /*#pod { 165 | top: 269px; 166 | }*/ 167 | #unlock1 { 168 | margin-top: 10px !important; 169 | } 170 | .top_bar { 171 | height: 20px; 172 | padding-top: 10px; 173 | padding-bottom: 10px; 174 | } 175 | .top_bar h1 { 176 | display: inline; 177 | font-size: 18px; 178 | } 179 | .top_bar h2 { 180 | display: inline; 181 | font-size: 12px; 182 | } 183 | 184 | .ttop { 185 | display: none; 186 | margin-bottom: -12px; 187 | } 188 | .middle { 189 | width: 430px; 190 | height: 140px; 191 | margin-top: -51px; 192 | } 193 | .tbutton { 194 | width: 212px; 195 | } 196 | .trightbutton { 197 | left: 222px; 198 | } 199 | .tbuttonhighlight { 200 | width: 203px; 201 | } 202 | .shadow { 203 | -webkit-transform: scaleX(22) /* 440/20 */ 204 | } 205 | .ttext { 206 | padding-left: 20px; 207 | padding-right: 20px; 208 | margin-top: 15px; 209 | } 210 | } 211 | 212 | /* iPad portrait */ 213 | @media screen and (min-width: 768px) and (max-width: 768px) { 214 | .tool_bar { 215 | top: 817px; 216 | } 217 | .middle { 218 | margin-top: 260px; 219 | } 220 | } 221 | 222 | /* iPad landscape */ 223 | @media screen and (min-width: 1024px) and (max-width: 1024px) { 224 | .tool_bar { 225 | top: 595px; 226 | } 227 | .middle { 228 | margin-top: 140px; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /public/tools/jailbreakme/wallpaper-retina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/BomberFish.github.io/8ee1040da7cd7452ecc4e6f91eca2e41f1ca67a9/public/tools/jailbreakme/wallpaper-retina.jpg -------------------------------------------------------------------------------- /public/tools/mememaker/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Super Epic Meme Generator™ - bomberfish.ca 9 | 10 | 11 | 12 | 13 | 14 | ❮ Go Back

15 |
16 | 17 |
18 | 19 |
20 | 21 |

27 | 28 |
29 |

30 |
31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /public/tools/mememaker/script.js: -------------------------------------------------------------------------------- 1 | // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 2 | console.log("OwO what's this") 3 | function updateTop() { 4 | var topInput = document.getElementById("topInput"); 5 | var topText = document.getElementById("topText"); 6 | topText.innerHTML = topInput.value; 7 | } 8 | 9 | function updateBottom() { 10 | var bottomInput = document.getElementById("bottomInput"); 11 | var bottomText = document.getElementById("bottomText"); 12 | bottomText.innerHTML = bottomInput.value; 13 | } 14 | 15 | function updateImg() { 16 | var img = document.querySelector('img'); 17 | var file = document.querySelector('input[type=file]').files[0]; 18 | img.src = window.URL.createObjectURL(file); 19 | } 20 | // @license-end -------------------------------------------------------------------------------- /public/tools/mememaker/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | color: black; 4 | font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI Variable', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif 5 | } 6 | 7 | @media (prefers-color-scheme: dark) { 8 | body { 9 | background-color: black; 10 | color: white; 11 | } 12 | } 13 | 14 | .memeText { 15 | background-color: transparent; 16 | font-family: Impact, Haettenschweiler, "Franklin Gothic Bold", Charcoal, "Helvetica Inserat", "Bitstream Vera Sans Bold", "Arial Black", sans-serif; 17 | font-size: 40px; 18 | font-style: normal; 19 | font-variant: normal; 20 | text-align: center; 21 | font-weight: 700; 22 | line-height: 44px; 23 | color: white; 24 | text-shadow: rgba(0, 0, 0, 0.3) 0px 0px 10px; 25 | width: 600px; 26 | position: absolute; 27 | -webkit-text-stroke-width: 1px; 28 | -webkit-text-stroke-color: black; 29 | /* Futureproofing yay */ 30 | text-stroke-width: 1px; 31 | text-stroke-color: black; 32 | } 33 | 34 | #topText { 35 | top: 2%; 36 | left: 50%; 37 | transform: translate(-50%, -2%); 38 | } 39 | 40 | #bottomText { 41 | top: 95%; 42 | left: 50%; 43 | transform: translate(-50%, -95%); 44 | } 45 | 46 | #wrapper { 47 | position: relative; 48 | text-align: center; 49 | color: white; 50 | } -------------------------------------------------------------------------------- /public/typography.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Inter Tight"; 3 | src: local("Inter Tight"),url("https://bomberfish.ca/fonts/display/InterTight-VariableFont_wght.woff2") format("woff2"),url("https://bomberfish.ca/fonts/display/InterTight-VariableFont_wght.woff") format("woff"),url("https://bomberfish.ca/fonts/display/InterTight-VariableFont_wght.ttf") format("truetype"); 4 | font-weight: 100 900; 5 | font-style: normal; 6 | font-display: swap; 7 | } 8 | @font-face { 9 | font-family: "Inter Tight"; 10 | src: local("Inter Tight"),url("https://bomberfish.ca/fonts/display/InterTight-Italic-VariableFont_wght.woff2") format("woff2"),url("https://bomberfish.ca/fonts/display/InterTight-Italic-VariableFont_wght.woff") format("woff"),url("https://bomberfish.ca/fonts/display/InterTight-Italic-VariableFont_wght.ttf") format("truetype"); 11 | font-weight: 100 900; 12 | font-style: italic; 13 | font-display: swap; 14 | } 15 | 16 | @font-face { 17 | font-family: "Adwaita Sans"; 18 | src: local("Adwaita Sans"),url("https://bomberfish.ca/fonts/body/AdwaitaSans-Regular.woff2") format("woff2"),url("https://bomberfish.ca/fonts/body/AdwaitaSans-Regular.woff") format("woff"),url("https://bomberfish.ca/fonts/body/AdwaitaSans-Regular.ttf") format("truetype"); 19 | font-weight: 100 900; 20 | font-style: normal; 21 | font-display: swap; 22 | } 23 | 24 | @font-face { 25 | font-family: "Adwaita Sans"; 26 | src: local("Adwaita Sans"),url("https://bomberfish.ca/fonts/body/AdwaitaSans-Italic.woff2") format("woff2"),url("https://bomberfish.ca/fonts/body/AdwaitaSans-Italic.woff") format("woff"),url("https://bomberfish.ca/fonts/body/AdwaitaSans-Italic.ttf") format("truetype"); 27 | font-weight: 100 900; 28 | font-style: italic; 29 | font-display: swap; 30 | } 31 | 32 | @font-face { 33 | font-family: "Adwaita Mono"; 34 | src: local("Adwaita Mono"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Regular.woff2") format("woff2"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Regular.woff") format("woff"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Regular.ttf") format("truetype"); 35 | font-weight: normal; 36 | font-style: normal; 37 | font-display: swap; 38 | } 39 | 40 | @font-face { 41 | font-family: "Adwaita Mono"; 42 | src: local("Adwaita Mono"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Italic.woff2") format("woff2"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Italic.woff") format("woff"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Italic.ttf") format("truetype"); 43 | font-weight: normal; 44 | font-style: italic; 45 | font-display: swap; 46 | } 47 | 48 | @font-face { 49 | font-family: "Adwaita Mono"; 50 | src: local("Adwaita Mono"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Bold.woff2") format("woff2"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Bold.woff") format("woff"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-Bold.ttf") format("truetype"); 51 | font-weight: bold; 52 | font-style: normal; 53 | font-display: swap; 54 | } 55 | 56 | @font-face { 57 | font-family: "Adwaita Mono"; 58 | src: local("Adwaita Mono"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-BoldItalic.woff2") format("woff2"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-BoldItalic.woff") format("woff"),url("https://bomberfish.ca/fonts/mono/AdwaitaMono-BoldItalic.ttf") format("truetype"); 59 | font-weight: bold; 60 | font-style: italic; 61 | font-display: swap; 62 | } 63 | 64 | @font-face { 65 | font-family: "Material Symbols Rounded"; 66 | font-style: normal; 67 | font-weight: 400; 68 | font-display: swap; 69 | src: url(https://fonts.gstatic.com/s/materialsymbolsrounded/v181/syl0-zNym6YjUruM-QrEh7-nyTnjDwKNJ_190FjpZIvDmUSVOK7BDB_Qb9vUSzq3wzLK-P0J-V_Zs-QtQth3-jOcbTCVpeRL2w5rwZu2rIelXxc.woff2) 70 | format("woff2"); 71 | } 72 | 73 | 74 | @font-face { 75 | font-family: "Apple Garamond"; 76 | src: url("/fonts/serif/AppleGaramond Bk.ttf") format("truetype"),local("Apple Garamond"),local("ITC Garamond Condensed"),local("ITC Garamond"),local("Garamond"),local("Adobe Garamond"),local("EB Garamond"); 77 | font-weight: normal; 78 | font-style: normal; 79 | } 80 | 81 | @font-face { 82 | font-family: "Apple Garamond"; 83 | src: url("/fonts/serif/AppleGaramond BkIt.ttf") format("truetype"),local("Apple Garamond"),local("ITC Garamond Condensed"),local("ITC Garamond"),local("Garamond"),local("Adobe Garamond"),local("EB Garamond"); 84 | font-weight: normal; 85 | font-style: italic; 86 | } 87 | 88 | @font-face { 89 | font-family: "Apple Garamond"; 90 | src: url("/fonts/serif/AppleGaramond Bd.ttf") format("truetype"),local("Apple Garamond"),local("ITC Garamond Condensed"),local("ITC Garamond"),local("Garamond"),local("Adobe Garamond"),local("EB Garamond"); 91 | font-weight: bolder; 92 | font-style: normal; 93 | } 94 | @font-face { 95 | font-family: "Apple Garamond"; 96 | src: url("/fonts/serif/AppleGaramond BdIt.ttf") format("truetype"),local("Apple Garamond"),local("ITC Garamond Condensed"),local("ITC Garamond"),local("Garamond"),local("Adobe Garamond"),local("EB Garamond"); 97 | font-weight: bolder; 98 | font-style: italic; 99 | } 100 | @font-face { 101 | font-family: "Apple Garamond"; 102 | src: url("/fonts/serif/AppleGaramond Lt.ttf") format("truetype"),local("Apple Garamond Light"),local("Apple Garamond"),local("ITC Garamond Condensed"),local("ITC Garamond"),local("Garamond"),local("Adobe Garamond"),local("EB Garamond"); 103 | font-weight: lighter; 104 | font-style: normal; 105 | } 106 | @font-face { 107 | font-family: "Apple Garamond"; 108 | src: url("/fonts/serif/AppleGaramond LtIt.ttf") format("truetype"),local("Apple Garamond Light"),local("Apple Garamond"),local("ITC Garamond Condensed"),local("ITC Garamond"),local("Garamond"),local("Adobe Garamond"),local("EB Garamond"); 109 | font-weight: lighter; 110 | font-style: italic; 111 | } 112 | .material-symbols-rounded { 113 | font-family: "Material Symbols Rounded"; 114 | font-weight: normal; 115 | font-style: normal; 116 | font-size: 24px; 117 | line-height: 1; 118 | letter-spacing: normal; 119 | text-transform: none; 120 | display: inline-block; 121 | white-space: nowrap; 122 | word-wrap: normal; 123 | direction: ltr; 124 | -webkit-font-feature-settings: "liga"; 125 | -webkit-font-smoothing: antialiased; 126 | } 127 | :root { 128 | --font-display: "Inter Tight", "Adwaita Sans", Inter, "SF Pro Display", "SF Pro", 129 | "SF Pro Text", -apple-system, BlinkMacSystemFont, system-ui, 130 | "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", 131 | "Noto Color Emoji", "Segoe UI Emoji"; 132 | --font-body: "Adwaita Sans", Inter, "SF Pro Text", "SF Pro", -apple-system, 133 | BlinkMacSystemFont, system-ui, "Helvetica Neue", Helvetica, Arial, 134 | sans-serif, "Apple Color Emoji", "Noto Color Emoji", "Segoe UI Emoji"; 135 | --font-mono: "Adwaita Mono", "Iosveka", "Source Code Pro", ui-monospace, monospace; 136 | --font-serif: "Apple Garamond", "ITC Garamond Condensed", "ITC Garamond", "Garamond", "Adobe Garamond", "EB Garamond", "Times New Roman", Times, "Linux Libertine", serif; 137 | } 138 | -------------------------------------------------------------------------------- /src/3DSite/3DFloor.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | 3 | export const SuperCoolAndEpicDanceFloor: Component<{}, {}> = function () { 4 | this.css = ` 5 | display: grid; 6 | grid-template-rows: repeat(9, 1fr); 7 | grid-template-columns: repeat(7, 1fr); 8 | 9 | .tile { 10 | background: var(--base); 11 | border: 10px solid var(--mantle); 12 | animation: 1s linear infinite forwards borderPulse; 13 | width: 150px; 14 | height: 150px; 15 | } 16 | 17 | @keyframes borderPulse { 18 | 0% { 19 | border-color: var(--crust); 20 | } 21 | 10%, 20% { 22 | border-color: var(--accent); 23 | } 24 | 7% { 25 | border-color: color-mix(in srgb, white 20%, var(--accent)) 26 | } 27 | 85%, 100% { 28 | border-color: var(--crust); 29 | } 30 | } 31 | 32 | `; 33 | 34 | return ( 35 |
36 | {/* mother of all jank */} 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | 53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | 77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | 93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | 101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | ); 110 | }; 111 | -------------------------------------------------------------------------------- /src/3DSite/ClickWall.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | 3 | export const ClickWall: Component<{}, {}> = function () { 4 | this.css = ` 5 | position: absolute; 6 | top: 0; 7 | left: 0; 8 | bottom: 0; 9 | right: 0; 10 | backdrop-filter: blur(40px); 11 | -webkit-backdrop-filter: blur(40px); 12 | background: rgba(0, 0, 0, 0.8); 13 | z-index: 1000; 14 | display: grid; 15 | place-items: center; 16 | font-size: 3rem; 17 | transition: 0.4s; 18 | 19 | 20 | &.transparent { 21 | background: rgba(255, 255, 255, 0); 22 | backdrop-filter: blur(0px); 23 | -webkit-backdrop-filter: blur(0px); 24 | opacity: 0; 25 | transition: 0.4s; 26 | pointer-events: none; 27 | } 28 | `; 29 | 30 | return ( 31 |
{ 33 | this.root.classList.add("transparent"); 34 | setTimeout(() => { 35 | this.root.remove(); 36 | }, 400); 37 | document.dispatchEvent(new Event("music-restart")); 38 | }} 39 | > 40 |

Click to continue

41 |
42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/3DSite/Screen.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | 3 | export const Screen: Component< 4 | { 5 | x?: number; 6 | y?: number; 7 | z?: number; 8 | rx?: number; 9 | ry?: number; 10 | rz?: number; 11 | width?: number; 12 | height?: number; 13 | autoHeight?: boolean; 14 | nomove?: boolean; 15 | }, 16 | { 17 | children: Element; 18 | } 19 | > = function () { 20 | this.css = ` 21 | position: absolute; 22 | top: 0; 23 | left: 0; 24 | bottom: 0; 25 | 26 | width: ${this.width + "px" || "auto"}; 27 | height: ${this.height + "px" || "auto"}; 28 | 29 | transition: 0.75s transform ease-out; 30 | 31 | transform-origin: 50% 0; 32 | transform: rotateX(calc(var(--rX))) rotateY(calc(var(--rY))) rotateZ(calc(var(--rZ))) translate3d(calc(var(--pX)*var(--gridsize)),calc(var(--pY)*var(--gridsize)),calc(var(--pZ)*var(--gridsize))); 33 | 34 | article { 35 | background: color-mix(in srgb, var(--crust), transparent 20%); 36 | backdrop-filter: blur(10px); 37 | -webkit-backdrop-filter: blur(10px); 38 | padding: 0.75em; 39 | border-radius: 1rem; 40 | border: 1px solid var(--surface0); 41 | transition: 0.3s; 42 | overflow: auto; 43 | height: ${this.autoHeight ? "auto" : "100%"}; 44 | 45 | resize: both; 46 | 47 | &:hover { 48 | border-color: var(--accent); 49 | transition: 0.3s; 50 | } 51 | 52 | width: 100%; 53 | } 54 | `; 55 | 56 | this.x ||= 0; 57 | this.y ||= 0; 58 | this.z ||= 0; 59 | this.rx ||= 0; 60 | this.ry ||= 0; 61 | this.rz ||= 0; 62 | 63 | this.mount = () => { 64 | // works around a bug i will fix later 65 | useChange( 66 | use`--pX: ${this.x || 0}; --pY: ${this.y || 0}; --pZ: ${this.z || 0 67 | }; --rX: ${this.rx || 0}deg; --rY: ${this.ry || 0}deg; --rZ: ${this.rz || 0 68 | }deg`, 69 | (v) => ((this.root as HTMLElement).style.cssText = v) 70 | ); 71 | }; 72 | 73 | return ( 74 |
{ 76 | if (this.rz !== undefined && !this.nomove) { 77 | this.z! += 0.3; 78 | } 79 | }} 80 | on:mouseleave={() => { 81 | if (this.rz !== undefined && !this.nomove) { 82 | this.z! -= 0.3; 83 | } 84 | }} 85 | on:dblclick={() => { 86 | if (this.rz !== undefined && !this.nomove) { 87 | this.rz! += 360; 88 | } 89 | }} 90 | > 91 | {this.children} 92 |
93 | ); 94 | }; 95 | -------------------------------------------------------------------------------- /src/3DSite/ThreeDeeApp.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import ProjectCardDetails from "../Project"; 3 | import { projects } from "../Projects"; 4 | import { ProjectList } from "../ProjectCard"; 5 | import { Screen } from "./Screen"; 6 | import { sharedCSS } from "../CommonCSS"; 7 | import { store } from "../App"; 8 | import { ThreeDeeInfo } from "./ThreeDeeInfo"; 9 | import { FullArticle } from "../SiteContent"; 10 | import { convertRemToPixels } from "../Utils"; 11 | import { SuperCoolAndEpicDanceFloor } from "./3DFloor"; 12 | import { Footer } from "../Footer"; 13 | import { LatestToot } from "../LatestToot"; 14 | 15 | let keys = new Map(); 16 | document.addEventListener("keydown", (e) => { 17 | keys.set(e.key, true); 18 | }); 19 | document.addEventListener("keyup", (e) => { 20 | keys.delete(e.key); 21 | }); 22 | function keydown(key: string) { 23 | return keys.has(key); 24 | } 25 | 26 | function numberInput(text: string, initialValue: number): number { 27 | let ans = prompt(text, initialValue.toString()); 28 | if (ans) { 29 | if (isNaN(+ans)) { 30 | alert("Invalid input"); 31 | return initialValue; 32 | } 33 | return +ans; 34 | } 35 | return initialValue; 36 | } 37 | 38 | export const ThreeDeeApp: Component< 39 | {}, 40 | { 41 | projects: ProjectCardDetails[]; 42 | rotation: number; 43 | x: number; 44 | y: number; 45 | z: number; 46 | r: number; 47 | speed: number; 48 | mult: number; 49 | } 50 | > = function () { 51 | this.projects = projects; 52 | this.rotation = 0; 53 | this.speed = 5; 54 | this.mult = 2; 55 | 56 | this.css = ` 57 | width: 100%; 58 | height: 100%; 59 | perspective: var(--perspective); 60 | 61 | * { 62 | image-rendering: pixelated; 63 | -webkit-image-rendering: pixelated; 64 | } 65 | 66 | camera { 67 | position: absolute; 68 | inset: 0; 69 | pointer-events: all; 70 | perspective: var(--perspective); 71 | backface-visibility: hidden; 72 | contain: layout style size; 73 | width: 100%; 74 | height: 100%; 75 | transform-style: preserve-3d; 76 | will-change: transform; 77 | } 78 | 79 | stage { 80 | position: fixed; 81 | top: 50%!important; 82 | left: 50%!important; 83 | inset: 0; 84 | backface-visibility: shown; 85 | transform-style: preserve-3d; 86 | } 87 | 88 | debug { 89 | font-family: var(--font-display); 90 | font-size: 1.25rem; 91 | font-weight: 600; 92 | margin: 0; 93 | position: fixed; 94 | top: 0; 95 | left: 0; 96 | padding-top: 0.5rem; 97 | padding-left: 0.8rem; 98 | width: 10em; 99 | 100 | border-radius: 0 0 0.5em 0; 101 | 102 | z-index: 999; 103 | 104 | background: color-mix(in srgb, var(--base), transparent 30%); 105 | 106 | a { 107 | display: flex; 108 | text-decoration: none; 109 | font-size: 100%!important; 110 | } 111 | } 112 | input { 113 | accent-color: var(--accent); 114 | } 115 | 116 | } 117 | `; 118 | 119 | this.x = window.innerWidth / -40; 120 | this.y = window.innerHeight / 2; 121 | this.z = -200; 122 | this.r = -18; 123 | 124 | this.mount = () => { 125 | // alert(window.innerWidth) 126 | function easeOutCirc(x: number) { 127 | return Math.sqrt(1 - Math.pow(x - 1, 2)); 128 | } 129 | 130 | console.debug("mount"); 131 | const c = Math.abs( 132 | -(1000 + (2188 /* god this is so jank */ / window.innerWidth) * 200) - 133 | this.z, 134 | ); 135 | console.debug(c); 136 | for (let i = 0; i < c; i++) { 137 | setTimeout( 138 | () => { 139 | // console.debug("z anim tick"); 140 | this.z -= 1; 141 | }, 142 | easeOutCirc(i / c) * 500, 143 | ); 144 | } 145 | 146 | for (let i = 0; i < 30; i++) { 147 | setTimeout( 148 | () => { 149 | // console.debug("rot anim tick"); 150 | this.r += 1; 151 | }, 152 | easeOutCirc(i / 10) * 500, 153 | ); 154 | } 155 | }; 156 | 157 | setInterval(() => { 158 | // console.debug("polling inputs", keys) 159 | 160 | if (keydown("ArrowRight")) { 161 | this.r += 0.5; 162 | let orig = document.documentElement.style 163 | .getPropertyValue("--bgmoveX") 164 | .replace("px", ""); 165 | document.documentElement.style.setProperty( 166 | "--bgmoveX", 167 | `${+orig - 2.5}px`, 168 | ); 169 | } 170 | if (keydown("ArrowLeft")) { 171 | this.r -= 0.5; 172 | let orig = document.documentElement.style 173 | .getPropertyValue("--bgmoveX") 174 | .replace("px", ""); 175 | document.documentElement.style.setProperty( 176 | "--bgmoveX", 177 | `${+orig + 2.5}px`, 178 | ); 179 | } 180 | 181 | if (keydown("ArrowUp")) { 182 | this.y += keydown("Shift") ? this.speed * this.mult : this.speed; 183 | let orig = document.documentElement.style 184 | .getPropertyValue("--bgmoveY") 185 | .replace("px", ""); 186 | document.documentElement.style.setProperty( 187 | "--bgmoveY", 188 | `${+orig + 0.35}px`, 189 | ); 190 | } 191 | if (keydown("ArrowDown")) { 192 | this.y -= keydown("Shift") ? this.speed * this.mult : this.speed; 193 | let orig = document.documentElement.style 194 | .getPropertyValue("--bgmoveY") 195 | .replace("px", ""); 196 | document.documentElement.style.setProperty( 197 | "--bgmoveY", 198 | `${+orig - 0.35}px`, 199 | ); 200 | } 201 | 202 | if (keydown("w")) { 203 | let speed = keydown("Shift") ? this.speed * this.mult : this.speed; 204 | move(0, speed); 205 | // too buggy 206 | // let orig = 207 | // document.documentElement.style.getPropertyValue("--bgscale") || "1"; 208 | // document.documentElement.style.setProperty( 209 | // "--bgscale", 210 | // `${+orig + speed * 0.00015}` 211 | // ); 212 | } 213 | 214 | if (keydown("s")) { 215 | let speed = -(keydown("Shift") ? this.speed * this.mult : this.speed); 216 | move(0, speed); 217 | // let orig = 218 | // document.documentElement.style.getPropertyValue("--bgscale") || "1"; 219 | // document.documentElement.style.setProperty( 220 | // "--bgscale", 221 | // `${+orig + speed * 0.00015}` 222 | // ); 223 | } 224 | 225 | if (keydown("a")) { 226 | let speed = -(keydown("Shift") ? this.speed * this.mult : this.speed); 227 | move(speed, 0); 228 | let orig = document.documentElement.style 229 | .getPropertyValue("--bgmoveX") 230 | .replace("px", ""); 231 | document.documentElement.style.setProperty( 232 | "--bgmoveX", 233 | `${+orig - speed * 0.35}px`, 234 | ); 235 | } 236 | 237 | if (keydown("d")) { 238 | let speed = keydown("Shift") ? this.speed * this.mult : this.speed; 239 | move(speed, 0); 240 | let orig = document.documentElement.style 241 | .getPropertyValue("--bgmoveX") 242 | .replace("px", ""); 243 | document.documentElement.style.setProperty( 244 | "--bgmoveX", 245 | `${+orig - speed * 0.35}px`, 246 | ); 247 | } 248 | }); 249 | 250 | const move = (x: number, z: number) => { 251 | // a bit more complex than neccesary because i'm bad at math 252 | const vel = -Math.hypot(x, z); 253 | const r = Math.atan2(z, x); 254 | const ang = this.r * -(Math.PI / 180) + r + Math.PI / 2; 255 | 256 | this.z += Math.cos(ang) * vel; 257 | this.x += Math.sin(ang) * vel; 258 | }; 259 | 260 | return ( 261 |
262 | 263 | 264 | arrow_back back to 265 | sanity 266 | 267 |

268 | 276 | v * 10)} 282 | on:change={() => { 283 | this.speed = 284 | +(document.getElementById("speed") as HTMLInputElement).value / 285 | 10; 286 | }} 287 | /> 288 |
{ 290 | this.x = numberInput("Enter new position", this.x); 291 | }} 292 | > 293 | x: {use(this.x, (v) => v.toFixed(2))} 294 |
295 |
{ 297 | this.y = numberInput("Enter new position", this.y); 298 | }} 299 | > 300 | y: {use(this.y, (v) => v.toFixed(2))} 301 |
302 |
{ 304 | this.z = numberInput("Enter new position", this.z); 305 | }} 306 | > 307 | z: {use(this.z, (v) => v.toFixed(2))} 308 |
309 |
{ 311 | this.r = numberInput("Enter new position", this.r); 312 | }} 313 | > 314 | r: {use(this.r, (v) => v.toFixed(2))} 315 |
316 |

317 | {use(store.playMusic, (v) => ( 318 |
{ 320 | store.playMusic = !store.playMusic; 321 | store.playMusic !== false 322 | ? document.dispatchEvent(new Event("music-restart")) 323 | : document.dispatchEvent(new Event("end-music")); 324 | }} 325 | > 326 | 327 | {v !== false ? "volume_up" : "volume_off"} 328 | 329 |
330 | ))} 331 |
332 | 337 | 342 | 351 |
352 |

my work

353 | 354 |
355 |
356 | 364 | 365 | 366 | 374 | 375 | 376 | 385 |
386 |
387 |
388 |
389 | 399 | 400 | 401 | 409 |
410 |

Blog

411 | 416 |
417 |
418 | 419 | Vtuber-style logo 424 | 425 | 435 | 436 | 437 |
438 |
439 |
440 | ); 441 | }; 442 | -------------------------------------------------------------------------------- /src/3DSite/ThreeDeeInfo.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import { DesignPhilosophy } from "../SiteContent"; 3 | 4 | export const ThreeDeeInfo: Component<{}, {}> = function () { 5 | return ( 6 |
7 |

about the 3d view!

8 |

quick start guide

9 |
    10 |
  • use WASD to move
  • 11 |
  • use the left/right arrow keys to rotate the camera
  • 12 |
  • use the up/down arrow keys to move up and down
  • 13 |
14 |

check behind you to see my blog!

15 |

how i did it

16 |

17 | the 3d effects are done purely with css 3d transforms! no webgl, canvas 18 | elements, smoke, or mirrors. i use a js framework called{" "} 19 | 20 | dreamland.js 21 | {" "} 22 | for basic reactivity, and to make development easier. you can check out 23 | the source of this page{" "} 24 | 28 | here 29 | 30 | ! 31 |

32 |

FAQ

33 |
Q: why?
34 |
A: because it's cool :3
35 |

36 |
Q: why can't I click any links?
37 |
38 | A: you are using firefox. the firefox devs (in their infinite wisdom) 39 | refuse to implement modern standards correctly. please just use chrome. 40 |
41 |

42 |
Q: this is so cool, why isn't it the default view?
43 |
44 | A: it's an accessibility nightmare, and doesn't even work right on most 45 | browsers (thanks mozilla). also, it's totally broken on phones! (i'm 46 | just too lazy to implement mobile controls :P) 47 |
48 |

49 |
Q: where'd you get the idea?
50 |
51 | i was mainly inspired by{" "} 52 | 53 | velzie's website 54 | 55 | , which pulls off something similar. i actually used some of his code 56 | for this site, so it wouldn't be possible without him. 57 |
58 |

59 | 60 |
61 | ); 62 | }; 63 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | console.log( 2 | "%c\n hello! :3c ", 3 | 'font-family: "IBM Plex Mono", ui-monospace, monospace;font-weight: 900; font-size: 50px;color: #f38ba8; text-shadow: -2px -2px 0 #fab387 , -4px -4px 0 #f9e2af , -6px -6px 0 #a6e3a1 , -8px -8px 0 #94e2d5 , -10px -10px 0 #89b4fa , -12px -12px 0 #b4befe , -14px -14px 0 #cba6f7', 4 | ); 5 | 6 | import "dreamland"; 7 | import { ThreeDeeApp } from "./3DSite/ThreeDeeApp.tsx"; 8 | import ProjectCardDetails from "./Project.ts"; 9 | import { ProjectList } from "./ProjectCard.tsx"; 10 | import { projects } from "./Projects.ts"; 11 | import { ClickWall } from "./3DSite/ClickWall.tsx"; 12 | import { DarkReaderWarning } from "./DarkReaderWarning.tsx"; 13 | import { sharedCSS, articleCSS } from "./CommonCSS.tsx"; 14 | import { updatePage } from "./Themes"; 15 | import { Intro, SiteMap, DesignPhilosophy } from "./SiteContent.tsx"; 16 | import { convertRemToPixels } from "./Utils.ts"; 17 | import { Footer } from "./Footer.tsx"; 18 | import { Nav, TabBar } from "./Navigation.tsx"; 19 | import { LatestToot } from "./LatestToot.tsx"; 20 | import { oled } from "./Themes"; 21 | // import { Cursor } from "./Cursor.tsx"; 22 | 23 | // MARK: THEMING 24 | export let store = $store( 25 | { 26 | theme: oled, 27 | playMusic: false, 28 | }, 29 | { ident: "userOptions", backing: "localstorage", autosave: "auto" }, 30 | ); 31 | 32 | export let globalState = $state({ 33 | freakyMode: false, 34 | }); 35 | 36 | const App: Component< 37 | {}, 38 | { 39 | rotation: number; 40 | projects: ProjectCardDetails[]; 41 | prevMouseX: number; 42 | prevMouseY: number; 43 | prevX: number; 44 | prevY: number; 45 | timeout: boolean; 46 | selectedTab: number; 47 | elements: Element[]; 48 | } 49 | > = function () { 50 | this.prevMouseX = 0; 51 | this.prevMouseY = 0; 52 | this.prevX = 0; 53 | this.prevY = 0; 54 | this.projects = projects; 55 | this.rotation = 0; 56 | this.timeout = false; 57 | this.selectedTab = 0; 58 | this.elements = [ 59 | , 60 |
61 |

my work

62 | 63 |
, 64 | // , 65 |
66 |

latest post

67 | 68 |
, 69 | , 70 | , 71 | ]; 72 | this.css = ` 73 | // background: var(--crust); 74 | color: var(--text); 75 | font-family: var(--font-body); 76 | margin: 0; 77 | padding: 0; 78 | display: flex; 79 | flex-direction: column; 80 | align-items: center; 81 | justify-content: flex-start; 82 | // height: 100vh; 83 | max-width: 100vw; 84 | overflow-x: hidden; 85 | margin-bottom: 1.5rem; 86 | 87 | #content { 88 | background: var(--crust); 89 | width: min(100vw, max(60vw, 800px)); 90 | height: 60vh; 91 | height: min-content; 92 | border-radius: 0 0 1.2rem 1.2rem; 93 | } 94 | 95 | #mainarticle { 96 | height: 100%; 97 | overflow: hidden; 98 | transition: 0.35s, opacity 0.15s; 99 | transition-timing-function: ease; 100 | } 101 | 102 | #content > *:not(#tabs) { 103 | padding-inline: 1rem; 104 | } 105 | 106 | #mainarticle.transparent { 107 | opacity: 0; 108 | transition: 0.35s, opacity 0.15s; 109 | transition-timing-function: ease; 110 | } 111 | 112 | footer { 113 | padding-bottom: 1rem; 114 | } 115 | `; 116 | 117 | function updateSize() { 118 | document.getElementById("mainarticle")!.style.height = 119 | document 120 | .getElementById("mainarticle")! 121 | .children[0]!.getBoundingClientRect().height + 122 | convertRemToPixels(1) + 123 | "px"; 124 | } 125 | 126 | setTimeout(() => { 127 | // console.warn(document.getElementById("content")!.getBoundingClientRect().height); 128 | // console.warn(document.getElementById("mainarticle")!.getBoundingClientRect().height); 129 | updateSize(); 130 | 131 | document.querySelector("main")?.dispatchEvent( 132 | new MouseEvent("move", { 133 | clientX: window.innerWidth, 134 | clientY: window.innerHeight, 135 | }), 136 | ); 137 | // console.warn(document.getElementById("content")!.getBoundingClientRect().height); 138 | // console.warn(document.getElementById("mainarticle")!.getBoundingClientRect().height); 139 | }, 1); 140 | 141 | window.addEventListener("resize", () => { 142 | updateSize(); 143 | }); 144 | 145 | document.addEventListener("force-tab-resize", () => { 146 | updateSize(); 147 | }); 148 | 149 | document.addEventListener("pointermove", (e: PointerEvent) => { 150 | // i feel like this is way more complicated than it needs to be 151 | // console.debug(e.clientX, e.clientY); 152 | if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) { 153 | console.info("user prefers less motion"); 154 | return; 155 | } 156 | const offsetX = this.prevMouseX - e.clientX; 157 | const offsetY = this.prevMouseY - e.clientY; 158 | // console.debug(offsetX, offsetY); 159 | const x = this.prevX - offsetX * 0.06; 160 | const y = this.prevY - offsetY * 0.06; 161 | // console.debug(x, y); 162 | document.documentElement.style.setProperty("--bgmoveX", x + "px"); 163 | document.documentElement.style.setProperty("--bgmoveY", y + "px"); 164 | this.prevMouseX = e.clientX; 165 | this.prevMouseY = e.clientY; 166 | this.prevX = x; 167 | this.prevY = y; 168 | }); 169 | 170 | return ( 171 |
175 | freak ? "Papyrus, cursive!important" : "var(--font-body)", 176 | ), 177 | }} 178 | > 179 |
198 | ); 199 | }; 200 | 201 | // MARK: WINDOW LOAD 202 | window.addEventListener("load", async () => { 203 | document.addEventListener("keydown", (e) => { 204 | if (e.key === "Escape") { 205 | e.preventDefault(); 206 | document.querySelectorAll(".popup").forEach((popup) => { 207 | popup.classList.add("transparent"); 208 | setTimeout(() => { 209 | popup.remove(); 210 | }, 200); 211 | }); 212 | } 213 | }); 214 | 215 | let params = new URL(window.location.href).searchParams; 216 | console.debug(params); 217 | if (params.has("higherdimension")) { 218 | let app; 219 | try { 220 | app = ; 221 | } catch (e) { 222 | (document.querySelector(".no-js")! as HTMLElement).style.display = 223 | "block"; 224 | document.body.style.margin = "2%"; 225 | return; 226 | } 227 | let audio: HTMLAudioElement = new Audio("/epic.ogg"); 228 | audio.loop = true; 229 | 230 | document.addEventListener("end-music", () => { 231 | audio.pause(); 232 | }); 233 | 234 | document.addEventListener("music-restart", () => { 235 | audio.play(); 236 | }); 237 | 238 | // const audioCtx = new AudioContext(); 239 | // const analyser = audioCtx.createAnalyser(); 240 | 241 | // const source = audioCtx.createMediaElementSource(audio); 242 | // console.debug(source); 243 | // source.connect(analyser).connect(audioCtx.destination); 244 | // console.debug(source); 245 | 246 | if (store.playMusic !== false) { 247 | console.info("music start"); 248 | audio.play().catch((e) => { 249 | console.error("could not start audio: " + e); 250 | document.body.appendChild(); 251 | return; 252 | }); 253 | // audioCtx.resume(); 254 | } 255 | 256 | document.getElementById("app")!.replaceWith(app); 257 | document.body.classList.add("cool"); 258 | } else { 259 | let app; 260 | try { 261 | app = ; 262 | } catch (e) { 263 | (document.querySelector(".no-js")! as HTMLElement).style.display = 264 | "block"; 265 | document.body.style.margin = "2%"; 266 | return; 267 | } 268 | let sc = document.createElement("script"); 269 | sc.src = "/oneko.js"; 270 | document.body.appendChild(sc); 271 | document.getElementById("app")!.replaceWith(app); 272 | 273 | var konamiCurrent = 0; 274 | var konamiCode = [ 275 | "ArrowUp", 276 | "ArrowUp", 277 | "ArrowDown", 278 | "ArrowDown", 279 | "ArrowLeft", 280 | "ArrowRight", 281 | "ArrowLeft", 282 | "ArrowRight", 283 | "b", 284 | "a", 285 | ]; 286 | 287 | document.documentElement.addEventListener("keydown", (e: KeyboardEvent) => { 288 | console.debug(e.key); 289 | 290 | if (e.key === konamiCode[konamiCurrent]) { 291 | console.debug("match"); 292 | document.getElementById("k" + konamiCurrent)?.classList.add("active"); 293 | e.preventDefault(); 294 | konamiCurrent++; 295 | 296 | if (konamiCurrent === konamiCode.length) { 297 | konamiCurrent = 0; 298 | window.location.href = 299 | new URL(window.location.href).origin + "/?higherdimension"; 300 | } 301 | } else { 302 | konamiCurrent = 0; 303 | for (let i = 0; i < konamiCode.length; i++) { 304 | document.getElementById("k" + i)?.classList.remove("active"); 305 | } 306 | } 307 | }); 308 | } 309 | 310 | updatePage(); 311 | 312 | // document.body.appendChild(); 313 | 314 | setInterval(() => { 315 | if ( 316 | document.documentElement.getAttribute("data-darkreader-mode") || 317 | document.documentElement.getAttribute("data-darkreader-scheme") 318 | ) { 319 | document.body.appendChild(); 320 | } 321 | }, 1000); 322 | 323 | const fnt = new FontFace( 324 | "Material Symbols Rounded", 325 | "url(https://fonts.gstatic.com/s/materialsymbolsrounded/v181/syl0-zNym6YjUruM-QrEh7-nyTnjDwKNJ_190FjpZIvDmUSVOK7BDB_Qb9vUSzq3wzLK-P0J-V_Zs-QtQth3-jOcbTCVpeRL2w5rwZu2rIelXxc.woff2)", 326 | ); 327 | document.fonts.add(fnt); 328 | fnt.load(); // async load the font to prevent really wanky shit when something that uses it first appears 329 | }); 330 | -------------------------------------------------------------------------------- /src/CommonCSS.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | 3 | export const articleCSS = css` 4 | width: 100%; 5 | ul { 6 | list-style-type: circle; 7 | padding-inline-start: 2rem; 8 | } 9 | 10 | ul ul { 11 | list-style-type: "→ "; 12 | padding-inline-start: 2rem; 13 | } 14 | `; 15 | 16 | export const sharedCSS = css` 17 | a { 18 | text-decoration: none!important; 19 | } 20 | 21 | a:not(:has(img)) { 22 | filter: drop-shadow(0 0 0px transparent); 23 | transition: 0.25s ease filter; 24 | 25 | &:not(nav a) { 26 | border-bottom: 1px dotted var(--accent); 27 | } 28 | 29 | &:hover { 30 | /* border-bottom-style: solid; */ 31 | filter: drop-shadow(0 0 4px var(--accent)) brightness(105%) contrast(110%); 32 | transition: 0.25s ease filter; 33 | } 34 | 35 | &:active { 36 | filter: drop-shadow(0 0 0px var(--accent)) brightness(95%) contrast(90%); 37 | transition: 0.25s ease filter; 38 | } 39 | } 40 | 41 | a, 42 | a:visited:hover { 43 | color: var(--accent); 44 | transition: color 0.1s; 45 | } 46 | 47 | a:visited { 48 | color: color-mix(in srgb, var(--accent) 70%, var(--base) 30%) !; 49 | } 50 | 51 | h1, 52 | h2:not(nav h2) { 53 | font-family: var(--font-serif); 54 | margin-top: 0.1rem; 55 | margin-bottom: 0.6rem; 56 | } 57 | 58 | h1 { 59 | font-weight: normal; 60 | font-size: 2rem; 61 | } 62 | 63 | h2 { 64 | font-weight: lighter; 65 | font-size: 1.5rem; 66 | } 67 | 68 | h3 { 69 | font-weight: lighter; 70 | } 71 | 72 | p, 73 | li { 74 | margin-block: 0.25rem; 75 | line-height: 1.5; 76 | } 77 | 78 | @media (prefers-reduced-motion: reduce) { 79 | .card:hover, 80 | .card:focus, 81 | .card:focus-visible, 82 | .card:active, 83 | .card:active:focus, 84 | nav a { 85 | transform: scale(1) !important; 86 | } 87 | 88 | .popup .inner { 89 | transition: opacity 0.4s !important; 90 | } 91 | } 92 | 93 | .card:focus kbd, 94 | .card:focus-visible kbd { 95 | opacity: 1; 96 | transition: opacity 0.2s; 97 | } 98 | 99 | 100 | * { 101 | outline-color: #cba6f7; 102 | outline-offset: 2px; 103 | } 104 | 105 | subt { 106 | color: var(--subtext0); 107 | } 108 | 109 | kbd { 110 | font-size: 0.85rem; 111 | /* margin: 0.5rem; */ 112 | font-family: var(--font-mono); 113 | color: var(--subtext0); 114 | border: 1px solid var(--subtext0); 115 | padding: 0.15rem 0.6rem; 116 | border-radius: 0.3rem; 117 | } 118 | 119 | @media (orientation: portrait) { 120 | display: initial !important; 121 | width: 100vw; 122 | 123 | #theme-name { 124 | display: none; 125 | } 126 | 127 | #content, 128 | nav { 129 | width: 100vw !important; 130 | } 131 | 132 | .popup .inner { 133 | width: max(100%, 100vw) !important; 134 | height: max(100%, 100vh) !important; 135 | transition: 0.4s cubic-bezier(0.3, 1.2, 0.4, 1); 136 | } 137 | 138 | .popup .inner, 139 | .popup .head { 140 | border-radius: 0; 141 | } 142 | 143 | .popup .inner article { 144 | height: max(100%, 100vh); 145 | } 146 | 147 | .popup .inner .article-inner { 148 | flex-direction: column; 149 | align-items: center; 150 | } 151 | 152 | /* .popup .inner article img { 153 | width: 90vw; 154 | max-width: initial; 155 | height: auto; 156 | justify-self: center; 157 | } */ 158 | } 159 | 160 | subt { 161 | color: var(--subtext0); 162 | font-size: 0.8rem; 163 | line-height: 1rem; 164 | } 165 | 166 | subt kbd { 167 | font-size: 0.7rem; 168 | } 169 | 170 | pre, 171 | code { 172 | font-size: 0.9rem; 173 | font-family: var(--font-mono); 174 | background: var(--base); 175 | } 176 | 177 | code { 178 | border-radius: 0.3rem; 179 | padding-inline: 0.5rem; 180 | padding-block: 0.15rem; 181 | } 182 | 183 | pre { 184 | padding: 1rem; 185 | border-radius: 0.6rem; 186 | overflow-x: auto; 187 | } 188 | 189 | // h1, 190 | // h2, 191 | // h3 { 192 | // font-family: var(--font-display); 193 | // } 194 | 195 | h2 { 196 | font-size: 1.6rem; 197 | margin-bottom: 0; 198 | margin-top: 0.1rem; 199 | } 200 | 201 | // nav h2 { 202 | // font-size: 1.5rem; 203 | // } 204 | 205 | // #content { 206 | // padding-inline: 1rem; 207 | // } 208 | `; 209 | -------------------------------------------------------------------------------- /src/Cursor.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland" 2 | 3 | export const Cursor: Component<{}, { x: number; y: number }> = function () { 4 | this.x = 0 5 | this.y = 0 6 | 7 | this.mount = () => { 8 | document.addEventListener("mousemove", (e) => { 9 | this.x = e.clientX 10 | this.y = e.clientY 11 | }) 12 | } 13 | 14 | this.css = ` 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | pointer-events: none; 19 | z-index: 10000; 20 | width: 20px; 21 | height: 20px; 22 | border-radius: 50%; 23 | backdrop-filter: invert(1); 24 | translate: -10px -10px; 25 | display: flex; 26 | align-items: center; 27 | justify-content: center; 28 | 29 | span { 30 | filter: invert(1); 31 | } 32 | 33 | ` 34 | 35 | return ( 36 |
37 | + 38 |
39 | ) 40 | } -------------------------------------------------------------------------------- /src/DarkReaderWarning.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | 3 | export const DarkReaderWarning: Component<{}, {}> = function () { 4 | this.mount = () => { 5 | setInterval(() => { 6 | if ( 7 | !document.documentElement.getAttribute("data-darkreader-mode") || 8 | !document.documentElement.getAttribute("data-darkreader-scheme") 9 | ) { 10 | this.root.remove(); 11 | } 12 | }); 13 | }; 14 | 15 | function ctpRed(): string { 16 | switch (document.body.classList[0]) { 17 | case "Mocha": 18 | return "#f38ba8"; 19 | case "Macchiato": 20 | return "#ed8796"; 21 | case "Frappe": 22 | return "#e78284"; 23 | case "Latte": 24 | return "#d20f39"; 25 | default: 26 | return "#f00"; 27 | } 28 | } 29 | 30 | this.css = ` 31 | position: fixed; 32 | bottom: 0; 33 | right: 0; 34 | background: var(--surface0); 35 | color: var(--text); 36 | padding: 0.5rem 1rem; 37 | font-family: var(--font-mono); 38 | font-size: 0.8rem; 39 | z-index: 1000; 40 | display: flex; 41 | align-items: center; 42 | justify-content: center; 43 | gap: 0.75rem; 44 | border-top-left-radius: 0.75rem; 45 | 46 | span { 47 | color: ${ctpRed()}; 48 | } 49 | 50 | `; 51 | 52 | return ( 53 |
54 | warning{" "} 55 |

Dark Reader breaks this site.

56 |

Please disable it.

57 |
58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /src/IsMobile.ts: -------------------------------------------------------------------------------- 1 | const mobileRE: RegExp = 2 | /(android|bb\d+|meego).+mobile|armv7l|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|samsungbrowser.*mobile|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i; 3 | const notMobileRE: RegExp = /CrOS/; 4 | 5 | const tabletRE = /android|ipad|playbook|silk/i; 6 | 7 | export default function isMobile(opts: { ua?: string; tablet?: boolean; featureDetect?: boolean } = {}) { 8 | let ua = opts.ua; 9 | if (!ua && typeof navigator !== "undefined") ua = navigator.userAgent; 10 | // if (ua && ua.headers && typeof ua.headers["user-agent"] === "string") { 11 | // ua = ua.headers["user-agent"]; 12 | // } 13 | if (typeof ua !== "string") return false; 14 | 15 | let result = 16 | (mobileRE.test(ua) && !notMobileRE.test(ua)) || 17 | (!!opts.tablet && tabletRE.test(ua)); 18 | 19 | if ( 20 | !result && 21 | opts.tablet && 22 | opts.featureDetect && 23 | navigator && 24 | navigator.maxTouchPoints > 1 && 25 | ua.indexOf("Macintosh") !== -1 && 26 | ua.indexOf("Safari") !== -1 27 | ) { 28 | result = true; 29 | } 30 | 31 | return result; 32 | } -------------------------------------------------------------------------------- /src/LargeProjectView.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import ProjectCardDetails from "./Project.ts"; 3 | import isMobile from "./IsMobile.ts"; 4 | 5 | // TODO: Use 6 | export const LargeProjectView: Component<{ project: ProjectCardDetails }, {}> = 7 | function () { 8 | this.mount = () => { 9 | setTimeout(() => { 10 | this.root.classList.remove("transparent"); 11 | }, 1); 12 | }; 13 | 14 | this.css = ` 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | right: 0; 19 | bottom: 0; 20 | z-index: 100; 21 | width: 100vw; 22 | height: 100vh; 23 | transform: translateZ(100px); 24 | 25 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 26 | 27 | @media (orientation: landscape) { 28 | .inner { 29 | aspect-ratio: 800 / 565; 30 | } 31 | } 32 | 33 | .inner { 34 | background: var(--base); 35 | // background-image: url(${this.project.img}); 36 | min-width: 400px; 37 | width: max(55vw, 50rem); 38 | height: 60vh; 39 | position: absolute; 40 | top: 50%; 41 | left: 50%; 42 | border-radius: 1.25rem; 43 | transform: translateX(-50%) translateY(-50%); 44 | z-index: 100; 45 | border: 0.25px solid var(--surface0); 46 | overflow: hidden; 47 | } 48 | 49 | img { 50 | width: 100%; 51 | height: 100%; 52 | position: absolute; 53 | top: 0; 54 | left: 0; 55 | right: 0; 56 | bottom: 0; 57 | user-select: none; 58 | -webkit-user-drag: none; 59 | -webkit-user-select: none; 60 | object-fit: cover; 61 | z-index: 1; 62 | } 63 | 64 | .inner::before { 65 | content: ""; 66 | position: absolute; 67 | top: 0; 68 | left: 0; 69 | width: 100%; 70 | height: 100%; 71 | backdrop-filter: blur(8px); 72 | mask-image: linear-gradient(rgba(0, 0, 0, 0.2) 30%, rgb(0, 0, 0, 1) 92%); 73 | background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.8) 95%); 74 | z-index: 2; 75 | pointer-events: none; 76 | } 77 | 78 | #title,p { 79 | filter: drop-shadow(0 0 30px rgb(0, 0, 0)); 80 | } 81 | 82 | #title { 83 | font-size: 4rem; 84 | padding-inline: 1.5rem; 85 | font-weight: bold; 86 | text-overflow: ellipsis; 87 | overflow: hidden; 88 | font-family: var(--font-serif); 89 | line-height: 1em; 90 | padding-bottom: 0.25em; 91 | margin-bottom: -0.1em; 92 | width: 100%; 93 | } 94 | 95 | button { 96 | user-select: none; 97 | -webkit-user-drag: none; 98 | -webkit-user-select: none; 99 | width: 36px; 100 | height: 36px; 101 | border-radius: 38px; 102 | background: color-mix(in srgb, var(--surface2) 45%, transparent)!important; 103 | transition: background 0.15s ease; 104 | backdrop-filter: blur(32px); 105 | cursor: pointer; 106 | 107 | overflow: hidden; 108 | transition: transform 0.3s ease; 109 | 110 | .label { 111 | font-size: 12px; 112 | margin-left: 2px; 113 | width: 0px; 114 | overflow: hidden; 115 | transition: width 0.3s ease; 116 | } 117 | } 118 | 119 | button:hover { 120 | background: color-mix(in srgb, var(--surface2) 60%, transparent)!important; 121 | } 122 | 123 | // button:hover .label { 124 | // width: 48px; 125 | // } 126 | 127 | article { 128 | overflow: scroll; 129 | display: grid; 130 | place-items: center; 131 | height: 60vh; 132 | } 133 | 134 | .article-inner { 135 | overflow: scroll; 136 | position: relative; 137 | width: 100%; 138 | height: calc(100% - 7rem); 139 | display: flex; 140 | flex-direction: column; 141 | align-items: flex-start; 142 | overflow: scroll; 143 | align-self: flex-end; 144 | justify-self: center; 145 | z-index: 3; 146 | padding-bottom: 1rem; 147 | } 148 | 149 | .head { 150 | display: flex; 151 | flex-direction: row; 152 | align-items: center; 153 | justify-content: space-between; 154 | width: 100%; 155 | height: 6rem; 156 | position: fixed; 157 | top: -1px; 158 | left: 0; 159 | right: 0; 160 | // background: var(--mantle); 161 | border-radius: 1.25rem 1.25rem 0 0; 162 | margin: 0; 163 | z-index: 1000; 164 | } 165 | 166 | .head > * { 167 | margin: 1.5rem; 168 | } 169 | 170 | .desc { 171 | align-self: flex-start; 172 | font-size: 1.25rem; 173 | margin-inline: 1rem; 174 | } 175 | 176 | .desc p { 177 | margin-left: 0.5rem; 178 | margin-bottom: 1.25rem; 179 | } 180 | 181 | button { 182 | appearance: none; 183 | background: none; 184 | border: none; 185 | // width: 25px; 186 | // height: 25px; 187 | color: var(--text); 188 | display: flex; 189 | align-items: center; 190 | } 191 | 192 | .popup-bg { 193 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 194 | background: rgba(17, 17, 27, 0.4); 195 | position: fixed; 196 | top: 0; 197 | left: 0; 198 | right: 0; 199 | bottom: 0; 200 | z-index: 90; 201 | width: 100vw; 202 | height: 100vh; 203 | backdrop-filter: blur(1.5px); 204 | -webkit-backdrop-filter: blur(1.5px); 205 | } 206 | 207 | kbd { 208 | margin-right: 0.65rem; 209 | } 210 | 211 | .link { 212 | color: var(--text)!important; 213 | 214 | background: color-mix(in srgb, var(--surface0) 60%, transparent)!important; 215 | backdrop-filter: blur(30px); 216 | text-decoration: none; 217 | cursor: pointer; 218 | 219 | border: none!important; 220 | 221 | font-size: 1.2rem; 222 | 223 | border-radius: 0.75rem; 224 | padding: 0.5rem; 225 | 226 | .material-symbols-rounded { 227 | font-size: 1.6rem; 228 | margin-right: 0.5rem; 229 | } 230 | 231 | .link-inner { 232 | display: flex; 233 | align-items: center; 234 | } 235 | 236 | display: flex; 237 | align-items: center; 238 | justify-content: center; 239 | 240 | border: 0.1px solid var(--overlay1)!important; 241 | 242 | transition: color 0.2s; 243 | } 244 | 245 | .link:hover { 246 | transition: color 0.2s; 247 | color: var(--accent); 248 | } 249 | 250 | p.link { 251 | color: var(--subtext0); 252 | //font-style: italic; 253 | border-style: dashed; 254 | cursor: not-allowed; 255 | } 256 | 257 | p.link:hover { 258 | color: var(--subtext0); 259 | } 260 | 261 | .links { 262 | display: grid; 263 | grid-template-columns: repeat(auto-fit, minmax(50%, 1fr)); 264 | gap: 0.5rem; 265 | } 266 | 267 | &.transparent .inner { 268 | top: 100vw; 269 | transition: 0.2s ease-in-out; 270 | } 271 | 272 | &.transparent 273 | { 274 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 275 | } 276 | 277 | .inner { 278 | transition: 0.4s cubic-bezier(0.3, 1.2, 0.4, 1); 279 | } 280 | 281 | &.transparent { 282 | opacity: 0; 283 | pointer-events: none; 284 | } 285 | 286 | &.transparent .popup-bg { 287 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 288 | backdrop-filter: blur(0px); 289 | -webkit-backdrop-filter: blur(0px); 290 | background: rgba(0, 0, 0, 0); 291 | } 292 | `; 293 | 294 | return ( 295 | 366 | ); 367 | }; 368 | -------------------------------------------------------------------------------- /src/N64.tsx: -------------------------------------------------------------------------------- 1 | import 'dreamland' 2 | import isMobile from "./IsMobile.ts"; 3 | 4 | export const MK64Frame: Component<{},{}> = function() { 5 | this.mount = () => { 6 | setTimeout(() => { 7 | this.root.classList.remove("transparent"); 8 | }, 1); 9 | }; 10 | 11 | this.css = ` 12 | position: fixed; 13 | top: 0; 14 | left: 0; 15 | right: 0; 16 | bottom: 0; 17 | z-index: 100; 18 | width: 100vw; 19 | height: 100vh; 20 | transform: translateZ(100px); 21 | 22 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 23 | 24 | .inner { 25 | background: var(--base); 26 | min-width: 400px; 27 | width: max(55vw, 50rem); 28 | height: 60vh; 29 | padding: 1rem; 30 | padding-top: 0; 31 | position: absolute; 32 | top: 50%; 33 | left: 50%; 34 | border-radius: 1.25rem; 35 | transform: translateX(-50%) translateY(-50%); 36 | z-index: 100; 37 | border: 0.25px solid var(--surface0); 38 | overflow: hidden; 39 | } 40 | 41 | img { 42 | width: auto; 43 | max-width: 35vw; 44 | height: 100%; 45 | border-radius: 1.25rem; 46 | user-select: none; 47 | -webkit-user-drag: none; 48 | -webkit-user-select: none; 49 | object-fit: cover; 50 | cursor: pointer; 51 | } 52 | 53 | #title { 54 | font-size: 2.25rem; 55 | margin-block: 0; 56 | font-weight: 600; 57 | text-overflow: ellipsis; 58 | overflow: hidden; 59 | white-space: nowrap; 60 | font-family: var(--font-serif); 61 | } 62 | 63 | button { 64 | user-select: none; 65 | -webkit-user-drag: none; 66 | -webkit-user-select: none; 67 | } 68 | 69 | article { 70 | overflow: scroll; 71 | display: grid; 72 | place-items: center; 73 | height: 60vh; 74 | } 75 | 76 | .article-inner { 77 | overflow: scroll; 78 | position: relative; 79 | width: 100%; 80 | height: calc(100% - 7rem); 81 | display: flex; 82 | flex-direction: row; 83 | align-items: flex-start; 84 | overflow: scroll; 85 | align-self: flex-end; 86 | justify-self: center 87 | } 88 | 89 | .head { 90 | display: flex; 91 | flex-direction: row; 92 | align-items: center; 93 | justify-content: space-between; 94 | width: 100%; 95 | height: 6rem; 96 | position: fixed; 97 | top: -1px; 98 | left: 0; 99 | right: 0; 100 | background: var(--mantle); 101 | border-radius: 1.25rem 1.25rem 0 0; 102 | margin: 0; 103 | z-index: 1000; 104 | } 105 | 106 | .head > * { 107 | margin: 1.5rem; 108 | } 109 | 110 | .desc { 111 | align-self: flex-start; 112 | font-size: 1.25rem; 113 | margin-inline: 1rem; 114 | } 115 | 116 | .desc p { 117 | margin-left: 0.5rem; 118 | margin-bottom: 1.25rem; 119 | } 120 | 121 | button { 122 | appearance: none; 123 | background: none; 124 | border: none; 125 | // width: 25px; 126 | // height: 25px; 127 | color: var(--text); 128 | display: flex; 129 | align-items: center; 130 | } 131 | 132 | .popup-bg { 133 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 134 | background: rgba(17, 17, 27, 0.4); 135 | position: fixed; 136 | top: 0; 137 | left: 0; 138 | right: 0; 139 | bottom: 0; 140 | z-index: 90; 141 | width: 100vw; 142 | height: 100vh; 143 | backdrop-filter: blur(1.5px); 144 | -webkit-backdrop-filter: blur(1.5px); 145 | } 146 | 147 | kbd { 148 | margin-right: 0.65rem; 149 | } 150 | 151 | .link { 152 | color: var(--text)!important; 153 | background: var(--surface0); 154 | text-decoration: none; 155 | cursor: pointer 156 | 157 | font-size: 1.2rem; 158 | 159 | border-radius: 0.5rem; 160 | padding: 0.5rem; 161 | 162 | .material-symbols-rounded { 163 | font-size: 1.6rem; 164 | margin-right: 0.5rem; 165 | } 166 | 167 | .link-inner { 168 | display: flex; 169 | align-items: center; 170 | } 171 | 172 | display: flex; 173 | align-items: center; 174 | justify-content: center; 175 | 176 | border: 0.1px solid var(--overlay1); 177 | 178 | transition: color 0.2s; 179 | } 180 | 181 | .link:hover { 182 | transition: color 0.2s; 183 | color: var(--accent); 184 | } 185 | 186 | p.link { 187 | color: var(--subtext0); 188 | //font-style: italic; 189 | border-style: dashed; 190 | cursor: not-allowed; 191 | } 192 | 193 | p.link:hover { 194 | color: var(--subtext0); 195 | } 196 | 197 | .links { 198 | display: grid; 199 | grid-template-columns: repeat(auto-fit, minmax(50%, 1fr)); 200 | gap: 0.5rem; 201 | } 202 | 203 | &.transparent .inner { 204 | top: 100vw; 205 | transition: 0.2s ease-in-out; 206 | } 207 | 208 | &.transparent 209 | { 210 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 211 | } 212 | 213 | .inner { 214 | transition: 0.4s cubic-bezier(0.3, 1.2, 0.4, 1); 215 | } 216 | 217 | &.transparent { 218 | opacity: 0; 219 | pointer-events: none; 220 | } 221 | 222 | &.transparent .popup-bg { 223 | transition: 0.2s cubic-bezier(0.3, 0, 0.6, 1); 224 | backdrop-filter: blur(0px); 225 | -webkit-backdrop-filter: blur(0px); 226 | background: rgba(0, 0, 0, 0); 227 | } 228 | `; 229 | 230 | return ( 231 | 262 | ); 263 | } 264 | -------------------------------------------------------------------------------- /src/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import { ThemePicker } from "./Themes"; 3 | import { convertRemToPixels } from "./Utils"; 4 | 5 | export const Nav: Component< 6 | {}, 7 | { rotation: number; name: string; nameState: boolean } 8 | > = function () { 9 | this.rotation = 0; 10 | this.name = "BomberFish"; 11 | this.nameState = false; 12 | this.css = ` 13 | background: var(--base); 14 | justify-self: flex-start; 15 | z-index: 100; 16 | padding: 0.25em 1em; 17 | width: min(100vw, max(60vw, 800px)); 18 | height: 3.75rem; 19 | margin: 0; 20 | display: -webkit-box; 21 | display: -webkit-flex; 22 | display: -moz-box; 23 | display: -ms-flexbox; 24 | display: flex; 25 | -webkit-box-orient: horizontal; 26 | -webkit-box-direction: normal; 27 | -webkit-flex-direction: row; 28 | -moz-box-orient: horizontal; 29 | -moz-box-direction: normal; 30 | -ms-flex-direction: row; 31 | flex-direction: row; 32 | -webkit-box-align: center; 33 | -webkit-align-items: center; 34 | -moz-box-align: center; 35 | -ms-flex-align: center; 36 | align-items: center; 37 | 38 | h2 { 39 | justify-self: flex-start; 40 | margin: 0; 41 | user-select: none; 42 | display: flex; 43 | align-items: flex-start; 44 | margin: 0!important; 45 | gap: 0.1rem; 46 | font-weight: normal !important; 47 | font-size: 1.75rem!important; 48 | 49 | /* Nintendo Switch-style 3d spinning text effect */ 50 | & > span { 51 | animation: spin 5s cubic-bezier(0.37, 0, 0.63, 1) infinite; 52 | 53 | &:nth-of-type(1) { 54 | animation-delay: 0.7s; 55 | } 56 | &:nth-of-type(2) { 57 | animation-delay: 0.775s; 58 | } 59 | &:nth-of-type(3) { 60 | animation-delay: 0.85s; 61 | } 62 | &:nth-of-type(4) { 63 | animation-delay: 0.925s; 64 | } 65 | &:nth-of-type(5) { 66 | animation-delay: 1.0s; 67 | } 68 | &:nth-of-type(6) { 69 | animation-delay: 1.075s; 70 | } 71 | &:nth-of-type(7) { 72 | animation-delay: 1.15s; 73 | } 74 | &:nth-of-type(8) { 75 | animation-delay: 1.225s; 76 | } 77 | &:nth-of-type(9) { 78 | animation-delay: 1.3s; 79 | } 80 | &:nth-of-type(10) { 81 | animation-delay: 1.375s; 82 | } 83 | } 84 | 85 | @media (prefers-reduced-motion: reduce) { 86 | & > span { 87 | animation: none; 88 | } 89 | } 90 | } 91 | 92 | img { 93 | -webkit-border-radius: 100%; 94 | -moz-border-radius: 100%; 95 | border-radius: 100%; 96 | display: inline; 97 | margin-left: 0; 98 | margin-right: 0.4em; 99 | cursor: pointer; 100 | -webkit-transition: -webkit-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 101 | transition: -webkit-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 102 | -o-transition: -o-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 103 | -moz-transition: transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 104 | -moz-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 105 | transition: transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 106 | transition: transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 107 | -webkit-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 108 | -moz-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1), 109 | -o-transform 0.5s cubic-bezier(0.3, 0, 0.6, 1); 110 | -webkit-transform: rotate(0deg); 111 | -moz-transform: rotate(0deg); 112 | -ms-transform: rotate(0deg); 113 | -o-transform: rotate(0deg); 114 | transform: rotate(0deg); 115 | background: black; 116 | padding: 2px; 117 | 118 | width: 2.2rem; 119 | height: 2.2rem; 120 | 121 | user-select: none; 122 | -webkit-user-drag: none; 123 | -webkit-user-select: none; 124 | } 125 | 126 | #title { 127 | display: flex; 128 | align-items: center; 129 | justify-self: flex-start; 130 | font-family: var(--font-serif); 131 | font-weight: normal !important; 132 | font-size: 2rem!important; 133 | } 134 | 135 | subt { 136 | font-size: 0.875rem !important; 137 | font-family: var(--font-mono); 138 | margin-top: 0.3em; 139 | } 140 | 141 | #right { 142 | justify-self: flex-end; 143 | margin-left: auto; 144 | display: flex; 145 | align-items: center; 146 | gap: 0.5rem; 147 | 148 | & > a { 149 | text-decoration: none; 150 | transform: scale(1); 151 | color: var(--accent); 152 | font-size: 1rem; 153 | display: flex; 154 | align-items: center; 155 | 156 | user-select: none; 157 | -webkit-user-drag: none; 158 | -webkit-user-select: none; 159 | } 160 | } 161 | 162 | @media (orientation: portrait) { 163 | #bloglink-title { 164 | display: none; 165 | } 166 | } 167 | 168 | @keyframes spin { 169 | 0% { 170 | transform: rotate3d(0, 1, 0, 0deg); 171 | } 172 | 10% { 173 | transform: rotate3d(0, 1, 0, 359deg); 174 | } 175 | 100% { 176 | transform: rotate3d(0, 1, 0, 360deg); 177 | } 178 | } 179 | `; 180 | return ( 181 | 224 | ); 225 | }; 226 | 227 | export const TabBar: Component< 228 | { tabs: string[]; tab?: number }, 229 | { tabInternal: number } 230 | > = function () { 231 | this.tab = 0; 232 | this.tabInternal = 0; 233 | this.css = ` 234 | margin-bottom: 1rem; 235 | padding-block: 0.5rem; 236 | overflow-x: auto; 237 | // background: var(--base); 238 | 239 | @keyframes bounce { 240 | 0%, 100% { 241 | transform: translateY(0.15rem); 242 | } 243 | 40%,60% { 244 | transform: translateY(-0.15rem); 245 | } 246 | } 247 | 248 | div { 249 | display: flex; 250 | justify-content: flex-start; 251 | gap: 1.5rem; 252 | width: 100%; 253 | margin-block: 0.25rem; 254 | // padding-inline: 0.5rem; 255 | } 256 | 257 | button, button.active, button:hover, button:focus, button:active { 258 | transition: all 0.35s cubic-bezier(0, 0.55, 0.45, 1)!important; 259 | } 260 | 261 | button { 262 | font-family: var(--font-display); 263 | font-weight: 500; 264 | font-size: 1.2rem; 265 | 266 | animation: bounce 1s cubic-bezier(0.2, 0, 0.8, 1) infinite; 267 | 268 | &:nth-of-type(7) { 269 | animation-delay: -0.1s; 270 | } 271 | 272 | &:nth-of-type(6) { 273 | animation-delay: -0.2s; 274 | } 275 | 276 | &:nth-of-type(5) { 277 | animation-delay: -0.3s; 278 | } 279 | 280 | &:nth-of-type(4) { 281 | animation-delay: -0.4s; 282 | } 283 | 284 | &:nth-of-type(3) { 285 | animation-delay: -0.5s; 286 | } 287 | 288 | &:nth-of-type(2) { 289 | animation-delay: -0.6s; 290 | } 291 | 292 | &:nth-of-type(1) { 293 | animation-delay: -0.7s; 294 | } 295 | 296 | white-space: nowrap; 297 | 298 | background: transparent; 299 | color: var(--text); 300 | padding: 0; 301 | padding-bottom: 1px; 302 | border: none; 303 | outline: none!important; 304 | cursor: pointer; 305 | 306 | user-select: none; 307 | -webkit-user-drag: none; 308 | -webkit-user-select: none; 309 | 310 | border-bottom: 3.25px solid transparent; 311 | border-radius: 0; 312 | 313 | font-weight: 500; 314 | 315 | color: var(--overlay1); 316 | filter: drop-shadow(0 0 10px transparent); 317 | scale: 1; 318 | 319 | &.selected { 320 | filter: drop-shadow(0 0 10px color-mix(in srgb, var(--accent) 70%, transparent)); 321 | color: var(--text); 322 | padding-bottom: 1px; 323 | border-bottom-color: var(--accent); 324 | font-weight: 700; 325 | } 326 | 327 | &:hover:not(.selected), 328 | &:focus:not(.selected) { 329 | filter: drop-shadow(0 0 10px var(--overlay1)); 330 | border-width: 2px; 331 | padding-bottom: 0.1em; 332 | border-bottom-color: var(--surface2); 333 | } 334 | 335 | &:hover:not(.selected) { 336 | font-weight: 350; 337 | color: var(--subtext0); 338 | } 339 | 340 | &:active:not(.selected) { 341 | scale: 0.95; 342 | font-weight: 250; 343 | border-width: 1.5px; 344 | } 345 | 346 | &:first-of-type { 347 | margin-left: 1rem; 348 | } 349 | 350 | &:last-of-type { 351 | margin-right: 1rem; 352 | } 353 | } 354 | 355 | @media (prefers-reduced-motion: reduce) { 356 | button { 357 | animation: none; 358 | } 359 | } 360 | `; 361 | 362 | return ( 363 |
364 |
365 | {use(this.tabs, (tabs) => 366 | tabs.map((tab, index) => ( 367 | 404 | )), 405 | )} 406 |
407 |
408 | ); 409 | }; 410 | -------------------------------------------------------------------------------- /src/Project.ts: -------------------------------------------------------------------------------- 1 | export default class ProjectCardDetails { 2 | img: string | undefined; 3 | title: string; 4 | blurb: string; 5 | year: number; 6 | largeDesc: string; 7 | links?: { name: string; url: string; icon?: string }[]; 8 | featured?: boolean; 9 | featuredPosition?: number | undefined; 10 | 11 | constructor( 12 | imgURL: string | undefined, 13 | title: string, 14 | blurb: string, 15 | year: number, 16 | largeDesc: string, 17 | links?: { name: string; url: string; icon?: string }[], 18 | featured?: boolean, 19 | featuredPosition?: number | undefined, 20 | ) { 21 | this.img = imgURL; 22 | this.title = title; 23 | this.blurb = blurb; 24 | this.year = year; 25 | this.largeDesc = largeDesc; 26 | this.links = links; 27 | this.featured = featured; 28 | this.featuredPosition = featuredPosition; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/ProjectCard.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import ProjectCardDetails from "./Project"; 3 | import { LargeProjectView } from "./LargeProjectView"; 4 | 5 | export const ProjectCard: Component<{ detail: ProjectCardDetails }, {}> = 6 | function () { 7 | this.css = ` 8 | background: var(--surface0); 9 | width: 100%; 10 | min-height: 280px; 11 | border-radius: 1rem!important; 12 | padding-bottom: 0.2rem; 13 | cursor: pointer; 14 | transform: scale(1); 15 | transition: 0.25s cubic-bezier(0, 0.55, 0.45, 1); 16 | --shadow-color: color-mix(in srgb, var(--accent) 30%, transparent); 17 | box-shadow: 0 0 0px var(--shadow-color); 18 | border: 1px dashed var(--overlay1); 19 | 20 | &:hover { 21 | transform: scale(1.02); 22 | transition: 0.25s cubic-bezier(0, 0.55, 0.45, 1); 23 | box-shadow: 0 0 30px var(--shadow-color); 24 | border-color: var(--accent); 25 | } 26 | 27 | &:focus, 28 | &:focus-visible { 29 | outline: none; 30 | border-color: var(--accent)!important; 31 | border-style: solid!important; 32 | transform: scale(1.05); 33 | transition: 0.25s cubic-bezier(0, 0.55, 0.45, 1); 34 | box-shadow: 0 0 20px var(--shadow-color); 35 | } 36 | 37 | &.active, 38 | &:active:focus { 39 | transform: scale(0.95); 40 | transition: 0.1s cubic-bezier(0, 0.55, 0.45, 1); 41 | } 42 | 43 | transform: translateZ(50px); 44 | 45 | .img-container { 46 | width: 100%; 47 | height: auto; 48 | aspect-ratio: 512 / 277; 49 | } 50 | 51 | img { 52 | user-select: none; 53 | -webkit-user-drag: none; 54 | -webkit-user-select: none; 55 | border-radius: 0.9rem; 56 | width: calc(100% - 1px); 57 | height: calc(100% - 1px); 58 | object-fit: cover; 59 | } 60 | 61 | .img-placeholder { 62 | display: flex; 63 | justify-content: center; 64 | align-items: center; 65 | width: calc(100% - 1px); 66 | height: calc(100% - 1px); 67 | border-radius: 0.9rem; 68 | background: var(--base); 69 | color: var(--subtext0); 70 | span { 71 | font-size: 3rem; 72 | } 73 | } 74 | 75 | .title { 76 | display: flex; 77 | align-items: center; 78 | margin-top: 0.2rem; 79 | font-family: var(--font-body); 80 | overflow: hidden; 81 | text-overflow: ellipsis; 82 | white-space: nowrap; 83 | font-family: 1rem; 84 | } 85 | 86 | p { 87 | margin: 0!important; 88 | margin-top: 0.025rem!important; 89 | } 90 | 91 | .title > span { 92 | font-size: 1.5rem; 93 | font-weight: 600; 94 | margin-right: 0.5rem; 95 | font-family: var(--font-serif); 96 | } 97 | 98 | .detail { 99 | margin: 1rem; 100 | margin-top: 0.1rem; 101 | } 102 | 103 | p { 104 | margin-top: 0.5rem; 105 | margin-bottom: 1rem; 106 | } 107 | 108 | kbd { 109 | position: absolute; 110 | right: 1rem; 111 | top: 75%; 112 | opacity: 0; 113 | transition: opacity 0.2s; 114 | } 115 | `; 116 | 117 | return ( 118 |
{ 121 | document 122 | .querySelector("main")! 123 | .appendChild(); 124 | (document.activeElement as HTMLElement)?.blur(); 125 | }} 126 | on:keydown={(e: KeyboardEvent) => { 127 | if (e.key === "Enter") { 128 | this.root.classList.add("active"); 129 | setTimeout(() => { 130 | var ptr = new PointerEvent("pointerup", { 131 | bubbles: true, 132 | cancelable: true, 133 | }); 134 | this.root.dispatchEvent(ptr); 135 | 136 | this.root.classList.remove("active"); 137 | }, 200); 138 | } 139 | }} 140 | tabindex="0" 141 | > 142 |
150 | {$if( 151 | this.detail.img != undefined, 152 | {this.detail.blurb}, 159 |
160 | broken_image 161 |
, 162 | )} 163 |
164 |
165 |
166 | {this.detail.title} 167 | • ({this.detail.year}) 168 |
169 |

{this.detail.blurb}

170 | 171 |
172 |
173 | ); 174 | }; 175 | 176 | export const ProjectList: Component<{ projects: ProjectCardDetails[] }, {}> = 177 | function () { 178 | this.css = ` 179 | .projects-group { 180 | display: grid; 181 | grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); 182 | grid-gap: 2rem; 183 | place-items: center; 184 | place-content: center; 185 | width: calc(100% - 4rem); 186 | margin: 0 2rem; 187 | } 188 | `; 189 | return ( 190 |
191 |

featured

192 |
193 | {use(this.projects, (projects) => 194 | projects 195 | .filter((project) => project.featured) 196 | .sort((a, b) => (a.featuredPosition || 0) - (b.featuredPosition || 0)) 197 | .map((project) => ), 198 | )} 199 |

200 |

other

201 |
202 | {use(this.projects, (projects) => 203 | projects 204 | .filter((project) => !project.featured) 205 | .map((project) => ), 206 | )} 207 |
208 |
209 | ); 210 | }; 211 | -------------------------------------------------------------------------------- /src/Projects.ts: -------------------------------------------------------------------------------- 1 | import ProjectCardDetails from "./Project"; 2 | 3 | export const projects = [ 4 | // new ProjectCardDetails( 5 | // "/proj-thumbnails/celeste.webp", 6 | // "Webshot", 7 | // "Port of OneShot to WebAssembly", 8 | // 2024, 9 | // "Webshot is a port of OneShot: World Machine Edition to WebAssembly. Like fez-wasm and Webleste, it leverages WebAssembly support in .NET and FNA to run the game in a web browser. Unlike Webleste, I led the porting efforts." 10 | // [ 11 | // { 12 | // name: "GitHub", 13 | // url: "https://github.com/MercuryWorkshop/webshot", 14 | // icon: "code", 15 | // }, 16 | // { 17 | // name: "Demo", 18 | // url: "https://youtube.com/watch?v=dQw4w9WgXcQ", 19 | // icon: "stadia_controller", 20 | // }, 21 | // ], 22 | // ), 23 | new ProjectCardDetails( 24 | undefined, 25 | "MergeFlow", 26 | "Gemini-powered Git merge conflict resolution", 27 | 2025, 28 | "In 2025, me and a small team created MergeFlow, a smart Git merge conflict resolution tool powered by Gemini. It uses advanced AI algorithms to automatically resolve merge conflicts, making the process faster and more efficient. This was created for Hack Canada 2025.", 29 | [ 30 | { 31 | name: "GitHub", 32 | url: "https://github.com/BomberFish/MergeFlow", 33 | icon: "code", 34 | }, 35 | { 36 | name: "Demo Video", 37 | url: "https://www.youtube.com/watch?v=EkSgNgF8pcU", 38 | icon: "play_circle", 39 | }, 40 | ], 41 | ), 42 | new ProjectCardDetails( 43 | undefined, 44 | "Voltaire", 45 | "Snazzy local LLM inference app for iOS", 46 | 2025, 47 | "Voltaire runs popular LLMs, including DeepSeek R1, LLaMa 3, and others, locally on iOS devices.", 48 | [ 49 | { 50 | name: "GitHub", 51 | url: "https://github.com/BomberFish/Voltaire", 52 | icon: "code", 53 | }, 54 | { 55 | name: "Demo Video", 56 | url: "https://youtube.com/watch?v=MipHd-EP9ok", 57 | icon: "play_circle", 58 | }, 59 | ], 60 | ), 61 | new ProjectCardDetails( 62 | undefined, 63 | "fez-wasm", 64 | "Port of Fez (2012) to WebAssembly", 65 | 2025, 66 | "fez-wasm is a WebAssembly port of Fez, a puzzle-platformer first released in 2012. It is a heavy work in progress, with many portions non-functional.", 67 | [ 68 | { 69 | name: "GitHub", 70 | url: "https://github.com/BomberFish/fez-wasm", 71 | icon: "code", 72 | }, 73 | { 74 | name: "Demo", 75 | url: "https://fez.bomberfish.ca", 76 | icon: "stadia_controller", 77 | }, 78 | ], 79 | ), 80 | new ProjectCardDetails( 81 | "/proj-thumbnails/Twitter Banner.png", 82 | "QuickSign", 83 | "iOS Codesigning App", 84 | 2025, 85 | "In late 2024 I joined the development of QuickSign, an app to sign sideloaded iOS apps. A private beta program is slated to begin in May 2025, with a full public release in July.", 86 | [ 87 | { 88 | name: "Website", 89 | url: "https://quicksignteam.github.io", 90 | }, 91 | { 92 | name: "Official Twitter", 93 | url: "https://twitter.com/QuickSigniOS", 94 | icon: "alternate_email", 95 | }, 96 | ], 97 | ), 98 | new ProjectCardDetails( 99 | "/proj-thumbnails/celeste.webp", 100 | "Webleste", 101 | "Port of Celeste (2018) to WebAssembly", 102 | 2024, 103 | "Webleste (formerly celeste-wasm) is a port of the popular platformer game Celeste to WebAssembly. I helped out with it during its initial development in May 2024, and with the development of version 2.0 in April 2025. It is a complete port of the game, using experimental WASM support in .NET and the FNA game engine. I worked with my fellow colleagues at Mercury Workshop to help with porting the game.", 104 | [ 105 | { 106 | name: "Play", 107 | url: "https://celeste.r58playz.dev", 108 | icon: "stadia_controller", 109 | }, 110 | { 111 | name: "Writeup", 112 | url: "https://velzie.rip/blog/celeste-wasm", 113 | icon: "article", 114 | }, 115 | { 116 | name: "GitHub", 117 | url: "https://github.com/MercuryWorkshop/celeste-wasm", 118 | icon: "code", 119 | }, 120 | ], 121 | true, 122 | 2, 123 | ), 124 | new ProjectCardDetails( 125 | "/proj-thumbnails/anura.webp", 126 | "AnuraOS", 127 | "Contributor to webOS since v2.x", 128 | 2024, 129 | "AnuraOS is the next-gen webOS and development environment with full Linux emulation. That is to say, a full desktop environment running locally in your browser, with x86 Linux emulation. I've been making various contributions since March 2024, most of which reworked various parts of the UI. AnuraOS 2.0, which contains my contributions, was released in November 2024.", 130 | [ 131 | { 132 | name: "Use Anura", 133 | url: "https://anura.pro", 134 | }, 135 | ], 136 | true, 137 | 3, 138 | ), 139 | new ProjectCardDetails( 140 | "/proj-thumbnails/mandelapro.webp", 141 | "Mandela Pro", 142 | "Cancelled customization app", 143 | 2024, 144 | "Mandela Pro was a cancelled iOS customization app I created solo in early 2024. It was intended for iOS 16.0-17.0, but was cancelled due to the release of Dopamine 2.0 for 16.x versions and the lack of interest for iOS 17.0.", 145 | [], 146 | ), 147 | new ProjectCardDetails( 148 | "/proj-thumbnails/SSC2024_Social_Static_16x9.jpg", 149 | "Swift Student Challenge", 150 | "2024 Competition Winner", 151 | 2024, 152 | "In early 2024, I won the Swift Student Challenge, a programming competition run by Apple. My winning submission was a carbon footprint calculator.", 153 | [], 154 | true, 155 | 1, 156 | ), 157 | new ProjectCardDetails( 158 | "/proj-thumbnails/dssos.webp", 159 | "dssOS", 160 | "Live dev environment for ChromeOS devices", 161 | 2023, 162 | "dssOS was one of my first projects involving ChromeOS, and was a live development environment for ChromeOS devices. It used a modified diagnostic tool to boot into a Linux chroot, which you could use for programming. dssOS was created in November 2023.", 163 | [ 164 | { 165 | name: "Website", 166 | url: "https://bomberfish.ca/dssOS/" 167 | }, 168 | ], 169 | ), 170 | new ProjectCardDetails( 171 | "/proj-thumbnails/picasso.webp", 172 | "Picasso", 173 | "iOS customization app with 100k+ peak MAU", 174 | 2023, 175 | "Picasso was a customization app for iOS 15.0-17.0, taking advantage of various security vulnerabilities to allow for deep customization. At its peak, it had over 100,000 active users. I worked with sourcelocation to develop it, and it was first released in August 2023 on our own third-party marketplace separate from Apple's App Store.", 176 | [ 177 | { 178 | name: "Source Release", 179 | url: "https://github.com/sourcelocation/Picasso-v3", 180 | icon: "code", 181 | }, 182 | { 183 | name: "Discord", 184 | url: "https://discord.gg/b6bwaDK2VZ", 185 | icon: "chat", 186 | }, 187 | ], 188 | true, 189 | 0, 190 | ), 191 | new ProjectCardDetails( 192 | "/proj-thumbnails/appcommander.webp", 193 | "AppCommander", 194 | "App Manager for iOS 15.0-18.4", 195 | 2023, 196 | "AppCommander (v1.x) was an app manager for iOS 15.0-16.1.2, and allowed the user to perform advanced app management using a sandbox escape that utilized the MacDirtyCow vunerability. Some key features included creating app backups, exporting IPA files, clearing app caches, and more. AppCommander 1.0.0 was released in July 2023.", 197 | [ 198 | { 199 | name: "Source Code (v1)", 200 | url: "https://github.com/BomberFish/AppCommander-legacy", 201 | icon: "code", 202 | }, 203 | { 204 | name: "Source Code (v2)", 205 | url: "https://github.com/BomberFish/AppCommander", 206 | icon: "code", 207 | }, 208 | ], 209 | ), 210 | new ProjectCardDetails( 211 | "/proj-thumbnails/cowabunga.webp", 212 | "Cowabunga MDC", 213 | "Major contributor to customization app", 214 | 2023, 215 | "Cowabunga was a major project I contributed to in 2023. It was a customization app for iOS 14.0-16.1.2, using the MacDirtyCow vunerability to allow for deep customization. My contributions included adding tools such as an enterprise certificate blacklist remover, and a tool to remove the three-app limit on developer-signed apps.", 216 | [ 217 | { 218 | name: "Source Code", 219 | url: "https://github.com/leminlimez/Cowabunga", 220 | icon: "code", 221 | }, 222 | { 223 | name: "Website", 224 | url: "https://cowabun.ga", 225 | }, 226 | { 227 | name: "Discord", 228 | url: "https://discord.gg/cowabunga", 229 | icon: "chat", 230 | }, 231 | ], 232 | ), 233 | ]; 234 | -------------------------------------------------------------------------------- /src/SiteContent.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import { articleCSS } from "./CommonCSS"; 3 | 4 | export const FullArticle: Component<{}, {}> = function () { 5 | return ( 6 |
7 | 8 | 9 | 10 | 11 |
12 | ); 13 | }; 14 | 15 | export const Intro: Component<{}, {}> = function () { 16 | this.css = ` 17 | #kawaii { 18 | width: max(20rem, 30%); 19 | height: auto; 20 | margin: 0 0 1rem 2rem; 21 | } 22 | 23 | .intro { 24 | display: flex; 25 | flex-direction: row; 26 | align-items: center; 27 | justify-content: space-between; 28 | 29 | } 30 | 31 | @media (orientation: portrait), (max-width: 800px) { 32 | #kawaii { 33 | display: none; /* TODO: Figure something out */ 34 | } 35 | } 36 | `; 37 | 38 | return ( 39 |
40 |
41 |
42 | 43 | Vtuber-style logo 44 |
45 |
46 | 47 | 48 |
49 |
50 | ); 51 | }; 52 | 53 | export const IntroSmall: Component<{}, {}> = function () { 54 | this.css = ` 55 | h1 { 56 | font-size: 4rem!important; 57 | cursor: default; 58 | font-weight: bold!important; 59 | } 60 | 61 | // h1>span { 62 | // font-weight: 900!important; 63 | // transition: font-weight 0.25s ease, 64 | // letter-spacing 0.25s ease; 65 | // letter-spacing: 0em; 66 | // } 67 | 68 | // h1:hover > span { 69 | // font-weight: 100!important; 70 | // transition: font-weight 0.25s ease, 71 | // letter-spacing 0.25s ease; 72 | // letter-spacing: 0.09em; 73 | // } 74 | `; 75 | return ( 76 |
77 |

78 | hiya! 👋 79 |

80 |

i'm hariz, a 16 y/o high school student from canada :3

81 |

82 | i sometimes make use(ful|less) projects among other stuff. 83 |

84 |

85 |

86 | as for .* engineering, I'm interested in: 87 |

    88 |
  • embedded systems and hardware
  • 89 |
  • ios development with swiftui
  • 90 |
  • 91 | webdev (as you can tell from this immaculately put-together 92 | portfolio) 93 |
  • 94 |
  • ...and a little bit of security research!
  • 95 |
96 |

97 |
98 | ); 99 | }; 100 | 101 | export const About: Component<{}, {}> = function () { 102 | return ( 103 |
104 |

i'm also...

105 |
    106 |
  • one of the winners of the 2024 swift student challenge
  • 107 |
  • 108 | fluent in the following programming languages: 109 |
      110 |
    • swift (the GOAT)
    • 111 |
    • javascript/typescript
    • 112 |
    • c/c++/objective-c
    • 113 |
    • bash
    • 114 |
    • python
    • 115 |
    116 |
  • 117 |
  • 118 | part of{" "} 119 | 120 | mercury workshop 121 | 122 | , a software development team best known for the{" "} 123 | 124 | sh1mmer 125 | {" "} 126 | chromebook exploit. 127 |
  • 128 |
  • an amateur music producer! stay tuned for more info!
  • 129 |
130 |
131 | ); 132 | }; 133 | 134 | export const Contact: Component<{}, {}> = function () { 135 | return ( 136 |
137 |

get in touch!

138 |
    139 |
  • 140 | 141 | github 142 | 143 |
  • 144 |
  • 145 | 146 | fediverse 147 | 148 |  (@fish@wetdry.world) 149 |
      150 |
    • 151 | if you use bluesky, you can follow  152 | 156 | @fish.wetdry.world.ap.brid.gy 157 | 158 |  to view my posts on the fediverse. please note, this is 159 | usually a few minutes behind! 160 |
    • 161 |
    • 162 | if you use neither of those, you can view my latest post in the 163 | "yapping" tab above. 164 |
    • 165 |
    166 |
  • 167 |
  • 168 | email (hariz@bomberfish.ca) 169 |
      170 |
    • 171 | i might be slow to respond, since i don't check my email often. 172 |
    • 173 |
    • every email address @bomberfish.ca belongs to me.
    • 174 |
    175 |
  • 176 |
  • 177 | discord 178 |
      179 |
    • 180 | @bomberfish (main account) 181 |
    • 182 |
    • @realtimsweeneyepic
    • 183 |
    • @pageprotectionlayer
    • 184 |
    • @securepagetablemonitor
    • 185 |
    186 |
  • 187 | {/*
  • */} 188 | {/* no elon i am not calling it "X" */} 189 | {/* 190 | Twitter (inactive) 191 | 192 |
  • */} 193 |
  • 194 | 199 | matrix 200 | {" "} 201 | (@bomberfish:omada.cafe) 202 |
  • 203 |
  • 204 | signal: @bomberfish.77 205 |
      206 |
    • please only use this if all else fails!
    • 207 |
    208 |
  • 209 |
210 |
211 | ); 212 | }; 213 | 214 | export const SiteMap: Component<{}, {}> = function () { 215 | return ( 216 |
217 |

other things on this site

218 | 229 |
230 | ); 231 | }; 232 | 233 | export const DesignPhilosophy: Component<{}, {}> = function () { 234 | return ( 235 |
236 |

website design philosophy

237 |
    238 |
  • be as keyboard-accessible as possible.
  • 239 |
  • 240 | javascript is not the enemy. take advantage of all the latest gizmos. 241 |
      242 |
    • always include source maps. why not show off your code?
    • 243 |
    244 |
  • 245 |
  • 246 | optimize for size. some people use (and pay for) Canadian cellular 247 | data. 248 |
  • 249 |
  • have some fun! Don't be too bland.
  • 250 |
251 |
252 | ); 253 | }; 254 | -------------------------------------------------------------------------------- /src/Status.ts: -------------------------------------------------------------------------------- 1 | // 100% generated by gpt-4o 2 | 3 | export class Emoji { 4 | shortcode: string; 5 | url: string; 6 | static_url: string; 7 | visible_in_picker: boolean; 8 | 9 | constructor(shortcode: string, url: string, static_url: string, visible_in_picker: boolean) { 10 | this.shortcode = shortcode; 11 | this.url = url; 12 | this.static_url = static_url; 13 | this.visible_in_picker = visible_in_picker; 14 | } 15 | } 16 | 17 | export class Mention { 18 | id: string; 19 | username: string; 20 | url: string; 21 | acct: string; 22 | 23 | constructor(id: string, username: string, url: string, acct: string) { 24 | this.id = id; 25 | this.username = username; 26 | this.url = url; 27 | this.acct = acct; 28 | } 29 | } 30 | 31 | export class Application { 32 | name: string; 33 | website: string | null; 34 | 35 | constructor(name: string, website: string | null) { 36 | this.name = name; 37 | this.website = website; 38 | } 39 | } 40 | 41 | export class Account { 42 | id: string; 43 | username: string; 44 | acct: string; 45 | display_name: string; 46 | locked: boolean; 47 | bot: boolean; 48 | discoverable: boolean | null; 49 | indexable: boolean; 50 | group: boolean; 51 | created_at: string; 52 | note: string; 53 | url: string; 54 | uri: string; 55 | avatar: string; 56 | avatar_static: string; 57 | header: string; 58 | header_static: string; 59 | followers_count: number; 60 | following_count: number; 61 | statuses_count: number; 62 | last_status_at: string; 63 | hide_collections: boolean | null; 64 | noindex: boolean; 65 | emojis: Emoji[]; 66 | roles: string[]; 67 | fields: any[]; 68 | 69 | constructor( 70 | id: string, username: string, acct: string, display_name: string, locked: boolean, bot: boolean, discoverable: boolean | null, 71 | indexable: boolean, group: boolean, created_at: string, note: string, url: string, uri: string, avatar: string, avatar_static: string, 72 | header: string, header_static: string, followers_count: number, following_count: number, statuses_count: number, last_status_at: string, 73 | hide_collections: boolean | null, noindex: boolean, emojis: Emoji[], roles: string[], fields: any[] 74 | ) { 75 | this.id = id; 76 | this.username = username; 77 | this.acct = acct; 78 | this.display_name = display_name; 79 | this.locked = locked; 80 | this.bot = bot; 81 | this.discoverable = discoverable; 82 | this.indexable = indexable; 83 | this.group = group; 84 | this.created_at = created_at; 85 | this.note = note; 86 | this.url = url; 87 | this.uri = uri; 88 | this.avatar = avatar; 89 | this.avatar_static = avatar_static; 90 | this.header = header; 91 | this.header_static = header_static; 92 | this.followers_count = followers_count; 93 | this.following_count = following_count; 94 | this.statuses_count = statuses_count; 95 | this.last_status_at = last_status_at; 96 | this.hide_collections = hide_collections; 97 | this.noindex = noindex; 98 | this.emojis = emojis; 99 | this.roles = roles; 100 | this.fields = fields; 101 | } 102 | } 103 | 104 | export class Status { 105 | id: string; 106 | created_at: string; 107 | in_reply_to_id: string | null; 108 | in_reply_to_account_id: string | null; 109 | sensitive: boolean; 110 | spoiler_text: string; 111 | visibility: string; 112 | language: string; 113 | uri: string; 114 | url: string; 115 | replies_count: number; 116 | reblogs_count: number; 117 | favourites_count: number; 118 | edited_at: string | null; 119 | conversation_id: number; 120 | favourited: boolean; 121 | reblogged: boolean; 122 | muted: boolean; 123 | bookmarked: boolean; 124 | pinned: boolean; 125 | local_only: boolean; 126 | content: string; 127 | filtered: any[]; 128 | reblog: any; 129 | application: Application; 130 | account: Account; 131 | media_attachments: any[]; 132 | mentions: Mention[]; 133 | tags: any[]; 134 | emojis: Emoji[]; 135 | reactions: any[]; 136 | card: any; 137 | poll: any; 138 | 139 | constructor( 140 | id: string, created_at: string, in_reply_to_id: string | null, in_reply_to_account_id: string | null, sensitive: boolean, spoiler_text: string, 141 | visibility: string, language: string, uri: string, url: string, replies_count: number, reblogs_count: number, favourites_count: number, 142 | edited_at: string | null, conversation_id: number, favourited: boolean, reblogged: boolean, muted: boolean, bookmarked: boolean, pinned: boolean, 143 | local_only: boolean, content: string, filtered: any[], reblog: any, application: Application, account: Account, media_attachments: any[], 144 | mentions: Mention[], tags: any[], emojis: Emoji[], reactions: any[], card: any, poll: any 145 | ) { 146 | this.id = id; 147 | this.created_at = created_at; 148 | this.in_reply_to_id = in_reply_to_id; 149 | this.in_reply_to_account_id = in_reply_to_account_id; 150 | this.sensitive = sensitive; 151 | this.spoiler_text = spoiler_text; 152 | this.visibility = visibility; 153 | this.language = language; 154 | this.uri = uri; 155 | this.url = url; 156 | this.replies_count = replies_count; 157 | this.reblogs_count = reblogs_count; 158 | this.favourites_count = favourites_count; 159 | this.edited_at = edited_at; 160 | this.conversation_id = conversation_id; 161 | this.favourited = favourited; 162 | this.reblogged = reblogged; 163 | this.muted = muted; 164 | this.bookmarked = bookmarked; 165 | this.pinned = pinned; 166 | this.local_only = local_only; 167 | this.content = content; 168 | this.filtered = filtered; 169 | this.reblog = reblog; 170 | this.application = application; 171 | this.account = account; 172 | this.media_attachments = media_attachments; 173 | this.mentions = mentions; 174 | this.tags = tags; 175 | this.emojis = emojis; 176 | this.reactions = reactions; 177 | this.card = card; 178 | this.poll = poll; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/Themes.tsx: -------------------------------------------------------------------------------- 1 | import "dreamland"; 2 | import { store } from "./App"; 3 | 4 | export const oled = { 5 | name: "OLED", 6 | shortName: "OLED", 7 | text: "#fbf9fb", 8 | overlay1: "#6e6b6e", 9 | surface2: "#413f41", 10 | surface0: "#1d1a1c", 11 | subtext0: "#a6a4a6", 12 | base: "#110f11", 13 | mantle: "#0b0a0b", 14 | crust: "#000000", 15 | accent: "#eba6ff", 16 | } 17 | 18 | export const mocha = { 19 | name: "Catppuccin Mocha", 20 | shortName: "Mocha", 21 | text: "#cdd6f4", 22 | overlay1: "#7f849c", 23 | surface2: "#585b70", 24 | surface0: "#313244", 25 | subtext0: "#a6adc8", 26 | base: "#1e1e2e", 27 | mantle: "#181825", 28 | crust: "#11111b", 29 | accent: "#cba6f7", 30 | }; 31 | 32 | 33 | export const macchiato = { 34 | name: "Catppuccin Macchiato", 35 | shortName: "Macchiato", 36 | text: "#cad3f5", 37 | overlay1: "#8087a2", 38 | surface2: "#5b6078", 39 | surface0: "#363a4f", 40 | subtext0: "#a5adcb", 41 | base: "#24273a", 42 | mantle: "#1e2030", 43 | crust: "#181926", 44 | accent: "#c6a0f6", 45 | }; 46 | 47 | 48 | export const frappe = { 49 | name: "Catppuccin Frappé", 50 | shortName: "Frappe", 51 | text: "#c6d0f5", 52 | overlay1: "#838ba7", 53 | surface2: "#626880", 54 | surface0: "#414559", 55 | subtext0: "#a5adce", 56 | base: "#303446", 57 | mantle: "#292c3c", 58 | crust: "#232634", 59 | accent: "#ca9ee6", 60 | }; 61 | 62 | 63 | export const latte = { 64 | name: "Catppuccin Latte", 65 | shortName: "Latte", 66 | text: "#4c4f69", 67 | overlay1: "#8c8fa1", 68 | surface2: "#acb0be", 69 | surface0: "#ccd0da", 70 | subtext0: "#6c6f85", 71 | base: "#eff1f5", 72 | mantle: "#e6e9ef", 73 | crust: "#dce0e8", 74 | accent: "#8839ef", 75 | }; 76 | 77 | export function updatePage() { 78 | var root = document.documentElement; 79 | console.debug(store.theme); 80 | if ( 81 | store.theme.name == undefined || 82 | store.theme.shortName == undefined || 83 | store.theme.text == undefined || 84 | store.theme.overlay1 == undefined || 85 | store.theme.surface2 == undefined || 86 | store.theme.surface0 == undefined || 87 | store.theme.subtext0 == undefined || 88 | store.theme.base == undefined || 89 | store.theme.crust == undefined || 90 | store.theme.accent == undefined 91 | ) { 92 | console.warn("theme is corrupted or out of date, resetting"); 93 | store.theme = oled; 94 | updatePage(); 95 | } 96 | 97 | document.head 98 | .querySelector("meta[name=theme-color]")! 99 | .setAttribute("content", store.theme.base); 100 | root.style.setProperty("--text", store.theme.text); 101 | root.style.setProperty("--overlay1", store.theme.overlay1); 102 | root.style.setProperty("--surface2", store.theme.surface2); 103 | root.style.setProperty("--surface0", store.theme.surface0); 104 | root.style.setProperty("--subtext0", store.theme.subtext0); 105 | root.style.setProperty("--base", store.theme.base); 106 | root.style.setProperty("--mantle", store.theme.mantle); 107 | root.style.setProperty("--crust", store.theme.crust); 108 | root.style.setProperty("--accent", store.theme.accent); 109 | document.body.classList.forEach((el) => { 110 | document.body.classList.remove(el); 111 | }); 112 | document.body.classList.add(store.theme.shortName); 113 | } 114 | 115 | export const ThemePicker: Component<{}, {}> = function () { 116 | 117 | const themes = [oled, mocha, macchiato, frappe, latte]; 118 | 119 | this.css = ` 120 | background: transparent; 121 | border-radius: 0.4rem; 122 | border: none; 123 | color: var(--text); 124 | font-family: var(--font-body); 125 | font-size: 1rem; 126 | // padding: 0.75rem; 127 | cursor: pointer; 128 | // position: fixed; 129 | // bottom: 0; 130 | // right: 0; 131 | // z-index: 1000; 132 | 133 | display: flex; 134 | align-items: center; 135 | 136 | user-select: none; 137 | -webkit-user-drag: none; 138 | -webkit-user-select: none; 139 | 140 | .material-symbols-rounded { 141 | font-size: 1.5rem; 142 | } 143 | 144 | @media (orientation: portrait) { 145 | #theme-name { 146 | display: none; 147 | } 148 | } 149 | `; 150 | 151 | return ( 152 | 171 | ); 172 | }; 173 | -------------------------------------------------------------------------------- /src/Utils.ts: -------------------------------------------------------------------------------- 1 | export function convertRemToPixels(rem: number) { 2 | return rem * parseFloat(getComputedStyle(document.documentElement).fontSize); 3 | } 4 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | "jsx": "react", 23 | "jsxFactory": "h", 24 | "jsxFragmentFactory": "Fragment", 25 | "types": ["dreamland"] 26 | }, 27 | "include": ["src/**/*"], 28 | } 29 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { dreamlandPlugin } from "vite-plugin-dreamland"; 3 | import legacy from "@vitejs/plugin-legacy"; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | dreamlandPlugin(), 8 | legacy({ 9 | targets: [ 10 | "fully supports flexbox", 11 | "partially supports css-grid", 12 | "supports proxy", 13 | "not dead", 14 | "BlackBerry 10", 15 | "Firefox ESR", 16 | ], 17 | }), 18 | ], 19 | base: "", 20 | build: { 21 | sourcemap: true, 22 | cssMinify: "lightningcss", 23 | minify: "terser", 24 | }, 25 | css: { 26 | devSourcemap: true, 27 | }, 28 | server: { 29 | host: true, 30 | cors: false, 31 | }, 32 | }); 33 | --------------------------------------------------------------------------------