Lobby: sheet theme switch in the tab bar, remove extra toolbar

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
Claude
2026-10-22 11:12:32 +00:00
parent 7e69b68b37
commit 59df681cfa
4 changed files with 42 additions and 23 deletions
+31 -13
View File
@@ -1,10 +1,30 @@
import { useLayoutEffect, useMemo, useRef, useState } from 'react'
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 (
<span className="seg" role="group" aria-label="Sheet colours">
<button className={dark ? 'ghost' : 'on'} onClick={() => setDark(false)} title="Light sheet">Light</button>
<button className={dark ? 'on' : 'ghost'} onClick={() => setDark(true)} title="Dark sheet (prints light)">Dark</button>
</span>
)
}
// 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) {
@@ -48,14 +68,13 @@ const Pips = ({ n, locked }) => (
)
// The character sheet as A4 pages (page 1: attributes and skills, page 2: talents, mutations, equipment, notes).
export default function PaperSheet({ data, baseRules, tools }) {
export default function PaperSheet({ data, baseRules, tools, bare }) {
const ch = useMemo(() => normalize(data, baseRules), [data, baseRules])
const rules = useMemo(() => effectiveRules(baseRules, ch), [baseRules, ch])
const c = useMemo(() => compute(ch, rules), [ch, rules])
const x = { ch, rules, c }
const root = useRef(null)
const [dark, setDark] = useState(() => { try { return localStorage.getItem('aw_sheettheme') === 'dark' } catch { return false } })
const pickTheme = (d) => { setDark(d); try { localStorage.setItem('aw_sheettheme', d ? 'dark' : 'light') } catch { /* storage may be blocked */ } }
const [dark] = useSheetTheme()
const prof = rules.professions.find((p) => p.id === ch.profession)
const cls = rules.classes.find((k) => k.id === ch.class_id)
@@ -73,16 +92,15 @@ export default function PaperSheet({ data, baseRules, tools }) {
return (
<div className="papersheet">
<div className="papertools">
{tools}
<span className="toolright">
<span className="seg" role="group" aria-label="Sheet colours">
<button className={dark ? 'ghost' : 'on'} onClick={() => pickTheme(false)} title="Light sheet">Light</button>
<button className={dark ? 'on' : 'ghost'} onClick={() => pickTheme(true)} title="Dark sheet (prints light)">Dark</button>
{!bare && (
<div className="papertools">
{tools}
<span className="toolright">
<ThemeSwitch />
<button className="ghost" onClick={() => printSheet(root.current, name)} title="Opens the pages for printing, choose Save as PDF there">Print / PDF (A4)</button>
</span>
<button className="ghost" onClick={() => printSheet(root.current, name)} title="Opens the pages for printing, choose Save as PDF there">Print / PDF (A4)</button>
</span>
</div>
</div>
)}
<Fit rootRef={root} dark={dark}>
<section className="paper">
<header className="phead">