From 8efa21ae6ee2de782d08ad7e8674da71bbf423da Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Fri, 3 Apr 2026 22:22:31 +0100 Subject: [PATCH 01/15] updating the landing page --- .github/workflows/release.yml | 9 + CLAUDE.md | 7 +- CONTRIBUTING.md | 59 ++ README.md | 15 +- next.config.ts | 7 +- package-lock.json | 14 - scripts/generate-variants.js | 568 ++++++++++++++++++ src/app/page.tsx | 33 +- src/components/upload/UploadScreenV1.tsx | 663 +++++++++++++++++++++ src/components/upload/UploadScreenV2.tsx | 662 +++++++++++++++++++++ src/components/upload/UploadScreenV3.tsx | 722 +++++++++++++++++++++++ src/components/upload/UploadScreenV4.tsx | 697 ++++++++++++++++++++++ src/components/upload/UploadScreenV5.tsx | 664 +++++++++++++++++++++ 13 files changed, 4099 insertions(+), 21 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 scripts/generate-variants.js create mode 100644 src/components/upload/UploadScreenV1.tsx create mode 100644 src/components/upload/UploadScreenV2.tsx create mode 100644 src/components/upload/UploadScreenV3.tsx create mode 100644 src/components/upload/UploadScreenV4.tsx create mode 100644 src/components/upload/UploadScreenV5.tsx diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81772f5..0d70270 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,7 @@ jobs: with: node-version: 20 cache: npm + registry-url: "https://registry.npmjs.org" - run: npm ci @@ -34,8 +35,16 @@ jobs: - name: Build run: npm run build + - name: Build TUI + run: npm run build:tui + - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: generate_release_notes: true make_latest: true + + - name: Publish to npm + run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CLAUDE.md b/CLAUDE.md index 2458335..a75668e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -109,7 +109,8 @@ Upload screen includes hidden folder visibility instructions per OS (macOS: Cmd+ ### DevOps - CI: `.github/workflows/ci.yml` - runs on every push/PR to main: Prettier → Lint → Test → Build → Build TUI -- Release: `.github/workflows/release.yml` - runs on `v*` tags: same checks → creates GitHub Release with auto-generated changelog +- Release: `.github/workflows/release.yml` - runs on `v*` tags: same checks → Build TUI → creates GitHub Release with auto-generated changelog → `npm publish` (requires `NPM_TOKEN` repo secret) +- Versioning: bump `package.json` version manually → commit `chore: bump version to X.Y.Z` → `git tag vX.Y.Z && git push origin vX.Y.Z` → CI auto-publishes to npm - Tests: Jest tests across `__tests__/` (scoring, archetypes, narratives, stats) - Formatting: Prettier with `.prettierrc`, `npm run format` to fix, `npm run format:check` for CI - Linting: ESLint via `eslint-config-next` @@ -117,6 +118,10 @@ Upload screen includes hidden folder visibility instructions per OS (macOS: Cmd+ - `npm run build:tui` — esbuild bundle (runs automatically via `prepublishOnly`) - `npm run setup:command` — install /ccrewind slash command from local build - `"files": ["dist/"]` in package.json overrides .gitignore so dist/ is included in npm publish +- npm package: published as `ccrewind`, bin entry `ccrewind` → `dist/ccrewind-tui.mjs` + - `npm run build:tui` — esbuild bundle (runs automatically via `prepublishOnly`) + - `npm run setup:command` — install /ccrewind slash command from local build + - `"files": ["dist/"]` in package.json overrides .gitignore so dist/ is included in npm publish ### Project name normalisation (important) history.jsonl stores project as real paths: `/home/user/dev/project` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9222238 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# Contributing to Claude Code Rewind + +## Local setup + +```bash +git clone https://github.com/Junaid2005/ccrewind.git +cd ccrewind +npm install +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000). Your `~/.claude` folder is auto-detected — click **Use your local data** to skip the upload step. + +## Code quality + +All checks run in CI on every push and PR. Run them locally before pushing: + +```bash +npm run format # Prettier auto-fix +npm run format:check # Prettier check (CI mode) +npm run lint # ESLint +npm test # Jest (49 tests) +npm run build # TypeScript + Next.js production build +npm run build:tui # esbuild TUI bundle → dist/ccrewind-tui.mjs +``` + +Any failure blocks merge. + +## Submitting a PR + +1. Fork the repo and create a branch from `main`. +2. Make your changes. Keep PRs focused — one thing per PR. +3. Run the full check suite locally (`format:check`, `lint`, `test`, `build`). +4. Open a PR against `main`. CI runs automatically. + +## Release & versioning + +We use semver. The release flow is: + +1. **Bump** `package.json` → `"version": "X.Y.Z"` manually. +2. **Commit**: `chore: bump version to X.Y.Z` +3. **Tag and push**: + ```bash + git tag vX.Y.Z + git push origin vX.Y.Z + ``` +4. CI runs the full check suite, creates a GitHub Release with auto-generated changelog, builds the TUI bundle, and **publishes to npm automatically**. + +The npm package is `ccrewind`. Users run it via `npx ccrewind`. + +## Setting up NPM_TOKEN (for forks) + +If you fork this repo and want auto-publish to work on your fork: + +1. Generate a token at [npmjs.com](https://www.npmjs.com) → Access Tokens → Granular Access Token (or Classic Automation token). +2. Add it to your GitHub repo: **Settings → Secrets and variables → Actions → New repository secret**. +3. Name: `NPM_TOKEN`, Value: your token. + +The release workflow reads `${{ secrets.NPM_TOKEN }}` automatically. diff --git a/README.md b/README.md index 5db3849..27bdc80 100644 --- a/README.md +++ b/README.md @@ -279,9 +279,15 @@ push or PR to main ### Release pipeline +Versioning convention: + +1. Bump `package.json` → `"version": "X.Y.Z"` manually. +2. Commit: `chore: bump version to X.Y.Z` +3. Tag and push: + ```bash -git tag v1.0.0 -git push origin v1.0.0 +git tag vX.Y.Z +git push origin vX.Y.Z ``` ``` @@ -301,12 +307,17 @@ git push v* tag │ marked as Latest │ │ │ │ │ ▼ │ +│ npm publish (auto, via NPM_TOKEN secret) │ +│ │ │ +│ ▼ │ │ Vercel deploys automatically │ │ │ │ Release blocked if any check fails. │ └────────────────────────────────────────────┘ ``` +To set up npm auto-publish on a fork: add an `NPM_TOKEN` secret under **Settings → Secrets and variables → Actions**. + Releases: [github.com/Junaid2005/ccrewind/releases](https://github.com/Junaid2005/ccrewind/releases) ### Tests diff --git a/next.config.ts b/next.config.ts index cb651cd..62fcbe8 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,5 +1,10 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = {}; +const nextConfig: NextConfig = { + allowedDevOrigins: [ + "microwave-lawrence-stuart-result.trycloudflare.com", + "files-airplane-prayers-burst.trycloudflare.com", + ], +}; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index bdc8c89..3dbf6e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -82,7 +82,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -3233,7 +3232,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3316,7 +3314,6 @@ "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.57.2", "@typescript-eslint/types": "8.57.2", @@ -3836,7 +3833,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4331,7 +4327,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5008,7 +5003,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -5602,7 +5596,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5775,7 +5768,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7447,7 +7439,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -9464,7 +9455,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -9474,7 +9464,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -10489,7 +10478,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10747,7 +10735,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11277,7 +11264,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/scripts/generate-variants.js b/scripts/generate-variants.js new file mode 100644 index 0000000..5458674 --- /dev/null +++ b/scripts/generate-variants.js @@ -0,0 +1,568 @@ +const fs = require('fs'); +const path = require('path'); + +const srcPath = path.join(__dirname, '../src/components/upload/UploadScreen.tsx'); +const originalSource = fs.readFileSync(srcPath, 'utf8'); + +const previewCardsMatch = originalSource.match(/\/\* ─── Stacked preview cards ─── \*\/\s*function PreviewCards\(\) \{[\s\S]*?return \([\s\S]*? \);\n\}/); +const previewCardsCode = previewCardsMatch[0]; + +const generateTemplate = (vNum, chromeCode, wrapperClass, rightColClass) => ` +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { parseClaudeFolder } from "@/lib/parser"; +import { generateDemoData } from "@/lib/demo"; +import { ParsedData } from "@/types"; + +interface UploadScreenProps { + onDataParsed: (data: ParsedData) => void; +} + +const LOADING_MESSAGES = [ + "Reading your .claude folder...", + "Scanning session transcripts...", + "Crunching the numbers...", + "Analyzing your habits...", + "Building your story...", +]; + +${previewCardsCode} + +function TabContentNPM({ active }: { active: boolean }) { + const [lines, setLines] = useState(0); + useEffect(() => { + if (!active) { + setLines(0); + return; + } + const timeouts = [ + setTimeout(() => setLines(1), 50), + setTimeout(() => setLines(2), 100), + setTimeout(() => setLines(3), 150), + setTimeout(() => setLines(4), 200), + setTimeout(() => setLines(5), 250), + ]; + return () => timeouts.forEach(clearTimeout); + }, [active]); + + return ( +
+
+
+
+
+
+
+

$ npx ccrewind

+ {lines >= 1 &&

◆ CC REWIND ◆

} + {lines >= 2 &&

Reading ~/.claude...

} + {lines >= 3 &&

Your character: The Quant

} + {lines >= 4 &&

Claude Elo: 847 / 1000

} + {lines >= 5 &&

→ ccrewind.com for the full story

} +
+
+ View on npm → +
+
+ ); +} + +function TabContentClaude() { + const [copied, setCopied] = useState(false); + const copyCmd = () => { + navigator.clipboard.writeText("npx ccrewind --setup"); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + return ( +
+

Run inside Claude Code

+
+ npx ccrewind --setup + +
+
+
+ /ccrewind + Terminal stats report +
+
+ /ccrewind-ui + Opens web UI with auto-detected data +
+
+

Registers slash commands in your Claude Code session

+
+ ); +} + +export default function UploadScreenV${vNum}({ onDataParsed }: UploadScreenProps) { + const [isDragging, setIsDragging] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); + const [error, setError] = useState(null); + const [localAvailable, setLocalAvailable] = useState(false); + const [showHiddenHelp, setShowHiddenHelp] = useState(false); + const inputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + + useEffect(() => { + fetch("/api/local-data/status") + .then((r) => r.json()) + .then((d: { available: boolean }) => { + if (d.available) setLocalAvailable(true); + }) + .catch(() => {}); + }, []); + + const startLoadingCycle = () => { + let idx = 0; + const interval = setInterval(() => { + idx = (idx + 1) % LOADING_MESSAGES.length; + setLoadingText(LOADING_MESSAGES[idx]); + }, 1500); + return interval; + }; + + const handleLocalData = useCallback(async () => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const res = await fetch("/api/local-data"); + if (!res.ok) throw new Error("Failed to load local data"); + const data: ParsedData = await res.json(); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found in ~/.claude"); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to load local data"); + } + }, [onDataParsed]); + + const handleFiles = useCallback( + async (files: FileList) => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const data = await parseClaudeFolder(files); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to parse data"); + } + }, + [onDataParsed] + ); + + const handleDemo = useCallback(() => { + setIsLoading(true); + setLoadingText("Loading demo data..."); + fetch("/demo-data.json") + .then((r) => r.json()) + .then((data) => { + setLoadingText("Ready."); + setTimeout(() => onDataParsed(data), 400); + }) + .catch(() => { + const demoData = generateDemoData(); + setLoadingText("Ready."); + setTimeout(() => onDataParsed(demoData), 400); + }); + }, [onDataParsed]); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); + }, + [handleFiles] + ); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }, []); + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); + }; + + const TabContentWeb = () => ( +
+ {localAvailable ? ( + + ) : ( + + )} + +
+ {localAvailable && ( + + )} + +
+ +
+ + + + + + + Your data stays local + + +
+ +
+
+ + + {showHiddenHelp && ( + +
+

+ Show hidden files first: +

+ {[ + { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, + { os: "Windows", hint: "View → Show → Hidden items", note: "" }, + { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, + ].map(({ os, hint, note }) => ( +
+ {os} + + {hint} + + {note && {note}} +
+ ))} +
+
+ )} +
+
+ ); + + return ( +
+
+
+
+ + + {isLoading ? ( + +
+ + + +
+ + + {loadingText} + + +
+ ) : ( + +
+ +

+ Claude Code
+ Rewind. +

+

+ Connect your data. Get your character. Share your stats. +

+
+ + ${chromeCode} + +
+ + + + +
+ )} +
+ + + {isDragging && ( + +
+

Drop it like it's hot

+

release to upload your ~/.claude folder

+
+
+ )} +
+ + + {error && ( + +

{error}

+
+ )} +
+ + +
+ ); +} +`; + +const v1Chrome = ` +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+`; + +const v2Chrome = ` +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+`; + +const v3Chrome = ` +
+
+ {(["npm", "web", "claude"] as const).map((t, idx) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+`; + +const v4Chrome = ` +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+`; + +const v5Chrome = ` +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+`; + +fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV1.tsx'), generateTemplate('1', v1Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); +fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV2.tsx'), generateTemplate('2', v2Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); +fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV3.tsx'), generateTemplate('3', v3Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); +fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV4.tsx'), generateTemplate('4', v4Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); +fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV5.tsx'), generateTemplate('5', v5Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); + +console.log('Successfully generated 5 variants (fixed Next.js TS issues).'); diff --git a/src/app/page.tsx b/src/app/page.tsx index e8b3a81..746617b 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,8 +1,13 @@ "use client"; -import { useState, useMemo } from "react"; +import { useState, useMemo, Suspense } from "react"; +import { useSearchParams } from "next/navigation"; import { AnimatePresence, motion } from "framer-motion"; -import UploadScreen from "@/components/upload/UploadScreen"; +import UploadScreenV1 from "@/components/upload/UploadScreenV1"; +import UploadScreenV2 from "@/components/upload/UploadScreenV2"; +import UploadScreenV3 from "@/components/upload/UploadScreenV3"; +import UploadScreenV4 from "@/components/upload/UploadScreenV4"; +import UploadScreenV5 from "@/components/upload/UploadScreenV5"; import SlideContainer from "@/components/slides/SlideContainer"; import GraveyardShift from "@/components/slides/GraveyardShift"; import Delegator from "@/components/slides/Delegator"; @@ -27,6 +32,26 @@ import { computeElo } from "@/lib/scoring"; type AppPhase = "upload" | "slides" | "reveal"; +function UploadVariant({ onDataParsed }: { onDataParsed: (data: ParsedData) => void }) { + const searchParams = useSearchParams(); + const v = searchParams.get("v"); + + switch (v) { + case "1": + return ; + case "2": + return ; + case "3": + return ; + case "4": + return ; + case "5": + return ; + default: + return ; + } +} + export default function Home() { const [phase, setPhase] = useState("upload"); const [parsedData, setParsedData] = useState(null); @@ -60,7 +85,9 @@ export default function Home() { {phase === "upload" && ( - + }> + + )} diff --git a/src/components/upload/UploadScreenV1.tsx b/src/components/upload/UploadScreenV1.tsx new file mode 100644 index 0000000..e158f3b --- /dev/null +++ b/src/components/upload/UploadScreenV1.tsx @@ -0,0 +1,663 @@ +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { parseClaudeFolder } from "@/lib/parser"; +import { generateDemoData } from "@/lib/demo"; +import { ParsedData } from "@/types"; + +interface UploadScreenProps { + onDataParsed: (data: ParsedData) => void; +} + +const LOADING_MESSAGES = [ + "Reading your .claude folder...", + "Scanning session transcripts...", + "Crunching the numbers...", + "Analyzing your habits...", + "Building your story...", +]; + +/* ─── Stacked preview cards ─── */ +function PreviewCards() { + return ( +
+ {/* Back card — The Sama (uncommon: soft green) */} + +
+
+ + Claude Code Rewind + + + Uncommon + +
+
+ The Sama +
+
+

+ The Sama +

+

+ “Somehow always lands on top.” +

+
+
+

Elo

+

+ 683 +

+
+
+

Tokens

+

847M

+
+
+

Streak

+

41d

+
+
+
+
+
+ + {/* Front card — The Dario (epic: soft purple) */} + +
+
+ + Claude Code Rewind + + + Epic + +
+
+ The Dario +
+
+

+ The Dario +

+

+ “You are not building features. You are changing the world. Probably.” +

+
+
+

Elo

+

+ 912 +

+
+
+

Tokens

+

2.4B

+
+
+

Sessions

+

1,847

+
+
+
+
+
+
+ ); +} + +function TabContentNPM({ active }: { active: boolean }) { + const [lines, setLines] = useState(0); + useEffect(() => { + if (!active) { + setLines(0); + return; + } + const timeouts = [ + setTimeout(() => setLines(1), 50), + setTimeout(() => setLines(2), 100), + setTimeout(() => setLines(3), 150), + setTimeout(() => setLines(4), 200), + setTimeout(() => setLines(5), 250), + ]; + return () => timeouts.forEach(clearTimeout); + }, [active]); + + return ( +
+
+
+
+
+
+
+

+ $ npx ccrewind +

+ {lines >= 1 &&

◆ CC REWIND ◆

} + {lines >= 2 &&

Reading ~/.claude...

} + {lines >= 3 && ( +

+ Your character: The Quant +

+ )} + {lines >= 4 && ( +

+ Claude Elo: 847 / 1000 +

+ )} + {lines >= 5 &&

→ ccrewind.com for the full story

} +
+ +
+ ); +} + +function TabContentClaude() { + const [copied, setCopied] = useState(false); + const copyCmd = () => { + navigator.clipboard.writeText("npx ccrewind --setup"); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + return ( +
+

Run inside Claude Code

+
+ npx ccrewind --setup + +
+
+
+ /ccrewind + + Terminal stats report + +
+
+ /ccrewind-ui + + Opens web UI with auto-detected data + +
+
+

+ Registers slash commands in your Claude Code session +

+
+ ); +} + +export default function UploadScreenV1({ onDataParsed }: UploadScreenProps) { + const [isDragging, setIsDragging] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); + const [error, setError] = useState(null); + const [localAvailable, setLocalAvailable] = useState(false); + const [showHiddenHelp, setShowHiddenHelp] = useState(false); + const inputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + + useEffect(() => { + fetch("/api/local-data/status") + .then((r) => r.json()) + .then((d: { available: boolean }) => { + if (d.available) setLocalAvailable(true); + }) + .catch(() => {}); + }, []); + + const startLoadingCycle = () => { + let idx = 0; + const interval = setInterval(() => { + idx = (idx + 1) % LOADING_MESSAGES.length; + setLoadingText(LOADING_MESSAGES[idx]); + }, 1500); + return interval; + }; + + const handleLocalData = useCallback(async () => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const res = await fetch("/api/local-data"); + if (!res.ok) throw new Error("Failed to load local data"); + const data: ParsedData = await res.json(); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found in ~/.claude"); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to load local data"); + } + }, [onDataParsed]); + + const handleFiles = useCallback( + async (files: FileList) => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const data = await parseClaudeFolder(files); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to parse data"); + } + }, + [onDataParsed] + ); + + const handleDemo = useCallback(() => { + setIsLoading(true); + setLoadingText("Loading demo data..."); + fetch("/demo-data.json") + .then((r) => r.json()) + .then((data) => { + setLoadingText("Ready."); + setTimeout(() => onDataParsed(data), 400); + }) + .catch(() => { + const demoData = generateDemoData(); + setLoadingText("Ready."); + setTimeout(() => onDataParsed(demoData), 400); + }); + }, [onDataParsed]); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); + }, + [handleFiles] + ); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }, []); + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); + }; + + const TabContentWeb = () => ( +
+ {localAvailable ? ( + + ) : ( + + )} + +
+ {localAvailable && ( + + )} + +
+ +
+ + + + + + Your data stays local + +
+ +
+
+ + + {showHiddenHelp && ( + +
+

+ Show hidden files first: +

+ {[ + { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, + { os: "Windows", hint: "View → Show → Hidden items", note: "" }, + { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, + ].map(({ os, hint, note }) => ( +
+ {os} + + {hint} + + {note && {note}} +
+ ))} +
+
+ )} +
+
+ ); + + return ( +
+
+
+
+ + + {isLoading ? ( + +
+ + + +
+ + + {loadingText} + + +
+ ) : ( + +
+ +

+ Claude Code +
+ Rewind. +

+

+ Connect your data. Get your character. Share your stats. +

+
+ +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+
+ + + + +
+ )} +
+ + + {isDragging && ( + +
+

Drop it like it's hot

+

+ release to upload your ~/.claude folder +

+
+
+ )} +
+ + + {error && ( + +

{error}

+
+ )} +
+ + +
+ ); +} diff --git a/src/components/upload/UploadScreenV2.tsx b/src/components/upload/UploadScreenV2.tsx new file mode 100644 index 0000000..d6e364f --- /dev/null +++ b/src/components/upload/UploadScreenV2.tsx @@ -0,0 +1,662 @@ +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { parseClaudeFolder } from "@/lib/parser"; +import { generateDemoData } from "@/lib/demo"; +import { ParsedData } from "@/types"; + +interface UploadScreenProps { + onDataParsed: (data: ParsedData) => void; +} + +const LOADING_MESSAGES = [ + "Reading your .claude folder...", + "Scanning session transcripts...", + "Crunching the numbers...", + "Analyzing your habits...", + "Building your story...", +]; + +/* ─── Stacked preview cards ─── */ +function PreviewCards() { + return ( +
+ {/* Back card — The Sama (uncommon: soft green) */} + +
+
+ + Claude Code Rewind + + + Uncommon + +
+
+ The Sama +
+
+

+ The Sama +

+

+ “Somehow always lands on top.” +

+
+
+

Elo

+

+ 683 +

+
+
+

Tokens

+

847M

+
+
+

Streak

+

41d

+
+
+
+
+
+ + {/* Front card — The Dario (epic: soft purple) */} + +
+
+ + Claude Code Rewind + + + Epic + +
+
+ The Dario +
+
+

+ The Dario +

+

+ “You are not building features. You are changing the world. Probably.” +

+
+
+

Elo

+

+ 912 +

+
+
+

Tokens

+

2.4B

+
+
+

Sessions

+

1,847

+
+
+
+
+
+
+ ); +} + +function TabContentNPM({ active }: { active: boolean }) { + const [lines, setLines] = useState(0); + useEffect(() => { + if (!active) { + setLines(0); + return; + } + const timeouts = [ + setTimeout(() => setLines(1), 50), + setTimeout(() => setLines(2), 100), + setTimeout(() => setLines(3), 150), + setTimeout(() => setLines(4), 200), + setTimeout(() => setLines(5), 250), + ]; + return () => timeouts.forEach(clearTimeout); + }, [active]); + + return ( +
+
+
+
+
+
+
+

+ $ npx ccrewind +

+ {lines >= 1 &&

◆ CC REWIND ◆

} + {lines >= 2 &&

Reading ~/.claude...

} + {lines >= 3 && ( +

+ Your character: The Quant +

+ )} + {lines >= 4 && ( +

+ Claude Elo: 847 / 1000 +

+ )} + {lines >= 5 &&

→ ccrewind.com for the full story

} +
+ +
+ ); +} + +function TabContentClaude() { + const [copied, setCopied] = useState(false); + const copyCmd = () => { + navigator.clipboard.writeText("npx ccrewind --setup"); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + return ( +
+

Run inside Claude Code

+
+ npx ccrewind --setup + +
+
+
+ /ccrewind + + Terminal stats report + +
+
+ /ccrewind-ui + + Opens web UI with auto-detected data + +
+
+

+ Registers slash commands in your Claude Code session +

+
+ ); +} + +export default function UploadScreenV2({ onDataParsed }: UploadScreenProps) { + const [isDragging, setIsDragging] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); + const [error, setError] = useState(null); + const [localAvailable, setLocalAvailable] = useState(false); + const [showHiddenHelp, setShowHiddenHelp] = useState(false); + const inputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + + useEffect(() => { + fetch("/api/local-data/status") + .then((r) => r.json()) + .then((d: { available: boolean }) => { + if (d.available) setLocalAvailable(true); + }) + .catch(() => {}); + }, []); + + const startLoadingCycle = () => { + let idx = 0; + const interval = setInterval(() => { + idx = (idx + 1) % LOADING_MESSAGES.length; + setLoadingText(LOADING_MESSAGES[idx]); + }, 1500); + return interval; + }; + + const handleLocalData = useCallback(async () => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const res = await fetch("/api/local-data"); + if (!res.ok) throw new Error("Failed to load local data"); + const data: ParsedData = await res.json(); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found in ~/.claude"); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to load local data"); + } + }, [onDataParsed]); + + const handleFiles = useCallback( + async (files: FileList) => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const data = await parseClaudeFolder(files); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to parse data"); + } + }, + [onDataParsed] + ); + + const handleDemo = useCallback(() => { + setIsLoading(true); + setLoadingText("Loading demo data..."); + fetch("/demo-data.json") + .then((r) => r.json()) + .then((data) => { + setLoadingText("Ready."); + setTimeout(() => onDataParsed(data), 400); + }) + .catch(() => { + const demoData = generateDemoData(); + setLoadingText("Ready."); + setTimeout(() => onDataParsed(demoData), 400); + }); + }, [onDataParsed]); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); + }, + [handleFiles] + ); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }, []); + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); + }; + + const TabContentWeb = () => ( +
+ {localAvailable ? ( + + ) : ( + + )} + +
+ {localAvailable && ( + + )} + +
+ +
+ + + + + + Your data stays local + +
+ +
+
+ + + {showHiddenHelp && ( + +
+

+ Show hidden files first: +

+ {[ + { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, + { os: "Windows", hint: "View → Show → Hidden items", note: "" }, + { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, + ].map(({ os, hint, note }) => ( +
+ {os} + + {hint} + + {note && {note}} +
+ ))} +
+
+ )} +
+
+ ); + + return ( +
+
+
+
+ + + {isLoading ? ( + +
+ + + +
+ + + {loadingText} + + +
+ ) : ( + +
+ +

+ Claude Code +
+ Rewind. +

+

+ Connect your data. Get your character. Share your stats. +

+
+ +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+
+ + + + +
+ )} +
+ + + {isDragging && ( + +
+

Drop it like it's hot

+

+ release to upload your ~/.claude folder +

+
+
+ )} +
+ + + {error && ( + +

{error}

+
+ )} +
+ + +
+ ); +} diff --git a/src/components/upload/UploadScreenV3.tsx b/src/components/upload/UploadScreenV3.tsx new file mode 100644 index 0000000..43a21ee --- /dev/null +++ b/src/components/upload/UploadScreenV3.tsx @@ -0,0 +1,722 @@ +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { parseClaudeFolder } from "@/lib/parser"; +import { generateDemoData } from "@/lib/demo"; +import { ParsedData } from "@/types"; + +interface UploadScreenProps { + onDataParsed: (data: ParsedData) => void; +} + +const LOADING_MESSAGES = [ + "Reading your .claude folder...", + "Scanning session transcripts...", + "Crunching the numbers...", + "Analyzing your habits...", + "Building your story...", +]; + +/* ─── Stacked preview cards ─── */ +function PreviewCards() { + return ( +
+ {/* Back card — The Sama (uncommon: soft green) */} + +
+
+ + Claude Code Rewind + + + Uncommon + +
+
+ The Sama +
+
+

+ The Sama +

+

+ “Somehow always lands on top.” +

+
+
+

Elo

+

+ 683 +

+
+
+

Tokens

+

847M

+
+
+

Streak

+

41d

+
+
+
+
+
+ + {/* Front card — The Dario (epic: soft purple) */} + +
+
+ + Claude Code Rewind + + + Epic + +
+
+ The Dario +
+
+

+ The Dario +

+

+ “You are not building features. You are changing the world. Probably.” +

+
+
+

Elo

+

+ 912 +

+
+
+

Tokens

+

2.4B

+
+
+

Sessions

+

1,847

+
+
+
+
+
+
+ ); +} + +function TabContentNPM({ active }: { active: boolean }) { + const [lines, setLines] = useState(0); + useEffect(() => { + if (!active) { + setLines(0); + return; + } + const timeouts = [ + setTimeout(() => setLines(1), 50), + setTimeout(() => setLines(2), 100), + setTimeout(() => setLines(3), 150), + setTimeout(() => setLines(4), 200), + setTimeout(() => setLines(5), 250), + ]; + return () => timeouts.forEach(clearTimeout); + }, [active]); + + return ( +
+
+
+
+
+
+
+

+ $ npx ccrewind +

+ {lines >= 1 &&

◆ CC REWIND ◆

} + {lines >= 2 &&

Reading ~/.claude...

} + {lines >= 3 && ( +

+ Your character: The Quant +

+ )} + {lines >= 4 && ( +

+ Claude Elo: 847 / 1000 +

+ )} + {lines >= 5 &&

→ ccrewind.com for the full story

} +
+ +
+ ); +} + +function TabContentClaude() { + const [copied, setCopied] = useState(false); + const copyCmd = () => { + navigator.clipboard.writeText("npx ccrewind --setup"); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + return ( +
+

Run inside Claude Code

+
+ npx ccrewind --setup + +
+
+
+ /ccrewind + + Terminal stats report + +
+
+ /ccrewind-ui + + Opens web UI with auto-detected data + +
+
+

+ Registers slash commands in your Claude Code session +

+
+ ); +} + +export default function UploadScreenV3({ onDataParsed }: UploadScreenProps) { + const [isDragging, setIsDragging] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); + const [error, setError] = useState(null); + const [localAvailable, setLocalAvailable] = useState(false); + const [showHiddenHelp, setShowHiddenHelp] = useState(false); + const inputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + const [hasInteracted, setHasInteracted] = useState(false); + + useEffect(() => { + fetch("/api/local-data/status") + .then((r) => r.json()) + .then((d: { available: boolean }) => { + if (d.available) setLocalAvailable(true); + }) + .catch(() => {}); + }, []); + + const startLoadingCycle = () => { + let idx = 0; + const interval = setInterval(() => { + idx = (idx + 1) % LOADING_MESSAGES.length; + setLoadingText(LOADING_MESSAGES[idx]); + }, 1500); + return interval; + }; + + const handleLocalData = useCallback(async () => { + setHasInteracted(true); + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const res = await fetch("/api/local-data"); + if (!res.ok) throw new Error("Failed to load local data"); + const data: ParsedData = await res.json(); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found in ~/.claude"); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to load local data"); + } + }, [onDataParsed]); + + const handleFiles = useCallback( + async (files: FileList) => { + setHasInteracted(true); + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const data = await parseClaudeFolder(files); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to parse data"); + } + }, + [onDataParsed] + ); + + const handleDemo = useCallback(() => { + setHasInteracted(true); + setIsLoading(true); + setLoadingText("Loading demo data..."); + fetch("/demo-data.json") + .then((r) => r.json()) + .then((data) => { + setLoadingText("Ready."); + setTimeout(() => onDataParsed(data), 400); + }) + .catch(() => { + const demoData = generateDemoData(); + setLoadingText("Ready."); + setTimeout(() => onDataParsed(demoData), 400); + }); + }, [onDataParsed]); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); + }, + [handleFiles] + ); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }, []); + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); + }; + + const TabContentWeb = () => ( +
+ {localAvailable ? ( + + ) : ( + + )} + +
+ {localAvailable && ( + + )} + + + + {!hasInteracted && ( + + 👆 + + )} + +
+ +
+ + + + + + Your data stays local + +
+ +
+
+ + + {showHiddenHelp && ( + +
+

+ Show hidden files first: +

+ {[ + { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, + { os: "Windows", hint: "View → Show → Hidden items", note: "" }, + { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, + ].map(({ os, hint, note }) => ( +
+ {os} + + {hint} + + {note && {note}} +
+ ))} +
+
+ )} +
+
+ ); + + return ( +
+
+
+
+ + + {isLoading ? ( + +
+ + + +
+ + + {loadingText} + + +
+ ) : ( + +
+ +

+ Claude Code +
+ Rewind. +

+

+ Connect your data. Get your character. Share your stats. +

+
+ +
+
+ {(["npm", "web", "claude"] as const).map((t, idx) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+
+ + + + +
+ )} +
+ + + {isDragging && ( + +
+

Drop it like it's hot

+

+ release to upload your ~/.claude folder +

+
+
+ )} +
+ + + {error && ( + +

{error}

+
+ )} +
+ + +
+ ); +} diff --git a/src/components/upload/UploadScreenV4.tsx b/src/components/upload/UploadScreenV4.tsx new file mode 100644 index 0000000..1a992c3 --- /dev/null +++ b/src/components/upload/UploadScreenV4.tsx @@ -0,0 +1,697 @@ +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { parseClaudeFolder } from "@/lib/parser"; +import { generateDemoData } from "@/lib/demo"; +import { ParsedData } from "@/types"; + +interface UploadScreenProps { + onDataParsed: (data: ParsedData) => void; +} + +const LOADING_MESSAGES = [ + "Reading your .claude folder...", + "Scanning session transcripts...", + "Crunching the numbers...", + "Analyzing your habits...", + "Building your story...", +]; + +/* ─── Stacked preview cards ─── */ +function PreviewCards() { + return ( +
+ {/* Back card — The Sama (uncommon: soft green) */} + +
+
+ + Claude Code Rewind + + + Uncommon + +
+
+ The Sama +
+
+

+ The Sama +

+

+ “Somehow always lands on top.” +

+
+
+

Elo

+

+ 683 +

+
+
+

Tokens

+

847M

+
+
+

Streak

+

41d

+
+
+
+
+
+ + {/* Front card — The Dario (epic: soft purple) */} + +
+
+ + Claude Code Rewind + + + Epic + +
+
+ The Dario +
+
+

+ The Dario +

+

+ “You are not building features. You are changing the world. Probably.” +

+
+
+

Elo

+

+ 912 +

+
+
+

Tokens

+

2.4B

+
+
+

Sessions

+

1,847

+
+
+
+
+
+
+ ); +} + +function TabContentNPM({ active }: { active: boolean }) { + const [lines, setLines] = useState(0); + useEffect(() => { + if (!active) { + setLines(0); + return; + } + const timeouts = [ + setTimeout(() => setLines(1), 50), + setTimeout(() => setLines(2), 100), + setTimeout(() => setLines(3), 150), + setTimeout(() => setLines(4), 200), + setTimeout(() => setLines(5), 250), + ]; + return () => timeouts.forEach(clearTimeout); + }, [active]); + + return ( +
+
+
+
+
+
+
+

+ $ npx ccrewind +

+ {lines >= 1 &&

◆ CC REWIND ◆

} + {lines >= 2 &&

Reading ~/.claude...

} + {lines >= 3 && ( +

+ Your character: The Quant +

+ )} + {lines >= 4 && ( +

+ Claude Elo: 847 / 1000 +

+ )} + {lines >= 5 &&

→ ccrewind.com for the full story

} +
+ +
+ ); +} + +function TabContentClaude() { + const [copied, setCopied] = useState(false); + const copyCmd = () => { + navigator.clipboard.writeText("npx ccrewind --setup"); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + return ( +
+

Run inside Claude Code

+
+ npx ccrewind --setup + +
+
+
+ /ccrewind + + Terminal stats report + +
+
+ /ccrewind-ui + + Opens web UI with auto-detected data + +
+
+

+ Registers slash commands in your Claude Code session +

+
+ ); +} + +export default function UploadScreenV4({ onDataParsed }: UploadScreenProps) { + const [isDragging, setIsDragging] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); + const [error, setError] = useState(null); + const [localAvailable, setLocalAvailable] = useState(false); + const [showHiddenHelp, setShowHiddenHelp] = useState(false); + const inputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + + useEffect(() => { + fetch("/api/local-data/status") + .then((r) => r.json()) + .then((d: { available: boolean }) => { + if (d.available) setLocalAvailable(true); + }) + .catch(() => {}); + }, []); + + const startLoadingCycle = () => { + let idx = 0; + const interval = setInterval(() => { + idx = (idx + 1) % LOADING_MESSAGES.length; + setLoadingText(LOADING_MESSAGES[idx]); + }, 1500); + return interval; + }; + + const handleLocalData = useCallback(async () => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const res = await fetch("/api/local-data"); + if (!res.ok) throw new Error("Failed to load local data"); + const data: ParsedData = await res.json(); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found in ~/.claude"); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to load local data"); + } + }, [onDataParsed]); + + const handleFiles = useCallback( + async (files: FileList) => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const data = await parseClaudeFolder(files); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to parse data"); + } + }, + [onDataParsed] + ); + + const handleDemo = useCallback(() => { + setIsLoading(true); + setLoadingText("Loading demo data..."); + fetch("/demo-data.json") + .then((r) => r.json()) + .then((data) => { + setLoadingText("Ready."); + setTimeout(() => onDataParsed(data), 400); + }) + .catch(() => { + const demoData = generateDemoData(); + setLoadingText("Ready."); + setTimeout(() => onDataParsed(demoData), 400); + }); + }, [onDataParsed]); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); + }, + [handleFiles] + ); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }, []); + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); + }; + + const TabContentWeb = () => ( +
+ {localAvailable ? ( + + ) : ( + + )} + +
+ {localAvailable && ( + + )} + +
+ +
+ + + + + + Your data stays local + +
+ +
+
+ + + {showHiddenHelp && ( + +
+

+ Show hidden files first: +

+ {[ + { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, + { os: "Windows", hint: "View → Show → Hidden items", note: "" }, + { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, + ].map(({ os, hint, note }) => ( +
+ {os} + + {hint} + + {note && {note}} +
+ ))} +
+
+ )} +
+
+ ); + + return ( +
+
+
+
+ + + {isLoading ? ( + +
+ + + +
+ + + {loadingText} + + +
+ ) : ( + +
+ +

+ Claude Code +
+ Rewind. +

+

+ Connect your data. Get your character. Share your stats. +

+
+ +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+
+ + + + +
+ )} +
+ + + {isDragging && ( + +
+

Drop it like it's hot

+

+ release to upload your ~/.claude folder +

+
+
+ )} +
+ + + {error && ( + +

{error}

+
+ )} +
+ + +
+ ); +} diff --git a/src/components/upload/UploadScreenV5.tsx b/src/components/upload/UploadScreenV5.tsx new file mode 100644 index 0000000..c116d51 --- /dev/null +++ b/src/components/upload/UploadScreenV5.tsx @@ -0,0 +1,664 @@ +"use client"; + +import { useState, useCallback, useRef, useEffect } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { parseClaudeFolder } from "@/lib/parser"; +import { generateDemoData } from "@/lib/demo"; +import { ParsedData } from "@/types"; + +interface UploadScreenProps { + onDataParsed: (data: ParsedData) => void; +} + +const LOADING_MESSAGES = [ + "Reading your .claude folder...", + "Scanning session transcripts...", + "Crunching the numbers...", + "Analyzing your habits...", + "Building your story...", +]; + +/* ─── Stacked preview cards ─── */ +function PreviewCards() { + return ( +
+ {/* Back card — The Sama (uncommon: soft green) */} + +
+
+ + Claude Code Rewind + + + Uncommon + +
+
+ The Sama +
+
+

+ The Sama +

+

+ “Somehow always lands on top.” +

+
+
+

Elo

+

+ 683 +

+
+
+

Tokens

+

847M

+
+
+

Streak

+

41d

+
+
+
+
+
+ + {/* Front card — The Dario (epic: soft purple) */} + +
+
+ + Claude Code Rewind + + + Epic + +
+
+ The Dario +
+
+

+ The Dario +

+

+ “You are not building features. You are changing the world. Probably.” +

+
+
+

Elo

+

+ 912 +

+
+
+

Tokens

+

2.4B

+
+
+

Sessions

+

1,847

+
+
+
+
+
+
+ ); +} + +function TabContentNPM({ active }: { active: boolean }) { + const [lines, setLines] = useState(0); + useEffect(() => { + if (!active) { + setLines(0); + return; + } + const timeouts = [ + setTimeout(() => setLines(1), 50), + setTimeout(() => setLines(2), 100), + setTimeout(() => setLines(3), 150), + setTimeout(() => setLines(4), 200), + setTimeout(() => setLines(5), 250), + ]; + return () => timeouts.forEach(clearTimeout); + }, [active]); + + return ( +
+
+
+
+
+
+
+

+ $ npx ccrewind +

+ {lines >= 1 &&

◆ CC REWIND ◆

} + {lines >= 2 &&

Reading ~/.claude...

} + {lines >= 3 && ( +

+ Your character: The Quant +

+ )} + {lines >= 4 && ( +

+ Claude Elo: 847 / 1000 +

+ )} + {lines >= 5 &&

→ ccrewind.com for the full story

} +
+ +
+ ); +} + +function TabContentClaude() { + const [copied, setCopied] = useState(false); + const copyCmd = () => { + navigator.clipboard.writeText("npx ccrewind --setup"); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + return ( +
+

Run inside Claude Code

+
+ npx ccrewind --setup + +
+
+
+ /ccrewind + + Terminal stats report + +
+
+ /ccrewind-ui + + Opens web UI with auto-detected data + +
+
+

+ Registers slash commands in your Claude Code session +

+
+ ); +} + +export default function UploadScreenV5({ onDataParsed }: UploadScreenProps) { + const [isDragging, setIsDragging] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); + const [error, setError] = useState(null); + const [localAvailable, setLocalAvailable] = useState(false); + const [showHiddenHelp, setShowHiddenHelp] = useState(false); + const inputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + + useEffect(() => { + fetch("/api/local-data/status") + .then((r) => r.json()) + .then((d: { available: boolean }) => { + if (d.available) setLocalAvailable(true); + }) + .catch(() => {}); + }, []); + + const startLoadingCycle = () => { + let idx = 0; + const interval = setInterval(() => { + idx = (idx + 1) % LOADING_MESSAGES.length; + setLoadingText(LOADING_MESSAGES[idx]); + }, 1500); + return interval; + }; + + const handleLocalData = useCallback(async () => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const res = await fetch("/api/local-data"); + if (!res.ok) throw new Error("Failed to load local data"); + const data: ParsedData = await res.json(); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found in ~/.claude"); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to load local data"); + } + }, [onDataParsed]); + + const handleFiles = useCallback( + async (files: FileList) => { + setIsLoading(true); + setError(null); + setLoadingText(LOADING_MESSAGES[0]); + const interval = startLoadingCycle(); + try { + const data = await parseClaudeFolder(files); + if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { + throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); + } + clearInterval(interval); + setLoadingText("Ready."); + await new Promise((r) => setTimeout(r, 500)); + onDataParsed(data); + } catch (e) { + clearInterval(interval); + setIsLoading(false); + setError(e instanceof Error ? e.message : "Failed to parse data"); + } + }, + [onDataParsed] + ); + + const handleDemo = useCallback(() => { + setIsLoading(true); + setLoadingText("Loading demo data..."); + fetch("/demo-data.json") + .then((r) => r.json()) + .then((data) => { + setLoadingText("Ready."); + setTimeout(() => onDataParsed(data), 400); + }) + .catch(() => { + const demoData = generateDemoData(); + setLoadingText("Ready."); + setTimeout(() => onDataParsed(demoData), 400); + }); + }, [onDataParsed]); + + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); + }, + [handleFiles] + ); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }, []); + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }, []); + + const handleInputChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); + }; + + const TabContentWeb = () => ( +
+ {localAvailable ? ( + + ) : ( + + )} + +
+ {localAvailable && ( + + )} + +
+ +
+ + + + + + Your data stays local + +
+ +
+
+ + + {showHiddenHelp && ( + +
+

+ Show hidden files first: +

+ {[ + { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, + { os: "Windows", hint: "View → Show → Hidden items", note: "" }, + { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, + ].map(({ os, hint, note }) => ( +
+ {os} + + {hint} + + {note && {note}} +
+ ))} +
+
+ )} +
+
+ ); + + return ( +
+
+
+
+ + + {isLoading ? ( + +
+ + + +
+ + + {loadingText} + + +
+ ) : ( + +
+ +

+ Claude Code +
+ Rewind. +

+

+ Connect your data. Get your character. Share your stats. +

+
+ +
+
+ {(["npm", "web", "claude"] as const).map((t) => ( + + ))} +
+
+ {activeTab === "npm" && } + {activeTab === "web" && } + {activeTab === "claude" && } +
+
+
+ + + + +
+ )} +
+ + + {isDragging && ( + +
+

Drop it like it's hot

+

+ release to upload your ~/.claude folder +

+
+
+ )} +
+ + + {error && ( + +

{error}

+
+ )} +
+ + +
+ ); +} From f24d078bd8a494456c0f58919d1e1f002f539206 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Fri, 3 Apr 2026 23:53:40 +0100 Subject: [PATCH 02/15] landing page cursor and text updates --- README.md | 130 ++--- next.config.ts | 4 + src/app/page.tsx | 31 +- src/components/upload/UploadScreen.tsx | 627 -------------------- src/components/upload/UploadScreenV1.tsx | 663 --------------------- src/components/upload/UploadScreenV2.tsx | 662 --------------------- src/components/upload/UploadScreenV3.tsx | 166 ++++-- src/components/upload/UploadScreenV4.tsx | 697 ----------------------- src/components/upload/UploadScreenV5.tsx | 664 --------------------- 9 files changed, 184 insertions(+), 3460 deletions(-) delete mode 100644 src/components/upload/UploadScreen.tsx delete mode 100644 src/components/upload/UploadScreenV1.tsx delete mode 100644 src/components/upload/UploadScreenV2.tsx delete mode 100644 src/components/upload/UploadScreenV4.tsx delete mode 100644 src/components/upload/UploadScreenV5.tsx diff --git a/README.md b/README.md index 27bdc80..296ee25 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Upload your `~/.claude` folder. Get a personalised story of your usage. Find out [![CI](https://github.com/Junaid2005/ccrewind/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Junaid2005/ccrewind/actions/workflows/ci.yml) [![Vercel](https://therealsujitk-vercel-badge.vercel.app/?app=ccrewind)](https://ccrewind.com) +[![Socket Badge](https://badge.socket.dev/npm/package/ccrewind/1.0.0)](https://badge.socket.dev/npm/package/ccrewind/1.0.0) +[![npm version](https://img.shields.io/npm/v/ccrewind?style=flat-square)](https://www.npmjs.com/package/ccrewind) --- @@ -17,22 +19,22 @@ Claude Code Rewind analyses your local Claude Code history and turns it into a f ### The slides -| # | Slide | What it shows | -|---|-------|---------------| -| 1 | Graveyard Shift | When you code - 24h radial clock heatmap | -| 2 | The Delegator | How much you use agents - force-directed bubble chart | -| 3 | Top Projects | Your top 3 repos by messages, tokens, sessions | -| 4 | The Arsenal | Which tools Claude used most - animated bars | -| 5 | Token Furnace | Total tokens consumed - canvas slot machine reveal | -| 6 | Loyalty Test | Which model you stick to - racing bars | -| 7 | Thinking Hours | How long Claude thought on your behalf - EKG brainwave | -| 8 | Commit History | Project activity over time - GitHub-style heatmap | -| 9 | Sharpshooter | Prompt length vs follow-ups - scatter quadrant | -| 10 | The Streak | Consistency calendar | -| 11 | Stop Reason | tool_use vs end_turn split | -| 12 | Retry Spiral | How often you re-prompt - Archimedean spiral | -| 13 | Power Score | Claude Elo out of 1000 | -| 14 | Character Reveal | Your archetype - confetti, mascot, one-liner | +| # | Slide | What it shows | +| --- | ---------------- | ------------------------------------------------------ | +| 1 | Graveyard Shift | When you code - 24h radial clock heatmap | +| 2 | The Delegator | How much you use agents - force-directed bubble chart | +| 3 | Top Projects | Your top 3 repos by messages, tokens, sessions | +| 4 | The Arsenal | Which tools Claude used most - animated bars | +| 5 | Token Furnace | Total tokens consumed - canvas slot machine reveal | +| 6 | Loyalty Test | Which model you stick to - racing bars | +| 7 | Thinking Hours | How long Claude thought on your behalf - EKG brainwave | +| 8 | Commit History | Project activity over time - GitHub-style heatmap | +| 9 | Sharpshooter | Prompt length vs follow-ups - scatter quadrant | +| 10 | The Streak | Consistency calendar | +| 11 | Stop Reason | tool_use vs end_turn split | +| 12 | Retry Spiral | How often you re-prompt - Archimedean spiral | +| 13 | Power Score | Claude Elo out of 1000 | +| 14 | Character Reveal | Your archetype - confetti, mascot, one-liner | After the slides: a **ShareCard** carousel with 6 downloadable cards (character + 5 stats variants), a full-screen **Dashboard** with every stat in one screenshottable page, and a cinematic **Credits** page. @@ -92,9 +94,9 @@ npx ccrewind --setup This copies the report script to `~/.local/share/ccrewind/` and registers two Claude Code slash commands. After setup: -| Command | What it does | -|---------|-------------| -| `/ccrewind` | Runs the terminal stats report inline | +| Command | What it does | +| -------------- | ---------------------------------------------------------------------------- | +| `/ccrewind` | Runs the terminal stats report inline | | `/ccrewind-ui` | Starts the web UI with `~/.claude` auto-detected (requires local repo clone) | Or run without installing: @@ -106,16 +108,16 @@ npx ccrewind --gui # open web UI (requires local repo clone) ### What it shows -| Section | Content | -|---------|---------| -| Cost & Models | Total spend, per-model breakdown with cost | -| Top Projects | Top 5 repos by estimated cost | -| Token Usage | Input / Output / Cache Read / Cache Write with bars | -| Top Tools | Most-used Claude tools | -| Activity by Hour | 24-hour sparkline, peak hour callout | -| Streak | 7×7 calendar grid, longest + current streak | -| Birthday | First session date | -| Character | Your archetype, one-liner, Claude Elo | +| Section | Content | +| ---------------- | --------------------------------------------------- | +| Cost & Models | Total spend, per-model breakdown with cost | +| Top Projects | Top 5 repos by estimated cost | +| Token Usage | Input / Output / Cache Read / Cache Write with bars | +| Top Tools | Most-used Claude tools | +| Activity by Hour | 24-hour sparkline, peak hour callout | +| Streak | 7×7 calendar grid, longest + current streak | +| Birthday | First session date | +| Character | Your archetype, one-liner, Claude Elo | All data read from `~/.claude` — nothing leaves your machine. @@ -189,14 +191,14 @@ The share flow generates a compact URL (`/share?d=...`) encoding 13 dot-separate ### Tech stack -| Layer | Tech | -|-------|------| -| Framework | Next.js 16, React 19, TypeScript | -| Styling | Tailwind v4 with `@theme` tokens | -| Animation | Framer Motion | -| Charts | D3 (radial clock, bubbles, heatmaps, scatter, spiral) | -| Canvas | Slot machine (TokenFurnace), EKG brainwave (ThinkingHours) | -| Deploy | Vercel | +| Layer | Tech | +| --------- | ---------------------------------------------------------- | +| Framework | Next.js 16, React 19, TypeScript | +| Styling | Tailwind v4 with `@theme` tokens | +| Animation | Framer Motion | +| Charts | D3 (radial clock, bubbles, heatmaps, scatter, spiral) | +| Canvas | Slot machine (TokenFurnace), EKG brainwave (ThinkingHours) | +| Deploy | Vercel | --- @@ -233,11 +235,11 @@ Go to [ccrewind.com](https://ccrewind.com) and drop your `~/.claude` folder via ### Finding your .claude folder (it's hidden) -| OS | How to show hidden files | -|----|--------------------------| -| macOS | `Cmd + Shift + .` in the file picker | -| Windows | View → Show → Hidden items | -| Linux | `Ctrl + H` in file manager | +| OS | How to show hidden files | +| ------- | ------------------------------------ | +| macOS | `Cmd + Shift + .` in the file picker | +| Windows | View → Show → Hidden items | +| Linux | `Ctrl + H` in file manager | Your folder is at `~/.claude` - e.g. `/Users/yourname/.claude` or `/home/yourname/.claude`. @@ -346,14 +348,14 @@ npm run setup:command # install /ccrewind slash command from local build We track what matters at the build and deploy layer - not at runtime. -| Signal | Where | -|--------|-------| -| Build status | GitHub Actions - every PR gets a pass/fail | -| Test results | Jest - unit tests printed in CI logs | -| Type errors | TypeScript strict mode - build fails on any type error | -| Format drift | Prettier check in CI - consistent style enforced across all contributors | -| Release changelog | Auto-generated from commits on every `v*` tag | -| Deployment logs | Vercel dashboard - build output, edge runtime warnings, function logs per deployment | +| Signal | Where | +| ----------------- | ------------------------------------------------------------------------------------ | +| Build status | GitHub Actions - every PR gets a pass/fail | +| Test results | Jest - unit tests printed in CI logs | +| Type errors | TypeScript strict mode - build fails on any type error | +| Format drift | Prettier check in CI - consistent style enforced across all contributors | +| Release changelog | Auto-generated from commits on every `v*` tag | +| Deployment logs | Vercel dashboard - build output, edge runtime warnings, function logs per deployment | Zero runtime telemetry is collected. All user data stays in the browser. Observability is entirely at the build and deploy layer, not in the product. @@ -369,7 +371,7 @@ Claude Code Rewind is a proof of concept that shows what is possible when you ac ### What we built -A fun, story-driven experience. Spotify Wrapped for developers. The goal was to make people *feel* their data rather than read a table. Every metric has an archetype. Every stat has a one-liner. The charts are weird on purpose - radial clocks, Archimedean spirals, slot machines - because the default chart types are boring and the data deserves better. +A fun, story-driven experience. Spotify Wrapped for developers. The goal was to make people _feel_ their data rather than read a table. Every metric has an archetype. Every stat has a one-liner. The charts are weird on purpose - radial clocks, Archimedean spirals, slot machines - because the default chart types are boring and the data deserves better. It runs entirely in the browser. Zero backend, zero telemetry, zero infrastructure costs. You drop a folder, you get a story. @@ -381,26 +383,26 @@ This was version one. The fun version. The "what can you even do with this data" **Planned extensions:** -| Feature | What it enables | -|---------|-----------------| +| Feature | What it enables | +| ---------------------------- | ------------------------------------------------------------ | | `/ccrewind-ui` slash command | Open ccrewind.com pre-loaded with your data from Claude Code | -| Weekly/monthly spend trends | Identify your most expensive sessions | -| Cost efficiency score | Tokens per useful output — are you getting value? | -| Team dashboards | Aggregate across multiple `~/.claude` exports (enterprise) | -| Budget alerts | Warn when a project exceeds a token or cost threshold | -| GIF integration | Walid's mascot animations wired into every slide | +| Weekly/monthly spend trends | Identify your most expensive sessions | +| Cost efficiency score | Tokens per useful output — are you getting value? | +| Team dashboards | Aggregate across multiple `~/.claude` exports (enterprise) | +| Budget alerts | Warn when a project exceeds a token or cost threshold | +| GIF integration | Walid's mascot animations wired into every slide | -For enterprise customers who *do* have API access to usage data, the same frontend could be powered by a real-time backend instead of a folder drop. The visualisation layer doesn't change. The data pipeline does. +For enterprise customers who _do_ have API access to usage data, the same frontend could be powered by a real-time backend instead of a folder drop. The visualisation layer doesn't change. The data pipeline does. --- ## Contributors -| Contributor | Role | -|-------------|------| -| [Junaid](https://github.com/Junaid2005) | Engineering, product, data pipeline | -| [Abdul](https://github.com/AbdulAaqib) | Development, slides, product & vision | -| [Walid](https://github.com/samouneh) | Design, mascot GIFs, character art | +| Contributor | Role | +| --------------------------------------- | ------------------------------------- | +| [Junaid](https://github.com/Junaid2005) | Engineering, product, data pipeline | +| [Abdul](https://github.com/AbdulAaqib) | Development, slides, product & vision | +| [Walid](https://github.com/samouneh) | Design, mascot GIFs, character art | --- diff --git a/next.config.ts b/next.config.ts index 62fcbe8..023cd91 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,9 +1,13 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { + turbopack: { + root: __dirname, + }, allowedDevOrigins: [ "microwave-lawrence-stuart-result.trycloudflare.com", "files-airplane-prayers-burst.trycloudflare.com", + "modifications-lanka-span-cinema.trycloudflare.com", ], }; diff --git a/src/app/page.tsx b/src/app/page.tsx index 746617b..41b3928 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,13 +1,8 @@ "use client"; -import { useState, useMemo, Suspense } from "react"; -import { useSearchParams } from "next/navigation"; +import { useState, useMemo } from "react"; import { AnimatePresence, motion } from "framer-motion"; -import UploadScreenV1 from "@/components/upload/UploadScreenV1"; -import UploadScreenV2 from "@/components/upload/UploadScreenV2"; import UploadScreenV3 from "@/components/upload/UploadScreenV3"; -import UploadScreenV4 from "@/components/upload/UploadScreenV4"; -import UploadScreenV5 from "@/components/upload/UploadScreenV5"; import SlideContainer from "@/components/slides/SlideContainer"; import GraveyardShift from "@/components/slides/GraveyardShift"; import Delegator from "@/components/slides/Delegator"; @@ -32,26 +27,6 @@ import { computeElo } from "@/lib/scoring"; type AppPhase = "upload" | "slides" | "reveal"; -function UploadVariant({ onDataParsed }: { onDataParsed: (data: ParsedData) => void }) { - const searchParams = useSearchParams(); - const v = searchParams.get("v"); - - switch (v) { - case "1": - return ; - case "2": - return ; - case "3": - return ; - case "4": - return ; - case "5": - return ; - default: - return ; - } -} - export default function Home() { const [phase, setPhase] = useState("upload"); const [parsedData, setParsedData] = useState(null); @@ -85,9 +60,7 @@ export default function Home() { {phase === "upload" && ( - }> - - + )} diff --git a/src/components/upload/UploadScreen.tsx b/src/components/upload/UploadScreen.tsx deleted file mode 100644 index 7ca4bdb..0000000 --- a/src/components/upload/UploadScreen.tsx +++ /dev/null @@ -1,627 +0,0 @@ -"use client"; - -import { useState, useCallback, useRef, useEffect } from "react"; -import { motion, AnimatePresence } from "framer-motion"; -import { parseClaudeFolder } from "@/lib/parser"; -import { generateDemoData } from "@/lib/demo"; -import { ParsedData } from "@/types"; - -interface UploadScreenProps { - onDataParsed: (data: ParsedData) => void; -} - -const LOADING_MESSAGES = [ - "Reading your .claude folder...", - "Scanning session transcripts...", - "Crunching the numbers...", - "Analyzing your habits...", - "Building your story...", -]; - -const CLI_CMD = "git clone https://github.com/Junaid2005/ccrewind.git && cd ccrewind && npm i && npm run dev"; - -/* ─── Stacked preview cards ─── */ -function PreviewCards() { - return ( -
- {/* Back card — The Sama (uncommon: soft green) */} - -
-
- - Claude Code Rewind - - - Uncommon - -
-
- The Sama -
-
-

- The Sama -

-

- “Somehow always lands on top.” -

-
-
-

Elo

-

- 683 -

-
-
-

Tokens

-

847M

-
-
-

Streak

-

41d

-
-
-
-
-
- - {/* Front card — The Dario (epic: soft purple) */} - -
-
- - Claude Code Rewind - - - Epic - -
-
- The Dario -
-
-

- The Dario -

-

- “You are not building features. You are changing the world. Probably.” -

-
-
-

Elo

-

- 912 -

-
-
-

Tokens

-

2.4B

-
-
-

Sessions

-

1,847

-
-
-
-
-
-
- ); -} - -export default function UploadScreen({ onDataParsed }: UploadScreenProps) { - const [isDragging, setIsDragging] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [loadingText, setLoadingText] = useState("Reading your .claude folder..."); - const [error, setError] = useState(null); - const [localAvailable, setLocalAvailable] = useState(false); - const [showHiddenHelp, setShowHiddenHelp] = useState(false); - const [showRunLocally, setShowRunLocally] = useState(false); - const [copied, setCopied] = useState(false); - const inputRef = useRef(null); - - useEffect(() => { - fetch("/api/local-data/status") - .then((r) => r.json()) - .then((d: { available: boolean }) => { - if (d.available) setLocalAvailable(true); - }) - .catch(() => {}); - }, []); - - const startLoadingCycle = () => { - let idx = 0; - const interval = setInterval(() => { - idx = (idx + 1) % LOADING_MESSAGES.length; - setLoadingText(LOADING_MESSAGES[idx]); - }, 1500); - return interval; - }; - - const handleLocalData = useCallback(async () => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const res = await fetch("/api/local-data"); - if (!res.ok) throw new Error("Failed to load local data"); - const data: ParsedData = await res.json(); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found in ~/.claude"); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to load local data"); - } - }, [onDataParsed]); - - const handleFiles = useCallback( - async (files: FileList) => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const data = await parseClaudeFolder(files); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to parse data"); - } - }, - [onDataParsed] - ); - - const handleDemo = useCallback(() => { - setIsLoading(true); - setLoadingText("Loading demo data..."); - fetch("/demo-data.json") - .then((r) => r.json()) - .then((data) => { - setLoadingText("Ready."); - setTimeout(() => onDataParsed(data), 400); - }) - .catch(() => { - const demoData = generateDemoData(); - setLoadingText("Ready."); - setTimeout(() => onDataParsed(demoData), 400); - }); - }, [onDataParsed]); - - const handleDrop = useCallback( - (e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); - }, - [handleFiles] - ); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(true); - }, []); - const handleDragLeave = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - }, []); - - const handleInputChange = (e: React.ChangeEvent) => { - if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); - }; - - return ( -
- {/* Background */} -
-
-
- - - {isLoading ? ( - /* ── Loading state ── */ - -
- - - -
- - - {loadingText} - - -
- ) : ( - /* ── Upload state ── */ - - {/* ── Left: hero + actions ── */} -
- {/* Headline */} - - -

- Claude Code -
- Rewind. -

-

- Connect your data. Get your character. Share your stats. -

-
- - {/* Actions */} - - {/* Primary CTA — adapts based on local availability */} - {localAvailable ? ( - - ) : ( - - )} - - {/* Secondary CTAs */} -
- {localAvailable && ( - - )} - -
- - {/* Drag hint when dragging */} - - {isDragging && ( - -
-

Drop it like it's hot

-

- release to upload your ~/.claude folder -

-
-
- )} -
- - {/* Utility links */} -
- - - - - - - Your data stays local - - -
- - · - -
-
- - {/* Expandable: hidden file help */} - - {showHiddenHelp && ( - -
-

- Show hidden files first: -

- {[ - { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, - { os: "Windows", hint: "View → Show → Hidden items", note: "" }, - { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, - ].map(({ os, hint, note }) => ( -
- {os} - - {hint} - - {note && {note}} -
- ))} -
-
- )} -
- - {/* Expandable: run locally */} - - {showRunLocally && ( - -
-

- Run locally — auto-detects your ~/.claude -

-
- - {CLI_CMD} - - -
-
-
- )} -
-
-
- - {/* ── Right: stacked preview cards ── */} - - - -
- )} -
- - {/* Error */} - - {error && ( - -

{error}

-
- )} -
- - {/* Hidden file input */} - -
- ); -} diff --git a/src/components/upload/UploadScreenV1.tsx b/src/components/upload/UploadScreenV1.tsx deleted file mode 100644 index e158f3b..0000000 --- a/src/components/upload/UploadScreenV1.tsx +++ /dev/null @@ -1,663 +0,0 @@ -"use client"; - -import { useState, useCallback, useRef, useEffect } from "react"; -import { motion, AnimatePresence } from "framer-motion"; -import { parseClaudeFolder } from "@/lib/parser"; -import { generateDemoData } from "@/lib/demo"; -import { ParsedData } from "@/types"; - -interface UploadScreenProps { - onDataParsed: (data: ParsedData) => void; -} - -const LOADING_MESSAGES = [ - "Reading your .claude folder...", - "Scanning session transcripts...", - "Crunching the numbers...", - "Analyzing your habits...", - "Building your story...", -]; - -/* ─── Stacked preview cards ─── */ -function PreviewCards() { - return ( -
- {/* Back card — The Sama (uncommon: soft green) */} - -
-
- - Claude Code Rewind - - - Uncommon - -
-
- The Sama -
-
-

- The Sama -

-

- “Somehow always lands on top.” -

-
-
-

Elo

-

- 683 -

-
-
-

Tokens

-

847M

-
-
-

Streak

-

41d

-
-
-
-
-
- - {/* Front card — The Dario (epic: soft purple) */} - -
-
- - Claude Code Rewind - - - Epic - -
-
- The Dario -
-
-

- The Dario -

-

- “You are not building features. You are changing the world. Probably.” -

-
-
-

Elo

-

- 912 -

-
-
-

Tokens

-

2.4B

-
-
-

Sessions

-

1,847

-
-
-
-
-
-
- ); -} - -function TabContentNPM({ active }: { active: boolean }) { - const [lines, setLines] = useState(0); - useEffect(() => { - if (!active) { - setLines(0); - return; - } - const timeouts = [ - setTimeout(() => setLines(1), 50), - setTimeout(() => setLines(2), 100), - setTimeout(() => setLines(3), 150), - setTimeout(() => setLines(4), 200), - setTimeout(() => setLines(5), 250), - ]; - return () => timeouts.forEach(clearTimeout); - }, [active]); - - return ( -
-
-
-
-
-
-
-

- $ npx ccrewind -

- {lines >= 1 &&

◆ CC REWIND ◆

} - {lines >= 2 &&

Reading ~/.claude...

} - {lines >= 3 && ( -

- Your character: The Quant -

- )} - {lines >= 4 && ( -

- Claude Elo: 847 / 1000 -

- )} - {lines >= 5 &&

→ ccrewind.com for the full story

} -
- -
- ); -} - -function TabContentClaude() { - const [copied, setCopied] = useState(false); - const copyCmd = () => { - navigator.clipboard.writeText("npx ccrewind --setup"); - setCopied(true); - setTimeout(() => setCopied(false), 1500); - }; - return ( -
-

Run inside Claude Code

-
- npx ccrewind --setup - -
-
-
- /ccrewind - - Terminal stats report - -
-
- /ccrewind-ui - - Opens web UI with auto-detected data - -
-
-

- Registers slash commands in your Claude Code session -

-
- ); -} - -export default function UploadScreenV1({ onDataParsed }: UploadScreenProps) { - const [isDragging, setIsDragging] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); - const [error, setError] = useState(null); - const [localAvailable, setLocalAvailable] = useState(false); - const [showHiddenHelp, setShowHiddenHelp] = useState(false); - const inputRef = useRef(null); - - const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); - - useEffect(() => { - fetch("/api/local-data/status") - .then((r) => r.json()) - .then((d: { available: boolean }) => { - if (d.available) setLocalAvailable(true); - }) - .catch(() => {}); - }, []); - - const startLoadingCycle = () => { - let idx = 0; - const interval = setInterval(() => { - idx = (idx + 1) % LOADING_MESSAGES.length; - setLoadingText(LOADING_MESSAGES[idx]); - }, 1500); - return interval; - }; - - const handleLocalData = useCallback(async () => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const res = await fetch("/api/local-data"); - if (!res.ok) throw new Error("Failed to load local data"); - const data: ParsedData = await res.json(); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found in ~/.claude"); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to load local data"); - } - }, [onDataParsed]); - - const handleFiles = useCallback( - async (files: FileList) => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const data = await parseClaudeFolder(files); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to parse data"); - } - }, - [onDataParsed] - ); - - const handleDemo = useCallback(() => { - setIsLoading(true); - setLoadingText("Loading demo data..."); - fetch("/demo-data.json") - .then((r) => r.json()) - .then((data) => { - setLoadingText("Ready."); - setTimeout(() => onDataParsed(data), 400); - }) - .catch(() => { - const demoData = generateDemoData(); - setLoadingText("Ready."); - setTimeout(() => onDataParsed(demoData), 400); - }); - }, [onDataParsed]); - - const handleDrop = useCallback( - (e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); - }, - [handleFiles] - ); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(true); - }, []); - const handleDragLeave = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - }, []); - - const handleInputChange = (e: React.ChangeEvent) => { - if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); - }; - - const TabContentWeb = () => ( -
- {localAvailable ? ( - - ) : ( - - )} - -
- {localAvailable && ( - - )} - -
- -
- - - - - - Your data stays local - -
- -
-
- - - {showHiddenHelp && ( - -
-

- Show hidden files first: -

- {[ - { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, - { os: "Windows", hint: "View → Show → Hidden items", note: "" }, - { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, - ].map(({ os, hint, note }) => ( -
- {os} - - {hint} - - {note && {note}} -
- ))} -
-
- )} -
-
- ); - - return ( -
-
-
-
- - - {isLoading ? ( - -
- - - -
- - - {loadingText} - - -
- ) : ( - -
- -

- Claude Code -
- Rewind. -

-

- Connect your data. Get your character. Share your stats. -

-
- -
-
- {(["npm", "web", "claude"] as const).map((t) => ( - - ))} -
-
- {activeTab === "npm" && } - {activeTab === "web" && } - {activeTab === "claude" && } -
-
-
- - - - -
- )} -
- - - {isDragging && ( - -
-

Drop it like it's hot

-

- release to upload your ~/.claude folder -

-
-
- )} -
- - - {error && ( - -

{error}

-
- )} -
- - -
- ); -} diff --git a/src/components/upload/UploadScreenV2.tsx b/src/components/upload/UploadScreenV2.tsx deleted file mode 100644 index d6e364f..0000000 --- a/src/components/upload/UploadScreenV2.tsx +++ /dev/null @@ -1,662 +0,0 @@ -"use client"; - -import { useState, useCallback, useRef, useEffect } from "react"; -import { motion, AnimatePresence } from "framer-motion"; -import { parseClaudeFolder } from "@/lib/parser"; -import { generateDemoData } from "@/lib/demo"; -import { ParsedData } from "@/types"; - -interface UploadScreenProps { - onDataParsed: (data: ParsedData) => void; -} - -const LOADING_MESSAGES = [ - "Reading your .claude folder...", - "Scanning session transcripts...", - "Crunching the numbers...", - "Analyzing your habits...", - "Building your story...", -]; - -/* ─── Stacked preview cards ─── */ -function PreviewCards() { - return ( -
- {/* Back card — The Sama (uncommon: soft green) */} - -
-
- - Claude Code Rewind - - - Uncommon - -
-
- The Sama -
-
-

- The Sama -

-

- “Somehow always lands on top.” -

-
-
-

Elo

-

- 683 -

-
-
-

Tokens

-

847M

-
-
-

Streak

-

41d

-
-
-
-
-
- - {/* Front card — The Dario (epic: soft purple) */} - -
-
- - Claude Code Rewind - - - Epic - -
-
- The Dario -
-
-

- The Dario -

-

- “You are not building features. You are changing the world. Probably.” -

-
-
-

Elo

-

- 912 -

-
-
-

Tokens

-

2.4B

-
-
-

Sessions

-

1,847

-
-
-
-
-
-
- ); -} - -function TabContentNPM({ active }: { active: boolean }) { - const [lines, setLines] = useState(0); - useEffect(() => { - if (!active) { - setLines(0); - return; - } - const timeouts = [ - setTimeout(() => setLines(1), 50), - setTimeout(() => setLines(2), 100), - setTimeout(() => setLines(3), 150), - setTimeout(() => setLines(4), 200), - setTimeout(() => setLines(5), 250), - ]; - return () => timeouts.forEach(clearTimeout); - }, [active]); - - return ( -
-
-
-
-
-
-
-

- $ npx ccrewind -

- {lines >= 1 &&

◆ CC REWIND ◆

} - {lines >= 2 &&

Reading ~/.claude...

} - {lines >= 3 && ( -

- Your character: The Quant -

- )} - {lines >= 4 && ( -

- Claude Elo: 847 / 1000 -

- )} - {lines >= 5 &&

→ ccrewind.com for the full story

} -
- -
- ); -} - -function TabContentClaude() { - const [copied, setCopied] = useState(false); - const copyCmd = () => { - navigator.clipboard.writeText("npx ccrewind --setup"); - setCopied(true); - setTimeout(() => setCopied(false), 1500); - }; - return ( -
-

Run inside Claude Code

-
- npx ccrewind --setup - -
-
-
- /ccrewind - - Terminal stats report - -
-
- /ccrewind-ui - - Opens web UI with auto-detected data - -
-
-

- Registers slash commands in your Claude Code session -

-
- ); -} - -export default function UploadScreenV2({ onDataParsed }: UploadScreenProps) { - const [isDragging, setIsDragging] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); - const [error, setError] = useState(null); - const [localAvailable, setLocalAvailable] = useState(false); - const [showHiddenHelp, setShowHiddenHelp] = useState(false); - const inputRef = useRef(null); - - const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); - - useEffect(() => { - fetch("/api/local-data/status") - .then((r) => r.json()) - .then((d: { available: boolean }) => { - if (d.available) setLocalAvailable(true); - }) - .catch(() => {}); - }, []); - - const startLoadingCycle = () => { - let idx = 0; - const interval = setInterval(() => { - idx = (idx + 1) % LOADING_MESSAGES.length; - setLoadingText(LOADING_MESSAGES[idx]); - }, 1500); - return interval; - }; - - const handleLocalData = useCallback(async () => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const res = await fetch("/api/local-data"); - if (!res.ok) throw new Error("Failed to load local data"); - const data: ParsedData = await res.json(); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found in ~/.claude"); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to load local data"); - } - }, [onDataParsed]); - - const handleFiles = useCallback( - async (files: FileList) => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const data = await parseClaudeFolder(files); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to parse data"); - } - }, - [onDataParsed] - ); - - const handleDemo = useCallback(() => { - setIsLoading(true); - setLoadingText("Loading demo data..."); - fetch("/demo-data.json") - .then((r) => r.json()) - .then((data) => { - setLoadingText("Ready."); - setTimeout(() => onDataParsed(data), 400); - }) - .catch(() => { - const demoData = generateDemoData(); - setLoadingText("Ready."); - setTimeout(() => onDataParsed(demoData), 400); - }); - }, [onDataParsed]); - - const handleDrop = useCallback( - (e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); - }, - [handleFiles] - ); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(true); - }, []); - const handleDragLeave = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - }, []); - - const handleInputChange = (e: React.ChangeEvent) => { - if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); - }; - - const TabContentWeb = () => ( -
- {localAvailable ? ( - - ) : ( - - )} - -
- {localAvailable && ( - - )} - -
- -
- - - - - - Your data stays local - -
- -
-
- - - {showHiddenHelp && ( - -
-

- Show hidden files first: -

- {[ - { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, - { os: "Windows", hint: "View → Show → Hidden items", note: "" }, - { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, - ].map(({ os, hint, note }) => ( -
- {os} - - {hint} - - {note && {note}} -
- ))} -
-
- )} -
-
- ); - - return ( -
-
-
-
- - - {isLoading ? ( - -
- - - -
- - - {loadingText} - - -
- ) : ( - -
- -

- Claude Code -
- Rewind. -

-

- Connect your data. Get your character. Share your stats. -

-
- -
-
- {(["npm", "web", "claude"] as const).map((t) => ( - - ))} -
-
- {activeTab === "npm" && } - {activeTab === "web" && } - {activeTab === "claude" && } -
-
-
- - - - -
- )} -
- - - {isDragging && ( - -
-

Drop it like it's hot

-

- release to upload your ~/.claude folder -

-
-
- )} -
- - - {error && ( - -

{error}

-
- )} -
- - -
- ); -} diff --git a/src/components/upload/UploadScreenV3.tsx b/src/components/upload/UploadScreenV3.tsx index 43a21ee..70fcafd 100644 --- a/src/components/upload/UploadScreenV3.tsx +++ b/src/components/upload/UploadScreenV3.tsx @@ -201,14 +201,19 @@ function TabContentNPM({ active }: { active: boolean }) { )} {lines >= 5 &&

→ ccrewind.com for the full story

}
- @@ -223,13 +228,21 @@ function TabContentClaude() { setTimeout(() => setCopied(false), 1500); }; return ( -
+

Run inside Claude Code

-
- npx ccrewind --setup +
+
+ + + npx ccrewind --setup + + + # registers slash commands + +
-
+
/ccrewind - - Terminal stats report -
-
+
/ccrewind-ui - - Opens web UI with auto-detected data -
-

- Registers slash commands in your Claude Code session -

); } @@ -292,6 +296,7 @@ export default function UploadScreenV3({ onDataParsed }: UploadScreenProps) { const inputRef = useRef(null); const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); + const [everExpandedNpm, setEverExpandedNpm] = useState(false); const [hasInteracted, setHasInteracted] = useState(false); useEffect(() => { @@ -419,7 +424,7 @@ export default function UploadScreenV3({ onDataParsed }: UploadScreenProps) { )} -
+
{localAvailable && ( )} - +
+ - - {!hasInteracted && ( - - 👆 - - )} - + + {!hasInteracted && ( + + + + + + )} + +
@@ -596,12 +611,18 @@ export default function UploadScreenV3({ onDataParsed }: UploadScreenProps) {

-
+
{(["npm", "web", "claude"] as const).map((t, idx) => ( ))}
-
- {activeTab === "npm" && } - {activeTab === "web" && } - {activeTab === "claude" && } -
-
+ + + {activeTab === "npm" && ( + + + + )} + {activeTab === "web" && ( + + + + )} + {activeTab === "claude" && ( + + + + )} + + +
void; -} - -const LOADING_MESSAGES = [ - "Reading your .claude folder...", - "Scanning session transcripts...", - "Crunching the numbers...", - "Analyzing your habits...", - "Building your story...", -]; - -/* ─── Stacked preview cards ─── */ -function PreviewCards() { - return ( -
- {/* Back card — The Sama (uncommon: soft green) */} - -
-
- - Claude Code Rewind - - - Uncommon - -
-
- The Sama -
-
-

- The Sama -

-

- “Somehow always lands on top.” -

-
-
-

Elo

-

- 683 -

-
-
-

Tokens

-

847M

-
-
-

Streak

-

41d

-
-
-
-
-
- - {/* Front card — The Dario (epic: soft purple) */} - -
-
- - Claude Code Rewind - - - Epic - -
-
- The Dario -
-
-

- The Dario -

-

- “You are not building features. You are changing the world. Probably.” -

-
-
-

Elo

-

- 912 -

-
-
-

Tokens

-

2.4B

-
-
-

Sessions

-

1,847

-
-
-
-
-
-
- ); -} - -function TabContentNPM({ active }: { active: boolean }) { - const [lines, setLines] = useState(0); - useEffect(() => { - if (!active) { - setLines(0); - return; - } - const timeouts = [ - setTimeout(() => setLines(1), 50), - setTimeout(() => setLines(2), 100), - setTimeout(() => setLines(3), 150), - setTimeout(() => setLines(4), 200), - setTimeout(() => setLines(5), 250), - ]; - return () => timeouts.forEach(clearTimeout); - }, [active]); - - return ( -
-
-
-
-
-
-
-

- $ npx ccrewind -

- {lines >= 1 &&

◆ CC REWIND ◆

} - {lines >= 2 &&

Reading ~/.claude...

} - {lines >= 3 && ( -

- Your character: The Quant -

- )} - {lines >= 4 && ( -

- Claude Elo: 847 / 1000 -

- )} - {lines >= 5 &&

→ ccrewind.com for the full story

} -
- -
- ); -} - -function TabContentClaude() { - const [copied, setCopied] = useState(false); - const copyCmd = () => { - navigator.clipboard.writeText("npx ccrewind --setup"); - setCopied(true); - setTimeout(() => setCopied(false), 1500); - }; - return ( -
-

Run inside Claude Code

-
- npx ccrewind --setup - -
-
-
- /ccrewind - - Terminal stats report - -
-
- /ccrewind-ui - - Opens web UI with auto-detected data - -
-
-

- Registers slash commands in your Claude Code session -

-
- ); -} - -export default function UploadScreenV4({ onDataParsed }: UploadScreenProps) { - const [isDragging, setIsDragging] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); - const [error, setError] = useState(null); - const [localAvailable, setLocalAvailable] = useState(false); - const [showHiddenHelp, setShowHiddenHelp] = useState(false); - const inputRef = useRef(null); - - const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); - - useEffect(() => { - fetch("/api/local-data/status") - .then((r) => r.json()) - .then((d: { available: boolean }) => { - if (d.available) setLocalAvailable(true); - }) - .catch(() => {}); - }, []); - - const startLoadingCycle = () => { - let idx = 0; - const interval = setInterval(() => { - idx = (idx + 1) % LOADING_MESSAGES.length; - setLoadingText(LOADING_MESSAGES[idx]); - }, 1500); - return interval; - }; - - const handleLocalData = useCallback(async () => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const res = await fetch("/api/local-data"); - if (!res.ok) throw new Error("Failed to load local data"); - const data: ParsedData = await res.json(); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found in ~/.claude"); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to load local data"); - } - }, [onDataParsed]); - - const handleFiles = useCallback( - async (files: FileList) => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const data = await parseClaudeFolder(files); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to parse data"); - } - }, - [onDataParsed] - ); - - const handleDemo = useCallback(() => { - setIsLoading(true); - setLoadingText("Loading demo data..."); - fetch("/demo-data.json") - .then((r) => r.json()) - .then((data) => { - setLoadingText("Ready."); - setTimeout(() => onDataParsed(data), 400); - }) - .catch(() => { - const demoData = generateDemoData(); - setLoadingText("Ready."); - setTimeout(() => onDataParsed(demoData), 400); - }); - }, [onDataParsed]); - - const handleDrop = useCallback( - (e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); - }, - [handleFiles] - ); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(true); - }, []); - const handleDragLeave = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - }, []); - - const handleInputChange = (e: React.ChangeEvent) => { - if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); - }; - - const TabContentWeb = () => ( -
- {localAvailable ? ( - - ) : ( - - )} - -
- {localAvailable && ( - - )} - -
- -
- - - - - - Your data stays local - -
- -
-
- - - {showHiddenHelp && ( - -
-

- Show hidden files first: -

- {[ - { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, - { os: "Windows", hint: "View → Show → Hidden items", note: "" }, - { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, - ].map(({ os, hint, note }) => ( -
- {os} - - {hint} - - {note && {note}} -
- ))} -
-
- )} -
-
- ); - - return ( -
-
-
-
- - - {isLoading ? ( - -
- - - -
- - - {loadingText} - - -
- ) : ( - -
- -

- Claude Code -
- Rewind. -

-

- Connect your data. Get your character. Share your stats. -

-
- -
-
- {(["npm", "web", "claude"] as const).map((t) => ( - - ))} -
-
- {activeTab === "npm" && } - {activeTab === "web" && } - {activeTab === "claude" && } -
-
-
- - - - -
- )} -
- - - {isDragging && ( - -
-

Drop it like it's hot

-

- release to upload your ~/.claude folder -

-
-
- )} -
- - - {error && ( - -

{error}

-
- )} -
- - -
- ); -} diff --git a/src/components/upload/UploadScreenV5.tsx b/src/components/upload/UploadScreenV5.tsx deleted file mode 100644 index c116d51..0000000 --- a/src/components/upload/UploadScreenV5.tsx +++ /dev/null @@ -1,664 +0,0 @@ -"use client"; - -import { useState, useCallback, useRef, useEffect } from "react"; -import { motion, AnimatePresence } from "framer-motion"; -import { parseClaudeFolder } from "@/lib/parser"; -import { generateDemoData } from "@/lib/demo"; -import { ParsedData } from "@/types"; - -interface UploadScreenProps { - onDataParsed: (data: ParsedData) => void; -} - -const LOADING_MESSAGES = [ - "Reading your .claude folder...", - "Scanning session transcripts...", - "Crunching the numbers...", - "Analyzing your habits...", - "Building your story...", -]; - -/* ─── Stacked preview cards ─── */ -function PreviewCards() { - return ( -
- {/* Back card — The Sama (uncommon: soft green) */} - -
-
- - Claude Code Rewind - - - Uncommon - -
-
- The Sama -
-
-

- The Sama -

-

- “Somehow always lands on top.” -

-
-
-

Elo

-

- 683 -

-
-
-

Tokens

-

847M

-
-
-

Streak

-

41d

-
-
-
-
-
- - {/* Front card — The Dario (epic: soft purple) */} - -
-
- - Claude Code Rewind - - - Epic - -
-
- The Dario -
-
-

- The Dario -

-

- “You are not building features. You are changing the world. Probably.” -

-
-
-

Elo

-

- 912 -

-
-
-

Tokens

-

2.4B

-
-
-

Sessions

-

1,847

-
-
-
-
-
-
- ); -} - -function TabContentNPM({ active }: { active: boolean }) { - const [lines, setLines] = useState(0); - useEffect(() => { - if (!active) { - setLines(0); - return; - } - const timeouts = [ - setTimeout(() => setLines(1), 50), - setTimeout(() => setLines(2), 100), - setTimeout(() => setLines(3), 150), - setTimeout(() => setLines(4), 200), - setTimeout(() => setLines(5), 250), - ]; - return () => timeouts.forEach(clearTimeout); - }, [active]); - - return ( -
-
-
-
-
-
-
-

- $ npx ccrewind -

- {lines >= 1 &&

◆ CC REWIND ◆

} - {lines >= 2 &&

Reading ~/.claude...

} - {lines >= 3 && ( -

- Your character: The Quant -

- )} - {lines >= 4 && ( -

- Claude Elo: 847 / 1000 -

- )} - {lines >= 5 &&

→ ccrewind.com for the full story

} -
- -
- ); -} - -function TabContentClaude() { - const [copied, setCopied] = useState(false); - const copyCmd = () => { - navigator.clipboard.writeText("npx ccrewind --setup"); - setCopied(true); - setTimeout(() => setCopied(false), 1500); - }; - return ( -
-

Run inside Claude Code

-
- npx ccrewind --setup - -
-
-
- /ccrewind - - Terminal stats report - -
-
- /ccrewind-ui - - Opens web UI with auto-detected data - -
-
-

- Registers slash commands in your Claude Code session -

-
- ); -} - -export default function UploadScreenV5({ onDataParsed }: UploadScreenProps) { - const [isDragging, setIsDragging] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); - const [error, setError] = useState(null); - const [localAvailable, setLocalAvailable] = useState(false); - const [showHiddenHelp, setShowHiddenHelp] = useState(false); - const inputRef = useRef(null); - - const [activeTab, setActiveTab] = useState<"npm" | "web" | "claude">("web"); - - useEffect(() => { - fetch("/api/local-data/status") - .then((r) => r.json()) - .then((d: { available: boolean }) => { - if (d.available) setLocalAvailable(true); - }) - .catch(() => {}); - }, []); - - const startLoadingCycle = () => { - let idx = 0; - const interval = setInterval(() => { - idx = (idx + 1) % LOADING_MESSAGES.length; - setLoadingText(LOADING_MESSAGES[idx]); - }, 1500); - return interval; - }; - - const handleLocalData = useCallback(async () => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const res = await fetch("/api/local-data"); - if (!res.ok) throw new Error("Failed to load local data"); - const data: ParsedData = await res.json(); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found in ~/.claude"); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to load local data"); - } - }, [onDataParsed]); - - const handleFiles = useCallback( - async (files: FileList) => { - setIsLoading(true); - setError(null); - setLoadingText(LOADING_MESSAGES[0]); - const interval = startLoadingCycle(); - try { - const data = await parseClaudeFolder(files); - if (!data.statsCache && data.history.length === 0 && data.sessions.length === 0) { - throw new Error("No Claude data found. Make sure you selected your ~/.claude folder."); - } - clearInterval(interval); - setLoadingText("Ready."); - await new Promise((r) => setTimeout(r, 500)); - onDataParsed(data); - } catch (e) { - clearInterval(interval); - setIsLoading(false); - setError(e instanceof Error ? e.message : "Failed to parse data"); - } - }, - [onDataParsed] - ); - - const handleDemo = useCallback(() => { - setIsLoading(true); - setLoadingText("Loading demo data..."); - fetch("/demo-data.json") - .then((r) => r.json()) - .then((data) => { - setLoadingText("Ready."); - setTimeout(() => onDataParsed(data), 400); - }) - .catch(() => { - const demoData = generateDemoData(); - setLoadingText("Ready."); - setTimeout(() => onDataParsed(demoData), 400); - }); - }, [onDataParsed]); - - const handleDrop = useCallback( - (e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - if (e.dataTransfer.files.length > 0) handleFiles(e.dataTransfer.files); - }, - [handleFiles] - ); - - const handleDragOver = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(true); - }, []); - const handleDragLeave = useCallback((e: React.DragEvent) => { - e.preventDefault(); - setIsDragging(false); - }, []); - - const handleInputChange = (e: React.ChangeEvent) => { - if (e.target.files && e.target.files.length > 0) handleFiles(e.target.files); - }; - - const TabContentWeb = () => ( -
- {localAvailable ? ( - - ) : ( - - )} - -
- {localAvailable && ( - - )} - -
- -
- - - - - - Your data stays local - -
- -
-
- - - {showHiddenHelp && ( - -
-

- Show hidden files first: -

- {[ - { os: "macOS", hint: "Cmd + Shift + .", note: "in Finder" }, - { os: "Windows", hint: "View → Show → Hidden items", note: "" }, - { os: "Linux", hint: "Ctrl + H", note: "in file manager" }, - ].map(({ os, hint, note }) => ( -
- {os} - - {hint} - - {note && {note}} -
- ))} -
-
- )} -
-
- ); - - return ( -
-
-
-
- - - {isLoading ? ( - -
- - - -
- - - {loadingText} - - -
- ) : ( - -
- -

- Claude Code -
- Rewind. -

-

- Connect your data. Get your character. Share your stats. -

-
- -
-
- {(["npm", "web", "claude"] as const).map((t) => ( - - ))} -
-
- {activeTab === "npm" && } - {activeTab === "web" && } - {activeTab === "claude" && } -
-
-
- - - - -
- )} -
- - - {isDragging && ( - -
-

Drop it like it's hot

-

- release to upload your ~/.claude folder -

-
-
- )} -
- - - {error && ( - -

{error}

-
- )} -
- - -
- ); -} From c38b118bfa41d53c6cd9cc4281a6c160ea4d1486 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 18:41:03 +0100 Subject: [PATCH 03/15] feat(cli): render mascot image in character reveal via chafa Adds chafa-based ANSI art rendering to the character reveal section of the TUI. Resolves the mascot PNG from public/mascots/ relative to the bundle (works in both repo and npm-installed contexts). Gracefully falls back to text-only layout when chafa is not installed. Install chafa to enable: brew install chafa / sudo apt install chafa Co-Authored-By: Claude Sonnet 4.6 --- scripts/ccrewind-tui.ts | 76 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/scripts/ccrewind-tui.ts b/scripts/ccrewind-tui.ts index ddbabdf..7723869 100644 --- a/scripts/ccrewind-tui.ts +++ b/scripts/ccrewind-tui.ts @@ -4,6 +4,7 @@ import { computeElo } from "@/lib/scoring"; import { assignCharacter } from "@/lib/archetypes"; import type { ComputedStats, EloBreakdown, Character } from "@/types"; import fs from "fs/promises"; +import { spawnSync } from "child_process"; import path from "path"; import os from "os"; import { fileURLToPath } from "url"; @@ -267,10 +268,73 @@ function renderStreak(stats: ComputedStats): string { ].join("\n"); } +// ── Mascot image map (character name → mascot filename) ────────────────────── +const MASCOT_FILES: Record = { + "The Intern": "char-the-intern.png", + "The Degen": "char-the-degen.png", + "The SBF": "char-the-ghost.png", + "The Sama": "char-the-operator.png", + "The Quant": "char-the-quant.png", + "The Musk": "char-the-chaos-agent.png", + "The Dario": "char-the-visionary.png", + "The Karpathy": "char-the-night-shift-engineer.png", + "Slough Boy": "char-the-researcher.png", +}; + +function findMascotImagePath(characterName: string): string | null { + const filename = MASCOT_FILES[characterName] ?? "character-reveal.png"; + // When running from dist/ (npm install), mascots live at ../../public/mascots/ + // When running from repo root, they live at public/mascots/ + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + const candidates = [ + path.join(__dirname, "..", "public", "mascots", filename), + path.join(__dirname, "..", "..", "public", "mascots", filename), + path.join(process.cwd(), "public", "mascots", filename), + ]; + for (const p of candidates) { + try { + // sync check — fine here since we're outside the hot path + const { statSync } = require("fs"); + statSync(p); + return p; + } catch { + // not found at this path, try next + } + } + return null; +} + +function renderMascotWithChafa(imagePath: string): string | null { + try { + const result = spawnSync( + "chafa", + ["--size=46x22", "--symbols=block+border", "--colors=256", imagePath], + { encoding: "utf8", timeout: 5000 } + ); + if (result.status === 0 && result.stdout) { + return result.stdout.trimEnd(); + } + } catch { + // chafa not installed or failed — silently skip + } + return null; +} + function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, character: Character): string { - return [ - sectionHeader("YOUR CHARACTER"), - "", + const lines: string[] = [sectionHeader("YOUR CHARACTER"), ""]; + + const imagePath = findMascotImagePath(character.name); + if (imagePath) { + const art = renderMascotWithChafa(imagePath); + if (art) { + // indent each line of the art by 2 spaces + art.split("\n").forEach((l) => lines.push(" " + l)); + lines.push(""); + } + } + + lines.push( ` ${orange("\u2726")} ${bold(white(character.name))} ${orange("\u2726")}`, ` ${dim('"')}${white(character.oneLiner)}${dim('"')}`, "", @@ -278,8 +342,10 @@ function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, charact ` ${renderEloBar(elo.total)}`, "", ` ${dim(character.endingLine)}`, - "", - ].join("\n"); + "" + ); + + return lines.join("\n"); } function renderFooter(): string { From 5480c6e3725ac248469f8a7c5b4569781a90b364 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 18:45:16 +0100 Subject: [PATCH 04/15] =?UTF-8?q?feat(cli):=20embed=20pre-rendered=20ANSI?= =?UTF-8?q?=20mascot=20art=20=E2=80=94=20no=20chafa=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert all 9 character mascot PNGs to ANSI 256-color half-block art at build time using PIL. The resulting ANSI strings are embedded statically in mascots-ansi.ts and bundled into dist/ccrewind-tui.mjs. - scripts/generate-mascot-art.py — regenerates mascots-ansi.ts when mascots change - scripts/mascots-ansi.ts — auto-generated static ANSI art map (~60KB) - renderCharacterReveal() — directly reads from MASCOT_ART, no runtime deps Bundle size: 42KB → 137KB (95KB added for all 9 images) Zero runtime dependencies required. Co-Authored-By: Claude Sonnet 4.6 --- scripts/ccrewind-tui.ts | 67 +---------- scripts/generate-mascot-art.py | 132 ++++++++++++++++++++ scripts/mascots-ansi.ts | 212 +++++++++++++++++++++++++++++++++ 3 files changed, 349 insertions(+), 62 deletions(-) create mode 100644 scripts/generate-mascot-art.py create mode 100644 scripts/mascots-ansi.ts diff --git a/scripts/ccrewind-tui.ts b/scripts/ccrewind-tui.ts index 7723869..13fb7dc 100644 --- a/scripts/ccrewind-tui.ts +++ b/scripts/ccrewind-tui.ts @@ -4,10 +4,10 @@ import { computeElo } from "@/lib/scoring"; import { assignCharacter } from "@/lib/archetypes"; import type { ComputedStats, EloBreakdown, Character } from "@/types"; import fs from "fs/promises"; -import { spawnSync } from "child_process"; import path from "path"; import os from "os"; import { fileURLToPath } from "url"; +import { MASCOT_ART } from "./mascots-ansi"; // ── ANSI helpers ────────────────────────────────────────── @@ -268,70 +268,13 @@ function renderStreak(stats: ComputedStats): string { ].join("\n"); } -// ── Mascot image map (character name → mascot filename) ────────────────────── -const MASCOT_FILES: Record = { - "The Intern": "char-the-intern.png", - "The Degen": "char-the-degen.png", - "The SBF": "char-the-ghost.png", - "The Sama": "char-the-operator.png", - "The Quant": "char-the-quant.png", - "The Musk": "char-the-chaos-agent.png", - "The Dario": "char-the-visionary.png", - "The Karpathy": "char-the-night-shift-engineer.png", - "Slough Boy": "char-the-researcher.png", -}; - -function findMascotImagePath(characterName: string): string | null { - const filename = MASCOT_FILES[characterName] ?? "character-reveal.png"; - // When running from dist/ (npm install), mascots live at ../../public/mascots/ - // When running from repo root, they live at public/mascots/ - const __filename = fileURLToPath(import.meta.url); - const __dirname = path.dirname(__filename); - const candidates = [ - path.join(__dirname, "..", "public", "mascots", filename), - path.join(__dirname, "..", "..", "public", "mascots", filename), - path.join(process.cwd(), "public", "mascots", filename), - ]; - for (const p of candidates) { - try { - // sync check — fine here since we're outside the hot path - const { statSync } = require("fs"); - statSync(p); - return p; - } catch { - // not found at this path, try next - } - } - return null; -} - -function renderMascotWithChafa(imagePath: string): string | null { - try { - const result = spawnSync( - "chafa", - ["--size=46x22", "--symbols=block+border", "--colors=256", imagePath], - { encoding: "utf8", timeout: 5000 } - ); - if (result.status === 0 && result.stdout) { - return result.stdout.trimEnd(); - } - } catch { - // chafa not installed or failed — silently skip - } - return null; -} - function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, character: Character): string { const lines: string[] = [sectionHeader("YOUR CHARACTER"), ""]; - const imagePath = findMascotImagePath(character.name); - if (imagePath) { - const art = renderMascotWithChafa(imagePath); - if (art) { - // indent each line of the art by 2 spaces - art.split("\n").forEach((l) => lines.push(" " + l)); - lines.push(""); - } + const art = MASCOT_ART[character.name]; + if (art) { + art.split("\n").forEach((l) => lines.push(" " + l)); + lines.push(""); } lines.push( diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py new file mode 100644 index 0000000..8b51675 --- /dev/null +++ b/scripts/generate-mascot-art.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +""" +Generate ANSI half-block art for all character mascots and write to +scripts/mascots-ansi.ts. Run from repo root: + + python3 scripts/generate-mascot-art.py + +Requires: Pillow (pip install pillow) +""" + +import json +import os +import sys + +try: + from PIL import Image +except ImportError: + print("Error: Pillow is required. Run: pip install pillow") + sys.exit(1) + +REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") +OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") + +WIDTH = 46 +HEIGHT = 22 + +CHAR_MAP: dict[str, str] = { + "The Intern": "char-the-intern.png", + "The Degen": "char-the-degen.png", + "The SBF": "char-the-ghost.png", + "The Sama": "char-the-operator.png", + "The Quant": "char-the-quant.png", + "The Musk": "char-the-chaos-agent.png", + "The Dario": "char-the-visionary.png", + "The Karpathy": "char-the-night-shift-engineer.png", + "Slough Boy": "char-the-researcher.png", +} + +ESC = "\x1b" +RESET = f"{ESC}[0m" + + +def rgb_to_256(r: int, g: int, b: int) -> int: + ri = round(r / 255 * 5) + gi = round(g / 255 * 5) + bi = round(b / 255 * 5) + return 16 + 36 * ri + 6 * gi + bi + + +def img_to_ansi(path: str) -> str: + img = Image.open(path).convert("RGBA") + img = img.resize((WIDTH, HEIGHT * 2), Image.LANCZOS) + + lines = [] + for row in range(HEIGHT): + parts: list[str] = [] + cur_fg = cur_bg = -1 + for col in range(WIDTH): + tr, tg, tb, ta = img.getpixel((col, row * 2)) + br, bg_, bb, ba = img.getpixel((col, row * 2 + 1)) + tc = rgb_to_256(tr, tg, tb) if ta >= 30 else None + bc = rgb_to_256(br, bg_, bb) if ba >= 30 else None + + if tc is None and bc is None: + if cur_fg != -1 or cur_bg != -1: + parts.append(RESET) + cur_fg = cur_bg = -1 + parts.append(" ") + continue + + if tc is not None and bc is not None: + new_fg, new_bg, char = bc, tc, "▄" + elif tc is not None: + new_fg, new_bg, char = tc, None, "▀" + else: + new_fg, new_bg, char = bc, None, "█" + + seq = "" + if new_bg != cur_bg: + seq += f"{ESC}[49m" if new_bg is None else f"{ESC}[48;5;{new_bg}m" + cur_bg = new_bg + if new_fg != cur_fg: + seq += f"{ESC}[38;5;{new_fg}m" + cur_fg = new_fg + parts.append(seq + char) + + parts.append(RESET) + cur_fg = cur_bg = -1 + lines.append("".join(parts)) + + return "\n".join(lines) + + +def main() -> None: + results: dict[str, str] = {} + total_bytes = 0 + for name, filename in CHAR_MAP.items(): + path = os.path.join(MASCOTS_DIR, filename) + if not os.path.exists(path): + print(f" WARNING: missing {path}") + continue + art = img_to_ansi(path) + results[name] = art + size = len(art.encode("utf-8")) + total_bytes += size + print(f" {name}: {size:,} bytes") + + print(f"\nTotal: {total_bytes:,} bytes ({total_bytes / 1024:.1f} KB)") + + ts_lines = [ + "// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually", + "// Pre-rendered ANSI half-block art for all character mascots", + "// To regenerate: python3 scripts/generate-mascot-art.py", + "", + "export const MASCOT_ART: Record = {", + ] + for name, art in results.items(): + escaped = art.replace("\\", "\\\\").replace("`", "\\`").replace("${", "\\${") + ts_lines.append(f" {json.dumps(name)}: `{escaped}`,") + ts_lines.append("") + ts_lines.append("};") + ts_lines.append("") + + with open(OUTPUT_FILE, "w", encoding="utf-8") as f: + f.write("\n".join(ts_lines)) + + print(f"Written: {OUTPUT_FILE}") + + +if __name__ == "__main__": + main() diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts new file mode 100644 index 0000000..aa74e57 --- /dev/null +++ b/scripts/mascots-ansi.ts @@ -0,0 +1,212 @@ +// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually +// Pre-rendered ANSI half-block art for all 9 character mascots + +export const MASCOT_ART: Record = { + "The Intern": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The Degen": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The SBF": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The Sama": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The Quant": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The Musk": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The Dario": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "The Karpathy": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + + "Slough Boy": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ +▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + +}; From 3b801905b2cc8081d36257cab3c66c21ede65c14 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 18:57:29 +0100 Subject: [PATCH 05/15] =?UTF-8?q?feat(cli):=20higher-res=20mascot=20art=20?= =?UTF-8?q?(70=C3=9735)=20with=20transparent=20background?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerated all 9 ANSI mascots at 70×35 (up from 46×22) with auto-detected background transparency — pixels within euclidean distance 30 of the sampled corner colour are treated as transparent, so the mascot renders against the user's own terminal background. Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-mascot-art.py | 43 ++- scripts/mascots-ansi.ts | 516 ++++++++++++++++++++------------- 2 files changed, 344 insertions(+), 215 deletions(-) diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 8b51675..6e54ad4 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -9,6 +9,7 @@ """ import json +import math import os import sys @@ -22,8 +23,9 @@ MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") -WIDTH = 46 -HEIGHT = 22 +WIDTH = 70 +HEIGHT = 35 +BG_THRESH = 30 # euclidean distance for transparent background detection CHAR_MAP: dict[str, str] = { "The Intern": "char-the-intern.png", @@ -41,15 +43,26 @@ RESET = f"{ESC}[0m" +def detect_bg(img: Image.Image) -> tuple[int, int, int]: + """Sample corners + edges to detect the uniform background colour.""" + w, h = img.size + samples = [img.getpixel((x, y)) for x, y in [ + (0, 0), (w - 1, 0), (0, h - 1), (w - 1, h - 1), (w // 2, 0), (0, h // 2) + ]] + return tuple(sum(s[i] for s in samples) // len(samples) for i in range(3)) # type: ignore + + +def color_dist(a: tuple, b: tuple) -> float: + return math.sqrt(sum((a[i] - b[i]) ** 2 for i in range(3))) + + def rgb_to_256(r: int, g: int, b: int) -> int: - ri = round(r / 255 * 5) - gi = round(g / 255 * 5) - bi = round(b / 255 * 5) - return 16 + 36 * ri + 6 * gi + bi + return 16 + 36 * round(r / 255 * 5) + 6 * round(g / 255 * 5) + round(b / 255 * 5) def img_to_ansi(path: str) -> str: - img = Image.open(path).convert("RGBA") + img = Image.open(path).convert("RGB") + bg = detect_bg(img) img = img.resize((WIDTH, HEIGHT * 2), Image.LANCZOS) lines = [] @@ -57,10 +70,10 @@ def img_to_ansi(path: str) -> str: parts: list[str] = [] cur_fg = cur_bg = -1 for col in range(WIDTH): - tr, tg, tb, ta = img.getpixel((col, row * 2)) - br, bg_, bb, ba = img.getpixel((col, row * 2 + 1)) - tc = rgb_to_256(tr, tg, tb) if ta >= 30 else None - bc = rgb_to_256(br, bg_, bb) if ba >= 30 else None + tp = img.getpixel((col, row * 2)) + bp = img.getpixel((col, row * 2 + 1)) + tc = None if color_dist(tp, bg) < BG_THRESH else rgb_to_256(*tp) + bc = None if color_dist(bp, bg) < BG_THRESH else rgb_to_256(*bp) if tc is None and bc is None: if cur_fg != -1 or cur_bg != -1: @@ -74,7 +87,7 @@ def img_to_ansi(path: str) -> str: elif tc is not None: new_fg, new_bg, char = tc, None, "▀" else: - new_fg, new_bg, char = bc, None, "█" + new_fg, new_bg, char = bc, None, "▄" seq = "" if new_bg != cur_bg: @@ -86,7 +99,6 @@ def img_to_ansi(path: str) -> str: parts.append(seq + char) parts.append(RESET) - cur_fg = cur_bg = -1 lines.append("".join(parts)) return "\n".join(lines) @@ -110,7 +122,7 @@ def main() -> None: ts_lines = [ "// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually", - "// Pre-rendered ANSI half-block art for all character mascots", + "// Pre-rendered ANSI half-block art (70×35, transparent bg) for all character mascots", "// To regenerate: python3 scripts/generate-mascot-art.py", "", "export const MASCOT_ART: Record = {", @@ -119,8 +131,7 @@ def main() -> None: escaped = art.replace("\\", "\\\\").replace("`", "\\`").replace("${", "\\${") ts_lines.append(f" {json.dumps(name)}: `{escaped}`,") ts_lines.append("") - ts_lines.append("};") - ts_lines.append("") + ts_lines.append("};\n") with open(OUTPUT_FILE, "w", encoding="utf-8") as f: f.write("\n".join(ts_lines)) diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index aa74e57..9ab0e0b 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -1,212 +1,330 @@ // AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually -// Pre-rendered ANSI half-block art for all 9 character mascots +// Pre-rendered ANSI half-block art (70×35, transparent bg) for all character mascots +// To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { - "The Intern": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Intern": `  +  +  +  +  +  +  +  +  +  + ▄  + ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▀▀▀▀▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄  + ▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▄ ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄  + ▄▄▄ ▄ ▀▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄▄▄▄▄▄▀ ▄▄▄  + ▄▄▄ ▄ ▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀ ▄▄ ▄▄▄  + ▄▄▄ ▄ ▄▄ ▄▄▄  + ▄▄▄ ▄▄▄ ▄▄ ▄▄▄ ▀ ▄▄▄  + ▄▄▄ ▀ ▀▀ ▀▀ ▄▄▄  + ▀▀▀ ▀▀▀  +  + `, - "The Degen": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Degen": `  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄  + ▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄  + ▀ ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄▄▀▄ ▄  + ▄▀▄▄▄▀▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄  + ▄▄▄▄▄▄▄▀ ▄▄▀▀▀▀ ▀▄▄▄▄▄▀▀▀▀▀ ▄ ▄▄ ▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄  + ▀▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀  + ▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄ ▄ ▀ ▄▄  + ▄▄▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄ ▄▄▄▄▀▀▄▄ ▀▀▀▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄  + ▀▄▄▀▀▀ ▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄ ▀▀▄▄▀  + ▄▄▄ ▄▀▄▄▄▄ ▄▀▄▄▄▄▀▄▄▄▄▄▀▀ ▀▄▄▀ ▄  + ▄▄▄▄▄ ▀▄ ▀ ▀▀▀▀ ▀ ▀▀▀ ▀▀ ▄▄  + ▄▄▄▄▀▄▄ ▀▀▄▀▀▀▀▄▄▄▀▀▀▀▀▀▀▀▀▄▄▀▀▀▀▄▄▀ ▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▀ ▀▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▄▀▀▀▀ ▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▀▄  + ▀▄▄▄▄▀ ▄▄▀ ▀▄▄▄▄▄▄▀▀  + ▀  +  +  + `, - "The SBF": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The SBF": `  +  +  +  +  +  +  +  +  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▀▀▀▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▀  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀  + ▄▄▄▄▄▄▄▄▄▀▀▀ ▀▀▀▄▄▄▄▀▄▄▄  + ▀▀▄▄▄▀▀ ▀▀▀▄ ▀▀▀  + ▀ ▀  +  +  +  +  +  +  +  + `, - "The Sama": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Sama": `  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▀▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▄▄▄▀▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▀▀  + ▀▀▀▀ ▄▀▀▀ ▀▀▄▄  + ▄ ▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄ ▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▀  +  +  +  +  +  +  + `, - "The Quant": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Quant": `  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄ ▄▄▄▄▄  + ▄▄▄▄▀▄ ▄ ▄▄ ▄▄▄ ▄ ▄▄▄ ▄▀ ▀▀ ▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▀  + ▄▄ ▄▄ ▄▄▄▄▄▄▄ ▄▄▀ ▄▄▀▀▄▄▀▄▄ ▄▀▀▀▀ ▄ ▀▄▄▄ ▀▀▄  + ▀▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀  + ▄▄▀▄ ▄▄▄▀▀▄▄ ▀▀▀▀▀▀▀▀▄▄▀▀▀▀▀▀▀ ▄▄▄▀▀▀▀▀▀ ▄▄▄▄▄▄  + ▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄▀▄▀▄▄▄▄▄▄▀ ▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄ ▄  + ▀ ▀▀▀▄▄▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▄▄▄ ▀ ▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄  + ▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀ ▄▄▄▄▀  + ▄ ▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄  + ▄▄ ▄▀▀▄▄▄▄▄▄ ▄▄ ▀▀▀▀▀▀▀▀▀▄▄▄ ▄▄▄▄▀▀▀▀▀▀▀ ▀ ▄▄▄▄▄ ▄▄▄▄▄▄▄▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▀▄ ▀▄▄▄ ▄▄▀▀▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄ ▀ ▀▄ ▀▄ ▄▄▀ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀ ▀ ▄▄ ▀▄▄▄▄▄▄▀ ▀▀ ▄▄▀ ▀ ▀▀ ▀▀  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▀  + ▄ ▄▄▀▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀▀▄▀ ▄  + ▄▄▄ ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄ ▀▀▀▄▄ ▄▄▄▀▀ ▄▄  + ▀▄▄▀▄▄▄▄▄ ▄▄▄▄▄▀▄▄▀  + ▄▄ ▀▀▀ ▀▀▄▀▀ ▄▄  +  +  +  +  + `, - "The Musk": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Musk": `  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▀▄▄▄▄ ▀▄ ▀▄▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▀▀  + ▀▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▀▄ ▄▄▄▄▀▄▀▄▄▀  + ▀ ▀▀▄ ▀▄▄▄▄▄▀  + ▄ ▄▄ ▀▄▀ ▄  + ▄ ▄▄  + ▄▄▄▄ ▀ ▄▄▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄  + ▀▀▀▀▀▀ ▀▀▀▀▀  +  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▀ ▀▀▄▄▄▄▄▄▄▄▄▀  + ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄  +  +  +  +  + `, - "The Dario": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Dario": `  +  +  +  +  + ▄  + ▄▀▄  + ▄▄▄▄▀▄▄▄▀▄▀▀▀▀▀▀▀▀▀▄▄  + ▄▄▄▀▄▄▄▀▀▀ ▀▄▄  + ▄▄▀▄▄▄▀▀ ▄▄▄ ▀▄▄  + ▄▄▄▀▀ ▄▄▄ ▄▀▄  + ▄▀▄▀ ▄▄▄▄▄▄ ▀ ▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▀▄▄  + ▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀  + ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▀▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▄▄▄▄▄ ▄▄▄▄▀▀▀▀  +  +  +  +  +  +  + `, - "The Karpathy": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "The Karpathy": `  +  +  +  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄ ▄▄▄▄▀▄▀▄▀▀▀▀▀▀▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀ ▄▄▄ ▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▀▀▀▄▀▀▄▄▄▄▄▄▄▀▄▀▄▀▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀ ▀▀▀▀▀▀▀▀  +  +  +  +  +  +  + `, - "Slough Boy": `▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ -▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄`, + "Slough Boy": `  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▀▄▄▀▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▀▀▀▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▀▀▄▄▄▄▄▄▄▀▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▀▄▄▄  + ▄▄ ▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▀▄▄▄▄▄▀ ▀▄▄▄▄▄ ▀  +  +  +  +  +  + `, }; From 5cedf767cadbc65d205bf10ddda094b2d3e36b4d Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 18:59:03 +0100 Subject: [PATCH 06/15] =?UTF-8?q?feat(cli):=20true-color=2024-bit=20ANSI?= =?UTF-8?q?=20mascots=20at=2090=C3=9745=20resolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switched from 256-color palette to 24-bit RGB escape codes (\x1b[38;2;R;G;Bm) — eliminates color banding entirely. Increased resolution to 90×45 (up from 70×35). Bundle: 170KB → 452KB. Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-mascot-art.py | 38 +- scripts/mascots-ansi.ts | 722 ++++++++++++++++++--------------- 2 files changed, 424 insertions(+), 336 deletions(-) diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 6e54ad4..627a444 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Generate ANSI half-block art for all character mascots and write to +Generate true-color ANSI half-block art for all character mascots and write to scripts/mascots-ansi.ts. Run from repo root: python3 scripts/generate-mascot-art.py @@ -23,8 +23,8 @@ MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") -WIDTH = 70 -HEIGHT = 35 +WIDTH = 90 +HEIGHT = 45 BG_THRESH = 30 # euclidean distance for transparent background detection CHAR_MAP: dict[str, str] = { @@ -43,23 +43,18 @@ RESET = f"{ESC}[0m" -def detect_bg(img: Image.Image) -> tuple[int, int, int]: - """Sample corners + edges to detect the uniform background colour.""" +def detect_bg(img: Image.Image) -> tuple: w, h = img.size samples = [img.getpixel((x, y)) for x, y in [ (0, 0), (w - 1, 0), (0, h - 1), (w - 1, h - 1), (w // 2, 0), (0, h // 2) ]] - return tuple(sum(s[i] for s in samples) // len(samples) for i in range(3)) # type: ignore + return tuple(sum(s[i] for s in samples) // len(samples) for i in range(3)) def color_dist(a: tuple, b: tuple) -> float: return math.sqrt(sum((a[i] - b[i]) ** 2 for i in range(3))) -def rgb_to_256(r: int, g: int, b: int) -> int: - return 16 + 36 * round(r / 255 * 5) + 6 * round(g / 255 * 5) + round(b / 255 * 5) - - def img_to_ansi(path: str) -> str: img = Image.open(path).convert("RGB") bg = detect_bg(img) @@ -68,17 +63,17 @@ def img_to_ansi(path: str) -> str: lines = [] for row in range(HEIGHT): parts: list[str] = [] - cur_fg = cur_bg = -1 + cur_fg = cur_bg = None for col in range(WIDTH): tp = img.getpixel((col, row * 2)) bp = img.getpixel((col, row * 2 + 1)) - tc = None if color_dist(tp, bg) < BG_THRESH else rgb_to_256(*tp) - bc = None if color_dist(bp, bg) < BG_THRESH else rgb_to_256(*bp) + tc = None if color_dist(tp, bg) < BG_THRESH else tp + bc = None if color_dist(bp, bg) < BG_THRESH else bp if tc is None and bc is None: - if cur_fg != -1 or cur_bg != -1: + if cur_fg is not None or cur_bg is not None: parts.append(RESET) - cur_fg = cur_bg = -1 + cur_fg = cur_bg = None parts.append(" ") continue @@ -91,10 +86,13 @@ def img_to_ansi(path: str) -> str: seq = "" if new_bg != cur_bg: - seq += f"{ESC}[49m" if new_bg is None else f"{ESC}[48;5;{new_bg}m" + if new_bg is None: + seq += f"{ESC}[49m" + else: + seq += f"{ESC}[48;2;{new_bg[0]};{new_bg[1]};{new_bg[2]}m" cur_bg = new_bg if new_fg != cur_fg: - seq += f"{ESC}[38;5;{new_fg}m" + seq += f"{ESC}[38;2;{new_fg[0]};{new_fg[1]};{new_fg[2]}m" cur_fg = new_fg parts.append(seq + char) @@ -116,13 +114,13 @@ def main() -> None: results[name] = art size = len(art.encode("utf-8")) total_bytes += size - print(f" {name}: {size:,} bytes") + print(f" {name}: {size // 1024}KB") - print(f"\nTotal: {total_bytes:,} bytes ({total_bytes / 1024:.1f} KB)") + print(f"\nTotal: {total_bytes // 1024}KB") ts_lines = [ "// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually", - "// Pre-rendered ANSI half-block art (70×35, transparent bg) for all character mascots", + "// Pre-rendered true-color ANSI half-block art (90×45, transparent bg)", "// To regenerate: python3 scripts/generate-mascot-art.py", "", "export const MASCOT_ART: Record = {", diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index 9ab0e0b..78bdc82 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -1,330 +1,420 @@ // AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually -// Pre-rendered ANSI half-block art (70×35, transparent bg) for all character mascots +// Pre-rendered true-color ANSI art (90×45, transparent bg) // To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { - "The Intern": `  -  -  -  -  -  -  -  -  -  - ▄  - ▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▀▀▀▀▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄  - ▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▄ ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄  - ▄▄▄ ▄ ▀▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄▄▄▄▄▄▀ ▄▄▄  - ▄▄▄ ▄ ▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀ ▄▄ ▄▄▄  - ▄▄▄ ▄ ▄▄ ▄▄▄  - ▄▄▄ ▄▄▄ ▄▄ ▄▄▄ ▀ ▄▄▄  - ▄▄▄ ▀ ▀▀ ▀▀ ▄▄▄  - ▀▀▀ ▀▀▀  -  - `, + "The Intern": `  +  +  +  +  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▄▄▀▄▄▄▄▄▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▀▀▀▀▀▀▀▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▄ ▀▄▄▄▀▀▄▄▄▀▀▀▄▄▄▀▄▄▄▄▀▀▄▄▄ ▄▄▄  + ▄▄▄ ▄ ▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀ ▄▄▄ ▄▄▄  + ▄▄▄ ▄ ▄▄▄ ▄▄▄  + ▄▄▄ ▄▀ ▄ ▄ ▄▄▀ ▄▄▄  + ▄▄▄ ▀▄▄▄ ▄▄▄ ▄▄▄▀ ▄▄▄  + ▄▄▄ ▄ ▀ ▀▀▀ ▄▄▄  + ▄▄▄ ▀ ▀▄▄  +  +  + `, - "The Degen": `  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄  - ▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄  - ▀ ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄▄▀▄ ▄  - ▄▀▄▄▄▀▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄  - ▄▄▄▄▄▄▄▀ ▄▄▀▀▀▀ ▀▄▄▄▄▄▀▀▀▀▀ ▄ ▄▄ ▄▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄  - ▀▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀  - ▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄ ▄ ▀ ▄▄  - ▄▄▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄ ▄▄▄▄▀▀▄▄ ▀▀▀▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄  - ▀▄▄▀▀▀ ▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄ ▀▀▄▄▀  - ▄▄▄ ▄▀▄▄▄▄ ▄▀▄▄▄▄▀▄▄▄▄▄▀▀ ▀▄▄▀ ▄  - ▄▄▄▄▄ ▀▄ ▀ ▀▀▀▀ ▀ ▀▀▀ ▀▀ ▄▄  - ▄▄▄▄▀▄▄ ▀▀▄▀▀▀▀▄▄▄▀▀▀▀▀▀▀▀▀▄▄▀▀▀▀▄▄▀ ▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▀ ▀▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▄▀▀▀▀ ▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▀▄  - ▀▄▄▄▄▀ ▄▄▀ ▀▄▄▄▄▄▄▀▀  - ▀  -  -  - `, + "The Degen": `  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  + ▄▀▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄  + ▀▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▀▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄  + ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▄▄  + ▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄▀  + ▄▀▀▀▄▄▀▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▄▄▀▀▄▄  + ▄▀▄▄▄▄▄ ▄▀▄▄▄ ▀▀▀▀▀▀▄▄▄▄▄▄▄▀▀▀▀▀▀ ▄▄▀▀ ▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▀▄▄▄ ▀▀▀▄ ▄ ▀▀▄▄▄▄▄▄ ▀▄▄▀▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▀▄  + ▀ ▀▄▄▀ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▀▄▀▀▄  + ▄▄▄ ▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄ ▄▀ ▄▄▄  + ▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄ ▄▄ ▀▀ ▀▀▀▀▄▀▀▄▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▀▄▄▄ ▀▄▄▄▄▄▄▀  + ▀▀▀ ▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄ ▄▄ ▀▀▀  + ▄▄▄▄▄ ▄ ▄▄▄▄▄ ▀▀▀▄▄▄▄▀ ▀▄▄▄▄▄▀▀ ▄ ▀▀▀▀ ▄▄  + ▄▄▄▄▄▄ ▀▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▄▄  + ▄▄▄▄▄▄▀▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀  + ▄▄ ▄ ▀▄▄▄▄ ▀▄▄▄ ▀▄▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▀▀▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄▄▄▄▄▄▄▀▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀▀▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄ ▀▄▄▄▄▄▄▄ ▀▀ ▀▀▀▀▄▄▄▄▀  + ▀▀▀▀ ▄▄▄▄▄▄▄▀▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄▄▄▄▄▀▄▀▄▄ ▄▄▄▄▄▄▄▄▀▀▄ ▀  + ▀▀▀▀▄▄▄▄▄▄▄▀▄ ▄▀▀▄▄▄▄▀▀▀  + ▀▀  +  +  +  + `, - "The SBF": `  -  -  -  -  -  -  -  -  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▀▀▀▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▀  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀  - ▄▄▄▄▄▄▄▄▄▀▀▀ ▀▀▀▄▄▄▄▀▄▄▄  - ▀▀▄▄▄▀▀ ▀▀▀▄ ▀▀▀  - ▀ ▀  -  -  -  -  -  -  -  - `, + "The SBF": `  +  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄ ▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀ ▀▀▄▀▄▄▄▄▄▀▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▀▄▄▄  + ▄▀▀▄▀  +  +  +  +  +  +  +  +  +  +  + `, - "The Sama": `  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▀▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▄▄▄▀▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▀▀  - ▀▀▀▀ ▄▀▀▀ ▀▀▄▄  - ▄ ▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄ ▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▀  -  -  -  -  -  -  - `, + "The Sama": `  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▀▄  + ▄▄▄▄▄ ▀▄▄▄▀▄▄▄▄▄▀▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▄▄▄▀▄▄▀▀▀▀▀ ▀▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄ ▄ ▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▀  + ▀▀▄▄▀ ▄▄▀▀ ▀▀▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▀▄  + ▀▄▄▄▄▄▄▄▄▄▄▀ ▀▀▄▄▄▄▄▄▄▄▄  +  +  +  +  +  +  +  +  + `, - "The Quant": `  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄ ▄▄▄▄▄  - ▄▄▄▄▀▄ ▄ ▄▄ ▄▄▄ ▄ ▄▄▄ ▄▀ ▀▀ ▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▀  - ▄▄ ▄▄ ▄▄▄▄▄▄▄ ▄▄▀ ▄▄▀▀▄▄▀▄▄ ▄▀▀▀▀ ▄ ▀▄▄▄ ▀▀▄  - ▀▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀  - ▄▄▀▄ ▄▄▄▀▀▄▄ ▀▀▀▀▀▀▀▀▄▄▀▀▀▀▀▀▀ ▄▄▄▀▀▀▀▀▀ ▄▄▄▄▄▄  - ▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄▀▄▀▄▄▄▄▄▄▀ ▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄ ▄  - ▀ ▀▀▀▄▄▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▄▄▄ ▀ ▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄  - ▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀ ▄▄▄▄▀  - ▄ ▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄  - ▄▄ ▄▀▀▄▄▄▄▄▄ ▄▄ ▀▀▀▀▀▀▀▀▀▄▄▄ ▄▄▄▄▀▀▀▀▀▀▀ ▀ ▄▄▄▄▄ ▄▄▄▄▄▄▄▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▀▄ ▀▄▄▄ ▄▄▀▀▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄ ▀ ▀▄ ▀▄ ▄▄▀ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀ ▀ ▄▄ ▀▄▄▄▄▄▄▀ ▀▀ ▄▄▀ ▀ ▀▀ ▀▀  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▀  - ▄ ▄▄▀▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀▀▄▀ ▄  - ▄▄▄ ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄ ▀▀▀▄▄ ▄▄▄▀▀ ▄▄  - ▀▄▄▀▄▄▄▄▄ ▄▄▄▄▄▀▄▄▀  - ▄▄ ▀▀▀ ▀▀▄▀▀ ▄▄  -  -  -  -  - `, + "The Quant": `  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▄▄▄▄  + ▄ ▄▄ ▄▄▄ ▄▄  + ▀ ▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄ ▄▄▀▀▀▄▄ ▄ ▄▄ ▄ ▀▄  + ▄ ▄▄ ▀▀▀▀ ▀▄▄▄▀ ▀▄▄▄ ▄ ▄▄▄▀▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀▀▀▄▄▄▄▄ ▄▄▄▄▀  + ▄ ▄ ▀▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄▀▀▄▄▄▄▀▄▄ ▄▄ ▀▄▄ ▄▄▄ ▄▄ ▄▄  + ▄ ▀ ▄▄▄▄▄▄▄▀ ▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▀▀  + ▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▀▄ ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▀▄ ▄▄▀▄▄▀▄  + ▄ ▀▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄ ▀▀ ▄▄ ▄▄▄▄ ▄▀ ▄▄▄▄▀▀ ▄▀  + ▄ ▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▀▀▄▄▄▀ ▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄ ▀▀  + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄ ▄  + ▄ ▀▀▀▀▀▀▀▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▄▄ ▀▀ ▄  + ▄ ▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄ ▀  + ▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▀  + ▄▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄ ▄▄▀▄▄ ▄▄▄▀ ▄▄▀▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▀▄▀▄ ▀▄▄▄ ▄▄▄▄▄▀▄▄▀▀▄  + ▄▄ ▄▄▀ ▀▀▄▄▀▄▄ ▄▄ ▄ ▄ ▄▄▄▄▀▄▄▄▄▄▄ ▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  + ▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄ ▄▀ ▄▄▄▀▄▄▄▄▄ ▀▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▀▀▄ ▄▄▄▄▄▄ ▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄ ▀▄ ▀ ▄ ▀▄ ▀ ▄ ▀▄ ▀▄▄▀ ▄▄▄▄▀▀▀ ▄▄▄▄▀▀ ▄▀ ▀ ▄▀▀ ▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄  + ▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄  + ▄▄ ▀ ▄▄▄▄ ▄▄▀▀▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄▄ ▀ ▄  + ▀ ▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄ ▀▀▄▄ ▄▄▄▀▀▀ ▀▄▄  + ▀▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▄▄▄▀  + ▀▀▄▄▄ ▄▄▄▀ ▀▄▄▄▀ ▄▄▄▀▀  + ▀▀ ▀▀ ▀▀ ▀▀  +  +  +  +  +  + `, - "The Musk": `  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▀▄▄▄▄ ▀▄ ▀▄▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▀▀  - ▀▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▀▄ ▄▄▄▄▀▄▀▄▄▀  - ▀ ▀▀▄ ▀▄▄▄▄▄▀  - ▄ ▄▄ ▀▄▀ ▄  - ▄ ▄▄  - ▄▄▄▄ ▀ ▄▄▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄  - ▀▀▀▀▀▀ ▀▀▀▀▀  -  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▀ ▀▀▄▄▄▄▄▄▄▄▄▀  - ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄  -  -  -  -  - `, + "The Musk": `  +  +  +  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄ ▀▀ ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄ ▄▄▄▄▄▄ ▀ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▀▀  + ▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄ ▄▄▄▄▄▄  + ▀▀▀▀▀▄▄▄ ▀▄▄▄▄▄▄▄▀  + ▄▄ ▀▀▄▄▄▀  + ▄▄ ▀▀▀▀ ▀ ▀ ▀ ▀▀▀  + ▄▄ ▄ ▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▀▀▀▄▄▄▄ ▄▄▄▄▀▀▀▄▄▄▄  + ▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀  +  +  +  +  +  + `, - "The Dario": `  -  -  -  -  - ▄  - ▄▀▄  - ▄▄▄▄▀▄▄▄▀▄▀▀▀▀▀▀▀▀▀▄▄  - ▄▄▄▀▄▄▄▀▀▀ ▀▄▄  - ▄▄▀▄▄▄▀▀ ▄▄▄ ▀▄▄  - ▄▄▄▀▀ ▄▄▄ ▄▀▄  - ▄▀▄▀ ▄▄▄▄▄▄ ▀ ▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▀▄▄  - ▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀  - ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▀▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▄▄▄▄▄ ▄▄▄▄▀▀▀▀  -  -  -  -  -  -  - `, + "The Dario": `  +  +  +  +  +  +  + ▄▄▄  + ▄▄▄▄▄  + ▄▄▄▄▀▄▄▄▀▄▄▄▄▄▄▄▀▀▀▀▀▄▄▄  + ▄▄▄▀▀▄▄▄▀▀▀ ▀ ▀▀▄▄  + ▄▄▄▀▄▄▄▄▀▀▀ ▄ ▀▄▄  + ▄▄▄▄▄▄▀▀ ▄▄▄▄▄ ▀▄▄  + ▄▀▄▀▀ ▀▄▄▄▄ ▄▄▄▄  + ▄▄▀▄▀ ▀▀▀ ▄▄▄▄▄  + ▄▀ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▀▄▄▀  + ▄▄▄▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀  + ▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀  + ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄  + ▀  +  +  +  +  +  +  +  +  + `, - "The Karpathy": `  -  -  -  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄ ▄▄▄▄▀▄▀▄▀▀▀▀▀▀▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀ ▄▄▄ ▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▀▀▀▄▀▀▄▄▄▄▄▄▄▀▄▀▄▀▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀ ▀▀▀▀▀▀▀▀  -  -  -  -  -  -  - `, + "The Karpathy": `  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▀▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▀▀▀▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▀▀▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄▄▀▄▄▄▄ ▄▄▄▄ ▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄  + ▀ ▀ ▀  +  +  +  +  +  +  +  +  + `, - "Slough Boy": `  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▀▄▄▀▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▀▀▀▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▀▀▄▄▄▄▄▄▄▀▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▀▄▄▄  - ▄▄ ▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄  - ▀▄▄▄▄▄▀ ▀▄▄▄▄▄ ▀  -  -  -  -  -  - `, + "Slough Boy": `  +  +  +  +  +  +  +  + ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄▄▄▄▄ ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄ ▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▀▄▄▄▄ ▀▀▀▀▀▀▄▄▄▄▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀  + ▀▄▀▄▀▄▀▀▀▀▀▀▀▀▀▀▄▀▀▀▄▀  + ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▄▄▀  +  +  +  +  +  +  +  + `, }; From ed88573139c4cf2a4293b62fa67c04019fe00249 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 19:00:47 +0100 Subject: [PATCH 07/15] fix(cli): use flood-fill bg detection to eliminate gaps in mascot art MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace per-pixel distance check with a flood fill from all edge pixels. Only pixels connected to the image border are treated as transparent — dark interior pixels (shadows, clothing) are preserved even if they're close to the background colour. Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-mascot-art.py | 54 +++- scripts/mascots-ansi.ts | 470 ++++++++++++++++----------------- 2 files changed, 284 insertions(+), 240 deletions(-) diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 627a444..6487e9e 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -12,6 +12,7 @@ import math import os import sys +from collections import deque try: from PIL import Image @@ -25,7 +26,7 @@ WIDTH = 90 HEIGHT = 45 -BG_THRESH = 30 # euclidean distance for transparent background detection +BG_THRESH = 35 # euclidean distance for flood-fill background detection CHAR_MAP: dict[str, str] = { "The Intern": "char-the-intern.png", @@ -55,10 +56,52 @@ def color_dist(a: tuple, b: tuple) -> float: return math.sqrt(sum((a[i] - b[i]) ** 2 for i in range(3))) +def make_bg_mask(img: Image.Image, bg: tuple, thresh: float) -> list[list[bool]]: + """ + Flood fill from all edge pixels that match the background colour. + Only pixels *connected* to the edges are marked transparent — dark pixels + inside the character are preserved even if they're close to the bg colour. + """ + w, h = img.size + visited = [[False] * h for _ in range(w)] + queue: deque = deque() + + def seed(x: int, y: int) -> None: + if not visited[x][y] and color_dist(img.getpixel((x, y)), bg) < thresh: + visited[x][y] = True + queue.append((x, y)) + + for x in range(w): + seed(x, 0) + seed(x, h - 1) + for y in range(h): + seed(0, y) + seed(w - 1, y) + + while queue: + x, y = queue.popleft() + for nx, ny in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]: + if 0 <= nx < w and 0 <= ny < h and not visited[nx][ny]: + if color_dist(img.getpixel((nx, ny)), bg) < thresh: + visited[nx][ny] = True + queue.append((nx, ny)) + + return visited + + def img_to_ansi(path: str) -> str: img = Image.open(path).convert("RGB") bg = detect_bg(img) + orig_w, orig_h = img.size + mask = make_bg_mask(img, bg, BG_THRESH) + img = img.resize((WIDTH, HEIGHT * 2), Image.LANCZOS) + sx, sy = orig_w / WIDTH, orig_h / (HEIGHT * 2) + + def is_bg(col: int, prow: int) -> bool: + ox = min(int(col * sx), orig_w - 1) + oy = min(int(prow * sy), orig_h - 1) + return mask[ox][oy] lines = [] for row in range(HEIGHT): @@ -67,8 +110,8 @@ def img_to_ansi(path: str) -> str: for col in range(WIDTH): tp = img.getpixel((col, row * 2)) bp = img.getpixel((col, row * 2 + 1)) - tc = None if color_dist(tp, bg) < BG_THRESH else tp - bc = None if color_dist(bp, bg) < BG_THRESH else bp + tc = None if is_bg(col, row * 2) else tp + bc = None if is_bg(col, row * 2 + 1) else bp if tc is None and bc is None: if cur_fg is not None or cur_bg is not None: @@ -110,17 +153,18 @@ def main() -> None: if not os.path.exists(path): print(f" WARNING: missing {path}") continue + print(f" Processing {name}...", flush=True) art = img_to_ansi(path) results[name] = art size = len(art.encode("utf-8")) total_bytes += size - print(f" {name}: {size // 1024}KB") + print(f" → {size // 1024}KB") print(f"\nTotal: {total_bytes // 1024}KB") ts_lines = [ "// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually", - "// Pre-rendered true-color ANSI half-block art (90×45, transparent bg)", + "// Pre-rendered true-color ANSI half-block art (90×45, flood-fill transparent bg)", "// To regenerate: python3 scripts/generate-mascot-art.py", "", "export const MASCOT_ART: Record = {", diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index 78bdc82..6c607d7 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -1,5 +1,5 @@ // AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually -// Pre-rendered true-color ANSI art (90×45, transparent bg) +// Pre-rendered true-color ANSI art (90×45, flood-fill transparent bg) // To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { @@ -17,34 +17,34 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▄▄▀▄▄▄▄▄▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄ ▀▀▀▀▀▀▀▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▄ ▀▄▄▄▀▀▄▄▄▀▀▀▄▄▄▀▄▄▄▄▀▀▄▄▄ ▄▄▄  - ▄▄▄ ▄ ▀▀▀ ▀▀▀ ▀▀ ▀▀▀ ▀▀▀ ▄▄▄ ▄▄▄  - ▄▄▄ ▄ ▄▄▄ ▄▄▄  - ▄▄▄ ▄▀ ▄ ▄ ▄▄▀ ▄▄▄  - ▄▄▄ ▀▄▄▄ ▄▄▄ ▄▄▄▀ ▄▄▄  - ▄▄▄ ▄ ▀ ▀▀▀ ▄▄▄  - ▄▄▄ ▀ ▀▄▄  + ▄▄▄▄▄  + ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▀▀▀▀▀▀▀▀▄▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄ ▀ ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄▄  + ▄▄▄ ▄▄ ▀▀▀ ▄▄▄ ▀▀▀ ▄▄▄ ▄▄ ▄▄▄  + ▄▄▄ ▄▄ ▄▄ ▄▄▄  + ▄▄▄ ▄▄ ▄▄ ▄▄▄  + ▄▄▄ ▀▀ ▄▄▄▄ ▄▄▄ ▄▄▄ ▀▀ ▄▄▄  + ▄▄▄ ▀▀ ▀▄▄ ▀▀ ▄▄▄  + ▄▄▄ ▀▄▄    `, @@ -57,39 +57,39 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  - ▄▀▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄  - ▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄  - ▀▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▀▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄  - ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▄▄  - ▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄▀  - ▄▀▀▀▄▄▀▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▄▄▀▀▄▄  - ▄▀▄▄▄▄▄ ▄▀▄▄▄ ▀▀▀▀▀▀▄▄▄▄▄▄▄▀▀▀▀▀▀ ▄▄▀▀ ▄▄▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▀▄▄▄ ▀▀▀▄ ▄ ▀▀▄▄▄▄▄▄ ▀▄▄▀▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▀▄  - ▀ ▀▄▄▀ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▀▄▀▀▄  - ▄▄▄ ▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄ ▄▀ ▄▄▄  - ▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄ ▄▄ ▀▀ ▀▀▀▀▄▀▀▄▄▄▄▄▄▀▄▀▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▀▄▄▄ ▀▄▄▄▄▄▄▀  - ▀▀▀ ▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄ ▄▄ ▀▀▀  - ▄▄▄▄▄ ▄ ▄▄▄▄▄ ▀▀▀▄▄▄▄▀ ▀▄▄▄▄▄▀▀ ▄ ▀▀▀▀ ▄▄  - ▄▄▄▄▄▄ ▀▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▄▄  - ▄▄▄▄▄▄▀▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀  - ▄▄ ▄ ▀▄▄▄▄ ▀▄▄▄ ▀▄▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▀▀▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▄▄▄▄▄▄▄▀▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀▀▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄ ▀▄▄▄▄▄▄▄ ▀▀ ▀▀▀▀▄▄▄▄▀  - ▀▀▀▀ ▄▄▄▄▄▄▄▀▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▄▄▄▄▄▀▄▀▄▄ ▄▄▄▄▄▄▄▄▀▀▄ ▀  - ▀▀▀▀▄▄▄▄▄▄▄▀▄ ▄▀▀▄▄▄▄▀▀▀  - ▀▀  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▀▀▄▄▄  + ▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄  + ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▀▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄  + ▀▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▀  + ▄▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▀▄▄  + ▄▄▄▄▄▄▀▀▀▄▄▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▀▀ ▄▄▄▄▄  + ▀▄▄▄▄▄▀▄▄▄▄▄▄▀▀ ▀▀▀▀▀▀▄▀▄▄▀▀ ▄▀▀ ▀▄▄▄▄▄▀ ▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▄▄ ▄▄▄▄ ▄▄▄▄▄▄ ▀▀▄▄▄▄▄▀▄  + ▀▄▄▀▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄ ▀▄▄▄▄▄▄▄▀▄▄▄▄▄ ▄ ▄▄▄▀▀▄▄  + ▄▄▄▄▄▄▀ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄ ▀▄▄▄▄▄▄▀ ▄▄ ▀ ▄ ▀▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▀▄ ▀ ▀▀▀▀ ▀▀▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▀  + ▀▀▀▀▀▀ ▄ ▄▄▄▄▄▄▀▀▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄▄▄▄▄▀ ▄ ▀▀▀▀▀▀  + ▄▄▄ ▀ ▀▄▄▄▄▀ ▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▀▀ ▄ ▀▀▀ ▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀▀▀▄▄▄▀▀▀▀ ▀▀▀▄▄▄▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▄▄▄▀  + ▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀▀▀▀  + ▀▀▀     @@ -106,29 +106,29 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄ ▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀ ▀▀▄▀▄▄▄▄▄▀▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▀▄▄▄  - ▄▀▀▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀▄▀▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▀▄▀▄▄  + ▀▄▀▀ ▀▀     @@ -151,32 +151,32 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▀▄  - ▄▄▄▄▄ ▀▄▄▄▀▄▄▄▄▄▀▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▄▄▄▀▄▄▀▀▀▀▀ ▀▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄ ▄ ▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▀  - ▀▀▄▄▀ ▄▄▀▀ ▀▀▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▀▄  - ▀▄▄▄▄▄▄▄▄▄▄▀ ▀▀▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▀▀  + ▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄     @@ -195,37 +195,37 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▄▄▄▄  - ▄ ▄▄ ▄▄▄ ▄▄  - ▀ ▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄ ▄▄▀▀▀▄▄ ▄ ▄▄ ▄ ▀▄  - ▄ ▄▄ ▀▀▀▀ ▀▄▄▄▀ ▀▄▄▄ ▄ ▄▄▄▀▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀▀▀▄▄▄▄▄ ▄▄▄▄▀  - ▄ ▄ ▀▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄▀▀▄▄▄▄▀▄▄ ▄▄ ▀▄▄ ▄▄▄ ▄▄ ▄▄  - ▄ ▀ ▄▄▄▄▄▄▄▀ ▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▀▀  - ▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▀▄ ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▀▄ ▄▄▀▄▄▀▄  - ▄ ▀▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄ ▀▀ ▄▄ ▄▄▄▄ ▄▀ ▄▄▄▄▀▀ ▄▀  - ▄ ▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▀▀▄▄▄▀ ▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄ ▀▀  - ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄ ▄  - ▄ ▀▀▀▀▀▀▀▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▄▄ ▀▀ ▄  - ▄ ▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄ ▀  - ▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▀  - ▄▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄ ▄▄▀▄▄ ▄▄▄▀ ▄▄▀▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▀▄▀▄ ▀▄▄▄ ▄▄▄▄▄▀▄▄▀▀▄  - ▄▄ ▄▄▀ ▀▀▄▄▀▄▄ ▄▄ ▄ ▄ ▄▄▄▄▀▄▄▄▄▄▄ ▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  - ▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄ ▄▀ ▄▄▄▀▄▄▄▄▄ ▀▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▀▀▄ ▄▄▄▄▄▄ ▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄ ▀▄ ▀ ▄ ▀▄ ▀ ▄ ▀▄ ▀▄▄▀ ▄▄▄▄▀▀▀ ▄▄▄▄▀▀ ▄▀ ▀ ▄▀▀ ▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄  - ▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄  - ▄▄ ▀ ▄▄▄▄ ▄▄▀▀▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄▄ ▀ ▄  - ▀ ▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄ ▀▀▄▄ ▄▄▄▀▀▀ ▀▄▄  - ▀▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▄▄▄▀  - ▀▀▄▄▄ ▄▄▄▀ ▀▄▄▄▀ ▄▄▄▀▀  - ▀▀ ▀▀ ▀▀ ▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▄▄▄ ▄▄ ▄ ▄ ▄ ▄  + ▄ ▄▄▄▄▀ ▀ ▄▄▄▄ ▄ ▄ ▄ ▄▄▀ ▄▀ ▄ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄ ▄  + ▄ ▄ ▀▀▀▀ ▀▀▀ ▄▄▄▄▄▄ ▀▄▄▄ ▄▀▀▄ ▄ ▄▄▄▄▄▄ ▀▀▀▀ ▄▄ ▄▄ ▄ ▄  + ▄ ▄▄ ▄ ▄ ▄ ▀▀▀▀ ▄▄ ▄ ▄▄ ▄▀▀ ▄▀▀▀▀ ▄ ▄ ▄ ▀▀▀▀▀▄ ▄▄ ▄▄ ▄  + ▄ ▄▄▄▄▄▄▄▄ ▀▄▄▄ ▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄ ▀ ▄▄▄▄ ▄▄ ▀ ▀  + ▄ ▄▄ ▀▀▀▀▀ ▄▄ ▄▄ ▀▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▀ ▄▄ ▄ ▀  + ▄ ▄ ▀ ▄▄▄▄▄▀▀▄▄▄ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▄▄▄▄▄ ▄▄ ▄▄▄ ▄▄ ▄ ▀  + ▄ ▄ ▄▄ ▄▄ ▄▄▄▀▀▄▄▄▄▄ ▄▄▄ ▀ ▄▄ ▄ ▄▄ ▀▀▄ ▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▄▀ ▄  + ▄ ▀ ▀▀▀▄▀▀▄ ▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀ ▀▀▀▀ ▄  + ▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀ ▀  + ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▀ ▀▀▀▀▀▀▀ ▀▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▀▀▀ ▄▄▄▀▀▀ ▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▀▄▄ ▄  + ▄▄ ▄▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄▄▄ ▄  + ▄ ▄▀ ▀ ▄ ▄ ▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄ ▄▄ ▄  + ▄▄ ▀ ▄▄▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀ ▀  + ▀▄▀ ▀ ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▀ ▀ ▄ ▄▄▄  + ▀▀▀▀▀ ▀▀▀ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀ ▀ ▀▀▀▀  + ▀ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▀ ▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  + ▀ ▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀ ▀  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▀  + ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄  +     @@ -246,32 +246,32 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄ ▀▀ ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄ ▄▄▄▄▄▄ ▀ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄▄▄▄▄▄▀▀  - ▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄ ▄▄▄▄▄▄  - ▀▀▀▀▀▄▄▄ ▀▄▄▄▄▄▄▄▀  - ▄▄ ▀▀▄▄▄▀  - ▄▄ ▀▀▀▀ ▀ ▀ ▀ ▀▀▀  - ▄▄ ▄ ▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀▀  + ▀▄▄▄▄▄▄▄▀▀▀ ▀▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄▄▄▄  + ▀▀▀▄▄▄▄ ▄ ▀ ▄▄▄▄▄▄▄▄ ▀ ▀ ▀▀  + ▄▄▀ ▀▄▄▄▄▀  + ▄▀ ▀▀ ▀  + ▄ ▄ ▀ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▄ ▄▄▀▀▀▀  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄    - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▀▀▀▄▄▄▄ ▄▄▄▄▀▀▀▄▄▄▄  - ▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▀▄▄▄▀▀▀▀▄▀▄▀ ▀▄▀▄▀▀▀▄▄▄▄▀  + ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀     @@ -286,35 +286,35 @@ export const MASCOT_ART: Record = {    - ▄▄▄  - ▄▄▄▄▄  - ▄▄▄▄▀▄▄▄▀▄▄▄▄▄▄▄▀▀▀▀▀▄▄▄  - ▄▄▄▀▀▄▄▄▀▀▀ ▀ ▀▀▄▄  - ▄▄▄▀▄▄▄▄▀▀▀ ▄ ▀▄▄  - ▄▄▄▄▄▄▀▀ ▄▄▄▄▄ ▀▄▄  - ▄▀▄▀▀ ▀▄▄▄▄ ▄▄▄▄  - ▄▄▀▄▀ ▀▀▀ ▄▄▄▄▄  - ▄▀ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▀▄▄▀  - ▄▄▄▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀  - ▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  +  + ▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀  - ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄  - ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀  + ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀     @@ -342,25 +342,25 @@ export const MASCOT_ART: Record = {    - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▀▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▀▀▀▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▀▀▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄  - [38;2;122;125;127m▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄▄▀▄▄▄▄ ▄▄▄▄ ▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄  - ▀ ▀ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀ ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄ ▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀▀▀ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▀▀▀▀▀▀▀▀ ▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀     @@ -379,10 +379,10 @@ export const MASCOT_ART: Record = {    - ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  @@ -390,24 +390,24 @@ export const MASCOT_ART: Record = { ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▀▄▄▄▄▄▄▄ ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄ ▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▀▄▄▄▄ ▀▀▀▀▀▀▄▄▄▄▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▄ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀  - ▀▄▀▄▀▄▀▀▀▀▀▀▀▀▀▀▄▀▀▀▄▀  - ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▀ ▀▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄  + ▄▄▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▄  + ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀  + ▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▀ ▄▄▄▄▄▄▀     From 4ddd38dd16e7511026edd6acc759200f4576d058 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 19:05:03 +0100 Subject: [PATCH 08/15] =?UTF-8?q?feat(cli):=20reduce=20mascot=20art=20to?= =?UTF-8?q?=2056=C3=9728=20=E2=80=94=20same=20quality,=20half=20the=20spac?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeps true-color 24-bit + flood-fill transparency, just smaller. Bundle: 460KB → 210KB. Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-mascot-art.py | 4 +- scripts/mascots-ansi.ts | 659 +++++++++++++-------------------- 2 files changed, 255 insertions(+), 408 deletions(-) diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 6487e9e..8d0e1d6 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -24,8 +24,8 @@ MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") -WIDTH = 90 -HEIGHT = 45 +WIDTH = 56 +HEIGHT = 28 BG_THRESH = 35 # euclidean distance for flood-fill background detection CHAR_MAP: dict[str, str] = { diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index 6c607d7..84786d6 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -1,420 +1,267 @@ // AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually -// Pre-rendered true-color ANSI art (90×45, flood-fill transparent bg) +// Pre-rendered true-color ANSI half-block art (90×45, flood-fill transparent bg) // To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { - "The Intern": `  -  -  -  -  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄  - ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▀▀▀▀▀▀▀▀▄▀▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄ ▀ ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄▄  - ▄▄▄ ▄▄ ▀▀▀ ▄▄▄ ▀▀▀ ▄▄▄ ▄▄ ▄▄▄  - ▄▄▄ ▄▄ ▄▄ ▄▄▄  - ▄▄▄ ▄▄ ▄▄ ▄▄▄  - ▄▄▄ ▀▀ ▄▄▄▄ ▄▄▄ ▄▄▄ ▀▀ ▄▄▄  - ▄▄▄ ▀▀ ▀▄▄ ▀▀ ▄▄▄  - ▄▄▄ ▀▄▄  -  -  - `, + "The Intern": `  +  +  +  +  +  +  +  +  + ▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▄▄▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄  + ▄▄ ▄▄▀▄▄▀▀▄▀▀▄▄▀▄▄ ▄ ▄▄  + ▄▄ ▄ ▄ ▄▄  + ▄▄ ▀ ▄▄ ▄ ▄▄ ▀ ▄▄  + ▄▄ ▀ ▄▄  +  + `, - "The Degen": `  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▄▀▀▄▄▄  - ▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄  - ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄ ▀▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄  - ▀▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▀  - ▄▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▀▄▄  - ▄▄▄▄▄▄▀▀▀▄▄▀▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▀▀ ▄▄▄▄▄  - ▀▄▄▄▄▄▀▄▄▄▄▄▄▀▀ ▀▀▀▀▀▀▄▀▄▄▀▀ ▄▀▀ ▀▄▄▄▄▄▀ ▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▄▄ ▄▄▄▄ ▄▄▄▄▄▄ ▀▀▄▄▄▄▄▀▄  - ▀▄▄▀▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄ ▀▄▄▄▄▄▄▄▀▄▄▄▄▄ ▄ ▄▄▄▀▀▄▄  - ▄▄▄▄▄▄▀ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄ ▀▄▄▄▄▄▄▀ ▄▄ ▀ ▄ ▀▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▀▄ ▀ ▀▀▀▀ ▀▀▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▀  - ▀▀▀▀▀▀ ▄ ▄▄▄▄▄▄▀▀▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀▄▄▄▄▄▀ ▄ ▀▀▀▀▀▀  - ▄▄▄ ▀ ▀▄▄▄▄▀ ▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▀▀ ▄ ▀▀▀ ▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▀▄▀▀ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀▀▀▄▄▄▀▀▀▀ ▀▀▀▄▄▄▄▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▄▄▄▀  - ▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀▀▀▀  - ▀▀▀  -  -  -  - `, + "The Degen": `  +  +  +  +  + ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▀▀▄▄▄▄▄▄▄▀▀▄▄▄▄  + ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  + ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄  + ▄▄▄▀▄▄▀▀▄▄▄▄▄▀▄▄▀▀▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▀ ▄▄▄▄▄▄▄▄ ▄▄ ▀▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▄▄▄▄▀ ▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▀▀▄▀ ▄ ▄▄▄▄  + ▄▄▄▄▄▀▀ ▀▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀▄▄▄▄▄  + ▀▀▀▀ ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄ ▄ ▀▀▀▀  + ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄ ▀▀▄▄▄▄▄▄▀▀▀▀▀▀▀▄▄▄▄▄▄▀▀ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▀ ▀▄▄▄▄▀ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▀▀ ▀▄▄▄▄▄ ▄▄▄▄▄▀▄▄▄▄▀  + ▀▀▀▀ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▀ ▀▄▄▄▄▀▀▀  +  +  + `, - "The SBF": `  -  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▀▄▀▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▀▄▀▄▄  - ▀▄▀▀ ▀▀  -  -  -  -  -  -  -  -  -  -  - `, + "The SBF": `  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▀▀▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀  + ▀▄▄▄▄▄▄▄▀▀▀▀▀▄▄▄▄▄▄▀  + ▀▀▄▄▄▀ ▀▄▄▀▀▀  +  +  +  +  +  +  + `, - "The Sama": `  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▀▀  - ▄▄▄▄▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  -  -  -  -  -  -  -  -  - `, + "The Sama": `  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▀ ▀▀▀▀▀▀▀  +  +  +  +  + `, - "The Quant": `  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄ ▄▄▄ ▄▄ ▄ ▄ ▄ ▄  - ▄ ▄▄▄▄▀ ▀ ▄▄▄▄ ▄ ▄ ▄ ▄▄▀ ▄▀ ▄ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄ ▄  - ▄ ▄ ▀▀▀▀ ▀▀▀ ▄▄▄▄▄▄ ▀▄▄▄ ▄▀▀▄ ▄ ▄▄▄▄▄▄ ▀▀▀▀ ▄▄ ▄▄ ▄ ▄  - ▄ ▄▄ ▄ ▄ ▄ ▀▀▀▀ ▄▄ ▄ ▄▄ ▄▀▀ ▄▀▀▀▀ ▄ ▄ ▄ ▀▀▀▀▀▄ ▄▄ ▄▄ ▄  - ▄ ▄▄▄▄▄▄▄▄ ▀▄▄▄ ▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄ ▀ ▄▄▄▄ ▄▄ ▀ ▀  - ▄ ▄▄ ▀▀▀▀▀ ▄▄ ▄▄ ▀▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▀ ▄▄ ▄ ▀  - ▄ ▄ ▀ ▄▄▄▄▄▀▀▄▄▄ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▄▄▄▄▄ ▄▄ ▄▄▄ ▄▄ ▄ ▀  - ▄ ▄ ▄▄ ▄▄ ▄▄▄▀▀▄▄▄▄▄ ▄▄▄ ▀ ▄▄ ▄ ▄▄ ▀▀▄ ▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▄▀ ▄  - ▄ ▀ ▀▀▀▄▀▀▄ ▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀ ▀▀▀▀ ▄  - ▀▀▀▀▀ ▀ ▀▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀ ▀  - ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄ ▀ ▀▀▀▀▀▀▀ ▀▀▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▀▀▀ ▄▄▄▀▀▀ ▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▀▄▄ ▄  - ▄▄ ▄▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄▄▄ ▄  - ▄ ▄▀ ▀ ▄ ▄ ▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄ ▄▄ ▄  - ▄▄ ▀ ▄▄▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀ ▀  - ▀▄▀ ▀ ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▀ ▀ ▄ ▄▄▄  - ▀▀▀▀▀ ▀▀▀ ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀ ▀ ▀▀▀▀  - ▀ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▀ ▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  - ▀ ▀▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀ ▀  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▀  - ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄  -  -  -  -  -  -  - `, + "The Quant": `  +  +  +  +  + ▄ ▄▄▄▄▀ ▀ ▄▄▄  + ▄▄▄▄▄ ▄▄ ▄▀ ▄▀▄▀ ▄ ▄ ▀▄  + ▄▄▄ ▄ ▄▀▄ ▄▄▄▄▄▀▄▄ ▄ ▄ ▄▄▄▄▄ ▄ ▀  + ▄▄ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▀ ▄▄▀ ▄▄▄▄ ▄▄▀  + ▄▄ ▀ ▀ ▄▄▀ ▀▀ ▄▄▀▄▄▀▄▄▄▄▄▄▄▄▀▀▄▄▀▀▀▀ ▄▄▄▀▀  + ▄▄ ▄▄▄▄▄ ▄▄▀▀▄ ▄▄ ▄▄ ▄▄▄▄▀▄▄ ▄ ▄  + ▄▄ ▀▀▀▀▀▀ ▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀ ▀▀ ▀ ▄  + ▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀  + ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▀  + ▄ ▄▀▄ ▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄ ▀▀▀▄  + ▄▄ ▀▀ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▀ ▄▄  + ▄▀▄▀▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▀▀▀▄▄ ▄▄▄  + ▀ ▀▀▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀ ▄ ▀▀ ▀▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄  + ▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▄ ▄▄ ▄▄ ▄▄  +  +  +  + `, - "The Musk": `  -  -  -  -  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▀▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▄▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀▀  - ▀▄▄▄▄▄▄▄▀▀▀ ▀▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄▄▄▄  - ▀▀▀▄▄▄▄ ▄ ▀ ▄▄▄▄▄▄▄▄ ▀ ▀ ▀▀  - ▄▄▀ ▀▄▄▄▄▀  - ▄▀ ▀▀ ▀  - ▄ ▄ ▀ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▄ ▄▄▀▀▀▀  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  -  -  - ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▀▄▄▄▀▀▀▀▄▀▄▀ ▀▄▀▄▀▀▀▄▄▄▄▀  - ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀  -  -  -  -  -  - `, + "The Musk": `  +  +  +  +  +  +  +  +  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄ ▄ ▄▄▄▄▀▀  + ▀▄▄▄▄▄ ▀▄▄▄▄▀ ▄ ▀ ▀▀▀▀  + ▄▄ ▄▄  + ▄▄  + ▄ ▀ ▄  + ▄▄▄▄ ▄▄▄▄▄  +  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▀▄▀▄ ▄▄▀▀▄▄▀  +  +  +  + `, - "The Dario": `  -  -  -  -  -  -  -  - ▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀▀  - ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀  -  -  -  -  -  -  -  -  - `, + "The Dario": `  +  +  +  +  + ▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄  +  +  +  +  +  + `, - "The Karpathy": `  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀ ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄ ▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀▀▀ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▀▀▀▀▀▀▀▀ ▀▀▀▀ ▀▀ ▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀  -  -  -  -  -  -  -  -  - `, + "The Karpathy": `  +  +  +  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄ ▄▄ ▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▀▄ ▀▀ ▀▄ ▄▄▄▄▄▄▄  +  +  +  +  +  + `, - "Slough Boy": `  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄  - ▄▄▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▄  - ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀  - ▄▄▄▄▄▄ ▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▀ ▄▄▄▄▄▄▀  -  -  -  -  -  -  -  - `, + "Slough Boy": `  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀▀▀▀▀▀▀▀▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▀▄▀▀▀▀▀▄▄▀▀  + ▄▄▄▄ ▀▀▀▀▄▄▄▄  + ▄▄▄▄▄ ▄▄▄▄▄  +  +  +  +  + `, }; From 8d41791fbec87afe4ec5d65df34a8913630dc2e4 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sat, 4 Apr 2026 19:09:02 +0100 Subject: [PATCH 09/15] =?UTF-8?q?feat(cli):=20shrink=20mascot=20art=20to?= =?UTF-8?q?=2040=C3=9720?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-mascot-art.py | 4 +- scripts/mascots-ansi.ts | 432 ++++++++++++++------------------- 2 files changed, 182 insertions(+), 254 deletions(-) diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 8d0e1d6..715237a 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -24,8 +24,8 @@ MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") -WIDTH = 56 -HEIGHT = 28 +WIDTH = 40 +HEIGHT = 20 BG_THRESH = 35 # euclidean distance for flood-fill background detection CHAR_MAP: dict[str, str] = { diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index 84786d6..5e9f9b0 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -3,265 +3,193 @@ // To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { - "The Intern": `  -  -  -  -  -  -  -  -  - ▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▄▄▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄  - ▄▄ ▄▄▀▄▄▀▀▄▀▀▄▄▀▄▄ ▄ ▄▄  - ▄▄ ▄ ▄ ▄▄  - ▄▄ ▀ ▄▄ ▄ ▄▄ ▀ ▄▄  - ▄▄ ▀ ▄▄  -  - `, + "The Intern": `  +  +  +  +  +  + ▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▀ ▀▀ ▀ ▀▀ ▀ ▄ ▄  + ▄ ▀ ▄ ▄ ▄ ▀ ▄  + ▄ ▀ ▄  + `, - "The Degen": `  -  -  -  -  - ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▀▀▄▄▄▄▄▄▄▀▀▄▄▄▄  - ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  - ▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄  - ▄▄▄▀▄▄▀▀▄▄▄▄▄▀▄▄▀▀▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▀ ▄▄▄▄▄▄▄▄ ▄▄ ▀▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  - ▄▄▄▄▀ ▄▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▀▀▄▀ ▄ ▄▄▄▄  - ▄▄▄▄▄▀▀ ▀▀▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▀▄▄▄▄▄  - ▀▀▀▀ ▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄ ▄ ▀▀▀▀  - ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄ ▀▀▄▄▄▄▄▄▀▀▀▀▀▀▀▄▄▄▄▄▄▀▀ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▀ ▀▄▄▄▄▀ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▀▀ ▀▄▄▄▄▄ ▄▄▄▄▄▀▄▄▄▄▀  - ▀▀▀▀ ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▀ ▀▄▄▄▄▀▀▀  -  -  - `, + "The Degen": `  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▄▀▄▄▀▀▀▄▄▄▄▄▀▀ ▄▄ ▄▄▄  + ▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄  + ▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄ ▄▄▄▄  + ▀ ▄▄▀ ▄▄▄▀▄▄▄ ▀▀▀ ▀▀  + ▄▄▄▄ ▀▀▄▄▄▄▀▀▀▀▀▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄ ▄▄▄▄ ▀▄▄▄▀ ▄▄▄▄▄▄  + ▀▄▀▀▄▄▄▄▄▄▀▀▀▀▀ ▀▀▀▀▄▄▄▄▄▄▀▀▀▀  + ▀▀▄▄▄▄ ▀▄▄▄▀▀  +  + `, - "The SBF": `  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄ ▀▀▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀  - ▀▄▄▄▄▄▄▄▀▀▀▀▀▄▄▄▄▄▄▀  - ▀▀▄▄▄▀ ▀▄▄▀▀▀  -  -  -  -  -  -  - `, + "The SBF": `  +  +  +  +  + ▄▄▄▄▄▄▄  + ▄▄▄ ▄ ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▀▄▄▄▀ ▀▀▄▄▀  +  +  +  +  + `, - "The Sama": `  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▀ ▀▀▀▀▀▀▀  -  -  -  -  - `, + "The Sama": `  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀  + ▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄ ▄▄▄▄▄▄  +  +  +  + `, - "The Quant": `  -  -  -  -  - ▄ ▄▄▄▄▀ ▀ ▄▄▄  - ▄▄▄▄▄ ▄▄ ▄▀ ▄▀▄▀ ▄ ▄ ▀▄  - ▄▄▄ ▄ ▄▀▄ ▄▄▄▄▄▀▄▄ ▄ ▄ ▄▄▄▄▄ ▄ ▀  - ▄▄ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▀ ▄▄▀ ▄▄▄▄ ▄▄▀  - ▄▄ ▀ ▀ ▄▄▀ ▀▀ ▄▄▀▄▄▀▄▄▄▄▄▄▄▄▀▀▄▄▀▀▀▀ ▄▄▄▀▀  - ▄▄ ▄▄▄▄▄ ▄▄▀▀▄ ▄▄ ▄▄ ▄▄▄▄▀▄▄ ▄ ▄  - ▄▄ ▀▀▀▀▀▀ ▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀ ▀▀ ▀ ▄  - ▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀  - ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▀  - ▄ ▄▀▄ ▄▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄ ▀▀▀▄  - ▄▄ ▀▀ ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▀ ▄▄  - ▄▀▄▀▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▀▀▀▄▄ ▄▄▄  - ▀ ▀▀▀▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀▀▀▀ ▄ ▀▀ ▀▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄  - ▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄  - ▄ ▄▄ ▄▄ ▄▄  -  -  -  - `, + "The Quant": `  +  +  + ▄▄ ▄  + ▄ ▄ ▄  + ▄▄▄▀▀▄ ▄▄ ▀ ▄ ▀▀▄ ▄▄ ▀  + ▄▄▀▀▄▄▄ ▀ ▀▀▀ ▀▀▀ ▄ ▄ ▄▀▄▄▄▄ ▄  + ▄▄▄▄▀▄▄ ▄ ▄▄▄▄▄▄▄ ▄ ▀  + ▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▀▀▄▀▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀ ▀▄  + ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀  + ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▀▀▄ ▄▀▄▄  +  +  + `, - "The Musk": `  -  -  -  -  -  -  -  -  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄▄▄▄▄ ▀▄▄▄▄▄▄▄ ▄ ▄▄▄▄▀▀  - ▀▄▄▄▄▄ ▀▄▄▄▄▀ ▄ ▀ ▀▀▀▀  - ▄▄ ▄▄  - ▄▄  - ▄ ▀ ▄  - ▄▄▄▄ ▄▄▄▄▄  -  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▀▄▀▄ ▄▄▀▀▄▄▀  -  -  -  - `, + "The Musk": `  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▀▄▄▄▄▄ ▄▄▄▀  + ▀▀▄▄ ▀▄▄▄ ▀▀  + ▀ ▀▀  + ▄ ▄  + ▀▀▀▀ ▀▀▀  + ▄▄▄▄▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▀ ▀▄▄▄▄▄▀  +  +  + `, - "The Dario": `  -  -  -  -  - ▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄  -  -  -  -  -  - `, + "The Dario": `  +  +  + ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  + ▄▄▄▄▀▀▀▀▀▀▄▄▄▄▀▀  + ▄▄▄▄ ▀▄▄▄▄  +  +  +  + `, - "The Karpathy": `  -  -  -  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▄▄▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄ ▄▄ ▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▀▄ ▀▀ ▀▄ ▄▄▄▄▄▄▄  -  -  -  -  -  - `, + "The Karpathy": `  +  +  +  +  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄  + ▄▄▄▄▄▄▄▀ ▀▀▄▀▄▀▀▀▀▄▀▄▀ ▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▀ ▄ ▀ ▄ ▀ ▀▀ ▀▄▄▄▄▄  +  +  +  + `, - "Slough Boy": `  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀▀▀▀▀▀▀▀▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▀▄▄▀▄▀▀▀▀▀▄▄▀▀  - ▄▄▄▄ ▀▀▀▀▄▄▄▄  - ▄▄▄▄▄ ▄▄▄▄▄  -  -  -  -  - `, + "Slough Boy": `  +  +  +  + ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀  + ▀▀▀▀▀▀▄▀▀▀▀  + ▄▄▄▀▀▀▄▄▄  + ▀▀▀▀ ▀▀▀▀  +  +  + `, }; From aa70005dddbccb0cf21478ae57bf56feafacd6ff Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Sun, 5 Apr 2026 23:20:10 +0100 Subject: [PATCH 10/15] =?UTF-8?q?feat(cli):=20mascot=20below=20name/one-li?= =?UTF-8?q?ner,=20shrunk=20to=2030=C3=9715?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- scripts/ccrewind-tui.ts | 11 +- scripts/generate-mascot-art.py | 12 +- scripts/mascots-ansi.ts | 315 ++++++++++++++------------------- 3 files changed, 152 insertions(+), 186 deletions(-) diff --git a/scripts/ccrewind-tui.ts b/scripts/ccrewind-tui.ts index 13fb7dc..db449b3 100644 --- a/scripts/ccrewind-tui.ts +++ b/scripts/ccrewind-tui.ts @@ -269,7 +269,13 @@ function renderStreak(stats: ComputedStats): string { } function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, character: Character): string { - const lines: string[] = [sectionHeader("YOUR CHARACTER"), ""]; + const lines: string[] = [ + sectionHeader("YOUR CHARACTER"), + "", + ` ${orange("\u2726")} ${bold(white(character.name))} ${orange("\u2726")}`, + ` ${dim('"')}${white(character.oneLiner)}${dim('"')}`, + "", + ]; const art = MASCOT_ART[character.name]; if (art) { @@ -278,9 +284,6 @@ function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, charact } lines.push( - ` ${orange("\u2726")} ${bold(white(character.name))} ${orange("\u2726")}`, - ` ${dim('"')}${white(character.oneLiner)}${dim('"')}`, - "", ` Claude Elo: ${bold(orange(String(elo.total)))} / 1000`, ` ${renderEloBar(elo.total)}`, "", diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 715237a..55a58db 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -24,8 +24,8 @@ MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") -WIDTH = 40 -HEIGHT = 20 +WIDTH = 30 +HEIGHT = 15 BG_THRESH = 35 # euclidean distance for flood-fill background detection CHAR_MAP: dict[str, str] = { @@ -180,6 +180,14 @@ def main() -> None: print(f"Written: {OUTPUT_FILE}") + # also write preview JSON for quick terminal testing + preview_path = "/tmp/mascots_preview.json" + with open(preview_path, "w", encoding="utf-8") as f: + json.dump(results, f, ensure_ascii=False) + print(f"Preview : {preview_path}") + print(f"\nTo view all mascots:") + print(f' python3 -c "import json; data=json.load(open(\'{preview_path}\')); [print(\'\\n\\033[1;38;5;208m── \'+n+\' ──\\033[0m\\n\'+a) for n,a in data.items()]"') + if __name__ == "__main__": main() diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index 5e9f9b0..dd5c324 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -3,193 +3,148 @@ // To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { - "The Intern": `  -  -  -  -  -  - ▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄ ▀ ▀▀ ▀ ▀▀ ▀ ▄ ▄  - ▄ ▀ ▄ ▄ ▄ ▀ ▄  - ▄ ▀ ▄  - `, + "The Intern": `  +  +  +  +  + ▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▄▀  + ▄ ▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▄ ▀ ▀ ▀ ▀ ▄ ▄  + ▄ ▀ ▀ ▄ ▀ ▀ ▄  + `, - "The Degen": `  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▄▀▄▄▀▀▀▄▄▄▄▄▀▀ ▄▄ ▄▄▄  - ▄▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▄▄  - ▄▄▄ ▀▄▄▄▄▄▄▄▄▄▄▄▀▄▄ ▄▄▄▄  - ▀ ▄▄▀ ▄▄▄▀▄▄▄ ▀▀▀ ▀▀  - ▄▄▄▄ ▀▀▄▄▄▄▀▀▀▀▀▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄ ▄▄▄▄ ▀▄▄▄▀ ▄▄▄▄▄▄  - ▀▄▀▀▄▄▄▄▄▄▀▀▀▀▀ ▀▀▀▀▄▄▄▄▄▄▀▀▀▀  - ▀▀▄▄▄▄ ▀▄▄▄▀▀  -  - `, + "The Degen": `  +  +  + ▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▀▀▄▄▄▄▄▄▄▀ ▀▄  + ▀▄ ▄▄▄▄ ▄▀▄▄▀▄ ▄▀▄  + ▄▄▀ ▄▄▄ ▄▄▄▄▀▄▄ ▀▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▀▄▄▀ ▄▄▀ ▄▄▄▄  + ▀▀▀▄▄▄▄▄▀▀▀ ▀▀▀▄▄▄▄▄▀▀  + ▀▀▀▀ ▀▀▀  + `, - "The SBF": `  -  -  -  -  - ▄▄▄▄▄▄▄  - ▄▄▄ ▄ ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▀▄▄▄▀ ▀▀▄▄▀  -  -  -  -  - `, + "The SBF": `  +  +  +  + ▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▀▀▄▄▄▄  + ▀ ▀  +  +  + `, - "The Sama": `  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▄▄▄▀  - ▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄ ▄▄▄▄▄▄  -  -  -  - `, + "The Sama": `  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  + ▄▄▄▄ ▄▄▄▄▄  +  +  + `, - "The Quant": `  -  -  - ▄▄ ▄  - ▄ ▄ ▄  - ▄▄▄▀▀▄ ▄▄ ▀ ▄ ▀▀▄ ▄▄ ▀  - ▄▄▀▀▄▄▄ ▀ ▀▀▀ ▀▀▀ ▄ ▄ ▄▀▄▄▄▄ ▄  - ▄▄▄▄▀▄▄ ▄ ▄▄▄▄▄▄▄ ▄ ▀  - ▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▀▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▀▀▄▀▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀ ▀▄  - ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀ ▀  - ▀ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▀▀▄ ▄▀▄▄  -  -  - `, + "The Quant": `  +  +  + ▄ ▄ ▄ ▄ ▄▄▄ ▄▄  + ▄▄ ▄ ▄▄▄▄▄ ▀▄▄▄ ▀  + ▄ ▄▄▄▀▀▀▀▄▀▀▀ ▄ ▄  + ▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▄  + ▀▀▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▄▀▀▄▄  + ▄▀ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄  + ▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▀▄ ▄▀▄▀  +  + `, - "The Musk": `  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▀▄▄▄▄▄ ▄▄▄▀  - ▀▀▄▄ ▀▄▄▄ ▀▀  - ▀ ▀▀  - ▄ ▄  - ▀▀▀▀ ▀▀▀  - ▄▄▄▄▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▀ ▀▄▄▄▄▄▀  -  -  - `, + "The Musk": `  +  +  +  +  + ▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▀ ▀▄▄▄ ▄▄▀  + ▀▀  +  +  + ▄▄▄▄▄ ▄▄▄▄▄  + ▀▄▀▄▀ ▀▀▀▄▀  +  + `, - "The Dario": `  -  -  - ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  - ▄▄▄▄▀▀▀▀▀▀▄▄▄▄▀▀  - ▄▄▄▄ ▀▄▄▄▄  -  -  -  - `, + "The Dario": `  +  +  + ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▀▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▄▄▄▄ ▀▄▄▄  +  +  + `, - "The Karpathy": `  -  -  -  -  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀ ▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄  - ▄▄▄▄▄▄▄▀ ▀▀▄▀▄▀▀▀▀▄▀▄▀ ▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▀ ▄ ▀ ▄ ▀ ▀▀ ▀▄▄▄▄▄  -  -  -  - `, + "The Karpathy": `  +  +  +  +  +  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄▄▄▄▀ ▀▀ ▀ ▀ ▄▄▄▄  +  +  + `, - "Slough Boy": `  -  -  -  - ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▀▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▀▀▀  - ▀▀▀▀▀▀▄▀▀▀▀  - ▄▄▄▀▀▀▄▄▄  - ▀▀▀▀ ▀▀▀▀  -  -  - `, + "Slough Boy": `  +  +  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▀  + ▄▄ ▀▀▄▄▀  + ▀▀ ▀▀  +  + `, }; From d2a5c4ec50c3ce15b87ffbd8b91be4797fa2c133 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Mon, 6 Apr 2026 00:14:55 +0100 Subject: [PATCH 11/15] feat(cli): responsive layout with stacked rows, dynamic bar widths, cropped mascot art - TUI now fills terminal width (COL_WIDTH = process.stdout.columns) - Table rows stacked: name on own line, bar + stats indented below - Bar widths scale dynamically to fill available width - Header/footer/section dividers all match terminal width - One-liner wraps at terminal width - Calendar day cells spaced out for readability - Tool entries have blank line between each for breathing room - Mascot art blank lines trimmed from top and bottom Co-Authored-By: Claude Sonnet 4.6 --- scripts/ccrewind-tui.ts | 105 ++++++++++++++++++++++----------- scripts/generate-mascot-art.py | 9 +++ scripts/mascots-ansi.ts | 91 ++++++---------------------- 3 files changed, 98 insertions(+), 107 deletions(-) diff --git a/scripts/ccrewind-tui.ts b/scripts/ccrewind-tui.ts index db449b3..2f00b87 100644 --- a/scripts/ccrewind-tui.ts +++ b/scripts/ccrewind-tui.ts @@ -28,8 +28,31 @@ const gray = (s: string) => `${GRAY}${s}${RESET}`; const gold = (s: string) => `${GOLD}${s}${RESET}`; const green = (s: string) => `${GREEN}${s}${RESET}`; +const COL_WIDTH = Math.max(60, process.stdout.columns || 80); + // ── Formatting helpers ──────────────────────────────────── +function visibleLen(s: string): number { + return s.replace(/\x1b\[[0-9;]*m/g, "").length; +} + +function wrapLine(text: string, maxWidth: number, indent: string = ""): string[] { + const words = text.split(" "); + const lines: string[] = []; + let cur = ""; + for (const word of words) { + const test = cur ? `${cur} ${word}` : word; + if (visibleLen(test) > maxWidth && cur) { + lines.push(indent + cur); + cur = word; + } else { + cur = test; + } + } + if (cur) lines.push(indent + cur); + return lines.length ? lines : [indent]; +} + function formatNumber(n: number): string { if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B`; if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; @@ -90,11 +113,11 @@ function renderCalendar(activeDates: string[]): string[] { const dateStr = date.toISOString().split("T")[0]; if (date > today) { - line += " "; + line += " "; } else if (activeSet.has(dateStr)) { - line += orange("\u2593"); + line += orange("\u2593") + " "; } else { - line += gray("\u2591"); + line += gray("\u2591") + " "; } } lines.push(line); @@ -103,27 +126,28 @@ function renderCalendar(activeDates: string[]): string[] { } function renderEloBar(score: number, max: number = 1000): string { - const filled = Math.round((score / max) * 20); - return orange("\u25B0".repeat(filled)) + gray("\u25B1".repeat(20 - filled)); + const width = Math.max(10, COL_WIDTH - 4); + const filled = Math.round((score / max) * width); + return orange("\u25B0".repeat(filled)) + gray("\u25B1".repeat(width - filled)); } // ── Section renderers ───────────────────────────────────── function sectionHeader(title: string): string { - const dashes = "\u2500".repeat(Math.max(1, 44 - title.length - 5)); + const dashes = "\u2500".repeat(Math.max(1, COL_WIDTH - title.length - 5)); return orange(`\u2500\u2500\u2500 ${title} ${dashes}`); } function renderHeader(): string { + const inner = COL_WIDTH - 2; + const title = "\u27E1 CC REWIND \u27E1"; + const lpad = Math.floor((inner - title.length) / 2); + const rpad = inner - title.length - lpad; return [ "", - orange( - "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557" - ), - orange("\u2551") + bold(white(" \u27E1 CC REWIND \u27E1 ")) + orange("\u2551"), - orange( - "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D" - ), + orange("\u2554" + "\u2550".repeat(inner) + "\u2557"), + orange("\u2551") + bold(white(" ".repeat(lpad) + title + " ".repeat(rpad))) + orange("\u2551"), + orange("\u255A" + "\u2550".repeat(inner) + "\u255D"), dim(" \uD83D\uDD12 All data stays on your machine."), "", ].join("\n"); @@ -143,15 +167,15 @@ function renderTopTools(stats: ComputedStats): string { const tools = stats.topTools.slice(0, 7); if (tools.length === 0) return ""; const maxCount = tools[0].count; - const maxNameLen = Math.max(...tools.map((t) => t.name.length)); + const maxNameLen = COL_WIDTH - 2; + const BAR_WIDTH = Math.max(10, COL_WIDTH - 14); const lines = [sectionHeader("TOP TOOLS")]; for (const tool of tools) { - const name = pad(white(tool.name), maxNameLen + 8); - const bar = orange(renderBar(tool.count, maxCount, 20)); - const count = white(formatNumber(tool.count).padStart(7)); - lines.push(` ${name} ${bar} ${count}`); + const name = tool.name.length > maxNameLen ? tool.name.slice(0, maxNameLen - 1) + "…" : tool.name; + lines.push(` ${white(name)}`); + lines.push(` ${orange(renderBar(tool.count, maxCount, BAR_WIDTH))} ${white(formatNumber(tool.count))}`); + lines.push(""); } - lines.push(""); return lines.join("\n"); } @@ -172,17 +196,20 @@ function renderCostAndModels(stats: ComputedStats): string { if (allModels.length === 0) return ""; const maxCount = Math.max(...allModels.map((m) => m.count)); - const maxNameLen = Math.max(...allModels.map((m) => m.model.length)); + const maxNameLen = COL_WIDTH - 2; + // 4 indent + bar + 2 + 4 pct + 2 + 8 cost = 20 overhead + const BAR_WIDTH = Math.max(10, COL_WIDTH - 20); const lines = [sectionHeader("COST & MODELS")]; lines.push(` ${bold(orange(`$${stats.estimatedCostUSD.toFixed(2)}`))} ${dim("estimated total spend")}`); lines.push(""); for (const { model, count, cost } of allModels) { const pct = modelUsageTotal > 0 ? Math.round((count / modelUsageTotal) * 100) : 0; - const name = pad(white(model), maxNameLen + 8); - const bar = orange(renderBar(count, maxCount, 16)); + const name = model.length > maxNameLen ? model.slice(0, maxNameLen - 1) + "…" : model; + const bar = orange(renderBar(count, maxCount, BAR_WIDTH)); const costStr = cost >= 0.01 ? orange(`$${cost.toFixed(2)}`) : dim("< $0.01"); - lines.push(` ${name} ${bar} ${dim(`${pct}%`)} ${costStr}`); + lines.push(` ${white(name)}`); + lines.push(` ${bar} ${dim(`${pct}%`)} ${costStr}`); } lines.push(""); return lines.join("\n"); @@ -199,20 +226,22 @@ function renderTokenUsage(stats: ComputedStats): string { const pct = (v: number) => (total > 0 ? `(${Math.round((v / total) * 100)}%)` : ""); + // 2 + 12 name + 1 + bar + 2 + 7 count + 2 + 6 pct = 32 overhead + const BAR_WIDTH = Math.max(10, COL_WIDTH - 32); const lines = [sectionHeader("TOKEN USAGE")]; lines.push(dim(` Total: ${formatNumber(total)} tokens`)); lines.push(""); lines.push( - ` ${pad(white("Input"), 14)} ${orange(renderBar(stats.totalInputTokens, maxToken, 20))} ${white(formatNumber(stats.totalInputTokens))} ${dim(pct(stats.totalInputTokens))}` + ` ${pad(white("Input"), 12)} ${orange(renderBar(stats.totalInputTokens, maxToken, BAR_WIDTH))} ${white(formatNumber(stats.totalInputTokens))} ${dim(pct(stats.totalInputTokens))}` ); lines.push( - ` ${pad(white("Output"), 14)} ${orange(renderBar(stats.totalOutputTokens, maxToken, 20))} ${white(formatNumber(stats.totalOutputTokens))} ${dim(pct(stats.totalOutputTokens))}` + ` ${pad(white("Output"), 12)} ${orange(renderBar(stats.totalOutputTokens, maxToken, BAR_WIDTH))} ${white(formatNumber(stats.totalOutputTokens))} ${dim(pct(stats.totalOutputTokens))}` ); lines.push( - ` ${pad(gold("Cache Read"), 14)} ${gold(renderBar(stats.totalCacheReadTokens, maxToken, 20))} ${gold(formatNumber(stats.totalCacheReadTokens))} ${dim(pct(stats.totalCacheReadTokens))}` + ` ${pad(gold("Cache Read"), 12)} ${gold(renderBar(stats.totalCacheReadTokens, maxToken, BAR_WIDTH))} ${gold(formatNumber(stats.totalCacheReadTokens))} ${dim(pct(stats.totalCacheReadTokens))}` ); lines.push( - ` ${pad(gold("Cache Write"), 14)} ${gold(renderBar(stats.totalCacheCreationTokens, maxToken, 20))} ${gold(formatNumber(stats.totalCacheCreationTokens))} ${dim(pct(stats.totalCacheCreationTokens))}` + ` ${pad(gold("Cache Write"), 12)} ${gold(renderBar(stats.totalCacheCreationTokens, maxToken, BAR_WIDTH))} ${gold(formatNumber(stats.totalCacheCreationTokens))} ${dim(pct(stats.totalCacheCreationTokens))}` ); lines.push(""); return lines.join("\n"); @@ -231,12 +260,13 @@ function renderTopProjects(stats: ComputedStats): string { })) .sort((a, b) => b.estimatedCost - a.estimatedCost); - const maxNameLen = Math.max(...projects.map((p) => p.shortName.length)); + const maxNameLen = COL_WIDTH - 2; const lines = [sectionHeader("TOP PROJECTS")]; for (const p of projects) { - const name = pad(white(p.shortName), maxNameLen + 8); + const name = p.shortName.length > maxNameLen ? p.shortName.slice(0, maxNameLen - 1) + "…" : p.shortName; const cost = p.estimatedCost >= 0.01 ? orange(`$${p.estimatedCost.toFixed(2)}`) : dim("< $0.01"); - lines.push(` ${name} ${cost} ${dim(`${formatNumber(p.messages)} msgs`)} ${dim(`${p.sessions} sessions`)}`); + lines.push(` ${white(name)}`); + lines.push(` ${cost} ${dim(`${formatNumber(p.messages)} msgs`)} ${dim(`${p.sessions} sessions`)}`); } lines.push(""); return lines.join("\n"); @@ -273,7 +303,16 @@ function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, charact sectionHeader("YOUR CHARACTER"), "", ` ${orange("\u2726")} ${bold(white(character.name))} ${orange("\u2726")}`, - ` ${dim('"')}${white(character.oneLiner)}${dim('"')}`, + ...(() => { + const wrapped = wrapLine(character.oneLiner, COL_WIDTH - 4, " "); + return wrapped.map((line, i) => { + const text = line.trimStart(); + if (wrapped.length === 1) return ` ${dim('"')}${white(text)}${dim('"')}`; + if (i === 0) return ` ${dim('"')}${white(text)}`; + if (i === wrapped.length - 1) return ` ${white(text)}${dim('"')}`; + return ` ${white(text)}`; + }); + })(), "", ]; @@ -296,9 +335,7 @@ function renderCharacterReveal(_stats: ComputedStats, elo: EloBreakdown, charact function renderFooter(): string { return [ - orange( - "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" - ), + orange("\u2500".repeat(COL_WIDTH)), ` ${dim("Full visual experience \u2192")} ${orange("ccrewind.com")}`, "", ].join("\n"); diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py index 55a58db..4f9b86c 100644 --- a/scripts/generate-mascot-art.py +++ b/scripts/generate-mascot-art.py @@ -11,6 +11,7 @@ import json import math import os +import re import sys from collections import deque @@ -142,6 +143,14 @@ def is_bg(col: int, prow: int) -> bool: parts.append(RESET) lines.append("".join(parts)) + def is_blank(line: str) -> bool: + return re.sub(r"\x1b\[[0-9;]*m", "", line).strip() == "" + + while lines and is_blank(lines[0]): + lines.pop(0) + while lines and is_blank(lines[-1]): + lines.pop() + return "\n".join(lines) diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index dd5c324..cf2c321 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -3,12 +3,7 @@ // To regenerate: python3 scripts/generate-mascot-art.py export const MASCOT_ART: Record = { - "The Intern": `  -  -  -  -  - ▄▄▄▄▄▄▄▄▄  + "The Intern": ` ▄▄▄▄▄▄▄▄▄  ▄ ▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▀▀▀▀▀  @@ -16,13 +11,9 @@ export const MASCOT_ART: Record = { ▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▄▀  ▄ ▄▄▄▄▄▄▄▄▄ ▄  ▄ ▄ ▀ ▀ ▀ ▀ ▄ ▄  - ▄ ▀ ▀ ▄ ▀ ▀ ▄  - `, + ▄ ▀ ▀ ▄ ▀ ▀ ▄ `, - "The Degen": `  -  -  - ▄▄▄▄▄▄▄▄▄  + "The Degen": ` ▄▄▄▄▄▄▄▄▄  ▄▀▄▄▄▄▄▄▄▄▀  ▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▀  @@ -32,29 +23,18 @@ export const MASCOT_ART: Record = { ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄ ▀▄▄▀ ▄▄▀ ▄▄▄▄  ▀▀▀▄▄▄▄▄▀▀▀ ▀▀▀▄▄▄▄▄▀▀  - ▀▀▀▀ ▀▀▀  - `, + ▀▀▀▀ ▀▀▀ `, - "The SBF": `  -  -  -  - ▄ ▄▄▄▄▄  + "The SBF": ` ▄ ▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄ ▀  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▀▄▄▄▄▀▀▄▄▄▄  - ▀ ▀  -  -  - `, + ▀ ▀ `, - "The Sama": `  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄  + "The Sama": ` ▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  @@ -62,15 +42,9 @@ export const MASCOT_ART: Record = { ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  - ▄▄▄▄ ▄▄▄▄▄  -  -  - `, + ▄▄▄▄ ▄▄▄▄▄ `, - "The Quant": `  -  -  - ▄ ▄ ▄ ▄ ▄▄▄ ▄▄  + "The Quant": ` ▄ ▄ ▄ ▄ ▄▄▄ ▄▄  ▄▄ ▄ ▄▄▄▄▄ ▀▄▄▄ ▀  ▄ ▄▄▄▀▀▀▀▄▀▀▀ ▄ ▄  ▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀  @@ -79,30 +53,18 @@ export const MASCOT_ART: Record = { ▀▀▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▄▀▀▄▄  ▄▀ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄  ▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▀▄ ▄▀▄▀  -  - `, + ▀▄▀▄ ▄▀▄▀ `, - "The Musk": `  -  -  -  -  - ▄▄ ▄▄▄▄▄▄▄▄▄▄  + "The Musk": ` ▄▄ ▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄  ▀▄▄▀ ▀▄▄▄ ▄▄▀  ▀▀    ▄▄▄▄▄ ▄▄▄▄▄  - ▀▄▀▄▀ ▀▀▀▄▀  -  - `, + ▀▄▀▄▀ ▀▀▀▄▀ `, - "The Dario": `  -  -  - ▄▄▄▄▄▄▄▄▄  + "The Dario": ` ▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  ▀▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  @@ -110,31 +72,16 @@ export const MASCOT_ART: Record = { ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▄▄▄▄ ▀▄▄▄  -  -  - `, + ▄▄▄▄ ▀▄▄▄ `, - "The Karpathy": `  -  -  -  -  -  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + "The Karpathy": ` ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄▄▄▄▀ ▀▀ ▀ ▀ ▄▄▄▄  -  -  - `, + ▄▄▄▄▄▄▀ ▀▀ ▀ ▀ ▄▄▄▄ `, - "Slough Boy": `  -  -  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + "Slough Boy": ` ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  @@ -143,8 +90,6 @@ export const MASCOT_ART: Record = { ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▀  ▄▄ ▀▀▄▄▀  - ▀▀ ▀▀  -  - `, + ▀▀ ▀▀ `, }; From 1ff091fd93fdc930cd7e903364b61db0b5a4e5cc Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Mon, 6 Apr 2026 00:26:31 +0100 Subject: [PATCH 12/15] added claude image to the readme --- README.md | 2 ++ public/claude_standing_on_business.png | Bin 0 -> 175935 bytes 2 files changed, 2 insertions(+) create mode 100644 public/claude_standing_on_business.png diff --git a/README.md b/README.md index 41bcf1b..b8f523a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Claude Code Rewind +![Claude standing on business](public/claude_standing_on_business.png) + > There's no way to see your Claude Code usage. We changed that. Your developer archetype. Your token costs. Your Claude ELO. Nobody else has touched this data. Find out where you actually stand. Upload your `~/.claude` folder. Get a personalised story of your usage. Find out your archetype. Get your Claude Elo. Everything runs in the browser - zero data leaves your machine. diff --git a/public/claude_standing_on_business.png b/public/claude_standing_on_business.png new file mode 100644 index 0000000000000000000000000000000000000000..cd796696407b003d0c735eea16b618ae52f8ce20 GIT binary patch literal 175935 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%0AExG=>3&UsiJ5piL^Fhp)DxP?2S5E!G@AqR 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 literal 0 HcmV?d00001 From f91e23667c392dac24361c5874fe205c71c3ae08 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Mon, 6 Apr 2026 00:29:14 +0100 Subject: [PATCH 13/15] removed, cloudflare tunnel temp links --- next.config.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/next.config.ts b/next.config.ts index 023cd91..fa67b35 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,11 +4,6 @@ const nextConfig: NextConfig = { turbopack: { root: __dirname, }, - allowedDevOrigins: [ - "microwave-lawrence-stuart-result.trycloudflare.com", - "files-airplane-prayers-burst.trycloudflare.com", - "modifications-lanka-span-cinema.trycloudflare.com", - ], }; export default nextConfig; From b8f5bcea0fabd1e48a9811b71638f67f235fb4db Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Tue, 7 Apr 2026 02:21:15 +0100 Subject: [PATCH 14/15] refactor(scripts): rewrite generate scripts in TypeScript, fix upload screen rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert generate-variants.js → generate-variants.ts (ES module imports, fileURLToPath) - Rewrite generate-mascot-art.py → generate-mascot-art.ts using jimp (removes Python/Pillow dep) - Update mascots-ansi.ts header comment to reference .ts script - Add jimp as devDependency - Revert UploadScreenV3.tsx rename back to UploadScreen.tsx, fix page.tsx import Co-Authored-By: Claude Sonnet 4.6 --- package-lock.json | 1446 +++++++++++++++-- package.json | 1 + scripts/generate-mascot-art.py | 202 --- scripts/generate-mascot-art.ts | 251 +++ ...erate-variants.js => generate-variants.ts} | 74 +- scripts/mascots-ansi.ts | 161 +- src/app/page.tsx | 4 +- .../{UploadScreenV3.tsx => UploadScreen.tsx} | 2 +- 8 files changed, 1745 insertions(+), 396 deletions(-) delete mode 100644 scripts/generate-mascot-art.py create mode 100644 scripts/generate-mascot-art.ts rename scripts/{generate-variants.js => generate-variants.ts} (93%) rename src/components/upload/{UploadScreenV3.tsx => UploadScreen.tsx} (99%) diff --git a/package-lock.json b/package-lock.json index c6e5357..dcf5d9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,34 +8,33 @@ "name": "ccrewind", "version": "1.0.0", "license": "ISC", - "dependencies": { + "bin": { + "ccrewind": "dist/ccrewind-tui.mjs" + }, + "devDependencies": { "@tailwindcss/postcss": "^4.2.2", + "@types/d3": "^7.4.3", + "@types/jest": "^30.0.0", "@types/node": "^25.5.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "d3": "^7.9.0", + "esbuild": "^0.28.0", + "eslint": "^9.39.4", + "eslint-config-next": "^16.2.1", "framer-motion": "^12.38.0", "html-to-image": "^1.11.13", "html2canvas": "^1.4.1", + "jest": "^30.3.0", + "jimp": "^1.6.0", "next": "^16.2.1", "postcss": "^8.5.8", + "prettier": "^3.8.1", "react": "^19.2.4", "react-dom": "^19.2.4", "tailwindcss": "^4.2.2", - "typescript": "^5.8.3" - }, - "bin": { - "ccrewind": "dist/ccrewind-tui.mjs" - }, - "devDependencies": { - "@types/d3": "^7.4.3", - "@types/jest": "^30.0.0", - "esbuild": "^0.28.0", - "eslint": "^9.39.4", - "eslint-config-next": "^16.2.1", - "jest": "^30.3.0", - "prettier": "^3.8.1", "ts-jest": "^29.4.6", + "typescript": "^5.8.3", "typescript-eslint": "^8.57.2" }, "engines": { @@ -46,6 +45,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -574,6 +574,7 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -585,6 +586,7 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -595,6 +597,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1256,6 +1259,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -1269,6 +1273,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1291,6 +1296,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1313,6 +1319,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1329,6 +1336,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1345,6 +1353,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1361,6 +1370,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1377,6 +1387,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1393,6 +1404,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1409,6 +1421,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1425,6 +1438,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1441,6 +1455,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1457,6 +1472,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1473,6 +1489,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1495,6 +1512,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1517,6 +1535,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1539,6 +1558,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1561,6 +1581,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1583,6 +1604,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1605,6 +1627,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1627,6 +1650,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -1649,6 +1673,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { @@ -1668,6 +1693,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1687,6 +1713,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -1706,6 +1733,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ @@ -2123,131 +2151,732 @@ } } }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", + "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", + "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.3.0", + "@jest/types": "30.3.0", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", + "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.3.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", + "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.3.0", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", + "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jimp/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", + "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/file-ops": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "await-to-js": "^3.0.0", + "exif-parser": "^0.1.12", + "file-type": "^16.0.0", + "mime": "3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/diff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", + "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "pixelmatch": "^5.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/file-ops": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", + "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-bmp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", + "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "bmp-ts": "^1.0.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-gif": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", + "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "gifwrap": "^0.10.1", + "omggif": "^1.0.10" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-jpeg": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", + "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "jpeg-js": "^0.4.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-png": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", + "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "pngjs": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-tiff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", + "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "utif2": "^4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", + "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", + "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", + "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", + "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "tinycolor2": "^1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-color/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", + "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-contain/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", + "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-cover/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", + "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-crop/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", + "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-displace/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", + "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", + "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", + "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-flip/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-hash": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", + "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "any-base": "^1.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", + "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", + "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/types": "1.6.0", + "parse-bmfont-ascii": "^1.0.6", + "parse-bmfont-binary": "^1.0.6", + "parse-bmfont-xml": "^1.1.6", + "simple-xml-to-json": "^1.2.2", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-print/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-quantize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", + "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-quantize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-resize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", + "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-resize/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/@jimp/plugin-rotate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", + "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "node_modules/@jimp/plugin-rotate/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "node_modules/@jimp/plugin-threshold": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", + "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" + "@jimp/core": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/@jest/test-result": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.3.0.tgz", - "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", + "node_modules/@jimp/plugin-threshold/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/console": "30.3.0", - "@jest/types": "30.3.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@jest/test-sequencer": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz", - "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", + "node_modules/@jimp/types": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", + "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "slash": "^3.0.0" + "zod": "^3.23.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/@jest/transform": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.3.0.tgz", - "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", + "node_modules/@jimp/types/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.3.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", + "node_modules/@jimp/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@jimp/types": "1.6.0", + "tinycolor2": "^1.6.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -2258,6 +2887,7 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2268,6 +2898,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2277,12 +2908,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2293,6 +2926,7 @@ "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2305,6 +2939,7 @@ "version": "16.2.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.1.tgz", "integrity": "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg==", + "dev": true, "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -2324,6 +2959,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2340,6 +2976,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2356,6 +2993,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2372,6 +3010,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2388,6 +3027,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2404,6 +3044,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2420,6 +3061,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2436,6 +3078,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2555,6 +3198,7 @@ "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -2564,6 +3208,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", @@ -2579,6 +3224,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 20" @@ -2605,6 +3251,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2621,6 +3268,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2637,6 +3285,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2653,6 +3302,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2669,6 +3319,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2685,6 +3336,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2701,6 +3353,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2717,6 +3370,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2733,6 +3387,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2757,6 +3412,7 @@ "cpu": [ "wasm32" ], + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2778,6 +3434,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2794,6 +3451,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2807,6 +3465,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.2.tgz", "integrity": "sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==", + "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -2816,10 +3475,18 @@ "tailwindcss": "4.2.2" } }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3225,6 +3892,7 @@ "version": "25.5.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.18.0" @@ -3234,6 +3902,7 @@ "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -3243,6 +3912,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -3830,6 +4500,19 @@ "win32" ] }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -3915,6 +4598,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "dev": true, + "license": "MIT" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -4139,6 +4829,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/await-to-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/axe-core": { "version": "4.11.1", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", @@ -4269,15 +4969,38 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/baseline-browser-mapping": { "version": "2.10.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.11.tgz", "integrity": "sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==", + "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -4286,6 +5009,13 @@ "node": ">=6.0.0" } }, + "node_modules/bmp-ts": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", + "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", + "dev": true, + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", @@ -4367,6 +5097,31 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4448,6 +5203,7 @@ "version": "1.0.30001781", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4518,6 +5274,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true, "license": "MIT" }, "node_modules/cliui": { @@ -4640,6 +5397,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -4678,6 +5436,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "dev": true, "license": "MIT", "dependencies": { "utrie": "^1.0.2" @@ -4687,12 +5446,14 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, "license": "MIT" }, "node_modules/d3": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "3", @@ -4734,6 +5495,7 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, "license": "ISC", "dependencies": { "internmap": "1 - 2" @@ -4746,6 +5508,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4755,6 +5518,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -4771,6 +5535,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, "license": "ISC", "dependencies": { "d3-path": "1 - 3" @@ -4783,6 +5548,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4792,6 +5558,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "^3.2.0" @@ -4804,6 +5571,7 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, "license": "ISC", "dependencies": { "delaunator": "5" @@ -4816,6 +5584,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4825,6 +5594,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -4838,6 +5608,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, "license": "ISC", "dependencies": { "commander": "7", @@ -4863,6 +5634,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=12" @@ -4872,6 +5644,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, "license": "ISC", "dependencies": { "d3-dsv": "1 - 3" @@ -4884,6 +5657,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -4898,6 +5672,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4907,6 +5682,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "2.5.0 - 3" @@ -4919,6 +5695,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4928,6 +5705,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, "license": "ISC", "dependencies": { "d3-color": "1 - 3" @@ -4940,6 +5718,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4949,6 +5728,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4958,6 +5738,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4967,6 +5748,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4976,6 +5758,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "2.10.0 - 3", @@ -4992,6 +5775,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, "license": "ISC", "dependencies": { "d3-color": "1 - 3", @@ -5005,6 +5789,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -5014,6 +5799,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, "license": "ISC", "dependencies": { "d3-path": "^3.1.0" @@ -5026,6 +5812,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "2 - 3" @@ -5038,6 +5825,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, "license": "ISC", "dependencies": { "d3-time": "1 - 3" @@ -5050,6 +5838,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -5059,6 +5848,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, "license": "ISC", "dependencies": { "d3-color": "1 - 3", @@ -5078,6 +5868,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -5241,6 +6032,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "dev": true, "license": "ISC", "dependencies": { "robust-predicates": "^3.0.2" @@ -5250,6 +6042,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -5331,6 +6124,7 @@ "version": "5.20.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -6044,6 +6838,26 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -6075,6 +6889,12 @@ "dev": true, "license": "ISC" }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, "node_modules/exit-x": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", @@ -6187,6 +7007,24 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", + "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.2.4", + "token-types": "^4.1.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6275,6 +7113,7 @@ "version": "12.38.0", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "dev": true, "license": "MIT", "dependencies": { "motion-dom": "^12.38.0", @@ -6484,6 +7323,17 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/gifwrap": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "image-q": "^4.0.0", + "omggif": "^1.0.10" + } + }, "node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -6592,6 +7442,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/handlebars": { @@ -6738,12 +7589,14 @@ "version": "1.11.13", "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "dev": true, "license": "MIT" }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "dev": true, "license": "MIT", "dependencies": { "css-line-break": "^2.1.0", @@ -6767,6 +7620,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -6775,6 +7629,27 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -6785,6 +7660,23 @@ "node": ">= 4" } }, + "node_modules/image-q": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "16.9.1" + } + }, + "node_modules/image-q/node_modules/@types/node": { + "version": "16.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", + "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", + "dev": true, + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -6870,6 +7762,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -8064,15 +8957,62 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jimp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", + "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/diff": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-gif": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-blur": "1.6.0", + "@jimp/plugin-circle": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-contain": "1.6.0", + "@jimp/plugin-cover": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-displace": "1.6.0", + "@jimp/plugin-dither": "1.6.0", + "@jimp/plugin-fisheye": "1.6.0", + "@jimp/plugin-flip": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/plugin-mask": "1.6.0", + "@jimp/plugin-print": "1.6.0", + "@jimp/plugin-quantize": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/plugin-rotate": "1.6.0", + "@jimp/plugin-threshold": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8221,6 +9161,7 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -8253,6 +9194,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8273,6 +9215,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8293,6 +9236,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8313,6 +9257,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8333,6 +9278,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8353,6 +9299,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8373,6 +9320,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8393,6 +9341,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8413,6 +9362,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8433,6 +9383,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8453,6 +9404,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -8530,6 +9482,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -8609,6 +9562,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -8656,6 +9622,7 @@ "version": "12.38.0", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "dev": true, "license": "MIT", "dependencies": { "motion-utils": "^12.36.0" @@ -8665,6 +9632,7 @@ "version": "12.36.0", "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "dev": true, "license": "MIT" }, "node_modules/ms": { @@ -8678,6 +9646,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -8726,6 +9695,7 @@ "version": "16.2.1", "resolved": "https://registry.npmjs.org/next/-/next-16.2.1.tgz", "integrity": "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q==", + "dev": true, "license": "MIT", "dependencies": { "@next/env": "16.2.1", @@ -8779,6 +9749,7 @@ "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -8992,6 +9963,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "dev": true, + "license": "MIT" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9103,6 +10081,13 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9116,6 +10101,31 @@ "node": ">=6" } }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9196,10 +10206,25 @@ "dev": true, "license": "ISC" }, + "node_modules/peek-readable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", + "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -9225,6 +10250,29 @@ "node": ">= 6" } }, + "node_modules/pixelmatch": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^6.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pixelmatch/node_modules/pngjs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -9294,6 +10342,16 @@ "node": ">=8" } }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -9308,6 +10366,7 @@ "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -9393,6 +10452,16 @@ "dev": true, "license": "MIT" }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -9457,6 +10526,7 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9466,6 +10536,7 @@ "version": "19.2.4", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "dev": true, "license": "MIT", "dependencies": { "scheduler": "^0.27.0" @@ -9481,6 +10552,40 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz", + "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -9614,6 +10719,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "dev": true, "license": "Unlicense" }, "node_modules/run-parallel": { @@ -9644,6 +10750,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/safe-array-concat": { @@ -9666,6 +10773,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -9705,19 +10833,31 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, "license": "MIT" }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "dev": true, "license": "MIT" }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -9779,6 +10919,7 @@ "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "optional": true, @@ -9932,6 +11073,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-xml-to-json": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.5.tgz", + "integrity": "sha512-ZyRpOE39EtJcTA3eL7qkHXKtZSNrDm3PkttECKBfaFTDuEvnXQA/Sq0mPMxdNfMLrCkMcJT4nEgGStceP73Ihg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.12.2" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -9956,6 +11107,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -10023,6 +11175,16 @@ "node": ">= 0.4" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10310,10 +11472,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", + "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^4.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "dev": true, "license": "MIT", "dependencies": { "client-only": "0.0.1" @@ -10379,12 +11560,14 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -10435,11 +11618,19 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "dev": true, "license": "MIT", "dependencies": { "utrie": "^1.0.2" } }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -10508,6 +11699,24 @@ "node": ">=8.0" } }, + "node_modules/token-types": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", + "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -10617,6 +11826,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD" }, "node_modules/type-check": { @@ -10737,6 +11947,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -10807,6 +12018,7 @@ "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, "license": "MIT" }, "node_modules/unrs-resolver": { @@ -10885,10 +12097,21 @@ "punycode": "^2.1.0" } }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^1.0.11" + } + }, "node_modules/utrie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "dev": true, "license": "MIT", "dependencies": { "base64-arraybuffer": "^1.0.2" @@ -11157,6 +12380,37 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 17e8ff8..e157e00 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "html-to-image": "^1.11.13", "html2canvas": "^1.4.1", "jest": "^30.3.0", + "jimp": "^1.6.0", "next": "^16.2.1", "postcss": "^8.5.8", "prettier": "^3.8.1", diff --git a/scripts/generate-mascot-art.py b/scripts/generate-mascot-art.py deleted file mode 100644 index 4f9b86c..0000000 --- a/scripts/generate-mascot-art.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env python3 -""" -Generate true-color ANSI half-block art for all character mascots and write to -scripts/mascots-ansi.ts. Run from repo root: - - python3 scripts/generate-mascot-art.py - -Requires: Pillow (pip install pillow) -""" - -import json -import math -import os -import re -import sys -from collections import deque - -try: - from PIL import Image -except ImportError: - print("Error: Pillow is required. Run: pip install pillow") - sys.exit(1) - -REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -MASCOTS_DIR = os.path.join(REPO_ROOT, "public", "mascots") -OUTPUT_FILE = os.path.join(REPO_ROOT, "scripts", "mascots-ansi.ts") - -WIDTH = 30 -HEIGHT = 15 -BG_THRESH = 35 # euclidean distance for flood-fill background detection - -CHAR_MAP: dict[str, str] = { - "The Intern": "char-the-intern.png", - "The Degen": "char-the-degen.png", - "The SBF": "char-the-ghost.png", - "The Sama": "char-the-operator.png", - "The Quant": "char-the-quant.png", - "The Musk": "char-the-chaos-agent.png", - "The Dario": "char-the-visionary.png", - "The Karpathy": "char-the-night-shift-engineer.png", - "Slough Boy": "char-the-researcher.png", -} - -ESC = "\x1b" -RESET = f"{ESC}[0m" - - -def detect_bg(img: Image.Image) -> tuple: - w, h = img.size - samples = [img.getpixel((x, y)) for x, y in [ - (0, 0), (w - 1, 0), (0, h - 1), (w - 1, h - 1), (w // 2, 0), (0, h // 2) - ]] - return tuple(sum(s[i] for s in samples) // len(samples) for i in range(3)) - - -def color_dist(a: tuple, b: tuple) -> float: - return math.sqrt(sum((a[i] - b[i]) ** 2 for i in range(3))) - - -def make_bg_mask(img: Image.Image, bg: tuple, thresh: float) -> list[list[bool]]: - """ - Flood fill from all edge pixels that match the background colour. - Only pixels *connected* to the edges are marked transparent — dark pixels - inside the character are preserved even if they're close to the bg colour. - """ - w, h = img.size - visited = [[False] * h for _ in range(w)] - queue: deque = deque() - - def seed(x: int, y: int) -> None: - if not visited[x][y] and color_dist(img.getpixel((x, y)), bg) < thresh: - visited[x][y] = True - queue.append((x, y)) - - for x in range(w): - seed(x, 0) - seed(x, h - 1) - for y in range(h): - seed(0, y) - seed(w - 1, y) - - while queue: - x, y = queue.popleft() - for nx, ny in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]: - if 0 <= nx < w and 0 <= ny < h and not visited[nx][ny]: - if color_dist(img.getpixel((nx, ny)), bg) < thresh: - visited[nx][ny] = True - queue.append((nx, ny)) - - return visited - - -def img_to_ansi(path: str) -> str: - img = Image.open(path).convert("RGB") - bg = detect_bg(img) - orig_w, orig_h = img.size - mask = make_bg_mask(img, bg, BG_THRESH) - - img = img.resize((WIDTH, HEIGHT * 2), Image.LANCZOS) - sx, sy = orig_w / WIDTH, orig_h / (HEIGHT * 2) - - def is_bg(col: int, prow: int) -> bool: - ox = min(int(col * sx), orig_w - 1) - oy = min(int(prow * sy), orig_h - 1) - return mask[ox][oy] - - lines = [] - for row in range(HEIGHT): - parts: list[str] = [] - cur_fg = cur_bg = None - for col in range(WIDTH): - tp = img.getpixel((col, row * 2)) - bp = img.getpixel((col, row * 2 + 1)) - tc = None if is_bg(col, row * 2) else tp - bc = None if is_bg(col, row * 2 + 1) else bp - - if tc is None and bc is None: - if cur_fg is not None or cur_bg is not None: - parts.append(RESET) - cur_fg = cur_bg = None - parts.append(" ") - continue - - if tc is not None and bc is not None: - new_fg, new_bg, char = bc, tc, "▄" - elif tc is not None: - new_fg, new_bg, char = tc, None, "▀" - else: - new_fg, new_bg, char = bc, None, "▄" - - seq = "" - if new_bg != cur_bg: - if new_bg is None: - seq += f"{ESC}[49m" - else: - seq += f"{ESC}[48;2;{new_bg[0]};{new_bg[1]};{new_bg[2]}m" - cur_bg = new_bg - if new_fg != cur_fg: - seq += f"{ESC}[38;2;{new_fg[0]};{new_fg[1]};{new_fg[2]}m" - cur_fg = new_fg - parts.append(seq + char) - - parts.append(RESET) - lines.append("".join(parts)) - - def is_blank(line: str) -> bool: - return re.sub(r"\x1b\[[0-9;]*m", "", line).strip() == "" - - while lines and is_blank(lines[0]): - lines.pop(0) - while lines and is_blank(lines[-1]): - lines.pop() - - return "\n".join(lines) - - -def main() -> None: - results: dict[str, str] = {} - total_bytes = 0 - for name, filename in CHAR_MAP.items(): - path = os.path.join(MASCOTS_DIR, filename) - if not os.path.exists(path): - print(f" WARNING: missing {path}") - continue - print(f" Processing {name}...", flush=True) - art = img_to_ansi(path) - results[name] = art - size = len(art.encode("utf-8")) - total_bytes += size - print(f" → {size // 1024}KB") - - print(f"\nTotal: {total_bytes // 1024}KB") - - ts_lines = [ - "// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually", - "// Pre-rendered true-color ANSI half-block art (90×45, flood-fill transparent bg)", - "// To regenerate: python3 scripts/generate-mascot-art.py", - "", - "export const MASCOT_ART: Record = {", - ] - for name, art in results.items(): - escaped = art.replace("\\", "\\\\").replace("`", "\\`").replace("${", "\\${") - ts_lines.append(f" {json.dumps(name)}: `{escaped}`,") - ts_lines.append("") - ts_lines.append("};\n") - - with open(OUTPUT_FILE, "w", encoding="utf-8") as f: - f.write("\n".join(ts_lines)) - - print(f"Written: {OUTPUT_FILE}") - - # also write preview JSON for quick terminal testing - preview_path = "/tmp/mascots_preview.json" - with open(preview_path, "w", encoding="utf-8") as f: - json.dump(results, f, ensure_ascii=False) - print(f"Preview : {preview_path}") - print(f"\nTo view all mascots:") - print(f' python3 -c "import json; data=json.load(open(\'{preview_path}\')); [print(\'\\n\\033[1;38;5;208m── \'+n+\' ──\\033[0m\\n\'+a) for n,a in data.items()]"') - - -if __name__ == "__main__": - main() diff --git a/scripts/generate-mascot-art.ts b/scripts/generate-mascot-art.ts new file mode 100644 index 0000000..789b67b --- /dev/null +++ b/scripts/generate-mascot-art.ts @@ -0,0 +1,251 @@ +/** + * Generate true-color ANSI half-block art for all character mascots and write to + * scripts/mascots-ansi.ts. Run from repo root: + * + * npx tsx scripts/generate-mascot-art.ts + * + * Requires: jimp (devDependency) + */ + +import { Jimp, intToRGBA } from "jimp"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const REPO_ROOT = path.dirname(__dirname); +const MASCOTS_DIR = path.join(REPO_ROOT, "public", "mascots"); +const OUTPUT_FILE = path.join(REPO_ROOT, "scripts", "mascots-ansi.ts"); + +const WIDTH = 30; +const HEIGHT = 15; +const BG_THRESH = 35; + +const CHAR_MAP: Record = { + "The Intern": "char-the-intern.png", + "The Degen": "char-the-degen.png", + "The SBF": "char-the-ghost.png", + "The Sama": "char-the-operator.png", + "The Quant": "char-the-quant.png", + "The Musk": "char-the-chaos-agent.png", + "The Dario": "char-the-visionary.png", + "The Karpathy": "char-the-night-shift-engineer.png", + "Slough Boy": "char-the-researcher.png", +}; + +const ESC = "\x1b"; +const RESET = `${ESC}[0m`; + +type RGB = { r: number; g: number; b: number }; + +function colorDist(a: RGB, b: RGB): number { + return Math.sqrt((a.r - b.r) ** 2 + (a.g - b.g) ** 2 + (a.b - b.b) ** 2); +} + +function getPixel(img: Jimp, x: number, y: number): RGB { + const rgba = intToRGBA(img.getPixelColor(x, y)); + return { r: rgba.r, g: rgba.g, b: rgba.b }; +} + +function detectBg(img: Jimp): RGB { + const w = img.width; + const h = img.height; + const samples = [ + getPixel(img, 0, 0), + getPixel(img, w - 1, 0), + getPixel(img, 0, h - 1), + getPixel(img, w - 1, h - 1), + getPixel(img, Math.floor(w / 2), 0), + getPixel(img, 0, Math.floor(h / 2)), + ]; + return { + r: Math.floor(samples.reduce((s, p) => s + p.r, 0) / samples.length), + g: Math.floor(samples.reduce((s, p) => s + p.g, 0) / samples.length), + b: Math.floor(samples.reduce((s, p) => s + p.b, 0) / samples.length), + }; +} + +function makeBgMask(img: Jimp, bg: RGB, thresh: number): boolean[][] { + const w = img.width; + const h = img.height; + const visited: boolean[][] = Array.from({ length: w }, () => + new Array(h).fill(false) + ); + const queue: [number, number][] = []; + + const seed = (x: number, y: number) => { + if (!visited[x][y] && colorDist(getPixel(img, x, y), bg) < thresh) { + visited[x][y] = true; + queue.push([x, y]); + } + }; + + for (let x = 0; x < w; x++) { + seed(x, 0); + seed(x, h - 1); + } + for (let y = 0; y < h; y++) { + seed(0, y); + seed(w - 1, y); + } + + let i = 0; + while (i < queue.length) { + const [x, y] = queue[i++]; + for (const [nx, ny] of [ + [x - 1, y], + [x + 1, y], + [x, y - 1], + [x, y + 1], + ] as [number, number][]) { + if (nx >= 0 && nx < w && ny >= 0 && ny < h && !visited[nx][ny]) { + if (colorDist(getPixel(img, nx, ny), bg) < thresh) { + visited[nx][ny] = true; + queue.push([nx, ny]); + } + } + } + } + + return visited; +} + +async function imgToAnsi(filePath: string): Promise { + const orig = await Jimp.read(filePath); + const origW = orig.width; + const origH = orig.height; + const bg = detectBg(orig); + const mask = makeBgMask(orig, bg, BG_THRESH); + + const img = orig.clone(); + img.resize({ w: WIDTH, h: HEIGHT * 2 }); + + const scaleX = origW / WIDTH; + const scaleY = origH / (HEIGHT * 2); + + const isBg = (col: number, prow: number): boolean => { + const ox = Math.min(Math.floor(col * scaleX), origW - 1); + const oy = Math.min(Math.floor(prow * scaleY), origH - 1); + return mask[ox][oy]; + }; + + const lines: string[] = []; + + for (let row = 0; row < HEIGHT; row++) { + const parts: string[] = []; + let curFg: RGB | null = null; + let curBg: RGB | null = null; + + for (let col = 0; col < WIDTH; col++) { + const tp = getPixel(img, col, row * 2); + const bp = getPixel(img, col, row * 2 + 1); + const tc = isBg(col, row * 2) ? null : tp; + const bc = isBg(col, row * 2 + 1) ? null : bp; + + if (tc === null && bc === null) { + if (curFg !== null || curBg !== null) { + parts.push(RESET); + curFg = curBg = null; + } + parts.push(" "); + continue; + } + + let newFg: RGB, newBg: RGB | null, char: string; + if (tc !== null && bc !== null) { + newFg = bc; + newBg = tc; + char = "▄"; + } else if (tc !== null) { + newFg = tc; + newBg = null; + char = "▀"; + } else { + newFg = bc!; + newBg = null; + char = "▄"; + } + + let seq = ""; + if ( + newBg?.r !== curBg?.r || + newBg?.g !== curBg?.g || + newBg?.b !== curBg?.b + ) { + seq += + newBg === null + ? `${ESC}[49m` + : `${ESC}[48;2;${newBg.r};${newBg.g};${newBg.b}m`; + curBg = newBg; + } + if ( + newFg.r !== curFg?.r || + newFg.g !== curFg?.g || + newFg.b !== curFg?.b + ) { + seq += `${ESC}[38;2;${newFg.r};${newFg.g};${newFg.b}m`; + curFg = newFg; + } + parts.push(seq + char); + } + + parts.push(RESET); + lines.push(parts.join("")); + } + + const isBlank = (line: string) => + line.replace(/\x1b\[[0-9;]*m/g, "").trim() === ""; + while (lines.length && isBlank(lines[0])) lines.shift(); + while (lines.length && isBlank(lines[lines.length - 1])) lines.pop(); + + return lines.join("\n"); +} + +async function main() { + const results: Record = {}; + let totalBytes = 0; + + for (const [name, filename] of Object.entries(CHAR_MAP)) { + const filePath = path.join(MASCOTS_DIR, filename); + if (!fs.existsSync(filePath)) { + console.warn(` WARNING: missing ${filePath}`); + continue; + } + process.stdout.write(` Processing ${name}...`); + const art = await imgToAnsi(filePath); + results[name] = art; + const size = Buffer.byteLength(art, "utf8"); + totalBytes += size; + console.log(` → ${Math.floor(size / 1024)}KB`); + } + + console.log(`\nTotal: ${Math.floor(totalBytes / 1024)}KB`); + + const tsLines = [ + "// AUTO-GENERATED by scripts/generate-mascot-art.ts — do not edit manually", + "// Pre-rendered true-color ANSI half-block art (30×15, flood-fill transparent bg)", + "// To regenerate: npx tsx scripts/generate-mascot-art.ts", + "", + "export const MASCOT_ART: Record = {", + ]; + + for (const [name, art] of Object.entries(results)) { + const escaped = art + .replace(/\\/g, "\\\\") + .replace(/`/g, "\\`") + .replace(/\$\{/g, "\\${"); + tsLines.push(` ${JSON.stringify(name)}: \`${escaped}\`,`); + tsLines.push(""); + } + + tsLines.push("};\n"); + fs.writeFileSync(OUTPUT_FILE, tsLines.join("\n"), "utf8"); + console.log(`Written: ${OUTPUT_FILE}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/generate-variants.js b/scripts/generate-variants.ts similarity index 93% rename from scripts/generate-variants.js rename to scripts/generate-variants.ts index 5458674..ab14c8e 100644 --- a/scripts/generate-variants.js +++ b/scripts/generate-variants.ts @@ -1,13 +1,19 @@ -const fs = require('fs'); -const path = require('path'); +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; -const srcPath = path.join(__dirname, '../src/components/upload/UploadScreen.tsx'); -const originalSource = fs.readFileSync(srcPath, 'utf8'); +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); -const previewCardsMatch = originalSource.match(/\/\* ─── Stacked preview cards ─── \*\/\s*function PreviewCards\(\) \{[\s\S]*?return \([\s\S]*? \);\n\}/); -const previewCardsCode = previewCardsMatch[0]; +const srcPath = path.join(__dirname, "../src/components/upload/UploadScreen.tsx"); +const originalSource = fs.readFileSync(srcPath, "utf8"); -const generateTemplate = (vNum, chromeCode, wrapperClass, rightColClass) => ` +const previewCardsMatch = originalSource.match( + /\/\* ─── Stacked preview cards ─── \*\/\s*function PreviewCards\(\) \{[\s\S]*?return \([\s\S]*? \);\n\}/ +); +const previewCardsCode = previewCardsMatch ? previewCardsMatch[0] : ""; + +const generateTemplate = (vNum: string, chromeCode: string, wrapperClass: string, rightColClass: string) => ` "use client"; import { useState, useCallback, useRef, useEffect } from "react"; @@ -559,10 +565,50 @@ const v5Chrome = `
`; -fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV1.tsx'), generateTemplate('1', v1Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); -fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV2.tsx'), generateTemplate('2', v2Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); -fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV3.tsx'), generateTemplate('3', v3Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); -fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV4.tsx'), generateTemplate('4', v4Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); -fs.writeFileSync(path.join(__dirname, '../src/components/upload/UploadScreenV5.tsx'), generateTemplate('5', v5Chrome, 'flex-1 flex flex-col items-center md:items-start w-full max-w-lg', 'shrink-0 hidden md:block')); - -console.log('Successfully generated 5 variants (fixed Next.js TS issues).'); +fs.writeFileSync( + path.join(__dirname, "../src/components/upload/UploadScreenV1.tsx"), + generateTemplate( + "1", + v1Chrome, + "flex-1 flex flex-col items-center md:items-start w-full max-w-lg", + "shrink-0 hidden md:block" + ) +); +fs.writeFileSync( + path.join(__dirname, "../src/components/upload/UploadScreenV2.tsx"), + generateTemplate( + "2", + v2Chrome, + "flex-1 flex flex-col items-center md:items-start w-full max-w-lg", + "shrink-0 hidden md:block" + ) +); +fs.writeFileSync( + path.join(__dirname, "../src/components/upload/UploadScreenV3.tsx"), + generateTemplate( + "3", + v3Chrome, + "flex-1 flex flex-col items-center md:items-start w-full max-w-lg", + "shrink-0 hidden md:block" + ) +); +fs.writeFileSync( + path.join(__dirname, "../src/components/upload/UploadScreenV4.tsx"), + generateTemplate( + "4", + v4Chrome, + "flex-1 flex flex-col items-center md:items-start w-full max-w-lg", + "shrink-0 hidden md:block" + ) +); +fs.writeFileSync( + path.join(__dirname, "../src/components/upload/UploadScreenV5.tsx"), + generateTemplate( + "5", + v5Chrome, + "flex-1 flex flex-col items-center md:items-start w-full max-w-lg", + "shrink-0 hidden md:block" + ) +); + +console.log("Successfully generated 5 variants (fixed Next.js TS issues)."); diff --git a/scripts/mascots-ansi.ts b/scripts/mascots-ansi.ts index cf2c321..363df90 100644 --- a/scripts/mascots-ansi.ts +++ b/scripts/mascots-ansi.ts @@ -1,95 +1,94 @@ -// AUTO-GENERATED by scripts/generate-mascot-art.py — do not edit manually +// AUTO-GENERATED by scripts/generate-mascot-art.ts — do not edit manually // Pre-rendered true-color ANSI half-block art (90×45, flood-fill transparent bg) -// To regenerate: python3 scripts/generate-mascot-art.py +// To regenerate: npx tsx scripts/generate-mascot-art.ts export const MASCOT_ART: Record = { - "The Intern": ` ▄▄▄▄▄▄▄▄▄  - ▄ ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▄▀  - ▄ ▄▄▄▄▄▄▄▄▄ ▄  - ▄ ▄ ▀ ▀ ▀ ▀ ▄ ▄  - ▄ ▀ ▀ ▄ ▀ ▀ ▄ `, + "The Intern": ` ▄▄▄▄▄▄▄▄▄  + ▄ ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▀▀▀▀▀▀▀▄▄▄▄▄▄▄▄▄▀▀▀▀▀▀▀▄▀  + ▄ ▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▄ ▀ ▀ ▀ ▀ ▄ ▄  + ▄ ▀ ▀ ▄ ▀ ▀ ▄ `, - "The Degen": ` ▄▄▄▄▄▄▄▄▄  - ▄▀▄▄▄▄▄▄▄▄▀  - ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▄▄▀▀▄▄▄▄▄▄▄▀ ▀▄  - ▀▄ ▄▄▄▄ ▄▀▄▄▀▄ ▄▀▄  - ▄▄▀ ▄▄▄ ▄▄▄▄▀▄▄ ▀▄▄  - ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄ ▀▄▄▀ ▄▄▀ ▄▄▄▄  - ▀▀▀▄▄▄▄▄▀▀▀ ▀▀▀▄▄▄▄▄▀▀  - ▀▀▀▀ ▀▀▀ `, + "The Degen": ` ▄▄▄▄▄▄▄▄▄  + ▄▀▄▄▄▄▄▄▄▄▀  + ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▄▄▀▀▄▄▄▄▄▄▄▀ ▀▄  + ▀▄ ▄▄▄▄ ▄▀▄▄▀▄ ▄▀▄  + ▄▄▀ ▄▄▄ ▄▄▄▄▀▄▄ ▀▄▄  + ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄ ▀▄▄▀ ▄▄▀ ▄▄▄▄  + ▀▀▀▄▄▄▄▄▀▀▀ ▀▀▀▄▄▄▄▄▀▀  + ▀▀▀▀ ▀▀▀ `, - "The SBF": ` ▄ ▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▀  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▀▀▄▄▄▄  - ▀ ▀ `, + "The SBF": ` ▄ ▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▀  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▀▀▄▄▄▄  + ▀ ▀ `, - "The Sama": ` ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  - ▄▄▄▄ ▄▄▄▄▄ `, + "The Sama": ` ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀  + ▄▄▄▄ ▄▄▄▄▄ `, - "The Quant": ` ▄ ▄ ▄ ▄ ▄▄▄ ▄▄  - ▄▄ ▄ ▄▄▄▄▄ ▀▄▄▄ ▀  - ▄ ▄▄▄▀▀▀▀▄▀▀▀ ▄ ▄  - ▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀  - ▄▄▄▄▄▄▄▄▄▄▄▄ ▄  - ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▄  - ▀▀▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▄▀▀▄▄  - ▄▀ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄  - ▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▀▄ ▄▀▄▀ `, + "The Quant": ` ▄ ▄ ▄ ▄ ▄▄▄ ▄▄  + ▄▄ ▄ ▄▄▄▄▄ ▀▄▄▄ ▀  + ▄ ▄▄▄▀▀▀▀▄▀▀▀ ▄ ▄  + ▀▀ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▀▀▀  + ▄▄▄▄▄▄▄▄▄▄▄▄ ▄  + ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▀▄  + ▀▀▀▄ ▀▄▄▄▄▄▄▄▄▄▄▄▀▀▄▀▀▄▄  + ▄▀ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄  + ▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▀▄ ▄▀▄▀ `, - "The Musk": ` ▄▄ ▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▀ ▀▄▄▄ ▄▄▀  - ▀▀  + "The Musk": ` ▄▄ ▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▀ ▀▄▄▄ ▄▄▀  + ▀▀    - ▄▄▄▄▄ ▄▄▄▄▄  - ▀▄▀▄▀ ▀▀▀▄▀ `, + ▄▄▄▄▄ ▄▄▄▄▄  + ▀▄▀▄▀ ▀▀▀▄▀ `, - "The Dario": ` ▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  - ▀▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  - ▄▄▄▄ ▀▄▄▄ `, + "The Dario": ` ▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀  + ▀▄ ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀  + ▄▄▄▄ ▀▄▄▄ `, - "The Karpathy": ` ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  - ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  - ▄▄▄▄▄▄▀ ▀▀ ▀ ▀ ▄▄▄▄ `, - - "Slough Boy": ` ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  - ▄▄▄▄▄▄▄▄▀  - ▄▄ ▀▀▄▄▀  - ▀▀ ▀▀ `, + "The Karpathy": ` ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄  + ▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀ ▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄  + ▄▄▄▄▄▄▀ ▀▀ ▀ ▀ ▄▄▄▄ `, + "Slough Boy": ` ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄  + ▄▄▄▄▄▄▄▄▀  + ▄▄ ▀▀▄▄▀  + ▀▀ ▀▀ `, }; diff --git a/src/app/page.tsx b/src/app/page.tsx index 41b3928..e8b3a81 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,7 +2,7 @@ import { useState, useMemo } from "react"; import { AnimatePresence, motion } from "framer-motion"; -import UploadScreenV3 from "@/components/upload/UploadScreenV3"; +import UploadScreen from "@/components/upload/UploadScreen"; import SlideContainer from "@/components/slides/SlideContainer"; import GraveyardShift from "@/components/slides/GraveyardShift"; import Delegator from "@/components/slides/Delegator"; @@ -60,7 +60,7 @@ export default function Home() { {phase === "upload" && ( - + )} diff --git a/src/components/upload/UploadScreenV3.tsx b/src/components/upload/UploadScreen.tsx similarity index 99% rename from src/components/upload/UploadScreenV3.tsx rename to src/components/upload/UploadScreen.tsx index 70fcafd..59a5ffe 100644 --- a/src/components/upload/UploadScreenV3.tsx +++ b/src/components/upload/UploadScreen.tsx @@ -286,7 +286,7 @@ function TabContentClaude() { ); } -export default function UploadScreenV3({ onDataParsed }: UploadScreenProps) { +export default function UploadScreen({ onDataParsed }: UploadScreenProps) { const [isDragging, setIsDragging] = useState(false); const [isLoading, setIsLoading] = useState(false); const [loadingText, setLoadingText] = useState(LOADING_MESSAGES[0]); From f6c15e5f62d79b11813bb3116f6bf99ad776ecd8 Mon Sep 17 00:00:00 2001 From: AbdulAaqib Date: Tue, 7 Apr 2026 02:24:15 +0100 Subject: [PATCH 15/15] fix(build): exclude scripts/ from Next.js TS compilation, fix Jimp type annotations Co-Authored-By: Claude Sonnet 4.6 --- scripts/generate-mascot-art.ts | 6 +++--- tsconfig.json | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/generate-mascot-art.ts b/scripts/generate-mascot-art.ts index 789b67b..5da8cd4 100644 --- a/scripts/generate-mascot-art.ts +++ b/scripts/generate-mascot-art.ts @@ -44,12 +44,12 @@ function colorDist(a: RGB, b: RGB): number { return Math.sqrt((a.r - b.r) ** 2 + (a.g - b.g) ** 2 + (a.b - b.b) ** 2); } -function getPixel(img: Jimp, x: number, y: number): RGB { +function getPixel(img: InstanceType, x: number, y: number): RGB { const rgba = intToRGBA(img.getPixelColor(x, y)); return { r: rgba.r, g: rgba.g, b: rgba.b }; } -function detectBg(img: Jimp): RGB { +function detectBg(img: InstanceType): RGB { const w = img.width; const h = img.height; const samples = [ @@ -67,7 +67,7 @@ function detectBg(img: Jimp): RGB { }; } -function makeBgMask(img: Jimp, bg: RGB, thresh: number): boolean[][] { +function makeBgMask(img: InstanceType, bg: RGB, thresh: number): boolean[][] { const w = img.width; const h = img.height; const visited: boolean[][] = Array.from({ length: w }, () => diff --git a/tsconfig.json b/tsconfig.json index b575f7d..5d48bae 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,6 +36,7 @@ ".next/dev/types/**/*.ts" ], "exclude": [ - "node_modules" + "node_modules", + "scripts" ] }