Fix build error in SpireActiveSpells.tsx - resolve JSX parsing issue with template literals
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m45s

- Fixed unclosed template literal in className attribute
- Added missing imports (Mountain, ELEMENTS) in SpireGolems.tsx
- Build now passes successfully
This commit is contained in:
2026-05-08 15:28:13 +02:00
parent d1c90cd544
commit cad72fe88c
3 changed files with 71 additions and 27 deletions
+24 -12
View File
@@ -52,6 +52,28 @@ export function SpireActiveSpells({
const progress = spellState?.castProgress || 0;
const canCast = canCastSpell(spellId);
// Compute progress bar JSX
let progressBar = null;
if (currentAction === 'climb') {
const widthPercent = Math.min(100, progress * 100);
const elemColor = spellDef.elem === 'raw' ? '#60A5FA' : ELEMENTS[spellDef.elem]?.color || '#60A5FA';
const backgroundGradient = 'linear-gradient(90deg, ' + elemColor + '99, ' + elemColor + ')';
progressBar = (
<div className="space-y-0.5">
<div className="flex justify-between text-xs text-gray-500">
<span>Cast</span>
<span>{widthPercent.toFixed(0)}%</span>
</div>
<div className="h-1.5 bg-gray-700 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-300"
style={{ width: widthPercent + '%', background: backgroundGradient }}
/>
</div>
</div>
);
}
return (
<div key={`${spellId}-${equipmentId}`} className="p-2 rounded bg-gray-800/30 border border-gray-700">
<div className="flex items-center justify-between mb-1">
@@ -59,22 +81,12 @@ export function SpireActiveSpells({
{spellDef.name}
{spellDef.tier === 0 && <span className="ml-2 bg-gray-600 text-gray-200 text-xs px-1 rounded">Basic</span>}
</span>
<span className={`text-xs ${canCast ? 'text-green-400' : 'text-red-400'`}>{canCast ? '✓' : '✗'}</span>
<span className={`text-xs ${canCast ? 'text-green-400' : 'text-red-400'}`}>{canCast ? '✓' : '✗'}</span>
</div>
<div className="text-xs text-gray-400 mb-1">
{calcDamage({ skills, signedPacts, skillUpgrades, skillTiers }, spellId, floorElem)} dmg <span style={{ color: getSpellCostColor(spellDef.cost) }}>{formatSpellCost(spellDef.cost)}</span> {' '} {Math.floor(calcDamage({ skills, signedPacts, skillUpgrades, skillTiers }, spellId, floorElem) * (spellDef.castSpeed || 1))} dmg/hr
</div>
{currentAction === 'climb' && (
<div className="space-y-0.5">
<div className="flex justify-between text-xs text-gray-500">
<span>Cast</span>
<span>{(progress * 100).toFixed(0)}%</span>
</div>
<div className="h-1.5 bg-gray-700 rounded-full overflow-hidden">
<div className="h-full rounded-full transition-all duration-300" style={{ width: `${Math.min(100, progress * 100)}%`, background: spellDef.elem === 'raw' ? 'linear-gradient(90deg, #60A5FA99, #60A5FA)' : `linear-gradient(90deg, ${ELEMENTS[spellDef.elem]?.color}99, ${ELEMENTS[spellDef.elem]?.color})` }} />
</div>
</div>
)}
{progressBar}
</div>
);
})}
+3
View File
@@ -1,7 +1,9 @@
'use client';
import { Card, CardContent } from '@/components/ui/card';
import { Mountain } from 'lucide-react';
import { GOLEMS_DEF, getGolemDamage, getGolemAttackSpeed } from '@/lib/game/data/golems';
import { ELEMENTS } from '@/lib/game/constants';
interface SpireGolemsProps {
golemancy: {
@@ -10,6 +12,7 @@ interface SpireGolemsProps {
};
skills: Record<string, number>;
floorElem: string;
currentAction: string;
}
export function SpireGolems({ golemancy, skills, floorElem }: SpireGolemsProps) {
+44 -15
View File
@@ -4,7 +4,8 @@ import { useMemo } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import { Mountain, ChevronDown } from 'lucide-react';
import { ELEMENTS, GUARDIANS, SPELLS_DEF } from '@/lib/game/constants';
import type { ActivityLogEntry } from '@/lib/game/types';
import { ELEMENTS, GUARDIANS, SPELLS_DEF, SKILLS_DEF } from '@/lib/game/constants';
import { calcDamage, getActiveEquipmentSpells, getTotalDPS } from '@/lib/game/utils';
import { getUnifiedEffects } from '@/lib/game/effects';
import { formatSpellCost, getSpellCostColor } from '@/lib/game/formatting';
@@ -23,10 +24,20 @@ import { SpireActiveSpells } from './SpireActiveSpells';
import { SpireGolems } from './SpireGolems';
import { DebugName } from '@/lib/game/debug-context';
// Room type configurations
const ROOM_TYPE_CONFIG: Record<string, { label: string; icon: string; color: string }> = {
combat: { label: 'Combat', icon: '⚔️', color: '#EF4444' },
swarm: { label: 'Swarm', icon: '🐝', color: '#F59E0B' },
speed: { label: 'Speed', icon: '💨', color: '#3B82F6' },
guardian: { label: 'Guardian', icon: '🛡️', color: '#EF4444' },
puzzle: { label: 'Puzzle', icon: '🧩', color: '#8B5CF6' },
};
interface SpireTabProps {
simpleMode?: boolean;
}
// Check if player can enter spire mode
const canEnterSpireMode = (spireMode: boolean): boolean => {
return !spireMode;
};
@@ -72,14 +83,8 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
const currentGuardian = GUARDIANS[currentFloor];
const isFloorCleared = clearedFloors[currentFloor];
const roomType = currentRoom?.roomType || 'combat';
const roomConfig = {
combat: { label: 'Combat', icon: '⚔️', color: '#EF4444' },
swarm: { label: 'Swarm', icon: '🐝', color: '#F59E0B' },
speed: { label: 'Speed', icon: '💨', color: '#3B82F6' },
guardian: { label: 'Guardian', icon: '🛡️', color: '#EF4444' },
puzzle: { label: 'Puzzle', icon: '🧩', color: '#8B5CF6' },
}[roomType] || { label: 'Combat', icon: '⚔️', color: '#EF4444' };
const roomConfig = ROOM_TYPE_CONFIG[roomType] || ROOM_TYPE_CONFIG.combat;
const activeEquipmentSpells = useMemo(
() => getActiveEquipmentSpells(equippedInstances, equipmentInstances),
[equippedInstances, equipmentInstances]
@@ -107,12 +112,36 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
return canAffordSpellCost(spell.cost, rawMana, elements);
};
// Climb handlers - defined OUTSIDE of JSX
const handleClimbUp = () => {
useCombatStore.getState().startClimbUp();
};
const handleClimbDown = () => {
useCombatStore.getState().startClimbDown();
};
const handleClimb = (direction: 'up' | 'down') => {
if (direction === 'up') {
handleClimbUp();
} else {
handleClimbDown();
}
};
const getSkillName = (skillId: string): string => {
return SPELLS_DEF[skillId]?.name || skillId;
return SKILLS_DEF[skillId]?.name || skillId;
};
// Handle exit spire mode
const exitSpireMode = useCombatStore((s) => s.exitSpireMode);
const exitSpireMode = () => {
useCombatStore.getState().exitSpireMode();
};
// Handle enter spire mode
const enterSpireMode = () => {
useCombatStore.getState().enterSpireMode();
};
return (
<DebugName name="SpireTab">
@@ -124,7 +153,7 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
<Button
className="w-full bg-gradient-to-r from-amber-600 to-orange-600 hover:from-amber-700 hover:to-orange-700"
size="lg"
onClick={useCombatStore.getState().enterSpireMode}
onClick={enterSpireMode}
disabled={!canEnterSpireMode(spireMode)}
>
<Mountain className="w-5 h-5 mr-2" />
@@ -194,7 +223,7 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
skills={skills}
currentAction={currentAction}
/>
))}
)}
{/* Guardian Panel */}
{isGuardianFloor && simpleMode && (
@@ -210,7 +239,7 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
roomType={roomType}
roomConfig={roomConfig}
primaryEnemy={currentRoom?.enemies?.[0] || null}
swarmEnemies={roomType === 'swarm' ? currentRoom?.enemies || [] : []}
swarmEnemies={roomType === 'swarm' ? (currentRoom?.enemies || []) : []}
puzzleId={currentRoom?.puzzleId}
puzzleProgress={currentRoom?.puzzleProgress}
simpleMode={true}
@@ -248,7 +277,7 @@ export function SpireTab({ simpleMode = false }: SpireTabProps) {
calcDamage={calcDamage}
SPELLS_DEF={SPELLS_DEF}
canCastSpell={canCastSpell}
handleClimb={(dir) => dir === 'up' ? useCombatStore.getState().startClimbUp() : useCombatStore.getState().startClimbDown()}
handleClimb={handleClimb}
formatSpellCost={formatSpellCost}
getSpellCostColor={getSpellCostColor}
/>