From 99a0118b68cbe0f754dbb6c9889cff3cb2412235 Mon Sep 17 00:00:00 2001 From: Anish George Date: Mon, 6 Jul 2026 20:24:41 +1000 Subject: [PATCH 1/7] feat(dnd): Implement default masteries --- apps/game-tools/src/data/dnd-characters/claw/claw.ts | 2 ++ .../gonvar-feathertide/gonvar-feathertide.ts | 4 ++++ .../data/dnd-characters/omarin-kenate/omarin-kenate.ts | 4 ++++ .../src/data/dnd-characters/splitter/splitter.ts | 2 ++ .../src/components/AttackList/AttackList.tsx | 9 +++++++-- .../src/components/layout/checkables.tsx | 7 ++++--- packages/dnd-character-sheet/src/lib/models/attacks.ts | 3 ++- 7 files changed, 25 insertions(+), 6 deletions(-) diff --git a/apps/game-tools/src/data/dnd-characters/claw/claw.ts b/apps/game-tools/src/data/dnd-characters/claw/claw.ts index a0093dad..a317b026 100644 --- a/apps/game-tools/src/data/dnd-characters/claw/claw.ts +++ b/apps/game-tools/src/data/dnd-characters/claw/claw.ts @@ -54,6 +54,7 @@ export const ClawData: Character = { }, ], masteryProperty: 'Nick', + hasMasteryByDefault: true, notes: 'Light, Finesse, Thrown (range 20/60)', }, { @@ -67,6 +68,7 @@ export const ClawData: Character = { }, ], masteryProperty: 'Vex', + hasMasteryByDefault: true, notes: 'Light, Finesse', }, { diff --git a/apps/game-tools/src/data/dnd-characters/gonvar-feathertide/gonvar-feathertide.ts b/apps/game-tools/src/data/dnd-characters/gonvar-feathertide/gonvar-feathertide.ts index 7ff80f81..9d6c7e14 100644 --- a/apps/game-tools/src/data/dnd-characters/gonvar-feathertide/gonvar-feathertide.ts +++ b/apps/game-tools/src/data/dnd-characters/gonvar-feathertide/gonvar-feathertide.ts @@ -44,6 +44,7 @@ export const GonvarData: Character = { kind: 'weapon', name: 'Scimitar', masteryProperty: 'Nick', + hasMasteryByDefault: true, damage: [{ dice: '1d6', type: 'Slashing' }], notes: 'Light, Finesse', }, @@ -51,6 +52,7 @@ export const GonvarData: Character = { ability: Ability.Strength, kind: 'weapon', name: 'Dagger', + masteryProperty: 'Nick', damage: [{ dice: '1d4', type: 'Piercing' }], notes: 'Light, Finesse, Thrown (range 20/60)', }, @@ -59,6 +61,7 @@ export const GonvarData: Character = { kind: 'weapon', name: 'Pike', masteryProperty: 'Push', + hasMasteryByDefault: true, damage: [{ dice: '1d10', type: 'Piercing' }], notes: '2-handed, Heavy, Reach (+5ft melee)', }, @@ -67,6 +70,7 @@ export const GonvarData: Character = { kind: 'weapon', name: 'Crossbow', masteryProperty: 'Slow', + hasMasteryByDefault: true, damage: [{ dice: '1d8', type: 'Piercing' }], notes: 'Ammo (bolt; 80/320), Loading, 2-handed', }, diff --git a/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts b/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts index b0560d11..76258a85 100644 --- a/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts +++ b/apps/game-tools/src/data/dnd-characters/omarin-kenate/omarin-kenate.ts @@ -53,6 +53,7 @@ export const OmarinData: Character = { ability: Ability.Dexterity, damage: [{ dice: '1d6', type: 'Piercing' }], masteryProperty: 'Vex', + hasMasteryByDefault: true, notes: 'Light, Finesse', }, { @@ -61,6 +62,7 @@ export const OmarinData: Character = { ability: Ability.Dexterity, damage: [{ dice: '1d6', type: 'Slashing' }], masteryProperty: 'Nick', + hasMasteryByDefault: true, notes: 'Light, Finesse', }, { @@ -75,12 +77,14 @@ export const OmarinData: Character = { ability: Ability.Dexterity, damage: [{ dice: '1d4', type: 'Piercing' }], masteryProperty: 'Nick', + hasMasteryByDefault: true, notes: 'Light, Finesse, Thrown (range 20/60)', }, { name: 'Light Crossbow', kind: 'weapon', ability: Ability.Dexterity, + masteryProperty: 'Slow', damage: [{ dice: '1d8', type: 'Piercing' }], notes: 'Ammo (80/20, Bolt), Loading, 2-handed', }, diff --git a/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts b/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts index fadb521d..ad9f5176 100644 --- a/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts +++ b/apps/game-tools/src/data/dnd-characters/splitter/splitter.ts @@ -47,6 +47,7 @@ export const SplitterData: Character = { ability: Ability.Strength, damage: [{ dice: '1d12', type: 'Slashing' }], masteryProperty: 'Graze', + hasMasteryByDefault: true, notes: 'Heavy, 2-handed', }, { @@ -55,6 +56,7 @@ export const SplitterData: Character = { ability: Ability.Strength, damage: [{ dice: '1d6', type: 'Slashing' }], masteryProperty: 'Vex', + hasMasteryByDefault: true, notes: 'Light, Thrown (range 20/60)', }, ], diff --git a/packages/dnd-character-sheet/src/components/AttackList/AttackList.tsx b/packages/dnd-character-sheet/src/components/AttackList/AttackList.tsx index 896dc57e..d9233d30 100644 --- a/packages/dnd-character-sheet/src/components/AttackList/AttackList.tsx +++ b/packages/dnd-character-sheet/src/components/AttackList/AttackList.tsx @@ -46,8 +46,13 @@ export function AttackList() { {formatDamage(attack.damage, damageBonus)} {attack.masteryProperty ? ( -  {' '} - {attack.masteryProperty} + {' '} +   {attack.masteryProperty} ) : showMasteries ? ( diff --git a/packages/dnd-character-sheet/src/components/layout/checkables.tsx b/packages/dnd-character-sheet/src/components/layout/checkables.tsx index 6e0ae08a..b2a3b58b 100644 --- a/packages/dnd-character-sheet/src/components/layout/checkables.tsx +++ b/packages/dnd-character-sheet/src/components/layout/checkables.tsx @@ -28,12 +28,13 @@ function BaseCheck({ checked = false, className, ...props }: CheckableProps) { ); } -export type CheckedState = boolean | 'special' | undefined; +export type CheckedState = boolean | 'special' | 'suggested' | undefined; -const CHECK_FILL: Record<'true' | 'false' | 'special', string> = { +const CHECK_FILL: Record<'true' | 'false' | 'special' | 'suggested', string> = { true: 'bg-primary-surface border-primary-foreground', special: 'bg-secondary-surface-2 border-secondary-foreground', - false: '', + suggested: 'bg-page-2', + false: 'bg-page-4', }; export function EmptyCheckList({ diff --git a/packages/dnd-character-sheet/src/lib/models/attacks.ts b/packages/dnd-character-sheet/src/lib/models/attacks.ts index 4510f276..187ef8de 100644 --- a/packages/dnd-character-sheet/src/lib/models/attacks.ts +++ b/packages/dnd-character-sheet/src/lib/models/attacks.ts @@ -4,7 +4,8 @@ import type { DamageType } from './damage-type'; export type Attack = { name: string; damage: AttackDamage[]; - masteryProperty?: string; + masteryProperty?: string; // easier to just keep this here than in weapon attack + hasMasteryByDefault?: boolean; attackBonusMod?: number; damageMod?: number; notProficient?: boolean; From 5c26329071e179867fd480d70cc3a13d6f297e0c Mon Sep 17 00:00:00 2001 From: Anish George Date: Mon, 6 Jul 2026 20:38:19 +1000 Subject: [PATCH 2/7] fix(dnd): auto columns does not work with flex --- .../src/components/feature-blocks/ClassFeatures.module.css | 3 --- .../src/components/feature-blocks/ClassFeatures.tsx | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) delete mode 100644 packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.module.css diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.module.css b/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.module.css deleted file mode 100644 index b06bef8d..00000000 --- a/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.ColumnLayout { - column-fill: auto; -} diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx b/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx index 6de3237d..615fbe3b 100644 --- a/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx +++ b/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx @@ -3,7 +3,6 @@ import { useCharacter } from '../CharacterSheet'; import { FeatureList } from '../feature-blocks/FeatureList'; import { Panel } from '../layout/Panel'; import { PanelTitle } from '../layout/PanelTitle'; -import styles from './ClassFeatures.module.css'; export function ClassFeatures() { const { character } = useCharacter(); @@ -12,7 +11,7 @@ export function ClassFeatures() { Class Features From 9f68b2b4ce981c58337485d7c2b919fc2e79e80e Mon Sep 17 00:00:00 2001 From: Anish George Date: Mon, 6 Jul 2026 21:31:01 +1000 Subject: [PATCH 3/7] feat(dnd): Start adding game infos --- .../SpellcastingBlock/SpellList.tsx | 2 +- .../StandardCharacterSheet.tsx | 6 +- .../components/VisualAdjustmentsContext.ts | 2 +- .../components/game-infos/ActionsInCombat.tsx | 94 +++++++++++++++++++ .../components/game-infos/WeaponMasteries.tsx | 51 ++++++++++ .../src/components/layout/GameInfoPanel.tsx | 33 +++++++ .../src/lib/models/attacks.ts | 3 +- .../src/lib/models/weapon-properties.ts | 17 ++++ 8 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 packages/dnd-character-sheet/src/components/game-infos/ActionsInCombat.tsx create mode 100644 packages/dnd-character-sheet/src/components/game-infos/WeaponMasteries.tsx create mode 100644 packages/dnd-character-sheet/src/components/layout/GameInfoPanel.tsx create mode 100644 packages/dnd-character-sheet/src/lib/models/weapon-properties.ts diff --git a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx index 32159123..0cff44d8 100644 --- a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx +++ b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx @@ -20,7 +20,7 @@ export function SpellList() { const { spellRows } = useVisualAdjustments(); const sortedSpells = sort(compareSpells, spellcasting?.spells ?? []); - const emptyRows = spellRows - sortedSpells.length; + const emptyRows = Math.max(0, spellRows - sortedSpells.length); return ( diff --git a/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx b/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx index b4e538a9..90065298 100644 --- a/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx +++ b/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx @@ -10,6 +10,8 @@ import { ClassFeatures } from '../feature-blocks/ClassFeatures'; import { Feats } from '../feature-blocks/Feats'; import { SpeciesTraits } from '../feature-blocks/SpeciesTraits'; import { GenericPanel } from '../GenericPanel/GenericPanel'; +import { ActionsInCombat } from '../game-infos/ActionsInCombat'; +import { WeaponMasteries } from '../game-infos/WeaponMasteries'; import { HeroicInspiration } from '../HeroicInspiration/HeroicInspiration'; import { Inventory } from '../Inventory/Inventory'; import { Page } from '../layout/Page'; @@ -79,8 +81,10 @@ export function StandardCharacterSheet({ data, visualAdjustments }: Props) {
-
+
{data.spellcasting && } + +
+ Actions in Combat + +
+
+ Weapon Attack. + Equip or un-equip a weapon, then make an Attack roll. +
+
+ Unarmed Strike. + + to hit: Attack roll + Str. + Proficiency Bonus.{' '} + on hit: 1 + Str. Bludgeoning damage. + +
+
+ Grapple. + + Str. or Dex. saving throw. on fail: target gains Grappled + condition. + +
+
+ Shove. + + Str. or Dex. saving throw. on fail: target is pushed 5ft or + target gains the Prone condition. + +
+
+ Dash. + Double your Speed (after modifiers) +
+
+ Disengage. + + Your movement this turn does not trigger Opportunity attacks. + +
+
+ Dodge. + + Until the start of your next turn, any attack rolls against you have + Disadvantage and you make Dex. saving throws with Advantage. + +
+
+ Help. + + Help another creature with an ability check if you are proficient in + it, or an attack roll, or administer first aid to an Unconscious + creature: Medicine check (DC 10) + +
+
+ Hide. + + Try to hide out of enemy's line of sight with a Stealth check (DC + 15). + +
+
+ Magic. + Cast a spell, use a magic item, or use a magical feature. +
+
+ Ready. + + Prepare an action or movement a a Reaction to a trigger you define + which should happen before the start of your next turn. Consumes the + resources required. Holding a spell requires Concentration. + +
+
+ Utilise. + + Use a nonmagical object that requires an action for its use + +
+
+ Improvise. + + Describe an action not detailed here, the DM tells you if that is + possible and what kind of D20 Test you need to make if any. + +
+
+ + ); +} diff --git a/packages/dnd-character-sheet/src/components/game-infos/WeaponMasteries.tsx b/packages/dnd-character-sheet/src/components/game-infos/WeaponMasteries.tsx new file mode 100644 index 00000000..6fa82d42 --- /dev/null +++ b/packages/dnd-character-sheet/src/components/game-infos/WeaponMasteries.tsx @@ -0,0 +1,51 @@ +import { filter, map, uniq } from 'ramda'; +import { useMemo } from 'react'; +import type { WeaponMasteryProperty } from '../../lib/models/weapon-properties'; +import { useCharacter } from '../CharacterSheet'; +import { GameInfoPanel, GameInfoPanelTitle } from '../layout/GameInfoPanel'; + +export function WeaponMasteries() { + const { character } = useCharacter(); + const allMasteries = useMemo( + () => + uniq( + filter( + Boolean, + map((atk) => atk.masteryProperty, character.attacks) + ) + ) as WeaponMasteryProperty[], + [character] + ); + + if (allMasteries.length < 1) { + return null; + } + + return ( + + Weapon Mastery Properties +
+ {allMasteries.map((m) => ( +
+ {m}. + {Descriptions[m]} +
+ ))} +
+
+ ); +} + +const Descriptions: Record = { + Cleave: + 'Once per turn, If you hit a creature with a melee attack roll using this weapon, you can make a melee attack roll with the weapon against a second creature within 5 ft of the first, that is also within your reach. On a hit, the second creature takes weapon damage without your ability modifier (unless the modifier is negative).', + Graze: + 'If your attack roll with this weapon misses a creature, you ca n deal damage to the creature = ability modifier you used to make the attack roll. Damage type = weapon damage type.', + Nick: 'Once per turn, when you make the extra attack of the Light property, you can make it as part of the Attack action instead of as a Bonus action.', + Push: 'If you hit a creature with this weapon, you can push the creature up to 10ft straight away from yourself if it is Large or smaller.', + Sap: 'If you hit a creature with this weapon, that creature has Disadvantage on its next attack roll before the start of your next turn.', + Slow: 'If you hit and deal damage to a creature with this weapon, you can reduce its Speed by 10ft until the start of your next turn. Speed reduction does not exceed 10ft for multiple hits with weapons of this property.', + Topple: + 'If you hit a creature with this weapon, you can force a Con. saving throw (DC 8 + ability mod used to make this roll + proficiency bonus). On a failed save, the creature has Prone condition.', + Vex: 'If you hit and deal damage to a creature with this weapon, you have Advantage on your next attack roll against that creature before the end of your next turn.', +}; diff --git a/packages/dnd-character-sheet/src/components/layout/GameInfoPanel.tsx b/packages/dnd-character-sheet/src/components/layout/GameInfoPanel.tsx new file mode 100644 index 00000000..9e234a24 --- /dev/null +++ b/packages/dnd-character-sheet/src/components/layout/GameInfoPanel.tsx @@ -0,0 +1,33 @@ +import { cn } from '@ageorgedev/toolbelt/cn'; + +export function GameInfoPanel({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ); +} + +export function GameInfoPanelTitle({ + className, + ...props +}: React.HTMLAttributes) { + return ( + <> +

+ {/* */} + + ); +} diff --git a/packages/dnd-character-sheet/src/lib/models/attacks.ts b/packages/dnd-character-sheet/src/lib/models/attacks.ts index 187ef8de..b220b8cf 100644 --- a/packages/dnd-character-sheet/src/lib/models/attacks.ts +++ b/packages/dnd-character-sheet/src/lib/models/attacks.ts @@ -1,10 +1,11 @@ import type { Ability } from './abilities'; import type { DamageType } from './damage-type'; +import type { WeaponMasteryProperty } from './weapon-properties'; export type Attack = { name: string; damage: AttackDamage[]; - masteryProperty?: string; // easier to just keep this here than in weapon attack + masteryProperty?: WeaponMasteryProperty; // easier to just keep this here than in weapon attack hasMasteryByDefault?: boolean; attackBonusMod?: number; damageMod?: number; diff --git a/packages/dnd-character-sheet/src/lib/models/weapon-properties.ts b/packages/dnd-character-sheet/src/lib/models/weapon-properties.ts new file mode 100644 index 00000000..1a33b008 --- /dev/null +++ b/packages/dnd-character-sheet/src/lib/models/weapon-properties.ts @@ -0,0 +1,17 @@ +export type WeaponProperty = + | 'Ammo' + | 'Thrown' + | 'Versatile' + | 'Light' + | 'Loading' + | 'Finesse'; + +export type WeaponMasteryProperty = + | 'Cleave' + | 'Graze' + | 'Nick' + | 'Push' + | 'Sap' + | 'Slow' + | 'Topple' + | 'Vex'; From 287a856724ffcbd419846424e690742f6232e9fc Mon Sep 17 00:00:00 2001 From: Anish George Date: Mon, 6 Jul 2026 21:44:51 +1000 Subject: [PATCH 4/7] chore(dnd): changed my mind, removed the rules reference for now --- apps/game-tools/src/data/dnd-characters/index.ts | 2 -- .../src/components/NotesPanel/NotesPanel.tsx | 12 ++++++++++++ .../src/components/Resources/Resources.tsx | 6 +++--- .../src/components/SpellcastingBlock/SpellList.tsx | 2 +- .../SpellcastingBlock/SpellcastingBlock.tsx | 2 +- .../StandardCharacterSheet.tsx | 10 ++++++++-- .../src/components/layout/checkables.tsx | 2 +- 7 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 packages/dnd-character-sheet/src/components/NotesPanel/NotesPanel.tsx diff --git a/apps/game-tools/src/data/dnd-characters/index.ts b/apps/game-tools/src/data/dnd-characters/index.ts index 6fee9bf5..0863bc23 100644 --- a/apps/game-tools/src/data/dnd-characters/index.ts +++ b/apps/game-tools/src/data/dnd-characters/index.ts @@ -34,12 +34,10 @@ const characters: BasePack[] = [ { data: Zoynari3Data, spellBook: Zoynari3SpellBook, - visualAdjustments: { spellRows: 30 }, }, { data: SaoraData, spellBook: SaoraSpellBook, - visualAdjustments: { spellRows: 35 }, }, { data: ElnorinData, spellBook: ElnorinSpellBook }, { data: GonvarData }, diff --git a/packages/dnd-character-sheet/src/components/NotesPanel/NotesPanel.tsx b/packages/dnd-character-sheet/src/components/NotesPanel/NotesPanel.tsx new file mode 100644 index 00000000..15d3f795 --- /dev/null +++ b/packages/dnd-character-sheet/src/components/NotesPanel/NotesPanel.tsx @@ -0,0 +1,12 @@ +import { HandWrittenNotes } from '../HandwrittenNotes/HandwrittenNotes'; +import { Panel, type PanelProps } from '../layout/Panel'; +import { PanelTitle } from '../layout/PanelTitle'; + +export function NotesPanel(props: PanelProps) { + return ( + + Notes + + + ); +} diff --git a/packages/dnd-character-sheet/src/components/Resources/Resources.tsx b/packages/dnd-character-sheet/src/components/Resources/Resources.tsx index 6b9594ab..3d175d2a 100644 --- a/packages/dnd-character-sheet/src/components/Resources/Resources.tsx +++ b/packages/dnd-character-sheet/src/components/Resources/Resources.tsx @@ -28,9 +28,9 @@ export function Resources() { Resources
{resources.map((r) => ( -
+
{r.name} - {r.refresh} + {r.refresh}
{r.display === 'dots' ? ( // Array.from({ length: r.value }, (_, i) => ) @@ -126,6 +126,6 @@ function getRefreshText(refresh: Resource['refresh']) { return 'Short Rest'; case 'short-and-long-rest': - return `${refresh.numberOfRefreshesOnShortRest} / Short Rest, all on Long Rest`; + return `${refresh.numberOfRefreshesOnShortRest}/Short Rest, all on Long Rest`; } } diff --git a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx index 0cff44d8..6e32ceb8 100644 --- a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx +++ b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.tsx @@ -103,7 +103,7 @@ export function SpellRow({ spell }: { spell: Spell }) { ) : spell.level === 0 ? ( ) : ( - + )} {spell.level} diff --git a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellcastingBlock.tsx b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellcastingBlock.tsx index 4f17e450..e548e553 100644 --- a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellcastingBlock.tsx +++ b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellcastingBlock.tsx @@ -4,7 +4,7 @@ import { SpellSlotsPanel } from './SpellSlotsPanel'; export function SpellcastingBlock() { return ( -
+
diff --git a/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx b/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx index 90065298..5b909698 100644 --- a/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx +++ b/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx @@ -15,6 +15,7 @@ import { WeaponMasteries } from '../game-infos/WeaponMasteries'; import { HeroicInspiration } from '../HeroicInspiration/HeroicInspiration'; import { Inventory } from '../Inventory/Inventory'; import { Page } from '../layout/Page'; +import { NotesPanel } from '../NotesPanel/NotesPanel'; import { ProficiencyBlock } from '../ProficiencyBlock/ProficiencyBlock'; import { Resources } from '../Resources/Resources'; import { SheetHeader } from '../SheetHeader/SheetHeader'; @@ -83,8 +84,13 @@ export function StandardCharacterSheet({ data, visualAdjustments }: Props) {
{data.spellcasting && } - - + + {!data.spellcasting && ( + <> + + + + )}
= { - true: 'bg-primary-surface border-primary-foreground', + true: 'bg-primary-surface-2 border-primary-foreground', special: 'bg-secondary-surface-2 border-secondary-foreground', suggested: 'bg-page-2', false: 'bg-page-4', From 88d16311d34ca58e42c61c833a28a64743ced513 Mon Sep 17 00:00:00 2001 From: Anish George Date: Tue, 7 Jul 2026 15:43:20 +1000 Subject: [PATCH 5/7] chore(dnd): fix dnd tests --- .../src/components/AbilityBox/AbilityBox.test.tsx | 6 +++--- turbo.json | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/dnd-character-sheet/src/components/AbilityBox/AbilityBox.test.tsx b/packages/dnd-character-sheet/src/components/AbilityBox/AbilityBox.test.tsx index 4166f6e0..8ed5d546 100644 --- a/packages/dnd-character-sheet/src/components/AbilityBox/AbilityBox.test.tsx +++ b/packages/dnd-character-sheet/src/components/AbilityBox/AbilityBox.test.tsx @@ -88,13 +88,13 @@ describe('AbilityBox', () => { }); renderAbilityBox(Ability.Strength, character); const checkbox = getCheckboxForLabel('Saving Throw'); - expect(checkbox).toHaveClass('bg-primary-surface'); + expect(checkbox).toHaveClass('bg-primary-surface-2'); }); it('saving throw checkbox is unchecked when ability is not in savingThrowProficiencies', () => { renderAbilityBox(Ability.Strength, makeCharacter()); const checkbox = getCheckboxForLabel('Saving Throw'); - expect(checkbox).not.toHaveClass('bg-primary-surface'); + expect(checkbox).not.toHaveClass('bg-primary-surface-2'); }); it('skill checkbox is checked when skill is in skillProficiencies', () => { @@ -102,7 +102,7 @@ describe('AbilityBox', () => { const character = makeCharacter({ skillProficiencies: [Skill.Athletics] }); renderAbilityBox(Ability.Strength, character); const checkbox = getCheckboxForLabel('Athletics'); - expect(checkbox).toHaveClass('bg-primary-surface'); + expect(checkbox).toHaveClass('bg-primary-surface-2'); }); it('skill checkbox is in special state when skill is in skillExpertise', () => { diff --git a/turbo.json b/turbo.json index 34420bb3..cd455cbc 100644 --- a/turbo.json +++ b/turbo.json @@ -21,7 +21,12 @@ }, "test": { "dependsOn": ["^build"], - "inputs": ["src/**/*.spec.tsx", "src/**/*.spec.ts", "vitest.config.ts"] + "inputs": [ + "src/**/*.spec.tsx", + "src/**/*.spec.ts", + "src/**/*.test.ts", + "vitest.config.ts" + ] }, "dev": { "cache": false, From 0bc74a28e77010e557f735864cd6bc704b44b49f Mon Sep 17 00:00:00 2001 From: Anish George Date: Tue, 7 Jul 2026 20:19:20 +1000 Subject: [PATCH 6/7] feat(dnd): Enhanced visual adjustments for printing --- .../src/data/dnd-characters/index.ts | 11 ++++++++++- .../saora-embervale/saora-embervale.ts | 18 +++++++++++++++++- .../StandardCharacterSheet.tsx | 6 +----- .../src/components/VisualAdjustmentsContext.ts | 15 ++++++++++----- .../feature-blocks/ClassFeatures.tsx | 3 +++ .../src/components/feature-blocks/Feats.tsx | 7 ++++++- .../components/feature-blocks/FeatureEntry.tsx | 10 ++++++++-- .../components/feature-blocks/FeatureList.tsx | 4 +++- .../feature-blocks/SpeciesTraits.tsx | 7 ++++++- packages/dnd-character-sheet/src/index.ts | 6 ++---- 10 files changed, 66 insertions(+), 21 deletions(-) diff --git a/apps/game-tools/src/data/dnd-characters/index.ts b/apps/game-tools/src/data/dnd-characters/index.ts index 0863bc23..5173bf88 100644 --- a/apps/game-tools/src/data/dnd-characters/index.ts +++ b/apps/game-tools/src/data/dnd-characters/index.ts @@ -34,6 +34,9 @@ const characters: BasePack[] = [ { data: Zoynari3Data, spellBook: Zoynari3SpellBook, + visualAdjustments: { + speciesAndFeatsFontSize: 'small', + }, }, { data: SaoraData, @@ -43,7 +46,13 @@ const characters: BasePack[] = [ { data: GonvarData }, { data: ClawData, spellBook: ClawSpellBook }, { data: OmarinData }, - { data: SplitterData }, + { + data: SplitterData, + visualAdjustments: { + speciesAndFeatsFontSize: 'small', + classFeaturesFontSize: 'small', + }, + }, ]; const processAndGroup = compose( diff --git a/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts b/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts index a47f115e..87995131 100644 --- a/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts +++ b/apps/game-tools/src/data/dnd-characters/saora-embervale/saora-embervale.ts @@ -44,7 +44,23 @@ export const SaoraData: Character = { hitPoints: { maximum: 24, }, - attacks: [], + attacks: [ + { + name: 'Dagger (x2)', + kind: 'weapon', + ability: Ability.Dexterity, + damage: [{ dice: '1d4', type: 'Piercing' }], + notes: 'Light, Finesse, Thrown (range 20/60)', + }, + { + name: 'Vicious Mockery', + kind: 'spell-with-save', + saveAbility: Ability.Wisdom, + damage: [{ dice: '1d6', type: 'Psychic' }], + notes: + 'target has disadvantage on next attack roll till end of its next turn', + }, + ], equipment: [ 'Bedroll, Bell, Bullseye Lantern, 2 Costumes, Fine clothes, Mirror, Tinderbox, Waterskin', 'Flute, Forgery kit, Disguise kit', diff --git a/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx b/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx index 5b909698..d6043dd7 100644 --- a/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx +++ b/packages/dnd-character-sheet/src/components/StandardCharacterSheet/StandardCharacterSheet.tsx @@ -22,14 +22,10 @@ import { SheetHeader } from '../SheetHeader/SheetHeader'; import { SpellcastingBlock } from '../SpellcastingBlock/SpellcastingBlock'; import { DEFAULT_VISUAL_ADUSTMENTS, + type VisualAdjustments, VisualAdjustmentsContext, } from '../VisualAdjustmentsContext'; -export type VisualAdjustments = { - spellRows?: number; - inventoryRows?: number; -}; - interface Props { data: Character; visualAdjustments?: VisualAdjustments; diff --git a/packages/dnd-character-sheet/src/components/VisualAdjustmentsContext.ts b/packages/dnd-character-sheet/src/components/VisualAdjustmentsContext.ts index 04e3583e..9cd2cbc7 100644 --- a/packages/dnd-character-sheet/src/components/VisualAdjustmentsContext.ts +++ b/packages/dnd-character-sheet/src/components/VisualAdjustmentsContext.ts @@ -1,19 +1,24 @@ import { createContext, useContext } from 'react'; -interface VisualAdjustments { +type FullVisualAdjustments = { spellRows: number; inventoryRows: number; -} + classFeaturesFontSize: 'normal' | 'small'; + speciesAndFeatsFontSize: 'normal' | 'small'; +}; +export type VisualAdjustments = Partial; -export const DEFAULT_VISUAL_ADUSTMENTS: VisualAdjustments = { +export const DEFAULT_VISUAL_ADUSTMENTS: FullVisualAdjustments = { spellRows: 20, inventoryRows: 10, + classFeaturesFontSize: 'normal', + speciesAndFeatsFontSize: 'normal', }; -export const VisualAdjustmentsContext = createContext( +export const VisualAdjustmentsContext = createContext( DEFAULT_VISUAL_ADUSTMENTS ); -export function useVisualAdjustments(): VisualAdjustments { +export function useVisualAdjustments(): FullVisualAdjustments { return useContext(VisualAdjustmentsContext); } diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx b/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx index 615fbe3b..e0cdfb0b 100644 --- a/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx +++ b/packages/dnd-character-sheet/src/components/feature-blocks/ClassFeatures.tsx @@ -3,9 +3,11 @@ import { useCharacter } from '../CharacterSheet'; import { FeatureList } from '../feature-blocks/FeatureList'; import { Panel } from '../layout/Panel'; import { PanelTitle } from '../layout/PanelTitle'; +import { useVisualAdjustments } from '../VisualAdjustmentsContext'; export function ClassFeatures() { const { character } = useCharacter(); + const { classFeaturesFontSize } = useVisualAdjustments(); return ( @@ -13,6 +15,7 @@ export function ClassFeatures() { ); diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/Feats.tsx b/packages/dnd-character-sheet/src/components/feature-blocks/Feats.tsx index c13786fc..58362e72 100644 --- a/packages/dnd-character-sheet/src/components/feature-blocks/Feats.tsx +++ b/packages/dnd-character-sheet/src/components/feature-blocks/Feats.tsx @@ -1,15 +1,20 @@ import { useCharacter } from '../CharacterSheet'; import { Panel } from '../layout/Panel'; import { PanelTitle } from '../layout/PanelTitle'; +import { useVisualAdjustments } from '../VisualAdjustmentsContext'; import { FeatureList } from './FeatureList'; export function Feats() { const { character } = useCharacter(); + const { speciesAndFeatsFontSize } = useVisualAdjustments(); return ( Feats - + ); } diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/FeatureEntry.tsx b/packages/dnd-character-sheet/src/components/feature-blocks/FeatureEntry.tsx index 8843108a..2ccbb212 100644 --- a/packages/dnd-character-sheet/src/components/feature-blocks/FeatureEntry.tsx +++ b/packages/dnd-character-sheet/src/components/feature-blocks/FeatureEntry.tsx @@ -1,7 +1,13 @@ import type { Feature } from '../../lib/models/feature'; import { RichTextDisplay } from '../RichTextDisplay/RichTextDisplay'; -export function FeatureEntry({ data: feat }: { data: Feature }) { +export function FeatureEntry({ + data: feat, + smallFont, +}: { + data: Feature; + smallFont?: boolean; +}) { const notes = [ feat.castingTime, feat.duration, @@ -11,7 +17,7 @@ export function FeatureEntry({ data: feat }: { data: Feature }) { .join(', '); return ( -
+
{feat.name}. {notes && ({notes})}   diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/FeatureList.tsx b/packages/dnd-character-sheet/src/components/feature-blocks/FeatureList.tsx index 6cf00a49..0d184b2c 100644 --- a/packages/dnd-character-sheet/src/components/feature-blocks/FeatureList.tsx +++ b/packages/dnd-character-sheet/src/components/feature-blocks/FeatureList.tsx @@ -4,11 +4,13 @@ import { FeatureEntry } from './FeatureEntry'; type FeatureListProps = React.HTMLAttributes & { features: Feature[]; + smallFont?: boolean; }; export function FeatureList({ className, features, + smallFont, ...props }: FeatureListProps) { if (!features.length) { @@ -21,7 +23,7 @@ export function FeatureList({ return (
{features.map((feature) => ( - + ))}
); diff --git a/packages/dnd-character-sheet/src/components/feature-blocks/SpeciesTraits.tsx b/packages/dnd-character-sheet/src/components/feature-blocks/SpeciesTraits.tsx index 3943b459..e664ec19 100644 --- a/packages/dnd-character-sheet/src/components/feature-blocks/SpeciesTraits.tsx +++ b/packages/dnd-character-sheet/src/components/feature-blocks/SpeciesTraits.tsx @@ -1,15 +1,20 @@ import { useCharacter } from '../CharacterSheet'; import { Panel } from '../layout/Panel'; import { PanelTitle } from '../layout/PanelTitle'; +import { useVisualAdjustments } from '../VisualAdjustmentsContext'; import { FeatureList } from './FeatureList'; export function SpeciesTraits() { const { character } = useCharacter(); + const { speciesAndFeatsFontSize } = useVisualAdjustments(); return ( Species Traits - + ); } diff --git a/packages/dnd-character-sheet/src/index.ts b/packages/dnd-character-sheet/src/index.ts index 080bbb95..fb95f876 100644 --- a/packages/dnd-character-sheet/src/index.ts +++ b/packages/dnd-character-sheet/src/index.ts @@ -2,11 +2,9 @@ export { ExampleSheet } from './characters/example'; // Standard assembled sheet -export { - StandardCharacterSheet, - type VisualAdjustments, -} from './components/StandardCharacterSheet/StandardCharacterSheet'; +export { StandardCharacterSheet } from './components/StandardCharacterSheet/StandardCharacterSheet'; +export type { VisualAdjustments } from './components/VisualAdjustmentsContext'; export { getCharacterBrief } from './lib/character-brief'; // Other models From 25f629419ff482c8590abb8080ab5a4ae248572a Mon Sep 17 00:00:00 2001 From: Anish George Date: Tue, 7 Jul 2026 20:21:24 +1000 Subject: [PATCH 7/7] chore(dnd): fix tests --- .../src/components/SpellcastingBlock/SpellList.test.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.test.tsx b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.test.tsx index de70ca6d..d3437460 100644 --- a/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.test.tsx +++ b/packages/dnd-character-sheet/src/components/SpellcastingBlock/SpellList.test.tsx @@ -51,7 +51,12 @@ function renderSpellList( return render(