Compare commits

...

10 commits

Author SHA1 Message Date
Jet Pham
e25803a767
feat: add nix module 2026-03-09 18:27:28 -07:00
Jet Pham
9905bf8759
fix: make svg background more appropriate 2026-03-09 18:27:16 -07:00
Jet Pham
a7ecb3a390
feat: make single html and add schemas for socials 2026-03-09 17:14:02 -07:00
Jet Pham
4c09d416cf
feat: move to vite 2026-03-09 15:50:38 -07:00
Jet Pham
5cbe032c23
feat: statically render the jet ansi text 2026-03-05 13:19:49 -08:00
Jet Pham
e6a9b1a111
fix: quadruple the cell density 2026-03-05 13:19:35 -08:00
Jet Pham
88c69d1f81
feat: allow cors on preview 2026-03-05 12:46:19 -08:00
Jet Pham
b3e15efb28
fix: make background color black in meta 2026-03-05 12:42:53 -08:00
Jet Pham
3e9fc81e02
feat: add meta tags 2026-03-05 12:42:35 -08:00
Jet Pham
e942fce4b4
fix: make deployment use uptmized wasm builds 2026-03-05 12:42:15 -08:00
22 changed files with 388 additions and 921 deletions

View file

@ -9,7 +9,7 @@ use wasm_bindgen::JsCast;
use js_sys::Math; use js_sys::Math;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData, Window}; use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData, Window};
const CELL_SIZE: u32 = 20; const CELL_SIZE: u32 = 10;
const TICK_MS: f64 = 1000.0 / 60.0; const TICK_MS: f64 = 1000.0 / 60.0;
const HUE_PERIOD_MS: f64 = 3000.0; const HUE_PERIOD_MS: f64 = 3000.0;

View file

@ -5,7 +5,7 @@ export default tseslint.config(
ignores: ['dist', 'cgol/pkg/**/*'] ignores: ['dist', 'cgol/pkg/**/*']
}, },
{ {
files: ['**/*.ts', '**/*.tsx'], files: ['**/*.ts'],
extends: [ extends: [
...tseslint.configs.recommended, ...tseslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.recommendedTypeChecked,

View file

@ -23,6 +23,7 @@
buildPhase = '' buildPhase = ''
export HOME=$TMPDIR export HOME=$TMPDIR
cd cgol && wasm-pack build --release --target web && cd .. cd cgol && wasm-pack build --release --target web && cd ..
wasm-opt cgol/pkg/cgol_bg.wasm -o cgol/pkg/cgol_bg.wasm -O4 --enable-bulk-memory --enable-nontrapping-float-to-int --enable-sign-ext --low-memory-unused --converge
npm ci npm ci
npm run build npm run build
''; '';
@ -40,5 +41,7 @@
]; ];
}; };
} }
)); )) // {
nixosModules.default = import ./module.nix self;
};
} }

View file

