feat: Add skill template picker to Classes page

Mirrors the spell template picker: pack skills.yml into the sheet and
let each class insert a formatted skill description from the book,
filtered to that class by default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 12:37:17 -04:00
parent bdd7d03d9c
commit 2063fffa29
3 changed files with 527 additions and 19 deletions

383
data/skills.yml Normal file
View File

@@ -0,0 +1,383 @@
skills:
- name: Arcane Circle
class: arcanist
max_level: 4
description: "After you willingly dismiss an Arcanum on your turn during a conflict (see next page), if that Arcanum had not been summoned during this same turn and you have an arcane weapon equipped, you may immediately perform the Spell action for free. The spell you cast this way must have a total Mind Point cost of【 SL × 5】 or lower (you must still pay the spell's MP cost)."
- name: "Arcane Regeneration"
class: arcanist
max_level: 2
description: "When you summon an Arcanum, you immediately recover 【SL × 5】 Hit Points."
- name: Bind and Summon
class: arcanist
max_level: 1
description: |
You may bind Arcana to your soul and summon them later. The Game Master will
tell you the details of each binding process when you first encounter the Arcanum in
question.
You may use an action and spend 40 Mind Points to summon an Arcanum you have
bound: the details of this process are explained on the next page.
If you take this Skill at character creation, you begin play with one Arcanum of your
choice already bound to you, chosen from the list on the next pages. Other than that,
you may only obtain new Arcana through exploration and story progression.
- name: Emergency Arcanum
class: arcanist
max_level: 6
description: "As long as you are in Crisis, the cost for summoning your Arcana is reduced by【 SL × 5】 Mind Points."
- name: Ritual Arcanism
class: arcanist
max_level: 1
description: "You may perform Rituals of the Arcanism discipline, as long as their effects fall within the domains of one or more Arcana you have bound (see next pages). Arcanism Rituals use【 WLP + WLP】 for the Magic Check."
- name: "Consume"
class: chimerist
max_level: 5
description: "After you deal damage to one or more creatures with a spell, if you have an arcane, dagger or flail weapon equipped, you recove【r SL × 2】 Mind Points."
- name: "Feral Speech"
class: chimerist
max_level: 1
description: "You can communicate with creatures of the beast, monster and plant Species."
- name: "Pathogenesis"
class: chimerist
max_level: 1
description: "When you deal damage to one or more creatures with one of your Chimerist spells, each of those creatures that share their Species with the creature you originally learned that spell from suffers poisoned."
- name: "Ritual Chimerism"
class: chimerist
max_level: 1
description: "You may perform Rituals whose effects fall within the Chimerism discipline. When you acquire this Skill, choose【 INS + WLP】 o【r MIG + WLP】. From now on, your Chimerism Rituals will use the chosen Attributes for the Magic Check."
- name: "Spell Mimic"
class: chimerist
max_level: 10
description: "When you see a creature belonging to the beast, monster or plant Species cast a spell, you may immediately choose to learn that spell as a Chimerist spell of your own: if you do, record the Species of the creature you learned it from. When you first acquire this Skill, choose【 INS + WLP】 or【 MIG + WLP】. From now on, your offensive (r) Chimerist spells will use the chosen Attributes for the Magic Check, regardless of the Attributes used by the creature you learned the spell from. You may have up to【 SL + 2】 different Chimerist spells memorized this way. If you want to memorize a new Chimerist spell but are already at your limit, you must forget one of your old spells and replace it with the new spell."
- name: Agony
class: darkblade
max_level: 5
description: "After you deal damage to one or more creatures, if you have a Bond towards at least one of those creatures, you may recove【r SL × 2】 Hit Points and【 SL × 2】 Mind Points."
- name: Dark Blood
class: darkblade
max_level: 1
description: "As long as you are in Crisis, you have Resistance to dark damage and poison damage."
- name: Heart of Darkness
class: darkblade
max_level: 1
description: "Once per scene upon entering Crisis, you may choose a specific creature you can see that you don't have a Bond towards. If you do, create a Bond of hatred towards that creature."
- name: Painful Lesson
class: darkblade
max_level: 3
description: "After another creature causes you to lose Hit Points (with an attack, a spell or any other method), you may immediately perform the Study action on that creature (see page 74) for free. If you do, gain a bonus equal to【 SL】 to your Check. Remember, you can study the same aspect of a creature only once."
- name: Shadow Strike
class: darkblade
max_level: 5
description: "You have learned to channel your vital force into your attacks. You may use an action to perform a Shadow Strike: roll your current Might die and lose an amount of Hit Points equal to【 the number rolled on your Might die】. If this didn't reduce your Hit Points to 0, you may perform a free attack with a weapon you have equipped: if this attack hits one or more targets, it deals extra damage equal to 【SL + the number rolled on your Might die】. However, all damage dealt by this attack becomes dark and its damage type cannot be changed."
- name: "Cataclysm"
class: elementalist
max_level: 3
description: "When you cast an instantaneous spell, if you have an arcane weapon equipped, you may increase the spell's total MP cost by up to【 SL × 10】 Mind Points. If you do so and the spell deals damage to one or more creatures, it will deal 5 extra damage to each creature for every 10 Mind Points by which you increased its total MP cost."
- name: "Elemental Magic"
class: elementalist
max_level: 10
description: "Each time you acquire this Skill, learn one Elementalist spell (see next two pages). Offensive (r) Elementalist spells use【 INS + WLP】 for the Magic Check."
- name: "Magical Artillery"
class: elementalist
max_level: 3
description: "When you cast an offensive (r) spell, if you have an arcane weapon equipped, you gain a bonus to your Magic Check equal to【 SL × 2】."
- name: "Ritual Elementalism"
class: elementalist
max_level: 1
description: "You may perform Rituals whose effects fall within the Elementalism discipline. Elementalism Rituals use【 INS + WLP】 for the Magic Check."
- name: "Spellblade"
class: elementalist
max_level: 4
description: "When you cast an offensive (r) spell targeting a single creature, if the spell has a total Mind Point cost of 【 SL × 10】 or lower and you have one or more bow, brawling, dagger, flail, spear or sword weapons equipped, you may choose one of those weapons. If you do, your Magic Check for the spell will use the chosen weapon's Accuracy Check formula; for instance, the Magic Check for an Elementalist spell cast through a bronze sword (page 131) will be d【 DEX + MIG】 +1 instead of 【 INS + WLP】."
- name: Absorb MP
class: entropist
max_level: 5
description: "After you suffer damage, you may immediately recove【r SL × 2】 Mind Points."
- name: Entropic Magic
class: entropist
max_level: 10
description: "Each time you acquire this Skill, learn one Entropist spell (see next two pages). Offensive (r) Entropist spells use【 INS + WLP】 for the Magic Check."
- name: Lucky Seven
class: entropist
max_level: 1
description: "You have a lucky number; at the beginning of each session, that number is 7. Once per scene after you perform a Check, you may replace the value shown on one of the dice you rolled with your lucky number (even if this would give an impossible Result, such as a value of 7 on a d6). If you do, the replaced value becomes your new lucky number."
- name: Ritual Entropism
class: entropist
max_level: 1
description: "You may perform Rituals whose effects fall within the Entropism discipline. Entropism Rituals use【 INS + WLP】 for the Magic Check."
- name: Stolen Time
class: entropist
max_level: 4
description: "During a conflict, you may use an action to interfere with the flow of time by spending up to【 SL × 5】 Mind Points. For every 5 Mind Points you spend this way, choose one option: one creature you can see suffers slow; or one creature you can see recovers from slow; or one creature you can see may immediately perform the Equipment action for free; or choose one ally you can see who has yet to take a turn during this round: that ally may take their turn immediately after yours during this round. Each option can only be chosen once per use of this Skill."
- name: Adrenaline
class: fury
max_level: 5
description: "As long as you are in Crisis, you dea【l SL × 2】 extra damage (be it with attacks, spells, Arcana, items or any other method)."
- name: Frenzy
class: fury
max_level: 1
description: "Your Accuracy Checks with brawling, dagger, flail and thrown weapons trigger a critical success if both dice show the same number (and the Check is not a fumble)."
- name: Indomitable Spirit
class: fury
max_level: 4
description: "When you spend one or more Fabula Points, you get an additional benefit — choose one option: you recover【 SL × 5】 Hit Points; or you recover【 SL × 5】 Mind Points; or you recover from a single status effect of your choice."
- name: Provoke
class: fury
max_level: 5
description: "You may use an action and spend 5 Mind Points to perform an Opposed【 MIG + WLP】 Check against a creature you can see — describe how you taunt them! If you succeed, the target suffers enraged and is compelled to focus their attention on you (their attacks and offensive spells must include you among the targets if possible). This compulsion ends if you fall unconscious or leave the scene, if the creature is no longer enraged, or if they are successfully provoked by someone else. You gain a bonus equal to【 SL】 to you【r MIG + WLP】 Checks for this Skill."
- name: Withstand
class: fury
max_level: 4
description: "When you perform the Guard action, if you choose not to provide cover to another creature, you recover Hit Points equal to 【SL, multiplied by the highest strength among your Bonds】 and choose Might or Willpower: you treat the chosen Attribute as being one die size higher (up to a maximum of d12) until the end of your next turn."
- name: Bodyguard
class: guardian
max_level: 1
description: "If you perform the Guard action and choose to provide cover to another creature, that creature gains Resistance to all damage types until the start of your next turn."
- name: Defensive Mastery
class: guardian
max_level: 5
description: "As long as you have a shield or a martial armor equipped, all damage you suffer is reduced by【 SL】 (applied before damage Affinities)."
- name: Dual Shieldbearer
class: guardian
max_level: 1
description: "You may now equip a shield in your main hand slot. As long as you have two shields equipped, you gain the benefits of both items and may treat them as a combined two-handed melee brawling weapon (see book)."
- name: Fortress
class: guardian
max_level: 5
description: "Permanently increase your maximum Hit Points by【 SL × 3】."
- name: Protect
class: guardian
max_level: 1
description: "When another creature is threatened by an attack, spell or other danger, you may take their place (any Checks that are part of the danger will be performed against you; you may declare the use of this Skill before or after the Checks have been made). If the danger already affected you, it affects you twice (resolve both instances separately); you also cannot protect multiple creatures from the same danger. If you use this Skill during a conflict, you cannot use it again until the start of your next turn."
- name: Flash of Insight
class: loremaster
max_level: 3
description: "When you roll a 13 or higher on a Check performed to investigate a creature, item or location — this includes using the Study action during a conflict — you may ask the Game Master up to【 SL】 questions concerning the subject of your investigation. You may ask these questions immediately or save them for later; whenever you ask one of these questions, the Game Master will answer truthfully and you will describe your character's deductive process. This Skill may only be used once on the same creature, item or location."
- name: Focused
class: loremaster
max_level: 5
description: "Permanently increase your maximum Mind Points by【 SL × 3】. When you perform an Open Check using【 INS + INS】, you gain a bonus equal to【 SL】 on that Check (this only applies to Open Checks)."
- name: Knowledge is Power
class: loremaster
max_level: 1
description: "When you perform an Accuracy Check, you may replace one of the Attribute dice with Insight (such as【 INS + INS】 for a pistol o【r INS + MIG】 for a waraxe)."
- name: Quick Assessment
class: loremaster
max_level: 6
description: "At the start of a conflict, you may spend up to【 SL × 5】 Mind Points. For every 5 Mind Points you spend this way, choose one option: choose a creature you can see and the GM reveals one of their Traits; or name a damage type and choose a creature you can see, and the GM reveals that creature's Affinity towards that damage type."
- name: Trained Memory
class: loremaster
max_level: 1
description: "You may perfectly recall the details of any scene you have visited within the past week. You can 'go back in time' within your mind in order to examine and investigate such scenes again — your Flash of Insight Skill will apply to these memories as well."
- name: Condemn
class: orator
max_level: 4
description: "You may use an action and spend 5 Mind Points to perform an Opposed【 INS + WLP】 Check against a creature that can hear and understand you — describe your accusations! If you succeed, the target loses【 SL × 10】 Mind Points and suffers dazed or shaken (your choice). You gain a bonus equal to【 SL】 to you【r INS + WLP】 Checks for this Skill."
- name: Encourage
class: orator
max_level: 6
description: "During a conflict, you may use an action and spend 5 Mind Points to choose another creature that can hear and understand you. That creature recovers【 SL × 5】 Hit Points and chooses Dexterity, Insight, Might, or Willpower: they treat the chosen Attribute as being one die size higher (up to a maximum of d12) until the start of your next turn."
- name: My Trust in You
class: orator
max_level: 2
description: "After another Player Character who is able to hear you performs a Check, you may spend 1 Fabula Point and invoke one of their Traits or Bonds in order to let them reroll dice or improve the Result of the Check (following the normal rules). Then, if you have a Bond towards that character, they recove【r SL × 10】 Mind Points."
- name: Persuasive
class: orator
max_level: 2
description: "When you successfully perform a Check to fill or erase sections of a Clock, if your approach relied on charm, diplomacy, deception or intimidation, you may spend up to 【SL × 20】 Mind Points. If you do, fill or erase an additional section of that Clock for every 20 Mind Points you spend this way."
- name: Unexpected Ally
class: orator
max_level: 1
description: "You may use an action and spend 1 Fabula Point to choose a non-hostile creature able to hear and understand you. If you do, that creature becomes helpful towards you so long as you are kind and respectful to them and your requests are reasonable."
- name: Cheap Shot
class: rogue
max_level: 5
description: "When you hit a creature with an attack, if the attack only targeted that creature and they are suffering from one or more status effects, you may have it deal extra damage equal to【 SL + the number of status effects on the creature】"
- name: Dodge
class: rogue
max_level: 3
description: "As long as you have no shields and no martial armor equipped, your Defense score is increased by【 SL】"
- name: High Speed
class: rogue
max_level: 3
description: "At the start of a conflict, you may spend 10 Mind Points. If you do, choose one option and apply it before the start of the first round: perform a free attack with a weapon you have equipped; or perform a Hinder or Objective action. You also gain a bonus equal to【 SL】 to all Checks you perform as part of the chosen option."
- name: See You Later
class: rogue
max_level: 1
description: "You may use an action and spend 1 Fabula Point to vanish from the current scene, reappearing whenever you want during a different scene in which another Player Character is present. Describe how you escaped and miraculously got here!"
- name: Soul Steal
class: rogue
max_level: 5
description: "You may use an action to perform a【 DEX + WLP】 Check against the Magic Defense of a creature you can see. If you succeed and the target is a soldier, you recover【 SL】 Inventory Points; if they are an elite or champion, the GM gives you the targets soul treasure, an item worth an amount of zenit equal to or lower than【 the target's level multiplied by 30, or by 50 if they are a Villain】. This soul treasure will appear inside your backpack; a creature can be successfully stolen from with this Skill only once. You gain a bonus equal to【 SL】 to you【r DEX + WLP】 Checks for this Skill."
- name: Barrage
class: sharpshooter
max_level: 1
description: "When you perform a ranged attack, you may spend 10 Mind Points to choose one option: the attack gains multi (2); or you increase the attack's multi property by one, up to a maximum of multi (3)."
- name: Crossfire
class: sharpshooter
max_level: 1
description: "After a creature you can see performs a ranged attack, you may spend an amount of Mind Points equal to the total Result of their Accuracy Check in order to have the attack fail automatically against all targets. You can only use this Skill if you have a ranged weapon equipped, and it has no effect if the Accuracy Check was a critical success."
- name: Hawkeye
class: sharpshooter
max_level: 5
description: "When you perform the Guard action, if you choose not to provide cover to another creature, you may choose one option: the next ranged attack you perform before the end of the current scene will deal【 SL × 2】 extra damage; or you may immediately perform a free attack with a bow or firearm you have equipped, treating your High Roll (HR) as 0 when calculating damage dealt by this attack."
- name: Ranged Weapon Mastery
class: sharpshooter
max_level: 4
description: "You gain a bonus equal to【 SL】 to all Accuracy Checks with ranged weapons."
- name: Warning Shot
class: sharpshooter
max_level: 4
description: "When you hit one or more targets with a ranged attack that would deal damage, you may have the attack deal no damage. If you do, choose one option: inflict shaken on each target hit by the attack; or inflict slow on each target hit by the attack; or each target hit by the attack loses【 SL × 10】 Mind Points. Describe your maneuver!"
- name: Healing Power
class: spiritist
max_level: 2
description: "When you cast a spell that targets one or more allies, if you have an arcane weapon equipped, you may have each of those allies recover an amount of Hit Points equal to 【SL, multiplied by the number of Bonds you have】. This healing is separate from any healing caused by the effects of the spell."
- name: Ritual Spiritism
class: spiritist
max_level: 1
description: "You may perform Rituals whose effects fall within the Spiritism discipline. Spiritism Rituals use【 INS + WLP】 for the Magic Check."
- name: Spiritual Magic
class: spiritist
max_level: 10
description: "Each time you acquire this Skill, learn one Spiritist spell (see next two pages). Offensive (r) Spiritist spells use【 INS + WLP】 for the Magic Check."
- name: Support Magic
class: spiritist
max_level: 1
description: "When you cast a spell that targets one or more allies, if you have an arcane weapon equipped, you may choose one of those allies you have a Bond towards. If you do, that ally gains a bonus to the next Check they perform during the current scene; this bonus is equal to the strength of your Bond towards them."
- name: Vismagus
class: spiritist
max_level: 1
description: "When you cast a spell, if you don't have enough Mind Points to pay for its total cost, you may choose to spend twice as many Hit Points instead. You cannot use this Skill if doing so would reduce you to 0 Hit Points. If a spell cast this way would cause you to recover Hit Points, you instead recover no Hit Points (the spell functions normally on any other target)."
- name: Emergency Item
class: tinkerer
max_level: 1
description: "Once per conflict scene, if you are in Crisis, you may perform an additional action on your turn. This action must be the Inventory action."
- name: Gadgets
class: tinkerer
max_level: 5
description: "When you first acquire this Skill, choose a gadget type: alchemy, infusions or magitech (see next four pages). You gain its basic benefits. Whenever you take this Skill again, choose one option: you gain the basic benefits of a new gadget type; or you gain the advanced benefits of a gadget type whose basic benefits you already obtained; or you gain the superior benefits of a gadget type whose advanced benefits you already obtained."
- name: Potion Rain
class: tinkerer
max_level: 2
description: "When you create a potion that restores a single creature's HP and/or MP, you may have it affect up to【 SL】 additional creatures. If you do, the potion only restores half the normal amount of HP and MP to each creature."
- name: Secret Formula
class: tinkerer
max_level: 5
description: "When you create a potion or magisphere whose effects restore HP and/or MP, each restored amount is increased by【 SL × 5】. When you create an elemental shard, potion or magisphere that deals damage, that item deals【 SL】 extra damage."
- name: Visionary
class: tinkerer
max_level: 5
description: "When you work on a Project, up to【 SL × 100】 zenit of material costs are automatically paid; additionally, you generate an additional 【SL】 progress every day. If multiple characters with this Skill work on the same Project, the effects will be cumulative."
- name: Faithful Companion
class: wayfarer
max_level: 5
description: "Together with the rest of your group, design a level 5 beast, construct, elemental or plant creature (see page 302) that becomes your companion. This creature has no Initiative score and does not level up, can have up to two basic attacks, gains a bonus equal to【 SL】 to Accuracy Checks and Magic Checks, and their maximum Hit Points are equal to【 (SL multiplied by the companion's base Might die size) + half your level】. Your companion doesn't get a turn during conflicts, but on your turn you can use an action to have the companion perform an action (only once per turn). If you leave a scene, your companion leaves with you. If your companion falls to 0 Hit Points, they flee and rejoin you at the start of the next scene in which you are present, with HP equal to their Crisis score. When you rest, your companion also gains the full benefits of resting."
- name: Resourceful
class: wayfarer
max_level: 4
description: "You recove【r SL】 Inventory Points after each travel roll (see page 106)."
- name: Tavern Talk
class: wayfarer
max_level: 3
description: "When you rest inside an inn or tavern, you may ask the Game Master up to 【SL】 questions about your surroundings and the people who live here; the Game Master will answer truthfully and you describe how you gathered the information."
- name: Treasure Hunter
class: wayfarer
max_level: 2
description: "When your group journeys on the world map, you will make a discovery on a roll of 【SL + 1】 or lower on the travel roll (instead of only on a 1)."
- name: Well-Traveled
class: wayfarer
max_level: 1
description: "You reduce the die rolled for your travel rolls by one size (to a minimum of d6). If multiple characters have this Skill, the effects are not cumulative."
- name: Bladestorm
class: weaponmaster
max_level: 1
description: "When you perform a melee attack, you may spend 10 Mind Points to choose one option: the attack gains multi (2); or you increase the attack's multi property by one, up to a maximum of multi (3)."
- name: Bone Crusher
class: weaponmaster
max_level: 1
description: "When you hit one or more targets with a melee attack that would deal damage, you may have the attack deal no damage. If you do, choose one option: inflict dazed on each target hit by the attack; or inflict weak on each target hit by the attack; or each target hit by the attack loses【 SL × 10】 Mind Points. Describe your maneuver!"
- name: Breach
class: weaponmaster
max_level: 1
description: "You may use an action and spend 5 Mind Points to perform a free attack with a melee weapon you have equipped. This attack must target a single creature. If the attack is successful, it deals no damage and you choose one option: you destroy one shield equipped by the target; or you destroy the target's equipped armor; or whenever the target suffers damage from a source before the start of your next turn, that source deals【 SL × 2】 extra damage to them."
- name: Counterattack
class: weaponmaster
max_level: 1
description: "After an enemy hits or misses you with a melee attack, if the Result of their Accuracy Check was an even number, you may perform a free attack against that enemy (after their attack has been fully resolved). This attack must be a melee attack and must have that enemy as its only target; treat your High Roll (HR) as 0 when calculating damage dealt by this attack."
- name: Melee Weapon Mastery
class: weaponmaster
max_level: 1
description: "You gain a bonus equal to【 SL】 to all Accuracy Checks with melee weapons."

