Generic (non-5e) ruleset mode + UI polish + combat switch fix #7

Merged
robert merged 25 commits from feat/generic-ruleset into main 2026-07-08 22:54:04 -04:00
Showing only changes of commit 69ab0c35c0 - Show all commits
+11 -11
View File
@@ -1792,7 +1792,7 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
onFocus={(e) => { e.target.select(); handleFieldFocus(p.id, 'Initiative'); }} onFocus={(e) => { e.target.select(); handleFieldFocus(p.id, 'Initiative'); }}
onBlur={(e) => { if (e.target.value !== String(p.initiative)) handleInlineInitiative(p.id, e.target.value); handleFieldBlur(); }} onBlur={(e) => { if (e.target.value !== String(p.initiative)) handleInlineInitiative(p.id, e.target.value); handleFieldBlur(); }}
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur(); }} onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur(); }}
className="w-6 bg-transparent text-white text-sm text-center border-b border-transparent hover:border-stone-500 focus:border-amber-500 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" className="w-7 bg-transparent text-white text-base text-center border-b border-transparent hover:border-stone-500 focus:border-amber-500 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
aria-label={`Initiative for ${p.name}`} aria-label={`Initiative for ${p.name}`}
/> />
</span> </span>
@@ -1805,10 +1805,10 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
onFocus={(e) => { e.target.select(); handleFieldFocus(p.id, 'HP'); }} onFocus={(e) => { e.target.select(); handleFieldFocus(p.id, 'HP'); }}
onBlur={(e) => { if (e.target.value !== String(p.currentHp)) handleInlineCurrentHp(p.id, e.target.value); handleFieldBlur(); }} onBlur={(e) => { if (e.target.value !== String(p.currentHp)) handleInlineCurrentHp(p.id, e.target.value); handleFieldBlur(); }}
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur(); if (e.key === 'Escape') e.target.value = p.currentHp; }} onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur(); if (e.key === 'Escape') e.target.value = p.currentHp; }}
className="w-8 bg-transparent text-white text-sm text-center border-b border-transparent hover:border-stone-500 focus:border-red-500 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" className="w-10 bg-transparent text-white text-base text-center border-b border-transparent hover:border-stone-500 focus:border-red-500 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
aria-label={`Current HP for ${p.name}`} aria-label={`Current HP for ${p.name}`}
/> />
<span className="text-stone-500 text-sm">/</span> <span className="text-stone-500 text-base">/</span>
<input <input
type="number" type="number"
defaultValue={p.maxHp} defaultValue={p.maxHp}
@@ -1816,23 +1816,23 @@ function ParticipantManager({ encounter, encounterPath, campaignCharacters, camp
onFocus={(e) => { e.target.select(); handleFieldFocus(p.id, 'Max HP'); }} onFocus={(e) => { e.target.select(); handleFieldFocus(p.id, 'Max HP'); }}
onBlur={(e) => { if (e.target.value !== String(p.maxHp)) handleInlineMaxHp(p.id, e.target.value); handleFieldBlur(); }} onBlur={(e) => { if (e.target.value !== String(p.maxHp)) handleInlineMaxHp(p.id, e.target.value); handleFieldBlur(); }}
onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur(); if (e.key === 'Escape') e.target.value = p.maxHp; }} onKeyDown={(e) => { if (e.key === 'Enter') e.target.blur(); if (e.key === 'Escape') e.target.value = p.maxHp; }}
className="w-8 bg-transparent text-stone-400 text-sm text-center border-b border-transparent hover:border-stone-500 focus:border-red-500 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" className="w-10 bg-transparent text-stone-400 text-base text-center border-b border-transparent hover:border-stone-500 focus:border-red-500 focus:outline-none [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
aria-label={`Max HP for ${p.name}`} aria-label={`Max HP for ${p.name}`}
/> />
</span> </span>
</div> <span className="ml-auto inline-flex items-center gap-1">
{participantStatus === 'dead' && ( {participantStatus === 'dead' && (
<button onClick={() => handleRevive(p.id)} className="mt-2 inline-flex items-center gap-1 self-start px-2.5 py-1 text-xs rounded bg-emerald-800 hover:bg-emerald-700 text-white" title="Revive (clear dead status)"> <button onClick={() => handleRevive(p.id)} className="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-emerald-800 hover:bg-emerald-700 text-white" title="Revive (clear dead status)">
<HeartPulse size={14} /> Revive <HeartPulse size={12} /> Revive
</button> </button>
)} )}
{isGeneric && !isDead && ( {isGeneric && !isDead && (
<button onClick={() => handleMarkDead(p.id)} className="mt-2 inline-flex items-center gap-1 self-start px-2.5 py-1 text-xs rounded bg-red-900 hover:bg-red-800 text-white" title="Mark dead"> <button onClick={() => handleMarkDead(p.id)} className="inline-flex items-center gap-1 px-2 py-0.5 text-xs rounded bg-red-900 hover:bg-red-800 text-white" title="Mark dead">
<HeartCrack size={14} /> Mark Dead <HeartCrack size={12} /> Mark Dead
</button> </button>
)} )}
</span>
</div>
{hasDeathSaves && participantStatus === 'stable' && ( {hasDeathSaves && participantStatus === 'stable' && (
<div className="mt-2 text-xs text-emerald-300/80 italic">Stable regains 1 HP after 1d4 hours</div> <div className="mt-2 text-xs text-emerald-300/80 italic">Stable regains 1 HP after 1d4 hours</div>