@ -6,13 +6,136 @@
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta name="apple-mobile-web-app-title" content="Jet Pham" /> <meta name="apple-mobile-web-app-title" content="Jet Pham" />
<title>Jet Pham - Software Extremist</title> <title>Jet Pham - Software Extremist</title>
<meta name="description" content="Jet Pham's personal website" /> <meta name="description" content="Jet Pham's personal website. Software extremist." />
<link rel="canonical" href="https://jetpham.com/" />
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" href="/apple-icon.png" /> <link rel="apple-touch-icon" href="/apple-icon.png" />
<link rel="preload" href="/Web437_IBM_VGA_8x16.woff" as="font" type="font/woff" crossorigin />
<meta property="og:title" content="Jet Pham - Software Extremist" />
<meta property="og:description" content="Jet Pham's personal website. Software extremist." />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://jetpham.com/" />
<meta property="og:site_name" content="Jet Pham" />
<meta property="og:image" content="https://jetpham.com/web-app-manifest-512x512.png" />
<meta property="og:image:width" content="512" />
<meta property="og:image:height" content="512" />
<meta property="og:image:alt" content="Jet Pham" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="Jet Pham - Software Extremist" />
<meta name="twitter:description" content="Jet Pham's personal website. Software extremist." />
<meta name="twitter:image" content="https://jetpham.com/web-app-manifest-512x512.png" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Person",
"name": "Jet Pham",
"givenName": "Jet",
"familyName": "Pham",
"description": "Software extremist.",
"url": "https://jetpham.com",
"jobTitle": "Software Extremist",
"hasOccupation": {
"@type": "Occupation",
"name": "Hacker"
},
"email": "jet@extremist.software",
"image": "https://jetpham.com/jet.svg",
"alumniOf": {
"@type": "CollegeOrUniversity",
"name": "University of San Francisco",
"url": "https://www.usfca.edu"
},
"hasCredential": {
"@type": "EducationalOccupationalCredential",
"credentialCategory": "degree",
"name": "Bachelor of Science in Computer Science"
},
"homeLocation": {
"@type": "City",
"name": "San Francisco, CA"
},
"workLocation": {
"@type": "City",
"name": "San Francisco, CA"
},
"memberOf": {
"@type": "Organization",
"name": "Noisebridge",
"url": "https://www.noisebridge.net"
},
"affiliation": {
"@type": "Organization",
"name": "Noisebridge",
"url": "https://www.noisebridge.net"
},
"sameAs": [
"https://github.com/jetpham",
"https://x.com/jetpham5",
"https://bsky.app/profile/jetpham.com",
"https://git.extremist.software"
]
}
</script>
</head> </head>
<body style="background:#000"> <body style="background:#000">
<div id="root"></div> <canvas id="canvas" class="fixed top-0 left-0 -z-10 h-screen w-screen" aria-hidden="true"></canvas>
<script type="module" src="/src/main.tsx"></script> <main>
<div class="flex flex-col items-center justify-start px-4">
<!-- FrostedBox -->
<div class="relative px-[2ch] py-[2ch] my-[2ch] w-full max-w-[66.666667%] min-w-fit md:mt-[4ch]">
<div
class="pointer-events-none absolute inset-0 h-[200%]"
style="background-color: rgba(0, 0, 0, 0.75); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); mask-image: linear-gradient(to bottom, black 0% 50%, transparent 50% 100%); -webkit-mask-image: linear-gradient(to bottom, black 0% 50%, transparent 50% 100%);"
aria-hidden="true"
></div>
<div class="absolute inset-0 border-2 border-white" aria-hidden="true"></div>
<!-- Content -->
<div class="relative z-10">
<div class="flex flex-col items-center justify-center gap-[2ch] md:flex-row">
<div class="order-1 flex flex-col items-center md:order-2">
<h1 class="sr-only">Jet Pham</h1>
<div id="ansi-art" aria-hidden="true"></div>
<p class="mt-[2ch]">Software Extremist</p>
</div>
<div class="order-2 shrink-0 md:order-1">
<img
src="/jet.svg"
alt="A picture of Jet wearing a beanie in purple and blue lighting"
width="250"
height="250"
class="aspect-square w-full max-w-[250px] object-cover md:h-[263px] md:w-[175px] md:max-w-none"
style="background-color: #a80055; color: transparent"
/>
</div>
</div>
<!-- Contact -->
<fieldset class="mt-[2ch] border-2 border-white px-[calc(1.5ch-0.5px)] pb-[1ch] pt-0">
<legend class="-mx-[0.5ch] px-[0.5ch] text-white">Contact</legend>
<a href="mailto:jet@extremist.software">jet@extremist.software</a>
</fieldset>
<!-- Links -->
<fieldset class="mt-[2ch] border-2 border-white px-[calc(1.5ch-0.5px)] pb-[1ch] pt-0">
<legend class="-mx-[0.5ch] px-[0.5ch] text-white">Links</legend>
<ol>
<li>
<a href="https://git.extremist.software" class="inline-flex items-center">Forgejo</a>
</li>
<li>
<a href="https://github.com/jetpham" class="inline-flex items-center">GitHub</a>
</li>
<li>
<a href="https://x.com/jetpham5" class="inline-flex items-center">X</a>
</li>
<li>
<a href="https://bsky.app/profile/jetpham.com" class="inline-flex items-center">Bluesky</a>
</li>
</ol>
</fieldset>
</div>
</div>
</div>
</main>
<script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

29
module.nix Normal file
View file

