Upgrade Biome to 2.4.7 and enable 54 additional lint rules

Add rules covering bug prevention (noLeakedRender, noFloatingPromises,
noImportCycles, noReactForwardRef), security (noScriptUrl, noAlert),
performance (noAwaitInLoops, useTopLevelRegex), and code style
(noNestedTernary, useGlobalThis, useNullishCoalescing, useSortedClasses,
plus ~40 more). Fix all violations: extract top-level regex constants,
guard React && renders with boolean coercion, refactor nested ternaries,
replace window with globalThis, sort Tailwind classes, and introduce
expectDomainError test helper to eliminate conditional expects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-14 14:25:09 +01:00
parent 473f1eaefe
commit 36768d3aa1
54 changed files with 428 additions and 441 deletions

View File

@@ -55,11 +55,10 @@ function useActionBarAnimation(combatantCount: number) {
const empty = combatantCount === 0;
const risingClass = rising ? " animate-rise-to-center" : "";
const settlingClass = settling ? " animate-settle-to-bottom" : "";
const topBarClass = settling
? " animate-slide-down-in"
: topBarExiting
? " absolute inset-x-0 top-0 z-10 px-4 animate-slide-up-out"
: "";
const exitingClass = topBarExiting
? " absolute inset-x-0 top-0 z-10 px-4 animate-slide-up-out"
: "";
const topBarClass = settling ? " animate-slide-down-in" : exitingClass;
const showTopBar = !empty || topBarExiting;
return {
@@ -194,7 +193,7 @@ export function App() {
block: "nearest",
behavior: "smooth",
});
}, [encounter.activeIndex]);
}, []);
// Auto-show stat block for the active combatant when turn changes,
// but only when the viewport is wide enough to show it alongside the tracker.
@@ -203,7 +202,7 @@ export function App() {
useEffect(() => {
if (prevActiveIndexRef.current === encounter.activeIndex) return;
prevActiveIndexRef.current = encounter.activeIndex;
if (!window.matchMedia("(min-width: 1024px)").matches) return;
if (!globalThis.matchMedia("(min-width: 1024px)").matches) return;
const active = encounter.combatants[encounter.activeIndex];
if (!active?.creatureId || !isLoaded) return;
sidePanel.showCreature(active.creatureId as CreatureId);
@@ -216,8 +215,8 @@ export function App() {
return (
<div className="flex h-screen flex-col">
<div className="relative mx-auto flex w-full max-w-2xl flex-1 flex-col gap-3 px-4 min-h-0">
{actionBarAnim.showTopBar && (
<div className="relative mx-auto flex min-h-0 w-full max-w-2xl flex-1 flex-col gap-3 px-4">
{!!actionBarAnim.showTopBar && (
<div
className={`shrink-0 pt-8${actionBarAnim.topBarClass}`}
onAnimationEnd={actionBarAnim.onTopBarExitEnd}
@@ -233,7 +232,7 @@ export function App() {
{isEmpty ? (
/* Empty state — ActionBar centered */
<div className="flex flex-1 items-center justify-center min-h-0 pb-[15%] pt-8">
<div className="flex min-h-0 flex-1 items-center justify-center pt-8 pb-[15%]">
<div
className={`w-full${actionBarAnim.risingClass}`}
onAnimationEnd={actionBarAnim.onRiseEnd}
@@ -263,7 +262,7 @@ export function App() {
) : (
<>
{/* Scrollable area — combatant list */}
<div className="flex-1 overflow-y-auto min-h-0">
<div className="min-h-0 flex-1 overflow-y-auto">
<div className="flex flex-col px-2 py-2">
{encounter.combatants.map((c, i) => (
<CombatantRow
@@ -322,7 +321,7 @@ export function App() {
</div>
{/* Pinned Stat Block Panel (left) */}
{sidePanel.pinnedCreatureId && sidePanel.isWideDesktop && (
{!!sidePanel.pinnedCreatureId && sidePanel.isWideDesktop && (
<StatBlockPanel
creatureId={sidePanel.pinnedCreatureId}
creature={pinnedCreature}