Hide concentration UI in PF2e mode
All checks were successful
CI / check (push) Successful in 2m26s
CI / build-image (push) Successful in 18s

PF2e uses action-based spell sustaining, not damage-triggered
concentration checks. The Brain icon, purple border accent, and
damage pulse animation are now hidden when PF2e is active, and
the freed gutter column is reclaimed for row content. Concentration
state is preserved so switching back to D&D restores it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-04-09 00:25:54 +02:00
parent f721d7e5da
commit 48795071f7
2 changed files with 44 additions and 19 deletions

View File

@@ -10,6 +10,7 @@ import { Brain, Pencil, X } from "lucide-react";
import { type Ref, useCallback, useEffect, useRef, useState } from "react";
import { useEncounterContext } from "../contexts/encounter-context.js";
import { useInitiativeRollsContext } from "../contexts/initiative-rolls-context.js";
import { useRulesEditionContext } from "../contexts/rules-edition-context.js";
import { useSidePanelContext } from "../contexts/side-panel-context.js";
import { useLongPress } from "../hooks/use-long-press.js";
import { cn } from "../lib/utils.js";
@@ -415,12 +416,14 @@ function InitiativeDisplay({
function rowBorderClass(
isActive: boolean,
isConcentrating: boolean | undefined,
isPf2e: boolean,
): string {
if (isActive && isConcentrating)
const showConcentration = isConcentrating && !isPf2e;
if (isActive && showConcentration)
return "border border-l-2 border-active-row-border border-l-purple-400 bg-active-row-bg card-glow";
if (isActive)
return "border border-l-2 border-active-row-border bg-active-row-bg card-glow";
if (isConcentrating)
if (showConcentration)
return "border border-l-2 border-transparent border-l-purple-400";
return "border border-l-2 border-transparent";
}
@@ -455,6 +458,8 @@ export function CombatantRow({
const { selectedCreatureId, showCreature, toggleCollapse } =
useSidePanelContext();
const { handleRollInitiative } = useInitiativeRollsContext();
const { edition } = useRulesEditionContext();
const isPf2e = edition === "pf2e";
// Derive what was previously conditional props
const isStatBlockOpen = combatant.creatureId === selectedCreatureId;
@@ -495,12 +500,16 @@ export function CombatantRow({
const tempHpDropped =
prevTempHp !== undefined && (combatant.tempHp ?? 0) < prevTempHp;
if ((realHpDropped || tempHpDropped) && combatant.isConcentrating) {
if (
(realHpDropped || tempHpDropped) &&
combatant.isConcentrating &&
!isPf2e
) {
setIsPulsing(true);
clearTimeout(pulseTimerRef.current);
pulseTimerRef.current = setTimeout(() => setIsPulsing(false), 1200);
}
}, [currentHp, combatant.tempHp, combatant.isConcentrating]);
}, [currentHp, combatant.tempHp, combatant.isConcentrating, isPf2e]);
useEffect(() => {
if (!combatant.isConcentrating) {
@@ -518,24 +527,33 @@ export function CombatantRow({
ref={ref}
className={cn(
"group rounded-lg pr-3 transition-colors",
rowBorderClass(isActive, combatant.isConcentrating),
rowBorderClass(isActive, combatant.isConcentrating, isPf2e),
isPulsing && "animate-concentration-pulse",
)}
>
<div className="grid grid-cols-[2rem_3rem_auto_1fr_auto_2rem] items-center gap-1.5 py-3 sm:grid-cols-[2rem_3.5rem_auto_1fr_auto_2rem] sm:gap-3 sm:py-2">
{/* Concentration */}
<button
type="button"
onClick={() => toggleConcentration(id)}
title="Concentrating"
aria-label="Toggle concentration"
className={cn(
"-my-2 -ml-[2px] flex w-full items-center justify-center self-stretch pl-[14px] transition-opacity hover:text-hover-neutral hover:opacity-100",
concentrationIconClass(combatant.isConcentrating, dimmed),
)}
>
<Brain size={16} />
</button>
<div
className={cn(
"grid items-center gap-1.5 py-3 sm:gap-3 sm:py-2",
isPf2e
? "grid-cols-[3rem_auto_1fr_auto_2rem] pl-3 sm:grid-cols-[3.5rem_auto_1fr_auto_2rem]"
: "grid-cols-[2rem_3rem_auto_1fr_auto_2rem] sm:grid-cols-[2rem_3.5rem_auto_1fr_auto_2rem]",
)}
>
{/* Concentration — hidden in PF2e mode */}
{!isPf2e && (
<button
type="button"
onClick={() => toggleConcentration(id)}
title="Concentrating"
aria-label="Toggle concentration"
className={cn(
"-my-2 -ml-[2px] flex w-full items-center justify-center self-stretch pl-[14px] transition-opacity hover:text-hover-neutral hover:opacity-100",
concentrationIconClass(combatant.isConcentrating, dimmed),
)}
>
<Brain size={16} />
</button>
)}
{/* Initiative */}
<div className="rounded-md bg-muted/30 px-1">