import { useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react' import Tip from './Tip.jsx' import { compute, effectiveRules, kitItems, normalize } from './calc.js' import { explainAttr, explainCapacity, explainCarry, explainDerived, explainSkill } from './explain.js' const PAPER_PX = 794 // 210 mm at 96 dpi // Light/dark choice for the sheet, shared by all sheets and switches on the page and remembered in the browser const THEME_KEY = 'aw_sheettheme' const listeners = new Set() const readTheme = () => { try { return localStorage.getItem(THEME_KEY) === 'dark' } catch { return false } } let darkNow = readTheme() export function useSheetTheme() { const dark = useSyncExternalStore((cb) => { listeners.add(cb); return () => listeners.delete(cb) }, () => darkNow) const set = (d) => { darkNow = d; try { localStorage.setItem(THEME_KEY, d ? 'dark' : 'light') } catch { /* storage may be blocked */ } listeners.forEach((f) => f()) } return [dark, set] } export function ThemeSwitch() { const [dark, setDark] = useSheetTheme() return ( ) } // Opens the sheet pages in a clean window (only the paper, A4, no margins) and starts the print dialog: // "Save as PDF" there gives an A4 PDF that looks exactly like the screen. export function printSheet(root, title) { const win = window.open('', '_blank') if (!win) { window.alert('Please allow pop-ups for this site to print the sheet.'); return } const clone = root.cloneNode(true) const src = root.querySelectorAll('textarea'), dst = clone.querySelectorAll('textarea') dst.forEach((t, i) => { t.textContent = src[i].value }) // typed text does not survive cloning on its own clone.style.zoom = '' clone.classList.remove('dark') // printed pages are always light const head = [...document.querySelectorAll('link[rel=stylesheet], style')].map((n) => n.outerHTML).join('') win.document.open() win.document.write(`${(title || 'Character').replace(/[<>&]/g, '')}${head} ${clone.outerHTML}`) win.document.close() const go = () => { win.focus(); win.print() } const links = [...win.document.querySelectorAll('link[rel=stylesheet]')] let pending = links.length if (!pending) return go() links.forEach((l) => { l.addEventListener('load', () => { if (--pending === 0) setTimeout(go, 150) }); l.addEventListener('error', () => { if (--pending === 0) go() }) }) } // Ruled note area. With onChange (own sheet in the summary or the lobby) it can be typed into, otherwise it just shows the text. function Notes({ title, value, onChange }) { return (

{title}

{onChange ?