init.ts (1790B)
1 /** 2 * Wires the shared daemon-sec effects into this site: the dot field 3 * behind the top of each page and the cursor with its action pill. 4 * 5 * Astro's ClientRouter swaps the body on navigation, which removes the 6 * cursor element and the canvas, so both are re-bound on every 7 * `astro:page-load` and torn down on `astro:before-swap`. 8 */ 9 import { mountParticleField } from './particle-field'; 10 import { mountCursor } from './cursor'; 11 12 const DECKS = 'gtfobins|lolbas|wadcoms|loobins|daemon'; 13 const TOOL = new RegExp(`^/(?:${DECKS})/[^/?#]+`); 14 const DECK = new RegExp(`^/(?:${DECKS}|catalog)/?(?:[?#].*)?$`); 15 16 let destroyCursor: (() => void) | null = null; 17 const fields = new Set<() => void>(); 18 19 function bindFields(): void { 20 document.querySelectorAll<HTMLCanvasElement>('canvas[data-dots]:not([data-bound])').forEach((cv) => { 21 cv.dataset.bound = ''; 22 const destroy = mountParticleField(cv, { 23 /* This site's mode-flipping tokens; a canvas inside a .plate band 24 reads that band's values. */ 25 vars: { ink: '--text', base: '--base', iris: '--iris', love: '--love' }, 26 onFail: () => cv.closest<HTMLElement>('.dmn-page-field')?.remove(), 27 }); 28 fields.add(destroy); 29 }); 30 } 31 32 function bindCursor(): void { 33 if (document.querySelector('.dmn-cursor')) return; 34 destroyCursor?.(); 35 destroyCursor = mountCursor({ 36 linkLabel: (href) => (TOOL.test(href) ? 'Read' : DECK.test(href) ? 'Browse' : null), 37 /* Dark bands are `.plate`; `.plate-dawn` panels inside them are light. */ 38 isDark: (t) => t.closest('.plate, .plate-dawn')?.classList.contains('plate') ?? false, 39 }); 40 } 41 42 export function initDmn(): void { 43 bindFields(); 44 bindCursor(); 45 } 46 47 document.addEventListener('astro:before-swap', () => { 48 fields.forEach((d) => d()); 49 fields.clear(); 50 });