@ -0,0 +1,29 @@
self:
{ config, lib, ... }:
let
cfg = config.services.jetpham-website;
package = self.packages.x86_64-linux.default;
in
{
options.services.jetpham-website = {
enable = lib.mkEnableOption "Jet Pham's personal website";
domain = lib.mkOption {
type = lib.types.str;
default = "jetpham.com";
description = "Domain to serve the website on.";
};
};
config = lib.mkIf cfg.enable {
services.caddy.virtualHosts.${cfg.domain} = {
extraConfig = ''
header Cross-Origin-Opener-Policy "same-origin"
header Cross-Origin-Embedder-Policy "require-corp"
root * ${package}
file_server
'';
};
};
}

737
package-lock.json generated
View file

@ -8,18 +8,13 @@
"name": "website", "name": "website",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"anser": "^2.3.5", "cgol": "file:./cgol/pkg"
"cgol": "file:./cgol/pkg",
"escape-carriage": "^1.3.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.1",
"@types/node": "^25.3.3", "@types/node": "^25.3.3",
"@types/react": "^19.2.14", "anser": "^2.3.5",
"@types/react-dom": "^19.2.3", "escape-carriage": "^1.3.1",
"@vitejs/plugin-react": "^5.1.4",
"eslint": "^10", "eslint": "^10",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
@ -27,6 +22,7 @@
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.56.1", "typescript-eslint": "^8.56.1",
"vite": "^7.3.1", "vite": "^7.3.1",
"vite-plugin-singlefile": "^2.3.0",
"vite-plugin-top-level-await": "^1.6.0", "vite-plugin-top-level-await": "^1.6.0",
"vite-plugin-wasm": "^3.5.0" "vite-plugin-wasm": "^3.5.0"
} }
@ -35,287 +31,15 @@
"name": "cgol", "name": "cgol",
"version": "0.1.0" "version": "0.1.0"
}, },
"node_modules/@babel/code-frame": { "node_modules/@emnapi/runtime": {
"version": "7.29.0", "version": "1.8.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
"integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true,
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.28.5", "tslib": "^2.4.0"
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
"integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"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",
"@babel/helper-compilation-targets": "^7.28.6",
"@babel/helper-module-transforms": "^7.28.6",
"@babel/helpers": "^7.28.6",
"@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
"@babel/traverse": "^7.29.0",
"@babel/types": "^7.29.0",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.3",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/generator": {
"version": "7.29.1",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.0",
"@babel/types": "^7.29.0",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
"integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.28.6",
"@babel/helper-validator-option": "^7.27.1",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
"integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.28.6",
"@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
"integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.28.6",
"@babel/helper-validator-identifier": "^7.28.5",
"@babel/traverse": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
"integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
"integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.28.6",
"@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
"integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.29.0"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
"integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz",
"integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/template": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
"integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/parser": "^7.28.6",
"@babel/types": "^7.28.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
"integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
"@babel/helper-globals": "^7.28.0",
"@babel/parser": "^7.29.0",
"@babel/template": "^7.28.6",
"@babel/types": "^7.29.0",
"debug": "^4.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5"
},
"engines": {
"node": ">=6.9.0"
} }
}, },
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
@ -969,13 +693,6 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@rolldown/pluginutils": {
"version": "1.0.0-rc.3",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz",
"integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==",
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/plugin-virtual": { "node_modules/@rollup/plugin-virtual": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz", "resolved": "https://registry.npmjs.org/@rollup/plugin-virtual/-/plugin-virtual-3.0.2.tgz",
@ -1849,51 +1566,6 @@
"vite": "^5.2.0 || ^6 || ^7" "vite": "^5.2.0 || ^6 || ^7"
} }
}, },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
"integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.20.7",
"@babel/types": "^7.20.7",
"@types/babel__generator": "*",
"@types/babel__template": "*",
"@types/babel__traverse": "*"
}
},
"node_modules/@types/babel__generator": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
"integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__template": {
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
"integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.1.0",
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__traverse": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
"integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.2"
}
},
"node_modules/@types/esrecurse": { "node_modules/@types/esrecurse": {
"version": "4.3.1", "version": "4.3.1",
"resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
@ -1926,27 +1598,6 @@
"undici-types": "~7.18.0" "undici-types": "~7.18.0"
} }
}, },
"node_modules/@types/react": {
"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",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
"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"
}
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.56.1", "version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
@ -2136,19 +1787,6 @@
"typescript": ">=4.8.4 <6.0.0" "typescript": ">=4.8.4 <6.0.0"
} }
}, },
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "8.56.1", "version": "8.56.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz",
@ -2191,27 +1829,6 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@vitejs/plugin-react": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz",
"integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.29.0",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
"@rolldown/pluginutils": "1.0.0-rc.3",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.18.0"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.16.0", "version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
@ -2257,6 +1874,7 @@
"version": "2.3.5", "version": "2.3.5",
"resolved": "https://registry.npmjs.org/anser/-/anser-2.3.5.tgz", "resolved": "https://registry.npmjs.org/anser/-/anser-2.3.5.tgz",
"integrity": "sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==", "integrity": "sha512-vcZjxvvVoxTeR5XBNJB38oTu/7eDCZlwdz32N1eNgpyPF7j/Z7Idf+CUwQOkKKpJ7RJyjxgLHCM7vdIK0iCNMQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
@ -2269,19 +1887,6 @@
"node": "18 || 20 || >=22" "node": "18 || 20 || >=22"
} }
}, },
"node_modules/baseline-browser-mapping": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
"integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "5.0.4", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
@ -2295,73 +1900,23 @@
"node": "18 || 20 || >=22" "node": "18 || 20 || >=22"
} }
}, },
"node_modules/browserslist": { "node_modules/braces": {
"version": "4.28.1", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true, "dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "fill-range": "^7.1.1"
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
}, },
"engines": { "engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": ">=8"
} }
}, },
"node_modules/caniuse-lite": {
"version": "1.0.30001776",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz",
"integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
},
"node_modules/cgol": { "node_modules/cgol": {
"resolved": "cgol/pkg", "resolved": "cgol/pkg",
"link": true "link": true
}, },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@ -2377,13 +1932,6 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/csstype": {
"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/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -2419,13 +1967,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/electron-to-chromium": {
"version": "1.5.307",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
"integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==",
"dev": true,
"license": "ISC"
},
"node_modules/enhanced-resolve": { "node_modules/enhanced-resolve": {
"version": "5.20.0", "version": "5.20.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz",
@ -2482,20 +2023,11 @@
"@esbuild/win32-x64": "0.27.3" "@esbuild/win32-x64": "0.27.3"
} }
}, },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-carriage": { "node_modules/escape-carriage": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz", "resolved": "https://registry.npmjs.org/escape-carriage/-/escape-carriage-1.3.1.tgz",
"integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==", "integrity": "sha512-GwBr6yViW3ttx1kb7/Oh+gKQ1/TrhYwxKqVmg5gS+BK+Qe2KrOa/Vh7w3HPBvgGf0LfcDGoY9I6NHKoA5Hozhw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/escape-string-regexp": { "node_modules/escape-string-regexp": {
@ -2716,6 +2248,19 @@
"node": ">=16.0.0" "node": ">=16.0.0"
} }
}, },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/find-up": { "node_modules/find-up": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
@ -2769,16 +2314,6 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0" "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
} }
}, },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/glob-parent": { "node_modules/glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@ -2842,6 +2377,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/isexe": { "node_modules/isexe": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@ -2859,26 +2404,6 @@
"jiti": "lib/jiti-cli.mjs" "jiti": "lib/jiti-cli.mjs"
} }
}, },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=6"
}
},
"node_modules/json-buffer": { "node_modules/json-buffer": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@ -2900,19 +2425,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -3214,16 +2726,6 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.21", "version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
@ -3234,6 +2736,33 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
"node_modules/micromatch/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "10.2.4", "version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
@ -3283,13 +2812,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/node-releases": {
"version": "2.0.36",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz",
"integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==",
"dev": true,
"license": "MIT"
},
"node_modules/optionator": { "node_modules/optionator": {
"version": "0.9.4", "version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@ -3526,38 +3048,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/react": {
"version": "19.2.4",
"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"
}
},
"node_modules/react-dom": {
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
},
"peerDependencies": {
"react": "^19.2.4"
}
},
"node_modules/react-refresh": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
"integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.59.0", "version": "4.59.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
@ -3604,20 +3094,17 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"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==",
"license": "MIT"
},
"node_modules/semver": { "node_modules/semver": {
"version": "6.3.1", "version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
} }
}, },
"node_modules/shebang-command": { "node_modules/shebang-command": {
@ -3691,6 +3178,19 @@
"url": "https://github.com/sponsors/SuperchupuDev" "url": "https://github.com/sponsors/SuperchupuDev"
} }
}, },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
@ -3704,6 +3204,14 @@
"typescript": ">=4.8.4" "typescript": ">=4.8.4"
} }
}, },
"node_modules/tslib": {
"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",
"optional": true
},
"node_modules/type-check": { "node_modules/type-check": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@ -3763,37 +3271,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": { "node_modules/uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@ -3894,6 +3371,23 @@
} }
} }
}, },
"node_modules/vite-plugin-singlefile": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.0.tgz",
"integrity": "sha512-DAcHzYypM0CasNLSz/WG0VdKOCxGHErfrjOoyIPiNxTPTGmO6rRD/te93n1YL/s+miXq66ipF1brMBikf99c6A==",
"dev": true,
"license": "MIT",
"dependencies": {
"micromatch": "^4.0.8"
},
"engines": {
"node": ">18.0.0"
},
"peerDependencies": {
"rollup": "^4.44.1",
"vite": "^5.4.11 || ^6.0.0 || ^7.0.0"
}
},
"node_modules/vite-plugin-top-level-await": { "node_modules/vite-plugin-top-level-await": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz", "resolved": "https://registry.npmjs.org/vite-plugin-top-level-await/-/vite-plugin-top-level-await-1.6.0.tgz",
@ -3946,13 +3440,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"license": "ISC"
},
"node_modules/yocto-queue": { "node_modules/yocto-queue": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",

View file

@ -8,26 +8,21 @@
"build:wasm": "cd cgol && wasm-pack build --release --target web && wasm-opt pkg/cgol_bg.wasm -o pkg/cgol_bg.wasm -O4 --enable-bulk-memory --enable-nontrapping-float-to-int --enable-sign-ext --low-memory-unused --converge", "build:wasm": "cd cgol && wasm-pack build --release --target web && wasm-opt pkg/cgol_bg.wasm -o pkg/cgol_bg.wasm -O4 --enable-bulk-memory --enable-nontrapping-float-to-int --enable-sign-ext --low-memory-unused --converge",
"check": "npm run lint && tsc --noEmit", "check": "npm run lint && tsc --noEmit",
"dev": "vite", "dev": "vite",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", "format:check": "prettier --check \"**/*.{ts,js,jsx,mdx}\" --cache",
"format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", "format:write": "prettier --write \"**/*.{ts,js,jsx,mdx}\" --cache",
"lint": "eslint .", "lint": "eslint .",
"lint:fix": "eslint . --fix", "lint:fix": "eslint . --fix",
"preview": "vite preview", "preview": "vite preview",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"anser": "^2.3.5", "cgol": "file:./cgol/pkg"
"cgol": "file:./cgol/pkg",
"escape-carriage": "^1.3.1",
"react": "^19.2.4",
"react-dom": "^19.2.4"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.2.1", "@tailwindcss/vite": "^4.2.1",
"@types/node": "^25.3.3", "@types/node": "^25.3.3",
"@types/react": "^19.2.14", "anser": "^2.3.5",
"@types/react-dom": "^19.2.3", "escape-carriage": "^1.3.1",
"@vitejs/plugin-react": "^5.1.4",
"eslint": "^10", "eslint": "^10",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"prettier-plugin-tailwindcss": "^0.7.2", "prettier-plugin-tailwindcss": "^0.7.2",
@ -35,6 +30,7 @@
"typescript": "^5.9.3", "typescript": "^5.9.3",
"typescript-eslint": "^8.56.1", "typescript-eslint": "^8.56.1",
"vite": "^7.3.1", "vite": "^7.3.1",
"vite-plugin-singlefile": "^2.3.0",
"vite-plugin-top-level-await": "^1.6.0", "vite-plugin-top-level-await": "^1.6.0",
"vite-plugin-wasm": "^3.5.0" "vite-plugin-wasm": "^3.5.0"
}, },

