feat: Add template spell picker from spells.yml; polish spell table
All checks were successful
Deploy / deploy (push) Successful in 1m20s

- Add yaml-loader; import data/spells.yml at build time
- Add SpellTemplate/SpellsFile types to globals.d.ts
- Add 'Add Template Spell' button that opens a modal picker pre-filling
  all spell fields from the YAML data
- Move spell data files into data/ directory
- Split spell rows into inputs row + full-width notes row (colspan=6)
- Shrink delete column to fit-content; bold spell name input
- Add class column to spell table; change MP cost to free-text input
- Auto-resize spell notes textarea on load and on input
- Add 10px padding between spells for visual separation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 17:52:02 -04:00
committed by drew
parent d655ad4afc
commit 7f81f85735
11 changed files with 649 additions and 20 deletions

69
scripts/anchors.py Normal file
View File

@@ -0,0 +1,69 @@
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "bs4",
# ]
# ///
from bs4 import BeautifulSoup
import re
from pathlib import Path
# Dictionary to track how many times we've seen each header ID
header_seen = {}
def add_anchors_to_headers(html_content):
# Parse the HTML content
soup = BeautifulSoup(html_content, 'html.parser')
# Find all header tags (h1 through h6)
header_tags = soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
# Add a count-based ID to each header tag
for header in header_tags:
# Extract header text (fallback to tag name if empty)
header_text = header.get_text(strip=True) or header.name
# Normalize header
header_text = header_text.lower().replace(" ", "-")
# Create base ID: hN-<text>
base_id = f"{header.name}-{header_text}"
# Check if we've seen this base ID before
if base_id in header_seen:
# Append next count: -1, -2, -3...
count = header_seen[base_id]
header_seen[base_id] = count + 1
header_id = f"{base_id}-{count + 1}"
else:
# First time seeing this text → count starts at 1
header_seen[base_id] = 1
header_id = base_id
# Add the ID to the header tag
header['id'] = header_id
# Wrap the header in an anchor link (clickable permalink)
# The link points to itself via the id attribute
# anchor_tag = soup.new_tag("a", href=f"#{header_id}", class_="anchor-link")
# anchor_tag.string = f"🔗"
# header.wrap(anchor_tag)
# Return the modified HTML content
return str(soup)
for book in ('./books/core', './books/natural-fantasy-atlas'):
for root, dirs, files in Path(book).walk():
for fn in files:
path = root / fn
if path.suffix != ".html":
continue
with path.open('r') as fh:
raw_html = fh.read()
new_html = add_anchors_to_headers(raw_html)
with path.open('w') as fh:
fh.write(new_html)