Add PF2e spell description popovers in stat blocks
All checks were successful
CI / check (push) Successful in 2m31s
CI / build-image (push) Successful in 26s

Clicking a spell name in a PF2e creature's stat block now opens a
popover (desktop) or bottom sheet (mobile) showing full spell details:
description, traits, rank, range, target, area, duration, defense,
action cost icons, and heightening rules. All data is sourced from
the embedded Foundry VTT spell items already in the bestiary cache.

- Add SpellReference type replacing bare string spell arrays
- Extract full spell data in pf2e-bestiary-adapter (description,
  traits, traditions, range, target, area, duration, defense,
  action cost, heightening, overlays)
- Strip inline heightening text from descriptions to avoid duplication
- Bold save outcome labels (Critical Success/Failure) in descriptions
- Bump DB_VERSION to 6 for cache invalidation
- Add useSwipeToDismissDown hook for mobile bottom sheet
- Portal popover to document.body to escape transformed ancestors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-04-10 16:18:08 +02:00
parent 9b0cb38897
commit e161645228
17 changed files with 1302 additions and 62 deletions

View File

@@ -31,16 +31,71 @@ export interface LegendaryBlock {
readonly entries: readonly TraitBlock[];
}
/**
* A single spell entry within a creature's spellcasting block.
*
* `name` is always populated. All other fields are optional and are only
* populated for PF2e creatures (sourced from embedded Foundry VTT spell items).
* D&D 5e creatures populate only `name`.
*/
export interface SpellReference {
readonly name: string;
/** Stable slug from Foundry VTT (e.g. "magic-missile"). PF2e only. */
readonly slug?: string;
/** Plain-text description with Foundry enrichment tags stripped. */
readonly description?: string;
/** Spell rank/level (0 = cantrip). */
readonly rank?: number;
/** Trait slugs (e.g. ["concentrate", "manipulate", "force"]). */
readonly traits?: readonly string[];
/** Tradition labels (e.g. ["arcane", "occult"]). */
readonly traditions?: readonly string[];
/** Range (e.g. "30 feet", "touch"). */
readonly range?: string;
/** Target (e.g. "1 creature"). */
readonly target?: string;
/** Area (e.g. "20-foot burst"). */
readonly area?: string;
/** Duration (e.g. "1 minute", "sustained up to 1 minute"). */
readonly duration?: string;
/** Defense / save (e.g. "basic Reflex", "Will"). */
readonly defense?: string;
/** Action cost. PF2e: number = action count, "reaction", "free", or
* "1 minute" / "10 minutes" for cast time. */
readonly actionCost?: string;
/**
* Heightening rules text. May come from `system.heightening` (fixed
* intervals) or `system.overlays` (variant casts). Plain text after
* tag stripping.
*/
readonly heightening?: string;
/** Uses per day for "(×N)" rendering, when > 1. PF2e only. */
readonly usesPerDay?: number;
}
export interface DailySpells {
readonly uses: number;
readonly each: boolean;
readonly spells: readonly string[];
readonly spells: readonly SpellReference[];
}
export interface SpellcastingBlock {
readonly name: string;
readonly headerText: string;
readonly atWill?: readonly string[];
readonly atWill?: readonly SpellReference[];
readonly daily?: readonly DailySpells[];
readonly restLong?: readonly DailySpells[];
}

View File

@@ -39,6 +39,7 @@ export {
type Pf2eCreature,
proficiencyBonus,
type SpellcastingBlock,
type SpellReference,
type TraitBlock,
type TraitListItem,
type TraitSegment,