View file

@ -16,7 +16,7 @@
} }
], ],
"theme_color": "#a80055", "theme_color": "#a80055",
"background_color": "#a80055", "background_color": "#000000",
"display": "standalone" "display": "standalone"
} }

4
public/robots.txt Normal file
View file

@ -0,0 +1,4 @@
User-agent: *
Allow: /
Sitemap: https://jetpham.com/sitemap.xml

8
public/sitemap.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://jetpham.com/</loc>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>

View file

@ -1,75 +0,0 @@
import { BorderedBox } from "~/components/bordered-box";
import { FrostedBox } from "~/components/frosted-box";
import Header from "~/components/header";
import { CgolCanvas } from "~/components/cgol-canvas";
import Jet from "~/assets/Jet.txt?raw";
export default function App() {
return (
<>
<CgolCanvas />
<main>
<div className="flex flex-col items-center justify-start px-4">
<FrostedBox className="my-[2ch] w-full max-w-[66.666667%] min-w-fit md:mt-[4ch]">
<div className="flex flex-col items-center justify-center gap-[2ch] md:flex-row">
<div className="order-1 flex flex-col items-center md:order-2">
<Header content={Jet} />
<div className="mt-[2ch]">Software Extremist</div>
</div>
<div className="order-2 shrink-0 md:order-1">
<img
src="/jet.svg"
alt="Jet"
width={250}
height={250}
className="aspect-square w-full max-w-[250px] object-cover md:h-[263px] md:w-[175px] md:max-w-none"
/>
</div>
</div>
<BorderedBox label="Contact" className="mt-[2ch]">
<a href="mailto:jet@extremist.software">
jet@extremist.software
</a>
</BorderedBox>
<BorderedBox label="Links">
<ol>
<li>
<a
href="https://git.extremist.software"
className="inline-flex items-center"
>
Forgejo
</a>
</li>
<li>
<a
href="https://github.com/jetpham"
className="inline-flex items-center"
>
GitHub
</a>
</li>
<li>
<a
href="https://x.com/jetpham5"
className="inline-flex items-center"
>
X
</a>
</li>
<li>
<a
href="https://bsky.app/profile/jetpham.com"
className="inline-flex items-center"
>
Bluesky
</a>
</li>
</ol>
</BorderedBox>
</FrostedBox>
</div>
</main>
</>
);
}