View File

@@ -1,7 +1,14 @@
import React from "react";
import React, { useState } from "react";
import skillsFile from "../../data/skills.yml";
import { ClassEntry } from "../types";
import { autoResize } from "../utils";
type SkillPickerTarget = { list: "primary" | "other"; idx: number };
function formatSkillLine(t: SkillTemplate): string {
return `${t.name} (${t.class}, max SL ${t.max_level}): ${t.description}`;
}
interface ClassesPageProps {
isActive: boolean;
primaryClasses: ClassEntry[];
@@ -21,6 +28,29 @@ export default function ClassesPage({
heroicSkills,
onHeroicSkillsChange,
}: ClassesPageProps) {
const [skillPickerTarget, setSkillPickerTarget] = useState<SkillPickerTarget | null>(null);
const [skillCategory, setSkillCategory] = useState<string>("all");
const allSkills = (skillsFile as SkillsFile).skills;
const skillCategories = ["all", ...Array.from(new Set(allSkills.map((s) => s.class))).sort()];
const visibleSkills =
skillCategory === "all" ? allSkills : allSkills.filter((s) => s.class === skillCategory);
const appendSkill = (target: SkillPickerTarget, line: string) => {
const setter = target.list === "primary" ? setPrimaryClasses : setOtherClasses;
setter((prev) =>
prev.map((c, i) =>
i === target.idx ? { ...c, skills: c.skills ? c.skills + "\n" + line : line } : c
)
);
};
const openSkillPicker = (target: SkillPickerTarget, className: string) => {
const match = allSkills.find((s) => s.class.toLowerCase() === className.trim().toLowerCase());
setSkillCategory(match ? match.class : "all");
setSkillPickerTarget(target);
};
return (
<div className={`page${isActive ? " active" : ""}`} id="page-classes">
<div className="grid-2" style={{ marginBottom: 20 }}>
@@ -95,15 +125,25 @@ export default function ClassesPage({
style={{ width: "50px" }}
/>
</label>
<button
type="button"
className="spell-del-btn"
onClick={() =>
setPrimaryClasses((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<button
type="button"
className="add-btn"
style={{ marginTop: 0 }}
onClick={() => openSkillPicker({ list: "primary", idx }, cls.name)}
>
+ Add Template Skill
</button>
<button
type="button"
className="spell-del-btn"
onClick={() =>
setPrimaryClasses((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
</div>
</div>
</div>
))}
@@ -189,15 +229,25 @@ export default function ClassesPage({
style={{ width: "50px" }}
/>
</label>
<button
type="button"
className="spell-del-btn"
onClick={() =>
setOtherClasses((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
<button
type="button"
className="add-btn"
style={{ marginTop: 0 }}
onClick={() => openSkillPicker({ list: "other", idx }, cls.name)}
>
+ Add Template Skill
</button>
<button
type="button"
className="spell-del-btn"
onClick={() =>
setOtherClasses((prev) => prev.filter((_, i) => i !== idx))
}
>
Remove
</button>
</div>
</div>
</div>
))}
@@ -227,6 +277,70 @@ export default function ClassesPage({
style={{ minHeight: 100 }}
/>
</div>
{skillPickerTarget &&
(() => {
const target = skillPickerTarget;
return (
<div
className="spell-picker-overlay"
onClick={() => setSkillPickerTarget(null)}
>
<div className="spell-picker-modal" onClick={(e) => e.stopPropagation()}>
<div className="spell-picker-header">
<span>Choose a skill</span>
<button
type="button"
className="spell-picker-close"
onClick={() => setSkillPickerTarget(null)}
>
</button>
</div>
<div
style={{
display: "flex",
gap: 6,
flexWrap: "wrap",
padding: "8px 14px",
borderBottom: "1px solid var(--border)",
}}
>
{skillCategories.map((cat) => (
<button
type="button"
key={cat}
className={`add-btn${skillCategory === cat ? " active-filter" : ""}`}
style={{
padding: "2px 8px",
fontSize: "0.65rem",
textTransform: "capitalize",
}}
onClick={() => setSkillCategory(cat)}
>
{cat}
</button>
))}
</div>
<ul className="spell-picker-list">
{visibleSkills.map((t, i) => (
<li
key={i}
className="spell-picker-item"
onClick={() => {
appendSkill(target, formatSkillLine(t));
setSkillPickerTarget(null);
}}
>
<span className="spell-picker-name">{t.name}</span>
<span className="spell-picker-class">{t.class}</span>
</li>
))}
</ul>
</div>
</div>
);
})()}
</div>
);
}

11
src/globals.d.ts vendored
View File

@@ -47,6 +47,17 @@ interface SpellsFile {
spells: SpellTemplate[];
}
interface SkillTemplate {
name: string;
class: string;
max_level: number;
description: string;
}
interface SkillsFile {
skills: SkillTemplate[];
}
interface BookPage {
n: number;
content: string;