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
2 changed files with 14 additions and 21 deletions
Showing only changes of commit 95db6fdfdc - Show all commits
-13
View File
@@ -50,19 +50,6 @@ lots of updates
- `isActive = id => updatedParticipants.find(p => p.id === id && p.isActive)` - `isActive = id => updatedParticipants.find(p => p.id === id && p.isActive)`
- Returns participant obj (truthy) not boolean. Works by accident, fragile. - Returns participant obj (truthy) not boolean. Works by accident, fragile.
### BUG: select campaign during active combat = screen flicker, no action
- In active encounter, click different campaign → flicker, no nav, no end.
- Either block (toast: end encounter first) or auto-end current + switch.
- Decide UX before fix.
### FEAT: generic/non-5e rules mode
- Campaign/encounter ruleset toggle: `5e` vs `generic`.
- Generic mode turns off death saves/state machine.
- Generic mode allows negative HP.
- 5e mode rejects negative damage/heal and clamps HP at 0.
### FEAT: clarify "Is NPC" in add-participant ### FEAT: clarify "Is NPC" in add-participant
- Ambiguous label. May expand work based on what NPC means here (ally? monster? - Ambiguous label. May expand work based on what NPC means here (ally? monster?
+14 -8
View File
@@ -5,7 +5,7 @@ import {
PlusCircle, Users, Swords, Trash2, Eye, Edit3, Save, XCircle, ChevronsUpDown, PlusCircle, Users, Swords, Trash2, Eye, Edit3, Save, XCircle, ChevronsUpDown,
UserCheck, UserX, HeartCrack, HeartPulse, Zap, EyeOff, ExternalLink, AlertTriangle, UserCheck, UserX, HeartCrack, HeartPulse, Zap, EyeOff, ExternalLink, AlertTriangle,
Play as PlayIcon, Pause as PauseIcon, SkipForward as SkipForwardIcon, Play as PlayIcon, Pause as PauseIcon, SkipForward as SkipForwardIcon,
StopCircle as StopCircleIcon, Users2, Dices, ChevronUp, ChevronDown, ScrollText, StopCircle as StopCircleIcon, Users2, Dices, ChevronDown, ScrollText,
Maximize2, Minimize2, Moon, Coffee, Clock, ChevronRight, X, Maximize2, Minimize2, Moon, Coffee, Clock, ChevronRight, X,
Undo2, Redo2, Crosshair Undo2, Redo2, Crosshair
} from 'lucide-react'; } from 'lucide-react';
@@ -682,7 +682,14 @@ function CharacterManager({ campaignId, campaignCharacters }) {
const [editingCharacter, setEditingCharacter] = useState(null); const [editingCharacter, setEditingCharacter] = useState(null);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [itemToDelete, setItemToDelete] = useState(null); const [itemToDelete, setItemToDelete] = useState(null);
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(() => {
try { return localStorage.getItem('ttrpg.charactersCollapsed') !== 'true'; }
catch { return true; }
});
useEffect(() => {
try { localStorage.setItem('ttrpg.charactersCollapsed', String(!isOpen)); }
catch {}
}, [isOpen]);
const handleAddCharacter = async () => { const handleAddCharacter = async () => {
if (!db || !characterName.trim() || !campaignId) return; if (!db || !characterName.trim() || !campaignId) return;
@@ -769,15 +776,14 @@ function CharacterManager({ campaignId, campaignCharacters }) {
<> <>
<div className="p-4 bg-stone-900 rounded-lg shadow"> <div className="p-4 bg-stone-900 rounded-lg shadow">
<div className="flex justify-between items-center mb-3"> <div className="flex justify-between items-center mb-3">
<h3 className="text-xl font-semibold text-amber-300 font-cinzel tracking-wide flex items-center">
<Users size={24} className="mr-2" /> Campaign Characters
</h3>
<button <button
onClick={() => setIsOpen(!isOpen)} onClick={() => setIsOpen(!isOpen)}
className="p-1 text-stone-400 hover:text-stone-200" className="flex items-center text-xl font-semibold text-amber-300 font-cinzel tracking-wide hover:text-amber-200 transition-colors"
aria-label={isOpen ? "Collapse" : "Expand"} aria-expanded={isOpen}
> >
{isOpen ? <ChevronUp size={20} /> : <ChevronDown size={20} />} {isOpen ? <ChevronDown size={20} className="mr-1" /> : <ChevronRight size={20} className="mr-1" />}
<Users size={24} className="mr-2" /> Campaign Characters
<span className="text-sm font-normal text-stone-400 ml-1">({campaignCharacters.length})</span>
</button> </button>
</div> </div>