View file

@ -1,28 +0,0 @@
import { type ReactNode } from "react";
interface BorderedBoxProps {
label?: string;
children: ReactNode;
className?: string;
}
export function BorderedBox({
label,
children,
className = "",
}: BorderedBoxProps) {
return (
<fieldset
className={`mt-[2ch] border-2 border-white px-[calc(1.5ch-0.5px)] pb-[1ch] pt-0 ${className}`}
>
{label && (
<legend className="-mx-[0.5ch] px-[0.5ch] text-white">
{label}
</legend>
)}
{children}
</fieldset>
);
}

View file

@ -1,33 +0,0 @@
import { useEffect, useRef } from "react";
let wasmStarted = false;
export function CgolCanvas() {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
if (wasmStarted) return;
wasmStarted = true;
import("cgol").then(async (cgolModule) => {
if (typeof cgolModule.default === "function") {
await cgolModule.default();
}
if (typeof cgolModule.start === "function") {
cgolModule.start();
}
}).catch((error: unknown) => {
console.error("Failed to initialize CGOL WebAssembly module:", error);
wasmStarted = false;
});
}, []);
return (
<canvas
ref={canvasRef}
id="canvas"
className="fixed top-0 left-0 -z-10 h-screen w-screen"
aria-hidden="true"
/>
);
}

