summaryrefslogtreecommitdiff
path: root/src/js
diff options
context:
space:
mode:
authorElizabeth Hunt <me@liz.coffee>2025-12-14 16:39:17 -0800
committerElizabeth Hunt <me@liz.coffee>2025-12-14 16:39:17 -0800
commit613632f3a8fccb998147e46d0e751ca4afc66544 (patch)
tree609c21fb12b95d54c3cdce4c20d02c152dbd81e2 /src/js
parente033a8fd8ce3fbe92ef87ca662ac6785d4ec9b7b (diff)
downloadadelie-613632f3a8fccb998147e46d0e751ca4afc66544.tar.gz
adelie-613632f3a8fccb998147e46d0e751ca4afc66544.zip
Fixes
Diffstat (limited to 'src/js')
-rw-r--r--src/js/oneko.ts277
-rw-r--r--src/js/script.ts78
2 files changed, 0 insertions, 355 deletions
diff --git a/src/js/oneko.ts b/src/js/oneko.ts
deleted file mode 100644
index 236c7cb..0000000
--- a/src/js/oneko.ts
+++ /dev/null
@@ -1,277 +0,0 @@
-// oneko.js: https://github.com/adryd325/oneko.js
-
-export function initOneko(): void {
- const isReducedMotion =
- window.matchMedia('(prefers-reduced-motion: reduce)') === true ||
- window.matchMedia('(prefers-reduced-motion: reduce)').matches === true;
-
- if (isReducedMotion) return;
-
- const nekoEl = document.createElement('div');
- let persistPosition = true;
-
- let nekoPosX = 32;
- let nekoPosY = 32;
-
- let mousePosX = 0;
- let mousePosY = 0;
-
- let frameCount = 0;
- let idleTime = 0;
- let idleAnimation: string | null = null;
- let idleAnimationFrame = 0;
-
- const nekoSpeed = 10;
- const spriteSets: Record<string, [number, number][]> = {
- idle: [[-3, -3]],
- alert: [[-7, -3]],
- scratchSelf: [
- [-5, 0],
- [-6, 0],
- [-7, 0],
- ],
- scratchWallN: [
- [0, 0],
- [0, -1],
- ],
- scratchWallS: [
- [-7, -1],
- [-6, -2],
- ],
- scratchWallE: [
- [-2, -2],
- [-2, -3],
- ],
- scratchWallW: [
- [-4, 0],
- [-4, -1],
- ],
- tired: [[-3, -2]],
- sleeping: [
- [-2, 0],
- [-2, -1],
- ],
- N: [
- [-1, -2],
- [-1, -3],
- ],
- NE: [
- [0, -2],
- [0, -3],
- ],
- E: [
- [-3, 0],
- [-3, -1],
- ],
- SE: [
- [-5, -1],
- [-5, -2],
- ],
- S: [
- [-6, -3],
- [-7, -2],
- ],
- SW: [
- [-5, -3],
- [-6, -1],
- ],
- W: [
- [-4, -2],
- [-4, -3],
- ],
- NW: [
- [-1, 0],
- [-1, -1],
- ],
- };
-
- function init(): void {
- const assetBase = window.ASSET_BASE || '';
- let nekoFile = `${assetBase}/oneko/oneko.gif`;
- const curScript = document.currentScript as HTMLScriptElement;
- if (curScript?.dataset.cat) {
- nekoFile = curScript.dataset.cat;
- }
- if (curScript?.dataset.persistPosition) {
- if (curScript.dataset.persistPosition === '') {
- persistPosition = true;
- } else {
- persistPosition = JSON.parse(curScript.dataset.persistPosition.toLowerCase());
- }
- }
-
- if (persistPosition) {
- const storedNekoStr = window.localStorage.getItem('oneko');
- const storedNeko = storedNekoStr ? JSON.parse(storedNekoStr) : null;
- if (storedNeko !== null) {
- nekoPosX = storedNeko.nekoPosX;
- nekoPosY = storedNeko.nekoPosY;
- mousePosX = storedNeko.mousePosX;
- mousePosY = storedNeko.mousePosY;
- frameCount = storedNeko.frameCount;
- idleTime = storedNeko.idleTime;
- idleAnimation = storedNeko.idleAnimation;
- idleAnimationFrame = storedNeko.idleAnimationFrame;
- nekoEl.style.backgroundPosition = storedNeko.bgPos;
- }
- }
-
- nekoEl.id = 'oneko';
- nekoEl.ariaHidden = 'true';
- nekoEl.style.width = '32px';
- nekoEl.style.height = '32px';
- nekoEl.style.position = 'fixed';
- nekoEl.style.pointerEvents = 'none';
- nekoEl.style.imageRendering = 'pixelated';
- nekoEl.style.left = `${nekoPosX - 16}px`;
- nekoEl.style.top = `${nekoPosY - 16}px`;
- nekoEl.style.zIndex = '2147483647';
-
- nekoEl.style.backgroundImage = `url(${nekoFile})`;
-
- document.body.appendChild(nekoEl);
-
- document.addEventListener('mousemove', (event: MouseEvent) => {
- mousePosX = event.clientX;
- mousePosY = event.clientY;
- });
-
- if (persistPosition) {
- window.addEventListener('beforeunload', () => {
- window.localStorage.setItem(
- 'oneko',
- JSON.stringify({
- nekoPosX: nekoPosX,
- nekoPosY: nekoPosY,
- mousePosX: mousePosX,
- mousePosY: mousePosY,
- frameCount: frameCount,
- idleTime: idleTime,
- idleAnimation: idleAnimation,
- idleAnimationFrame: idleAnimationFrame,
- bgPos: nekoEl.style.backgroundPosition,
- })
- );
- });
- }
-
- window.requestAnimationFrame(onAnimationFrame);
- }
-
- let lastFrameTimestamp: number | undefined;
-
- function onAnimationFrame(timestamp: number): void {
- if (!nekoEl.isConnected) {
- return;
- }
- if (!lastFrameTimestamp) {
- lastFrameTimestamp = timestamp;
- }
- if (lastFrameTimestamp && timestamp - lastFrameTimestamp > 100) {
- lastFrameTimestamp = timestamp;
- frame();
- }
- window.requestAnimationFrame(onAnimationFrame);
- }
-
- function setSprite(name: string, frame: number): void {
- const sprite = spriteSets[name][frame % spriteSets[name].length];
- nekoEl.style.backgroundPosition = `${sprite[0] * 32}px ${sprite[1] * 32}px`;
- }
-
- function resetIdleAnimation(): void {
- idleAnimation = null;
- idleAnimationFrame = 0;
- }
-
- function idle(): void {
- idleTime += 1;
-
- // every ~ 20 seconds
- if (idleTime > 10 && Math.floor(Math.random() * 200) === 0 && idleAnimation == null) {
- const avalibleIdleAnimations: string[] = ['sleeping', 'scratchSelf'];
- if (nekoPosX < 32) {
- avalibleIdleAnimations.push('scratchWallW');
- }
- if (nekoPosY < 32) {
- avalibleIdleAnimations.push('scratchWallN');
- }
- if (nekoPosX > window.innerWidth - 32) {
- avalibleIdleAnimations.push('scratchWallE');
- }
- if (nekoPosY > window.innerHeight - 32) {
- avalibleIdleAnimations.push('scratchWallS');
- }
- idleAnimation =
- avalibleIdleAnimations[Math.floor(Math.random() * avalibleIdleAnimations.length)];
- }
-
- switch (idleAnimation) {
- case 'sleeping':
- if (idleAnimationFrame < 8) {
- setSprite('tired', 0);
- break;
- }
- setSprite('sleeping', Math.floor(idleAnimationFrame / 4));
- if (idleAnimationFrame > 192) {
- resetIdleAnimation();
- }
- break;
- case 'scratchWallN':
- case 'scratchWallS':
- case 'scratchWallE':
- case 'scratchWallW':
- case 'scratchSelf':
- setSprite(idleAnimation, idleAnimationFrame);
- if (idleAnimationFrame > 9) {
- resetIdleAnimation();
- }
- break;
- default:
- setSprite('idle', 0);
- return;
- }
- idleAnimationFrame += 1;
- }
-
- function frame(): void {
- frameCount += 1;
- const diffX = nekoPosX - mousePosX;
- const diffY = nekoPosY - mousePosY;
- const distance = Math.sqrt(diffX ** 2 + diffY ** 2);
-
- if (distance < nekoSpeed || distance < 48) {
- idle();
- return;
- }
-
- idleAnimation = null;
- idleAnimationFrame = 0;
-
- if (idleTime > 1) {
- setSprite('alert', 0);
- // count down after being alerted before moving
- idleTime = Math.min(idleTime, 7);
- idleTime -= 1;
- return;
- }
-
- let direction = '';
- direction += diffY / distance > 0.5 ? 'N' : '';
- direction += diffY / distance < -0.5 ? 'S' : '';
- direction += diffX / distance > 0.5 ? 'W' : '';
- direction += diffX / distance < -0.5 ? 'E' : '';
- setSprite(direction, frameCount);
-
- nekoPosX -= (diffX / distance) * nekoSpeed;
- nekoPosY -= (diffY / distance) * nekoSpeed;
-
- nekoPosX = Math.min(Math.max(16, nekoPosX), window.innerWidth - 16);
- nekoPosY = Math.min(Math.max(16, nekoPosY), window.innerHeight - 16);
-
- nekoEl.style.left = `${nekoPosX - 16}px`;
- nekoEl.style.top = `${nekoPosY - 16}px`;
- }
-
- init();
-}
diff --git a/src/js/script.ts b/src/js/script.ts
deleted file mode 100644
index e0b0b85..0000000
--- a/src/js/script.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import Prism from 'prismjs';
-import 'prismjs/components/prism-javascript';
-import 'prismjs/components/prism-css';
-import 'prismjs/components/prism-markup';
-import { initOneko } from './oneko';
-
-(() => {
- const toggleButton = document.getElementById('theme-toggle') as HTMLInputElement;
- const html = document.documentElement;
-
- const sessionTheme = sessionStorage.getItem('theme');
- const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
-
- const initialTheme = sessionTheme || (systemPrefersDark ? 'dark' : 'light');
-
- if (initialTheme === 'dark') {
- html.setAttribute('data-theme', 'dark');
- toggleButton.checked = true;
- }
-
- toggleButton.addEventListener('change', () => {
- const theme = html.getAttribute('data-theme');
-
- if (theme === 'dark') {
- html.removeAttribute('data-theme');
- sessionStorage.setItem('theme', 'light');
- toggleButton.checked = false;
- } else {
- html.setAttribute('data-theme', 'dark');
- sessionStorage.setItem('theme', 'dark');
- toggleButton.checked = true;
- }
- });
-})();
-
-(() => {
- const colors = ['#ff69b4', '#b19cd9', '#8b6f47', '#ff85c0', '#c4b5fd', '#d4a574'];
- const shapes = ['❀', '✿', '✽', '✾', '✻', '❊', '❋', '✼'];
-
- document.addEventListener('mousemove', (e: MouseEvent) => {
- createParticle(e.clientX, e.clientY);
- });
-
- const createParticle = (x: number, y: number) => {
- const particle = document.createElement('div');
- particle.className = 'fairy-dust';
-
- const shape = shapes[Math.floor(Math.random() * shapes.length)];
- const size = Math.random() * 8 + 6;
- const color = colors[Math.floor(Math.random() * colors.length)];
- const offsetX = (Math.random() - 0.5) * 20;
- const offsetY = (Math.random() - 0.5) * 20;
- const rotation = Math.random() * 360;
-
- particle.textContent = shape;
- particle.style.cssText = `
- position: fixed;
- left: ${x + offsetX}px;
- top: ${y + offsetY}px;
- font-size: ${size}px;
- color: ${color};
- opacity: 0.4;
- pointer-events: none;
- z-index: 9001; /* it's over 9000 */
- line-height: 1;
- transform: rotate(${rotation}deg);
- animation: fairy-float 0.8s ease-out forwards;
- `;
-
- document.body.appendChild(particle);
- setTimeout(() => particle.remove(), 800);
- };
-})();
-
-document.addEventListener('DOMContentLoaded', () => {
- Prism.highlightAll();
- initOneko();
-});