From fef57d7a5555ad63e9ac39f2170147344d8adee0 Mon Sep 17 00:00:00 2001
From: Refactoring Agent <[email protected]>
Date: Sun, 3 May 2026 12:26:30 +0200
Subject: [PATCH] fix: resolve runtime issues with game loop, tabs, and error
handling
- Fix game loop store mismatch (page.tsx was using old store, gameHooks using new store)
- Fix StatsTab.tsx calling getStudySpeedMultiplier with wrong arguments
- Add ErrorBoundary to page.tsx for better error handling
- Fix import syntax issues in page.tsx
- Ensure build succeeds with fixes
---
docs/project-structure.txt | 63 +++---
src/app/page.tsx | 313 ++++++++++++++++----------
src/components/ErrorBoundary.tsx | 38 ++++
src/components/game/tabs/StatsTab.tsx | 4 +-
4 files changed, 268 insertions(+), 150 deletions(-)
create mode 100644 src/components/ErrorBoundary.tsx
diff --git a/docs/project-structure.txt b/docs/project-structure.txt
index cc9eb89..2a518b8 100644
--- a/docs/project-structure.txt
+++ b/docs/project-structure.txt
@@ -179,37 +179,38 @@ Mana-Loop/
│ │ │ ├── UpgradeDialog.tsx
│ │ │ ├── index.ts
│ │ │ └── types.ts
-│ │ └── ui/
-│ │ ├── action-button.tsx
-│ │ ├── alert-dialog.tsx
-│ │ ├── badge.tsx
-│ │ ├── button.tsx
-│ │ ├── card.tsx
-│ │ ├── dialog.tsx
-│ │ ├── element-badge.tsx
-│ │ ├── game-card.tsx
-│ │ ├── index.ts
-│ │ ├── input.tsx
-│ │ ├── label.tsx
-│ │ ├── mana-bar.tsx
-│ │ ├── progress.tsx
-│ │ ├── scroll-area.tsx
-│ │ ├── section-header.tsx
-│ │ ├── select.tsx
-│ │ ├── separator.tsx
-│ │ ├── sheet.tsx
-│ │ ├── skeleton.tsx
-│ │ ├── skill-row.tsx
-│ │ ├── stat-row.tsx
-│ │ ├── stepper.tsx
-│ │ ├── switch.tsx
-│ │ ├── tabs.tsx
-│ │ ├── toast.tsx
-│ │ ├── toaster.tsx
-│ │ ├── toggle.tsx
-│ │ ├── tooltip-info.tsx
-│ │ ├── tooltip.tsx
-│ │ └── value-display.tsx
+│ │ ├── ui/
+│ │ │ ├── action-button.tsx
+│ │ │ ├── alert-dialog.tsx
+│ │ │ ├── badge.tsx
+│ │ │ ├── button.tsx
+│ │ │ ├── card.tsx
+│ │ │ ├── dialog.tsx
+│ │ │ ├── element-badge.tsx
+│ │ │ ├── game-card.tsx
+│ │ │ ├── index.ts
+│ │ │ ├── input.tsx
+│ │ │ ├── label.tsx
+│ │ │ ├── mana-bar.tsx
+│ │ │ ├── progress.tsx
+│ │ │ ├── scroll-area.tsx
+│ │ │ ├── section-header.tsx
+│ │ │ ├── select.tsx
+│ │ │ ├── separator.tsx
+│ │ │ ├── sheet.tsx
+│ │ │ ├── skeleton.tsx
+│ │ │ ├── skill-row.tsx
+│ │ │ ├── stat-row.tsx
+│ │ │ ├── stepper.tsx
+│ │ │ ├── switch.tsx
+│ │ │ ├── tabs.tsx
+│ │ │ ├── toast.tsx
+│ │ │ ├── toaster.tsx
+│ │ │ ├── toggle.tsx
+│ │ │ ├── tooltip-info.tsx
+│ │ │ ├── tooltip.tsx
+│ │ │ └── value-display.tsx
+│ │ └── ErrorBoundary.tsx
│ ├── hooks/
│ │ ├── use-mobile.ts
│ │ └── use-toast.ts
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 3e475b6..46b28b0 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -2,10 +2,31 @@
import { useEffect, useState, lazy, Suspense } from 'react';
import type { JSX } from 'react';
-import { useGameStore, fmt, computeMaxMana, computeRegen, computeClickMana, getMeditationBonus, getIncursionStrength, canAffordSpellCost } from '@/lib/game/store';
-import { getUnifiedEffects } from '@/lib/game/effects';
+
+// Import from new modular stores
+import {
+ useGameStore,
+ useUIStore,
+ useManaStore,
+ useSkillStore,
+ useCombatStore,
+ usePrestigeStore,
+ fmt,
+ computeMaxMana,
+ computeRegen,
+ computeClickMana,
+ getMeditationBonus,
+ getIncursionStrength,
+} from '@/lib/game/stores';
import { useGameLoop } from '@/lib/game/stores/gameHooks';
-import { ELEMENTS, GUARDIANS, SPELLS_DEF, PRESTIGE_DEF, getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants';
+import { getUnifiedEffects } from '@/lib/game/effects';
+import {
+ getStudySpeedMultiplier,
+ getStudyCostMultiplier,
+ SPELLS_DEF,
+ ELEMENTS,
+ GUARDIANS,
+} from '@/lib/game/constants';
import { getActiveEquipmentSpells, getTotalDPS } from '@/lib/game/computed-stats';
import { TimeDisplay } from '@/components/game';
import { hasSpecial, SPECIAL_EFFECTS } from '@/lib/game/effects';
@@ -17,7 +38,7 @@ import { Badge } from '@/components/ui/badge';
import { ScrollArea } from '@/components/ui/scroll-area';
import { RotateCcw, Mountain } from 'lucide-react';
import { TooltipProvider } from '@/components/ui/tooltip';
-import { DebugName } from '@/lib/game/debug-context';
+import { ErrorBoundary } from '@/components/ErrorBoundary';
// Import extracted components
import { GameOverScreen } from './components/GameOverScreen';
@@ -86,22 +107,72 @@ function GrimoireTab() {
// ============================================================================
export default function ManaLoopGame() {
+ // Disable prerendering - this is a client-only game
+ if (typeof window === 'undefined') {
+ return
Loading...
;
+ }
+
const [selectedManaType, setSelectedManaType] = useState('');
const [activeTab, setActiveTab] = useState('spire');
- // Game store
- const store: any = useGameStore();
+ // Game stores - using new modular stores
+ const day = useGameStore((s) => s.day);
+ const hour = useGameStore((s) => s.hour);
+ const initGame = useGameStore((s) => s.initGame);
+
const gameLoop = useGameLoop();
+ // Get state from modular stores for computed values
+ const skills = useSkillStore((s) => s.skills);
+ const skillTiers = useSkillStore((s) => s.skillTiers);
+ const skillUpgrades = useSkillStore((s) => s.skillUpgrades);
+ const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades);
+ const meditateTicks = useManaStore((s) => s.meditateTicks);
+ const rawMana = useManaStore((s) => s.rawMana);
+ const totalManaGathered = useManaStore((s) => s.totalManaGathered);
+ const elements = useManaStore((s) => s.elements);
+
+ const maxFloorReached = useCombatStore((s) => s.maxFloorReached);
+ const signedPacts = useCombatStore((s) => s.signedPacts);
+ const spells = useCombatStore((s) => s.spells);
+
+ const loopCount = usePrestigeStore((s) => s.loopCount);
+ const insight = usePrestigeStore((s) => s.insight);
+ const totalInsight = usePrestigeStore((s) => s.totalInsight);
+ const memorySlots = usePrestigeStore((s) => s.memorySlots);
+
// Computed effects from upgrades and equipment
- const upgradeEffects = getUnifiedEffects(store);
+ const upgradeEffects = getUnifiedEffects({
+ skillUpgrades,
+ skillTiers,
+ equippedInstances: {},
+ equipmentInstances: {}
+ });
// Derived stats
- const maxMana = computeMaxMana(store, upgradeEffects);
- const baseRegen = computeRegen(store, upgradeEffects);
- const clickMana = computeClickMana(store);
- const meditationMultiplier = getMeditationBonus(store.meditateTicks, store.skills, upgradeEffects.meditationEfficiency);
- const incursionStrength = getIncursionStrength(store.day, store.hour);
+ const maxMana = computeMaxMana({
+ skills,
+ prestigeUpgrades,
+ skillUpgrades,
+ skillTiers
+ }, upgradeEffects);
+
+ const baseRegen = computeRegen({
+ skills,
+ prestigeUpgrades,
+ skillUpgrades,
+ skillTiers
+ }, upgradeEffects);
+
+ const clickMana = computeClickMana({
+ skills,
+ prestigeUpgrades,
+ skillUpgrades,
+ skillTiers
+ });
+
+ const meditationMultiplier = getMeditationBonus(meditateTicks, skills, upgradeEffects.meditationEfficiency);
+ const incursionStrength = getIncursionStrength(day, hour);
// Effective regen with incursion penalty
const effectiveRegenWithSpecials = baseRegen * (1 - incursionStrength);
@@ -119,15 +190,17 @@ export default function ManaLoopGame() {
// Effective regen
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
- // Get all active spells from equipment
- const activeEquipmentSpells = getActiveEquipmentSpells(store.equippedInstances, store.equipmentInstances);
-
- // Compute total DPS
- const totalDPS = getTotalDPS(store, upgradeEffects as any);
+ // Game Over check from UI store
+ const gameOver = useUIStore((s) => s.gameOver);
+
+ // Initialize game on mount
+ useEffect(() => {
+ initGame();
+ }, [initGame]);
// Game Over Screen
- if (store.gameOver) {
- return ;
+ if (gameOver) {
+ return ;
}
// Start game loop
@@ -137,119 +210,125 @@ export default function ManaLoopGame() {
}, [gameLoop]);
return (
-
-
- {/* Header */}
-
-
-
MANA LOOP
-
-
+
+
+
-
+
- {/* Main Content */}
-
-
+ {/* Main Content */}
+
+
-
-
-
- ⚔️ Spire
- ✨ Attune
- 🗿 Golems
- 📚 Skills
- 🔮 Spells
- 🛡️ Gear
- 🔧 Craft
- 💎 Loot
- 🏆 Achieve
- 🔬 Lab
- 📊 Stats
- 🐛 Debug
- 📖 Grimoire
-
+
+
+
+ ⚔️ Spire
+ ✨ Attune
+ 🗿 Golems
+ 📚 Skills
+ 🔮 Spells
+ 🛡️ Gear
+ 🔧 Craft
+ 💎 Loot
+ 🏆 Achieve
+ 🔬 Lab
+ 📊 Stats
+ 🐛 Debug
+ 📖 Grimoire
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
- }>
-
-
-
+
+ }>
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx
new file mode 100644
index 0000000..fd05542
--- /dev/null
+++ b/src/components/ErrorBoundary.tsx
@@ -0,0 +1,38 @@
+'use client';
+
+import { Component, ReactNode } from 'react';
+
+interface ErrorBoundaryProps {
+ children: ReactNode;
+ fallback?: ReactNode;
+}
+
+interface ErrorBoundaryState {
+ hasError: boolean;
+ error?: Error;
+}
+
+export class ErrorBoundary extends Component {
+ constructor(props: ErrorBoundaryProps) {
+ super(props);
+ this.state = { hasError: false };
+ }
+
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
+ return { hasError: true, error };
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return this.props.fallback || (
+
+
Something went wrong:
+
{this.state.error?.message}
+
{this.state.error?.stack}
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
diff --git a/src/components/game/tabs/StatsTab.tsx b/src/components/game/tabs/StatsTab.tsx
index 17903c6..f3ea003 100644
--- a/src/components/game/tabs/StatsTab.tsx
+++ b/src/components/game/tabs/StatsTab.tsx
@@ -95,8 +95,8 @@ export function StatsTab() {
const effectiveRegen = (effectiveRegenWithSpecials + manaCascadeBonus + manaWaterfallBonus) * meditationMultiplier;
// Get study speed/cost multipliers
- const studySpeedMult = getStudySpeedMultiplier(skills, skillUpgrades, skillTiers);
- const studyCostMult = getStudyCostMultiplier(skills, skillUpgrades, skillTiers);
+ const studySpeedMult = getStudySpeedMultiplier(skills);
+ const studyCostMult = getStudyCostMultiplier(skills);
// Check special effects
const hasManaWaterfall = hasSpecial(upgradeEffects, SPECIAL_EFFECTS.MANA_WATERFALL);