View file

@ -1,47 +0,0 @@
import { type ReactNode } from "react";
interface FrostedBoxProps {
label?: string;
children: ReactNode;
className?: string;
}
export function FrostedBox({
label,
children,
className = "",
}: FrostedBoxProps) {
return (
<div
className={`relative my-[calc(2ch-2px)] px-[2ch] py-[2ch] ${className}`}
>
{/* Extended frosted glass backdrop with mask */}
<div
className="pointer-events-none absolute inset-0 h-[200%]"
style={{
backgroundColor: "rgba(0, 0, 0, 0.75)",
backdropFilter: "blur(10px)",
WebkitBackdropFilter: "blur(10px)",
maskImage:
"linear-gradient(to bottom, black 0% 50%, transparent 50% 100%)",
WebkitMaskImage:
"linear-gradient(to bottom, black 0% 50%, transparent 50% 100%)",
}}
/>
{/* Border */}
<div className="absolute inset-0 border-2 border-white" />
{/* Content */}
<div className="relative z-10">
{label && (
<span className="absolute -top-[1ch] left-2 bg-transparent text-white">
{label}
</span>
)}
{children}
</div>
</div>
);
}

View file

@ -1,16 +0,0 @@
import React from "react";
import Ansi from "./ansi";
interface HeaderProps {
content: string;
className?: string;
}
export default function Header({ content, className }: HeaderProps) {
return (
<div className={className}>
<Ansi>{content}</Ansi>
</div>
);
}

