fix: persist enchantment design state across tab navigation
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m2s

- EnchantmentDesigner now reads selection state directly from useCraftingStore
  instead of receiving it as props, so state survives tab unmount/remount
- EnchanterSubTab no longer uses local useState for selectedEquipmentType,
  selectedEffects, designName, selectedDesign — all sourced from store
- Removed unused EnchantmentDesignerProps interface from types
- All 1158 existing tests pass, no new type errors introduced

Fixes #366
This commit is contained in:
2026-06-11 11:54:45 +02:00
parent 9476e92a4b
commit 1dce061cdd
5 changed files with 40 additions and 50 deletions
+6 -5
View File
@@ -1,10 +1,11 @@
# Circular Dependencies # Circular Dependencies
Generated: 2026-06-11T07:08:59.875Z Generated: 2026-06-11T09:37:23.110Z
Found: 3 circular chain(s) — these MUST be fixed before modifying involved files. Found: 4 circular chain(s) — these MUST be fixed before modifying involved files.
1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts 1. 1) data/guardian-encounters.ts > data/guardian-procedural.ts
2. 2) stores/attunementStore.ts > stores/combatStore.ts > stores/combat-descent-actions.ts 2. 2) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts
3. 3) stores/attunementStore.ts > stores/combatStore.ts > stores/combat-descent-actions.ts > stores/non-combat-room-actions.ts 3. 3) stores/attunementStore.ts > stores/combatStore.ts > stores/combat-descent-actions.ts
4. 4) stores/attunementStore.ts > stores/combatStore.ts > stores/combat-descent-actions.ts > stores/non-combat-room-actions.ts
## How to fix ## How to fix
1. Identify which import in the chain can be extracted to a shared types/utils file. 1. Identify which import in the chain can be extracted to a shared types/utils file.
+9 -2
View File
@@ -1,6 +1,6 @@
{ {
"_meta": { "_meta": {
"generated": "2026-06-11T07:08:57.908Z", "generated": "2026-06-11T09:37:21.146Z",
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
}, },
@@ -492,6 +492,11 @@
], ],
"data/guardian-encounters.ts": [ "data/guardian-encounters.ts": [
"data/guardian-data.ts", "data/guardian-data.ts",
"data/guardian-procedural.ts",
"types.ts"
],
"data/guardian-procedural.ts": [
"data/guardian-encounters.ts",
"types.ts", "types.ts",
"utils/guardian-utils.ts" "utils/guardian-utils.ts"
], ],
@@ -532,6 +537,7 @@
"effects/upgrade-effects.ts", "effects/upgrade-effects.ts",
"stores/attunementStore.ts", "stores/attunementStore.ts",
"stores/combatStore.ts", "stores/combatStore.ts",
"stores/craftingStore.ts",
"stores/gameStore.ts", "stores/gameStore.ts",
"stores/manaStore.ts", "stores/manaStore.ts",
"stores/prestigeStore.ts", "stores/prestigeStore.ts",
@@ -613,6 +619,8 @@
"crafting-design.ts", "crafting-design.ts",
"crafting-utils.ts", "crafting-utils.ts",
"effects/discipline-effects.ts", "effects/discipline-effects.ts",
"effects/special-effects.ts",
"effects/upgrade-effects.ts",
"stores/combatStore.ts", "stores/combatStore.ts",
"stores/crafting-equipment-tick.ts", "stores/crafting-equipment-tick.ts",
"stores/crafting-initial-state.ts", "stores/crafting-initial-state.ts",
@@ -790,7 +798,6 @@
"crafting-prep.ts", "crafting-prep.ts",
"effects/discipline-effects.ts", "effects/discipline-effects.ts",
"effects/upgrade-effects.types.ts", "effects/upgrade-effects.types.ts",
"stores/craftingStore.ts",
"stores/tick-pipeline.ts" "stores/tick-pipeline.ts"
], ],
"stores/pipelines/equipment-crafting.ts": [ "stores/pipelines/equipment-crafting.ts": [
@@ -2,8 +2,7 @@
import { GameCard } from '@/components/ui/game-card'; import { GameCard } from '@/components/ui/game-card';
import { Separator } from '@/components/ui/separator'; import { Separator } from '@/components/ui/separator';
import type { EquipmentInstance, EnchantmentDesign, DesignEffect, EquipmentCraftingProgress, EquipmentCategory } from '@/lib/game/types'; import type { DesignEffect, EquipmentCategory } from '@/lib/game/types';
import type { EnchantmentDesignerProps } from './EnchantmentDesigner/types';
import { EquipmentTypeSelector } from './EnchantmentDesigner/EquipmentTypeSelector'; import { EquipmentTypeSelector } from './EnchantmentDesigner/EquipmentTypeSelector';
import { EffectSelector } from './EnchantmentDesigner/EffectSelector'; import { EffectSelector } from './EnchantmentDesigner/EffectSelector';
import { SavedDesigns } from './EnchantmentDesigner/SavedDesigns'; import { SavedDesigns } from './EnchantmentDesigner/SavedDesigns';
@@ -22,20 +21,21 @@ import {
import { useCraftingStore, useAttunementStore } from '@/lib/game/stores'; import { useCraftingStore, useAttunementStore } from '@/lib/game/stores';
import { DebugName } from '@/components/game/debug/debug-context'; import { DebugName } from '@/components/game/debug/debug-context';
export function EnchantmentDesigner({ export function EnchantmentDesigner() {
selectedEquipmentType,
setSelectedEquipmentType,
selectedEffects,
setSelectedEffects,
designName,
setDesignName,
selectedDesign,
setSelectedDesign,
}: EnchantmentDesignerProps) {
// Attunement store — get Enchanter level for effect selector gating // Attunement store — get Enchanter level for effect selector gating
const enchanterLevel = useAttunementStore((s) => s.attunements?.enchanter?.level ?? 0); const enchanterLevel = useAttunementStore((s) => s.attunements?.enchanter?.level ?? 0);
// Crafting store selectors // Crafting store selectors — persisted selection state
const selectedEquipmentType = useCraftingStore((s) => s.enchantmentSelection.selectedEquipmentType);
const setSelectedEquipmentType = useCraftingStore((s) => s.setSelectedEquipmentType);
const selectedEffects = useCraftingStore((s) => s.enchantmentSelection.selectedEffects);
const setSelectedEffects = useCraftingStore((s) => s.setSelectedEffects);
const designName = useCraftingStore((s) => s.enchantmentSelection.designName);
const setDesignName = useCraftingStore((s) => s.setDesignName);
const selectedDesign = useCraftingStore((s) => s.enchantmentSelection.selectedDesign);
const setSelectedDesign = useCraftingStore((s) => s.setSelectedDesign);
// Crafting store — other state
const enchantmentDesigns = useCraftingStore((s) => s.enchantmentDesigns); const enchantmentDesigns = useCraftingStore((s) => s.enchantmentDesigns);
const designProgress = useCraftingStore((s) => s.designProgress); const designProgress = useCraftingStore((s) => s.designProgress);
const startDesigningEnchantment = useCraftingStore((s) => s.startDesigningEnchantment); const startDesigningEnchantment = useCraftingStore((s) => s.startDesigningEnchantment);
@@ -1,15 +1,4 @@
import type { EquipmentInstance, EnchantmentDesign, DesignEffect, DesignProgress, EquipmentCategory } from '@/lib/game/types'; import type { EnchantmentDesign, DesignEffect, DesignProgress, EquipmentCategory } from '@/lib/game/types';
export interface EnchantmentDesignerProps {
selectedEquipmentType: string | null;
setSelectedEquipmentType: (type: string | null) => void;
selectedEffects: DesignEffect[];
setSelectedEffects: (effects: DesignEffect[]) => void;
designName: string;
setDesignName: (name: string) => void;
selectedDesign: string | null;
setSelectedDesign: (id: string | null) => void;
}
export interface EquipmentTypeSelectorProps { export interface EquipmentTypeSelectorProps {
ownedEquipmentTypes: Array<{ id: string; name: string; baseCapacity: number }>; ownedEquipmentTypes: Array<{ id: string; name: string; baseCapacity: number }>;
@@ -9,7 +9,6 @@ import {
EnchantmentApplier, EnchantmentApplier,
} from '@/components/game/crafting'; } from '@/components/game/crafting';
import { useCraftingStore } from '@/lib/game/stores'; import { useCraftingStore } from '@/lib/game/stores';
import type { DesignEffect } from '@/lib/game/types';
import { DebugName } from '@/components/game/debug/debug-context'; import { DebugName } from '@/components/game/debug/debug-context';
type EnchanterPhase = 'design' | 'prepare' | 'apply'; type EnchanterPhase = 'design' | 'prepare' | 'apply';
@@ -23,14 +22,17 @@ const PHASES: { key: EnchanterPhase; label: string; icon: typeof PenLine }[] = [
export function EnchanterSubTab() { export function EnchanterSubTab() {
const [activePhase, setActivePhase] = useState<EnchanterPhase>('design'); const [activePhase, setActivePhase] = useState<EnchanterPhase>('design');
// Shared state for the enchantment flow
const [selectedEquipmentType, setSelectedEquipmentType] = useState<string | null>(null);
const [selectedEffects, setSelectedEffects] = useState<DesignEffect[]>([]);
const [designName, setDesignName] = useState('');
const [selectedDesign, setSelectedDesign] = useState<string | null>(null);
const [selectedEquipmentInstance, setSelectedEquipmentInstance] = useState<string | null>(null);
const resetEnchantmentSelection = useCraftingStore((s) => s.resetEnchantmentSelection); const resetEnchantmentSelection = useCraftingStore((s) => s.resetEnchantmentSelection);
const setSelectedEquipmentInstance = useCraftingStore((s) => s.setSelectedEquipmentInstance);
const setSelectedDesign = useCraftingStore((s) => s.setSelectedDesign);
// Read persisted selection state from the store
const selectedEquipmentInstance = useCraftingStore(
(s) => s.enchantmentSelection.selectedEquipmentInstance,
);
const selectedDesign = useCraftingStore(
(s) => s.enchantmentSelection.selectedDesign,
);
const handlePhaseChange = (phase: EnchanterPhase) => { const handlePhaseChange = (phase: EnchanterPhase) => {
setActivePhase(phase); setActivePhase(phase);
@@ -67,16 +69,7 @@ export function EnchanterSubTab() {
{/* Phase content */} {/* Phase content */}
{activePhase === 'design' && ( {activePhase === 'design' && (
<EnchantmentDesigner <EnchantmentDesigner />
selectedEquipmentType={selectedEquipmentType}
setSelectedEquipmentType={setSelectedEquipmentType}
selectedEffects={selectedEffects}
setSelectedEffects={setSelectedEffects}
designName={designName}
setDesignName={setDesignName}
selectedDesign={selectedDesign}
setSelectedDesign={setSelectedDesign}
/>
)} )}
{activePhase === 'prepare' && ( {activePhase === 'prepare' && (