5
src/global.d.ts vendored
View file

@ -5,6 +5,11 @@ declare module "*.txt?raw" {
export default content; export default content;
} }
declare module "*.txt?ansi" {
const content: string;
export default content;
}
declare module "*.utf8ans?raw" { declare module "*.utf8ans?raw" {
const content: string; const content: string;
export default content; export default content;

8
src/main.ts Normal file
View file

@ -0,0 +1,8 @@
import "~/styles/globals.css";
import Jet from "~/assets/Jet.txt?ansi";
import init, { start } from "cgol";
document.getElementById("ansi-art")!.innerHTML = Jet;
await init();
start();

View file

@ -1,10 +0,0 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "~/styles/globals.css";
import App from "./App";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);

View file

@ -39,7 +39,7 @@
margin: 0 auto; margin: 0 auto;
font-family: "IBM VGA", monospace; font-family: "IBM VGA", monospace;
font-size: 1.25rem; /* Smaller font size for mobile */ font-size: 1.25rem; /* Smaller font size for mobile */
white-space: pre; white-space: normal;
line-height: 1; line-height: 1;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; /* Disable scrolling */ overflow: hidden; /* Disable scrolling */

View file

@ -1,30 +1,12 @@
{ {
"compilerOptions": { "compilerOptions": {
/* Base Options: */ "target": "ESNext",
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": true,
/* Strictness */
"strict": true,
"noUncheckedIndexedAccess": true,
"checkJs": true,
/* Bundled projects */
"lib": [
"dom",
"dom.iterable",
"ES2022"
],
"noEmit": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Bundler", "moduleResolution": "bundler",
"jsx": "react-jsx", "strict": true,
"incremental": true, "verbatimModuleSyntax": true,
/* Path Aliases */ "noUncheckedIndexedAccess": true,
"skipLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"~/*": [ "~/*": [
@ -33,11 +15,9 @@
} }
}, },
"include": [ "include": [
"**/*.ts", "src",
"**/*.tsx", "vite.config.ts",
"**/*.cjs", "vite-plugin-ansi.ts"
"**/*.js",
"vite.config.ts"
], ],
"exclude": [ "exclude": [
"node_modules", "node_modules",

View file

@ -1,6 +1,7 @@
import Anser, { type AnserJsonEntry } from "anser"; import Anser, { type AnserJsonEntry } from "anser";
import { escapeCarriageReturn } from "escape-carriage"; import { escapeCarriageReturn } from "escape-carriage";
import React, { memo, useMemo } from "react"; import fs from "node:fs";
import type { Plugin } from "vite";
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
"ansi-black": "text-[var(--black)]", "ansi-black": "text-[var(--black)]",
@ -19,7 +20,7 @@ const colorMap: Record<string, string> = {
"ansi-bright-magenta": "text-[var(--light-magenta)]", "ansi-bright-magenta": "text-[var(--light-magenta)]",
"ansi-bright-cyan": "text-[var(--light-cyan)]", "ansi-bright-cyan": "text-[var(--light-cyan)]",
"ansi-bright-white": "text-[var(--white)]", "ansi-bright-white": "text-[var(--white)]",
} as const; };
const bgColorMap: Record<string, string> = { const bgColorMap: Record<string, string> = {
"ansi-black": "bg-transparent", "ansi-black": "bg-transparent",
@ -38,7 +39,7 @@ const bgColorMap: Record<string, string> = {
"ansi-bright-magenta": "bg-[var(--light-magenta)]", "ansi-bright-magenta": "bg-[var(--light-magenta)]",
"ansi-bright-cyan": "bg-[var(--light-cyan)]", "ansi-bright-cyan": "bg-[var(--light-cyan)]",
"ansi-bright-white": "bg-[var(--white)]", "ansi-bright-white": "bg-[var(--white)]",
} as const; };
const decorationMap: Record<string, string> = { const decorationMap: Record<string, string> = {
bold: "font-bold", bold: "font-bold",
@ -48,7 +49,7 @@ const decorationMap: Record<string, string> = {
strikethrough: "line-through", strikethrough: "line-through",
underline: "underline", underline: "underline",
blink: "animate-pulse", blink: "animate-pulse",
} as const; };
function fixBackspace(txt: string): string { function fixBackspace(txt: string): string {
let tmp = txt; let tmp = txt;
@ -61,7 +62,6 @@ function fixBackspace(txt: string): string {
function createClass(bundle: AnserJsonEntry): string | null { function createClass(bundle: AnserJsonEntry): string | null {
const classes: string[] = []; const classes: string[] = [];
if (bundle.bg && bgColorMap[bundle.bg]) { if (bundle.bg && bgColorMap[bundle.bg]) {
classes.push(bgColorMap[bundle.bg]!); classes.push(bgColorMap[bundle.bg]!);
} }
@ -74,41 +74,59 @@ function createClass(bundle: AnserJsonEntry): string | null {
return classes.length ? classes.join(" ") : null; return classes.length ? classes.join(" ") : null;
} }
interface Props { function escapeHtml(str: string): string {
children?: string; return str
className?: string; .replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
} }
const Ansi = memo(function Ansi({ className, children = "" }: Props) { function renderAnsiToHtml(raw: string): string {
const bundles = useMemo(() => { const input = escapeCarriageReturn(fixBackspace(raw));
const input = escapeCarriageReturn(fixBackspace(children)); const bundles = Anser.ansiToJson(input, {
return Anser.ansiToJson(input, {
json: true, json: true,
remove_empty: true, remove_empty: true,
use_classes: true, use_classes: true,
}); });
}, [children]);
const renderedContent = useMemo( const spans = bundles
() => .map((bundle) => {
bundles.map((bundle, key) => { const cls = createClass(bundle);
const bundleClassName = createClass(bundle); const content = escapeHtml(bundle.content);
return ( if (cls) {
<span key={key} className={bundleClassName ?? undefined}> return `<span class="${cls}">${content}</span>`;
{bundle.content} }
</span> return `<span>${content}</span>`;
); })
}), .join("");
[bundles],
);
return ( return `<div class="flex justify-center"><pre style="text-align:left"><code>${spans}</code></pre></div>`;
<div className="flex justify-center"> }
<pre className={className ?? ""} style={{ textAlign: "left" }}>
<code>{renderedContent}</code>
</pre>
</div>
);
});
export default Ansi; const ANSI_SUFFIX = "?ansi";
export default function ansiPlugin(): Plugin {
return {
name: "vite-plugin-ansi",
enforce: "pre",
async resolveId(source, importer, options) {
if (!source.endsWith(ANSI_SUFFIX)) return;
const bare = source.slice(0, -ANSI_SUFFIX.length);
const resolved = await this.resolve(bare, importer, {
...options,
skipSelf: true,
});
if (resolved) {
return resolved.id + ANSI_SUFFIX;
}
},
load(id) {
if (!id.endsWith(ANSI_SUFFIX)) return;
const filePath = id.slice(0, -ANSI_SUFFIX.length);
const raw = fs.readFileSync(filePath, "utf-8");
const html = renderAnsiToHtml(raw);
return `export default ${JSON.stringify(html)};`;
},
};
}

View file

@ -1,11 +1,18 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite"; import tailwindcss from "@tailwindcss/vite";
import wasm from "vite-plugin-wasm"; import wasm from "vite-plugin-wasm";
import topLevelAwait from "vite-plugin-top-level-await"; import topLevelAwait from "vite-plugin-top-level-await";
import { viteSingleFile } from "vite-plugin-singlefile";
import ansi from "./vite-plugin-ansi";
export default defineConfig({ export default defineConfig({
plugins: [react(), tailwindcss(), wasm(), topLevelAwait()], plugins: [
ansi(),
tailwindcss(),
wasm(),
topLevelAwait(),
viteSingleFile({ useRecommendedBuildConfig: false }),
],
resolve: { resolve: {
alias: { alias: {
"~": "/src", "~": "/src",
@ -17,7 +24,15 @@ export default defineConfig({
"Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Embedder-Policy": "require-corp",
}, },
}, },
preview: {
headers: {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
},
},
build: { build: {
target: "esnext", target: "esnext",
assetsInlineLimit: 0,
cssCodeSplit: false,
}, },
}); });