Update hooks and ignore markdown files in size check
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 3m16s

This commit is contained in:
Refactoring Agent
2026-04-29 12:18:08 +02:00
parent 88d6016557
commit 454195cdfb
13 changed files with 1064 additions and 68 deletions
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
if echo "$changed_files" | grep --quiet -E "package.json|package-lock.json"; then
echo "📦 Dependencies changed. Syncing..."
# --no-progress stops the terminal spam
# --loglevel error ensures we only see the bad stuff
if npm install --no-progress --loglevel error; then
echo "✅ Node modules are up to date."
else
echo "❌ npm install failed! Please check your connection or package.json."
exit 1
fi
fi
+26
View File
@@ -0,0 +1,26 @@
#!/bin/sh
echo "🔍 Running pre-commit checks..."
# Get staged files (added, copied, modified)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -n "$STAGED_FILES" ]; then
echo "📏 Checking file sizes..."
node .husky/scripts/check-file-size.js $STAGED_FILES
if [ $? -ne 0 ]; then
exit 1
fi
fi
# Generate project structure
echo "🗺️ Updating project structure..."
node .husky/scripts/generate-project-tree.js
if [ $? -ne 0 ]; then
exit 1
fi
# Auto-add the generated project structure to the commit
git add docs/project-structure.txt
echo "✅ All pre-commit checks passed!"
+61
View File
@@ -0,0 +1,61 @@
const fs = require('fs');
const path = require('path');
const MAX_LINES = 400;
// List of file patterns to ignore (optional, can be customized)
const IGNORE_PATTERNS = [
/\.lock$/, // Lock files
/\.min\.js$/, // Minified files
/\.map$/, // Source maps
/package-lock\.json$/,
/bun\.lock$/,
/tsconfig\.tsbuildinfo$/,
/\.md$/, // Markdown documentation files
/context\.md$/, // Context files for sub-agents
/project-structure\.txt$/, // Generated project structure
];
function shouldIgnore(filePath) {
return IGNORE_PATTERNS.some(pattern => pattern.test(filePath));
}
const files = process.argv.slice(2);
if (files.length === 0) {
console.log('️ No files to check');
process.exit(0);
}
let hasError = false;
files.forEach(file => {
// Skip ignored patterns
if (shouldIgnore(file)) {
console.log(`⏭️ Skipping ${file} (ignored pattern)`);
return;
}
// Check if file exists (it might have been deleted)
if (!fs.existsSync(file)) {
console.log(`⏭️ Skipping ${file} (file does not exist)`);
return;
}
try {
const content = fs.readFileSync(file, 'utf8');
const lines = content.split('\n').length;
if (lines > MAX_LINES) {
console.error(`${file} is too large (${lines} lines, max ${MAX_LINES}). AI agents will struggle. Please refactor!`);
hasError = true;
} else {
console.log(`${file} (${lines} lines) - OK`);
}
} catch (err) {
console.error(`⚠️ Error reading ${file}: ${err.message}`);
// Don't fail on read errors, just warn
}
});
if (hasError) {
process.exit(1);
}
+108
View File
@@ -0,0 +1,108 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
// Directory to start from (project root)
const ROOT_DIR = process.cwd();
// Output file path
const OUTPUT_FILE = path.join(ROOT_DIR, 'docs', 'project-structure.txt');
// Function to check if a path is ignored by git
function isGitIgnored(filePath) {
try {
// git check-ignore -q returns 0 if ignored, 1 if not
execSync(`git check-ignore -q "${filePath}"`, {
cwd: ROOT_DIR,
stdio: 'ignore'
});
return true; // Ignored
} catch (e) {
return false; // Not ignored
}
}
// Function to generate tree structure
function generateTree(dir, prefix = '', isRoot = true) {
let structure = '';
// Add root directory name if it's the root
if (isRoot) {
structure += `${path.basename(dir)}/\n`;
}
let items;
try {
items = fs.readdirSync(dir);
} catch (e) {
console.error(`Error reading directory ${dir}: ${e.message}`);
return structure;
}
// Sort items: directories first, then files
const dirs = [];
const files = [];
items.forEach(item => {
const itemPath = path.join(dir, item);
// Skip if ignored by git
if (isGitIgnored(itemPath)) {
return;
}
try {
const stat = fs.statSync(itemPath);
if (stat.isDirectory()) {
dirs.push(item);
} else {
files.push(item);
}
} catch (e) {
// Skip items we can't stat
}
});
// Sort directories and files alphabetically
dirs.sort();
files.sort();
const allItems = [...dirs, ...files];
allItems.forEach((item, index) => {
const isLast = index === allItems.length - 1;
const connector = isLast ? '└── ' : '├── ';
const itemPath = path.join(dir, item);
structure += `${prefix}${connector}${item}${dirs.includes(item) ? '/' : ''}\n`;
// Recurse into directories
if (dirs.includes(item)) {
const newPrefix = prefix + (isLast ? ' ' : '│ ');
structure += generateTree(itemPath, newPrefix, false);
}
});
return structure;
}
try {
console.log('🗺️ Generating project structure...');
// Ensure docs directory exists
const docsDir = path.join(ROOT_DIR, 'docs');
if (!fs.existsSync(docsDir)) {
fs.mkdirSync(docsDir, { recursive: true });
console.log('📁 Created docs directory');
}
// Generate tree
const tree = generateTree(ROOT_DIR, '', true);
// Write to file
fs.writeFileSync(OUTPUT_FILE, tree);
console.log(`✅ Project structure updated: ${OUTPUT_FILE}`);
} catch (err) {
console.error(`❌ Error generating project structure: ${err.message}`);
process.exit(1);
}
+26
View File
@@ -120,6 +120,8 @@ src/
└── utils.ts # General utilities (cn function) └── utils.ts # General utilities (cn function)
``` ```
*Note: A complete, up-to-date project tree is automatically generated on each commit and saved to `docs/project-structure.txt`. This file is generated by the pre-commit hook using `.husky/scripts/generate-project-tree.js` and respects `.gitignore` rules.*
## Key Systems ## Key Systems
### 0. Task 2 Completion Summary ### 0. Task 2 Completion Summary
@@ -304,6 +306,27 @@ damage *= effects.myNewStatMultiplier;
3. **Add research skill in `constants.ts`** 3. **Add research skill in `constants.ts`**
4. **Map research to effect in `EFFECT_RESEARCH_MAPPING`** 4. **Map research to effect in `EFFECT_RESEARCH_MAPPING`**
## Git Hooks (Husky)
This project uses **Husky** to manage git hooks for automated checks and agent assistance:
### Pre-Commit Hook (`.husky/pre-commit`)
Runs automatically before each commit:
1. **File Size Check**: Ensures no staged file exceeds 400 lines (improves AI agent readability)
2. **Project Structure Generation**: Updates `docs/project-structure.txt` with current tree (respects `.gitignore`)
### Post-Merge Hook (`.husky/post-merge`)
Runs after merging branches:
- Checks if `package.json` or `package-lock.json` changed
- Automatically runs `npm install` to sync dependencies
### Implementation Files
- Hook scripts: `.husky/` directory
- File size check: `.husky/scripts/check-file-size.js`
- Tree generator: `.husky/scripts/generate-project-tree.js`
---
## Common Pitfalls ## Common Pitfalls
1. **Forgetting to call `getUnifiedEffects()`**: Always use unified effects for stat calculations 1. **Forgetting to call `getUnifiedEffects()`**: Always use unified effects for stat calculations
@@ -402,6 +425,9 @@ const useGameStore = create<GameStore>()(
- **After Task 2**: `page.tsx` reduced from ~2554 to ~548 lines (78% reduction) - **After Task 2**: `page.tsx` reduced from ~2554 to ~548 lines (78% reduction)
- **After Task 2**: `store.ts` increased due to crafting-slice integration, but better organized - **After Task 2**: `store.ts` increased due to crafting-slice integration, but better organized
### Automated File Size Check
A pre-commit hook automatically checks all staged files. Files exceeding **400 lines** will be rejected. The hook runs via Husky and uses `.husky/scripts/check-file-size.js`. If your file is too large, refactor it into smaller modules before committing.
--- ---
## 🚫 BANNED CONTENT - NEVER ADD THESE ## 🚫 BANNED CONTENT - NEVER ADD THESE
+39 -10
View File
@@ -30,6 +30,7 @@ Mana-Loop/
│ ├── objects/ │ ├── objects/
│ │ ├── 00/ │ │ ├── 00/
│ │ │ ├── 89865f52503a4ed6ce8e44f85ad1548823d17f │ │ │ ├── 89865f52503a4ed6ce8e44f85ad1548823d17f
│ │ │ ├── af629716dd116c017ffcfcec42f231f25fc8f7
│ │ │ └── df0433801bffaed8e862470cd9d877a392a0bf │ │ │ └── df0433801bffaed8e862470cd9d877a392a0bf
│ │ ├── 01/ │ │ ├── 01/
│ │ │ ├── 83373f92a07b4ca99d42064797d3883e67a299 │ │ │ ├── 83373f92a07b4ca99d42064797d3883e67a299
@@ -69,6 +70,7 @@ Mana-Loop/
│ │ │ ├── 0e2686b4b2ef6886a68e0dc042996dfa7cfc6f │ │ │ ├── 0e2686b4b2ef6886a68e0dc042996dfa7cfc6f
│ │ │ └── d7110257638a2d5d5edc62cfa9da4bac292d27 │ │ │ └── d7110257638a2d5d5edc62cfa9da4bac292d27
│ │ ├── 0a/ │ │ ├── 0a/
│ │ │ ├── 433c725df76a2755255866d16907c9986f4781
│ │ │ ├── 5e710ea895a4dc8dc0dc3ce043306ffa76c239 │ │ │ ├── 5e710ea895a4dc8dc0dc3ce043306ffa76c239
│ │ │ ├── 76fb517289fa32b9818769bf492160fcb04e0f │ │ │ ├── 76fb517289fa32b9818769bf492160fcb04e0f
│ │ │ └── 86027f029d266e9624b71e2d4da5cd045bc0a4 │ │ │ └── 86027f029d266e9624b71e2d4da5cd045bc0a4
@@ -85,6 +87,7 @@ Mana-Loop/
│ │ │ └── d75b80bc89dc1737e29fc4cf44f652efd7382e │ │ │ └── d75b80bc89dc1737e29fc4cf44f652efd7382e
│ │ ├── 0e/ │ │ ├── 0e/
│ │ │ ├── 44af05f746a329122b423966e616ecc9d880cb │ │ │ ├── 44af05f746a329122b423966e616ecc9d880cb
│ │ │ ├── 5111d1b3e03498dbfb5d6c67543a2593b19c73
│ │ │ ├── 513309eaaaedd39fbc935f632b84e808f4a5c7 │ │ │ ├── 513309eaaaedd39fbc935f632b84e808f4a5c7
│ │ │ ├── 6425c95064668170bd76995ebdfd98989632b0 │ │ │ ├── 6425c95064668170bd76995ebdfd98989632b0
│ │ │ ├── a05052d51de061defbfa77a00dd1b530e33a70 │ │ │ ├── a05052d51de061defbfa77a00dd1b530e33a70
@@ -187,6 +190,7 @@ Mana-Loop/
│ │ │ └── f2966be85f1216c437bec6a67b5786c8d3235c │ │ │ └── f2966be85f1216c437bec6a67b5786c8d3235c
│ │ ├── 24/ │ │ ├── 24/
│ │ │ ├── 0aec75eea23003fd6d26f39380abc96de7faea │ │ │ ├── 0aec75eea23003fd6d26f39380abc96de7faea
│ │ │ ├── 50c7a463d43572a93ece21003b2838fbbb8694
│ │ │ ├── b4cf1e08880fda17b4b5ce07271a6b7c5c5608 │ │ │ ├── b4cf1e08880fda17b4b5ce07271a6b7c5c5608
│ │ │ └── db8f784221244600ab86c2648718af2eccb6c4 │ │ │ └── db8f784221244600ab86c2648718af2eccb6c4
│ │ ├── 25/ │ │ ├── 25/
@@ -204,6 +208,7 @@ Mana-Loop/
│ │ │ ├── 3544e7ab0cf747c14c43e244d769028812e8b3 │ │ │ ├── 3544e7ab0cf747c14c43e244d769028812e8b3
│ │ │ └── da1ccfc277949c75a70ca8fb7109d4fedae84d │ │ │ └── da1ccfc277949c75a70ca8fb7109d4fedae84d
│ │ ├── 28/ │ │ ├── 28/
│ │ │ ├── 2d4bbc5a89a3f3d5438ba115fe51b2fae55d67
│ │ │ ├── 45b77c45e5c13af45bea3a4cb795fa50a3464b │ │ │ ├── 45b77c45e5c13af45bea3a4cb795fa50a3464b
│ │ │ └── 5de6345a053b7c7bad981a1cc6f2461e4b46d8 │ │ │ └── 5de6345a053b7c7bad981a1cc6f2461e4b46d8
│ │ ├── 29/ │ │ ├── 29/
@@ -288,7 +293,8 @@ Mana-Loop/
│ │ │ └── 9d39f38428f63d1b92c3a6ea768714bb3710a4 │ │ │ └── 9d39f38428f63d1b92c3a6ea768714bb3710a4
│ │ ├── 39/ │ │ ├── 39/
│ │ │ ├── 508e799dd32d52064e3b11ba96bd6b5f7b445a │ │ │ ├── 508e799dd32d52064e3b11ba96bd6b5f7b445a
│ │ │ ── ee95c04f1a5d27ca73b6e6d09b0f19d44dc050 │ │ │ ── ee95c04f1a5d27ca73b6e6d09b0f19d44dc050
│ │ │ └── ff63ea469ee826c8fde88059b96cdfc9600dbd
│ │ ├── 3a/ │ │ ├── 3a/
│ │ │ ├── 3246f8b7541af387e748d41035f85c23258fdc │ │ │ ├── 3246f8b7541af387e748d41035f85c23258fdc
│ │ │ ├── 4a9bbe2b814564896931f84d54f32b9635772b │ │ │ ├── 4a9bbe2b814564896931f84d54f32b9635772b
@@ -396,6 +402,8 @@ Mana-Loop/
│ │ │ ├── 583f4ef4769141208222d7ab59add0dc5e057e │ │ │ ├── 583f4ef4769141208222d7ab59add0dc5e057e
│ │ │ ├── 5973360217a5a0c5513b4231d7d7aecb5491dc │ │ │ ├── 5973360217a5a0c5513b4231d7d7aecb5491dc
│ │ │ └── 80b8a67674b1ec24b0946807796de0bcc25865 │ │ │ └── 80b8a67674b1ec24b0946807796de0bcc25865
│ │ ├── 4e/
│ │ │ └── 0f1c944391f23057de9a41fb3025df77c55fa2
│ │ ├── 4f/ │ │ ├── 4f/
│ │ │ ├── 03544eafd322a6fc08e7e3060b354112fa1dd6 │ │ │ ├── 03544eafd322a6fc08e7e3060b354112fa1dd6
│ │ │ ├── 6959b82211e4f322fb4dfb076d11297ea89190 │ │ │ ├── 6959b82211e4f322fb4dfb076d11297ea89190
@@ -432,7 +440,8 @@ Mana-Loop/
│ │ │ └── eac17f0ca858a8b819dbd5563cdcbbe868e0ee │ │ │ └── eac17f0ca858a8b819dbd5563cdcbbe868e0ee
│ │ ├── 56/ │ │ ├── 56/
│ │ │ ├── 35711332332069bd1ea6bf8c4b515d2091d645 │ │ │ ├── 35711332332069bd1ea6bf8c4b515d2091d645
│ │ │ ── 3e41dbe3fa66bf37dd7fdb3841d25f3b63e1b4 │ │ │ ── 3e41dbe3fa66bf37dd7fdb3841d25f3b63e1b4
│ │ │ └── a7f639893730faed9c87db0b32bd53b198301d
│ │ ├── 57/ │ │ ├── 57/
│ │ │ └── 5c36d9b01d4de7c019bf4a567c660e45e59e43 │ │ │ └── 5c36d9b01d4de7c019bf4a567c660e45e59e43
│ │ ├── 58/ │ │ ├── 58/
@@ -459,7 +468,8 @@ Mana-Loop/
│ │ │ ├── 81cc9e4f334986679aa0b5abd0d50f47cc90b3 │ │ │ ├── 81cc9e4f334986679aa0b5abd0d50f47cc90b3
│ │ │ ├── b84bae8fba238223329506f97466e7cf5fa7b0 │ │ │ ├── b84bae8fba238223329506f97466e7cf5fa7b0
│ │ │ ├── e045637a637062a848a01825e2cd649b72a6fe │ │ │ ├── e045637a637062a848a01825e2cd649b72a6fe
│ │ │ ── e52c7cd25543b9f8bf8bfa773155ade4c85142 │ │ │ ── e52c7cd25543b9f8bf8bfa773155ade4c85142
│ │ │ └── eb80db0c59760a036a4cfd0d76ec0dccf9a547
│ │ ├── 5c/ │ │ ├── 5c/
│ │ │ ├── 2ba11cb18dd30ce96066dc68b5b4a636c954b2 │ │ │ ├── 2ba11cb18dd30ce96066dc68b5b4a636c954b2
│ │ │ ├── a57fa8b5e7f22e42b8eab270a5f0fb8943638d │ │ │ ├── a57fa8b5e7f22e42b8eab270a5f0fb8943638d
@@ -527,6 +537,7 @@ Mana-Loop/
│ │ │ ├── f784695f332975747ab0433ffe1a44abf199ce │ │ │ ├── f784695f332975747ab0433ffe1a44abf199ce
│ │ │ └── f899df9295233ec6453fbd6085ab737cdd4868 │ │ │ └── f899df9295233ec6453fbd6085ab737cdd4868
│ │ ├── 6b/ │ │ ├── 6b/
│ │ │ ├── 31a5c036630ed1f68ff328fa3b10648c4be095
│ │ │ ├── 51408735e74d4fc645ae30843d713c57df7071 │ │ │ ├── 51408735e74d4fc645ae30843d713c57df7071
│ │ │ ├── 6e5b13cdf380b8bf5de755155f6e907da7a61e │ │ │ ├── 6e5b13cdf380b8bf5de755155f6e907da7a61e
│ │ │ ├── 8b3486bc93f8e52d6b2e47d13a2a7dd23e2b07 │ │ │ ├── 8b3486bc93f8e52d6b2e47d13a2a7dd23e2b07
@@ -556,7 +567,8 @@ Mana-Loop/
│ │ │ ├── 0b86d4d7fbadef15d4ebd2254cd0efeeb00f02 │ │ │ ├── 0b86d4d7fbadef15d4ebd2254cd0efeeb00f02
│ │ │ ├── 5183cded881cf19d6f98a9d3ab06df6644c16a │ │ │ ├── 5183cded881cf19d6f98a9d3ab06df6644c16a
│ │ │ ├── 86a8191ff528525e5aba6820cce68bbff546d8 │ │ │ ├── 86a8191ff528525e5aba6820cce68bbff546d8
│ │ │ ── dcbb75d5ff17ba47c9069128d7021411730057 │ │ │ ── dcbb75d5ff17ba47c9069128d7021411730057
│ │ │ └── e17f665e36bf698ec4b16d499a3824f9299f33
│ │ ├── 70/ │ │ ├── 70/
│ │ │ ├── 286f121cdcab90944bd40dd7f87b27207a5be2 │ │ │ ├── 286f121cdcab90944bd40dd7f87b27207a5be2
│ │ │ ├── 56dc04d621a2da9a18615ddf93e74207a5c7f1 │ │ │ ├── 56dc04d621a2da9a18615ddf93e74207a5c7f1
@@ -579,7 +591,8 @@ Mana-Loop/
│ │ │ ├── 4040d64067bea8a20f1ab1befadbf55e88c187 │ │ │ ├── 4040d64067bea8a20f1ab1befadbf55e88c187
│ │ │ ├── 5f1fb0cf9b4075a25d64d8dabc754ffe292f8a │ │ │ ├── 5f1fb0cf9b4075a25d64d8dabc754ffe292f8a
│ │ │ ├── 9321d2cbb10a22eff108f2c478b6147b6053e8 │ │ │ ├── 9321d2cbb10a22eff108f2c478b6147b6053e8
│ │ │ ── 9c1991f12974b3eba86893dd274ae20ea07441 │ │ │ ── 9c1991f12974b3eba86893dd274ae20ea07441
│ │ │ └── f9d0674966a673c6044c2ad2cfd9e4da092081
│ │ ├── 75/ │ │ ├── 75/
│ │ │ ├── 7130d618de424546c7a7b56f84199d12de4bb2 │ │ │ ├── 7130d618de424546c7a7b56f84199d12de4bb2
│ │ │ ├── 863af2c6c9724bca37a9067c03f18fa8025407 │ │ │ ├── 863af2c6c9724bca37a9067c03f18fa8025407
@@ -604,7 +617,9 @@ Mana-Loop/
│ │ │ └── ccc917fdf05df95c374e74b2fb621282423c4e │ │ │ └── ccc917fdf05df95c374e74b2fb621282423c4e
│ │ ├── 79/ │ │ ├── 79/
│ │ │ ├── 4e834147c25b9f95eeeb0a034ed304d59eb65b │ │ │ ├── 4e834147c25b9f95eeeb0a034ed304d59eb65b
│ │ │ ── 6dd6d47c5dae286ecfd9a37f2247bd66bc8241 │ │ │ ── 6dd6d47c5dae286ecfd9a37f2247bd66bc8241
│ │ │ ├── e0c74973caa1b70808fd5289a41fb8bd9823d8
│ │ │ └── f55a91b03a0be6732e1b1b491c7118c80f945d
│ │ ├── 7a/ │ │ ├── 7a/
│ │ │ ├── 8a2b7b540f5a728563ef3d12598fea3477ad53 │ │ │ ├── 8a2b7b540f5a728563ef3d12598fea3477ad53
│ │ │ ├── b82229865f4bfeb9ee986c0b8bb9cc35822d93 │ │ │ ├── b82229865f4bfeb9ee986c0b8bb9cc35822d93
@@ -632,6 +647,7 @@ Mana-Loop/
│ │ │ └── abe59cc2483a0ddb8e5e1aacd333bc1442fba4 │ │ │ └── abe59cc2483a0ddb8e5e1aacd333bc1442fba4
│ │ ├── 80/ │ │ ├── 80/
│ │ │ ├── 4d76e2372cb0762d0d5a386312743a16129aaa │ │ │ ├── 4d76e2372cb0762d0d5a386312743a16129aaa
│ │ │ ├── 8639f4858f4556f65d2abe08fa6b256a5d8f89
│ │ │ ├── e5aa7b866e6c9409d98f144b364929aa6e5d34 │ │ │ ├── e5aa7b866e6c9409d98f144b364929aa6e5d34
│ │ │ └── f63e03a3c4c816ab638943eeb4528e8f31b967 │ │ │ └── f63e03a3c4c816ab638943eeb4528e8f31b967
│ │ ├── 81/ │ │ ├── 81/
@@ -655,6 +671,7 @@ Mana-Loop/
│ │ │ └── f1872bee2f7c9a91ac5f2467b2529aa4568217 │ │ │ └── f1872bee2f7c9a91ac5f2467b2529aa4568217
│ │ ├── 85/ │ │ ├── 85/
│ │ │ ├── 262a3c51bf450062054385ec4bf239f00323ec │ │ │ ├── 262a3c51bf450062054385ec4bf239f00323ec
│ │ │ ├── 8555d2b8e21ed1663584937f1270200088cef7
│ │ │ └── 9efc3d27e0e6b5ed91a9c10f4d0604eda3486c │ │ │ └── 9efc3d27e0e6b5ed91a9c10f4d0604eda3486c
│ │ ├── 86/ │ │ ├── 86/
│ │ │ ├── 47ecac91e1311dac4b642d58f12b1ce7200a7d │ │ │ ├── 47ecac91e1311dac4b642d58f12b1ce7200a7d
@@ -663,6 +680,7 @@ Mana-Loop/
│ │ │ ├── d109e356e178eed0db83c51eab10e61ea8b2bf │ │ │ ├── d109e356e178eed0db83c51eab10e61ea8b2bf
│ │ │ └── f668e0697cd495803457545dc4c4bcb0984aee │ │ │ └── f668e0697cd495803457545dc4c4bcb0984aee
│ │ ├── 88/ │ │ ├── 88/
│ │ │ ├── d6016557cec6d8a77df16e9952246052cc5839
│ │ │ └── e8cd0873832532404e8add60bff3021876a4ca │ │ │ └── e8cd0873832532404e8add60bff3021876a4ca
│ │ ├── 89/ │ │ ├── 89/
│ │ │ ├── 622d12bf59536536bb5a2720423f2b23c43ec3 │ │ │ ├── 622d12bf59536536bb5a2720423f2b23c43ec3
@@ -754,6 +772,7 @@ Mana-Loop/
│ │ │ └── e9ad439342d53781cb100f042a8765883ebd7c │ │ │ └── e9ad439342d53781cb100f042a8765883ebd7c
│ │ ├── 9e/ │ │ ├── 9e/
│ │ │ ├── 50b42df6804da2182069b2ec3924017860b421 │ │ │ ├── 50b42df6804da2182069b2ec3924017860b421
│ │ │ ├── 672cec517992f90eb140f794cb3e1fd5687f5c
│ │ │ └── 899d667e232e60a14b57f1608551abe3b53ee8 │ │ │ └── 899d667e232e60a14b57f1608551abe3b53ee8
│ │ ├── 9f/ │ │ ├── 9f/
│ │ │ ├── 029d93e1a36fb94d980c59b50d1ece90b78e4b │ │ │ ├── 029d93e1a36fb94d980c59b50d1ece90b78e4b
@@ -769,6 +788,7 @@ Mana-Loop/
│ │ ├── a1/ │ │ ├── a1/
│ │ │ ├── 596a04fee80d51dd192d438c1a9956f6baae1c │ │ │ ├── 596a04fee80d51dd192d438c1a9956f6baae1c
│ │ │ ├── 601b8fa106b067c9c8168ba5e33d62c1e32884 │ │ │ ├── 601b8fa106b067c9c8168ba5e33d62c1e32884
│ │ │ ├── c5c7892060a62ec535bba1dc074d4aab099a24
│ │ │ └── ce17196dacab0a9e67e34f576e1bf9649c0112 │ │ │ └── ce17196dacab0a9e67e34f576e1bf9649c0112
│ │ ├── a2/ │ │ ├── a2/
│ │ │ ├── 125a21ae91abbc2b229881e77adc8de8bb1119 │ │ │ ├── 125a21ae91abbc2b229881e77adc8de8bb1119
@@ -779,6 +799,7 @@ Mana-Loop/
│ │ │ ├── 06e50d3c7cdafd5290a5259638db6aacdb3231 │ │ │ ├── 06e50d3c7cdafd5290a5259638db6aacdb3231
│ │ │ ├── 15020820665e5cd0e83f5abd16eb0f7c7af59b │ │ │ ├── 15020820665e5cd0e83f5abd16eb0f7c7af59b
│ │ │ ├── 38a29fcffd8b6ed2554a7e4eae7c0bf3130424 │ │ │ ├── 38a29fcffd8b6ed2554a7e4eae7c0bf3130424
│ │ │ ├── 824c68a25945963efcbdbde55a86cebea43454
│ │ │ └── 9d4f0ef1c79b402210fb92d711b8cd1bb56d63 │ │ │ └── 9d4f0ef1c79b402210fb92d711b8cd1bb56d63
│ │ ├── a5/ │ │ ├── a5/
│ │ │ ├── 28feb8e27f2d1484bbf56fa98235aa89dda32c │ │ │ ├── 28feb8e27f2d1484bbf56fa98235aa89dda32c
@@ -792,6 +813,7 @@ Mana-Loop/
│ │ │ ├── 0cc68fb3b23eac335805e864eaa822e59430cd │ │ │ ├── 0cc68fb3b23eac335805e864eaa822e59430cd
│ │ │ ├── 265b055632e407d5acc83fc57c245c4762c56b │ │ │ ├── 265b055632e407d5acc83fc57c245c4762c56b
│ │ │ ├── 8c72da042ab31e7a3c1d0c61027b8e2a15946a │ │ │ ├── 8c72da042ab31e7a3c1d0c61027b8e2a15946a
│ │ │ ├── 98115932b3d0e9fbb6e53d6ea30702cb493529
│ │ │ ├── b91225ec8ce3af2529715acfddafbd2b573f46 │ │ │ ├── b91225ec8ce3af2529715acfddafbd2b573f46
│ │ │ └── e587bb0946929a79a3130518751241b3bcf97c │ │ │ └── e587bb0946929a79a3130518751241b3bcf97c
│ │ ├── a8/ │ │ ├── a8/
@@ -802,6 +824,7 @@ Mana-Loop/
│ │ │ ├── dfe6258ae5c081fbe9a9e5c6cef67f2b9692a5 │ │ │ ├── dfe6258ae5c081fbe9a9e5c6cef67f2b9692a5
│ │ │ └── f5fa002ee2a633659d76b11ee3cf3c4312c082 │ │ │ └── f5fa002ee2a633659d76b11ee3cf3c4312c082
│ │ ├── a9/ │ │ ├── a9/
│ │ │ ├── 46e3b6aaa9e3e200465467235ee8a98a9b9b61
│ │ │ ├── 4d2ca0680551c9e48836e882b4287627eef295 │ │ │ ├── 4d2ca0680551c9e48836e882b4287627eef295
│ │ │ ├── 7c1a2ab27a9500547b545b82760391cc566e21 │ │ │ ├── 7c1a2ab27a9500547b545b82760391cc566e21
│ │ │ └── da14f29a95fe06481330a240259ad97266c001 │ │ │ └── da14f29a95fe06481330a240259ad97266c001
@@ -909,6 +932,7 @@ Mana-Loop/
│ │ │ ├── 8ed0f92cb1fb67c7c2757697c36c969c0361c2 │ │ │ ├── 8ed0f92cb1fb67c7c2757697c36c969c0361c2
│ │ │ └── 94586f56c1f38f1bf58f2acb6db0e2f1f22533 │ │ │ └── 94586f56c1f38f1bf58f2acb6db0e2f1f22533
│ │ ├── bc/ │ │ ├── bc/
│ │ │ ├── 0fdf0416b50197ff719252a7dcc9dd923c28c5
│ │ │ ├── 21c14176f27a1b89c57858fa10efaae88cbc97 │ │ │ ├── 21c14176f27a1b89c57858fa10efaae88cbc97
│ │ │ ├── 312ed47776862d880a89b4271b6596cfdf3641 │ │ │ ├── 312ed47776862d880a89b4271b6596cfdf3641
│ │ │ └── b5e902f9a50a1baa5619c5f1288e8458f41f0f │ │ │ └── b5e902f9a50a1baa5619c5f1288e8458f41f0f
@@ -943,7 +967,8 @@ Mana-Loop/
│ │ │ ├── 5e6920066307295c3619317dec8642423bba4e │ │ │ ├── 5e6920066307295c3619317dec8642423bba4e
│ │ │ ├── 8b734bc40f9c3bfc6d2cf85c16e212a0e6e482 │ │ │ ├── 8b734bc40f9c3bfc6d2cf85c16e212a0e6e482
│ │ │ ├── 9ae605d632bf8a76788af5f02310b1ee2a2e5d │ │ │ ├── 9ae605d632bf8a76788af5f02310b1ee2a2e5d
│ │ │ ── b5f158618b61280eec85677f79347fb248b4b4 │ │ │ ── b5f158618b61280eec85677f79347fb248b4b4
│ │ │ └── e9f8576c45c8a6975b91858f63ef73afd82e06
│ │ ├── c4/ │ │ ├── c4/
│ │ │ ├── 481f618950447b053639d3e46b4e8df430c5be │ │ │ ├── 481f618950447b053639d3e46b4e8df430c5be
│ │ │ ├── 4af1fe7699ee609a9c8dfea201b2cba349fe0b │ │ │ ├── 4af1fe7699ee609a9c8dfea201b2cba349fe0b
@@ -1005,7 +1030,8 @@ Mana-Loop/
│ │ │ ├── 138d78652bd3a2e13b19623b27e123b3387915 │ │ │ ├── 138d78652bd3a2e13b19623b27e123b3387915
│ │ │ ├── 7b0dc137be02425724f3fcde8d298ed3141aa2 │ │ │ ├── 7b0dc137be02425724f3fcde8d298ed3141aa2
│ │ │ ├── 8872e8032bae8afcf60220279a7da3c82725c7 │ │ │ ├── 8872e8032bae8afcf60220279a7da3c82725c7
│ │ │ ── 9981f3ad69bb4819f040597c91eae14dc2a459 │ │ │ ── 9981f3ad69bb4819f040597c91eae14dc2a459
│ │ │ └── ee77a4f340fdbdf973a8c6e9e3cb1dfa77f350
│ │ ├── cf/ │ │ ├── cf/
│ │ │ ├── 795db7e988a37b31cef9e54940209c2d84d9c1 │ │ │ ├── 795db7e988a37b31cef9e54940209c2d84d9c1
│ │ │ ├── e561cbc6da79d0158c5c275f5c4bb9f6fb8b82 │ │ │ ├── e561cbc6da79d0158c5c275f5c4bb9f6fb8b82
@@ -1020,13 +1046,15 @@ Mana-Loop/
│ │ │ ├── 1e2f0d1cba4887db91cd104b16583bf8a00542 │ │ │ ├── 1e2f0d1cba4887db91cd104b16583bf8a00542
│ │ │ ├── 3c55f27c2a512dc84c8d0ddb7768167decd088 │ │ │ ├── 3c55f27c2a512dc84c8d0ddb7768167decd088
│ │ │ ├── 54d7be0517d636132d32332162b310b02f7f90 │ │ │ ├── 54d7be0517d636132d32332162b310b02f7f90
│ │ │ ├── 90afa6d553099c5f4fce7dea54a26033cfecf7
│ │ │ └── fcf1fc21915c4c6bd9460506e4ebe41e2ec556 │ │ │ └── fcf1fc21915c4c6bd9460506e4ebe41e2ec556
│ │ ├── d2/ │ │ ├── d2/
│ │ │ ├── 3705894748304c5e9220b7a85d693498564d4b │ │ │ ├── 3705894748304c5e9220b7a85d693498564d4b
│ │ │ └── 7f11576582712b3494db9ed41d7e97455e78d0 │ │ │ └── 7f11576582712b3494db9ed41d7e97455e78d0
│ │ ├── d3/ │ │ ├── d3/
│ │ │ ├── 9eb8296bc4d58c26172dd460373a64f36034c2 │ │ │ ├── 9eb8296bc4d58c26172dd460373a64f36034c2
│ │ │ ── c69322412a4816c6af9ec3bf8b529619300d8b │ │ │ ── c69322412a4816c6af9ec3bf8b529619300d8b
│ │ │ └── f7f494ed92048a25651d724690387633cc60c6
│ │ ├── d4/ │ │ ├── d4/
│ │ │ ├── 97d2ee7e845dfa7774a41e20fbbb8be16d942e │ │ │ ├── 97d2ee7e845dfa7774a41e20fbbb8be16d942e
│ │ │ ├── 9f55d93bc595604f4d79a42795e4f537487522 │ │ │ ├── 9f55d93bc595604f4d79a42795e4f537487522
@@ -1193,6 +1221,7 @@ Mana-Loop/
│ │ │ ├── 520e15b8a46087c8b64798d277ca3ea032c79e │ │ │ ├── 520e15b8a46087c8b64798d277ca3ea032c79e
│ │ │ ├── 754e2dec9d0f324442a65c7cdc21b5ced1a222 │ │ │ ├── 754e2dec9d0f324442a65c7cdc21b5ced1a222
│ │ │ ├── 8a2d260f95779857064c2e7c4837eb6f260002 │ │ │ ├── 8a2d260f95779857064c2e7c4837eb6f260002
│ │ │ ├── b618e095fd23c24f7364578165a0ef83318650
│ │ │ └── dd633525609ba758e8f2a0ba41e30ea49bdac4 │ │ │ └── dd633525609ba758e8f2a0ba41e30ea49bdac4
│ │ ├── f9/ │ │ ├── f9/
│ │ │ ├── 664a28797bc0099963b4d2317d912336ce9c8a │ │ │ ├── 664a28797bc0099963b4d2317d912336ce9c8a
@@ -1270,6 +1299,7 @@ Mana-Loop/
│ │ ├── subtask_12_context.md │ │ ├── subtask_12_context.md
│ │ └── subtask_13_context.md │ │ └── subtask_13_context.md
│ ├── GAME_BRIEFING.md │ ├── GAME_BRIEFING.md
│ ├── project-structure.txt
│ ├── skills.md │ ├── skills.md
│ └── task5.md │ └── task5.md
├── download/ ├── download/
@@ -1499,6 +1529,5 @@ Mana-Loop/
├── package.json ├── package.json
├── postcss.config.mjs ├── postcss.config.mjs
├── tailwind.config.ts ├── tailwind.config.ts
├── test-small.ts
├── tsconfig.json ├── tsconfig.json
└── vitest.config.ts └── vitest.config.ts
+28 -27
View File
@@ -2,69 +2,70 @@
## Status Overview ## Status Overview
- **Start Date**: 2025-05-19 - **Start Date**: 2025-05-19
- **Current Phase**: PRIORITY 2 (Spire Mode Fixes) - **Current Phase**: PRIORITY 3 (UI/UX Restructuring)
- **Overall Progress**: 21% complete (4/19 tasks done) - **Overall Progress**: 42% complete (8/19 tasks done)
--- ---
## PRIORITY 0 — Crashes (Fix First, Parallel) ✅ COMPLETED ## PRIORITY 0 — Crashes (Fix First, Parallel) ✅ COMPLETED
| Task | Status | Notes | | Task | Status | Notes |
|------|--------|-------| |------|--------|-------|
| SpellsTab crash diagnosis/fix | Completed | Fixed unprotected ENCHANTMENT_EFFECTS access, added spell.cost guards | | SpellsTab crash diagnosis/fix | Completed | Fixed unprotected ENCHANTMENT_EFFECTS access |
| LabTab crash diagnosis/fix | Completed | Added safe access to store.elements with `|| {}` fallbacks | | LabTab crash diagnosis/fix | Completed | Added safe access to store.elements |
| DebugTab crash diagnosis/fix | Completed | Moved Toaster/GameToaster inside DebugProvider in layout.tsx | | DebugTab crash diagnosis/fix | Completed | Moved Toaster/GameToaster inside DebugProvider |
--- ---
## PRIORITY 1 — Mana Conversion Mechanic Fix ✅ COMPLETED ## PRIORITY 1 — Mana Conversion Mechanic Fix ✅ COMPLETED
| Task | Status | Notes | | Task | Status | Notes |
|------|--------|-------| |------|--------|-------|
| Wire conversion drain to effectiveRegen instead of active mana pool | Completed | Removed redundant `rawMana -= actualConversion` in store.ts since effectiveRegen already accounts for conversion drain | | Wire conversion drain to effectiveRegen | Completed | Removed redundant rawMana -= actualConversion |
--- ---
## PRIORITY 2 — Spire Mode Fixes ## PRIORITY 2 — Spire Mode Fixes
| Task | Status | Notes | | Task | Status | Notes |
|------|--------|-------| |------|--------|-------|
| 2a. Floor Rendering & Identity (type, named enemy, special properties) | Pending | | | 2a. Floor Rendering & Identity | Pending | Context gathering next |
| 2b. Swarm Floors (show multiple enemies, verify generation) | Pending | | | 2b. Swarm Floors | ✅ Completed | Verified by check sub-agent |
| 2c. HP Bar Live Updates | Pending | | | 2c. HP Bar Live Updates | ✅ Completed | floorHP synced to enemy HP |
| 2d. Casting Progress Overflow Fix | Pending | | | 2d. Casting Progress Overflow | Pending | Context gathering next |
| 2e. Climb/Descend Controls (spam fix, re-entry resume, button rename) | Pending | | | 2e. Climb/Descend Controls | Pending | Context gathering next |
| 2f. Activity Log Implementation | Pending | | | 2f. Activity Log Implementation | Pending | Context gathering next |
| 2g. Spell Info Display Fix (dmg/cast vs DPS) | Pending | | | 2g. Spell Info Display Fix | Pending | Context gathering next |
--- ---
## PRIORITY 3 — UI/UX Restructuring ## PRIORITY 3 — UI/UX Restructuring
| Task | Status | Notes | | Task | Status | Notes |
|------|--------|-------| |------|--------|-------|
| 3a. CraftingTab Restructure (remove 1-4 bar, split Fabricate/Enchant, top sub-tabs) | Pending | | | 3a. CraftingTab Restructure | ✅ Completed | Removed stepper, added Fabricate/Enchant tabs |
| 3b. LootTab Nesting Fix (remove redundant layers) | Pending | | | 3b. LootTab Nesting Fix | ✅ Completed | Removed redundant LootTab wrapper |
| 3c. AchievementsTab Nesting Fix (remove duplicate headings) | Pending | | | 3c. AchievementsTab Nesting Fix | In Progress | Context gathering → execution |
--- ---
## PRIORITY 4 — Enchantment Effects & Research ## PRIORITY 4 — Enchantment Effects & Research
| Task | Status | Notes | | Task | Status | Notes |
|------|--------|-------| |------|--------|-------|
| 4a. Mana-Type Capacity Enchantment Effects | Pending | Per unlocked mana type | | 4a. Mana-Type Capacity Enchantment Effects | Pending | Context gathering next |
| 4b. Mana Capacity Research Visibility Gate | Pending | Only show if mana type unlocked | | 4b. Mana Capacity Research Visibility Gate | Pending | Context gathering next |
| 4c. Skill Requirement Display Bug Fix (undefined Lv.[object Object]) | Pending | | | 4c. Skill Requirement Display Bug Fix | Pending | Context gathering next |
| 4d. Enchantment Power Effect Implementation + Stub Audit | Pending | Replace placeholder, audit all stubs | | 4d. Enchantment Power Effect Implementation | Pending | Partially done |
--- ---
## PRIORITY 5 — Insight Upgrade Analysis ## PRIORITY 5 — Insight Upgrade Analysis
| Task | Status | Notes | | Task | Status | Notes |
|------|--------|-------| |------|--------|-------|
| 5a. Create design proposal in docs/task5_insight_proposals.md | Pending | Wait for human sign-off | | 5a. Create design proposal | Pending | Context gathering next |
--- ---
## Notes & Decisions ## Workflow Log
- ✅ PRIORITY 0 crashes fixed via parallel sub-agents, verified and applied all fixes - ✅ PRIORITY 0 crashes fixed via parallel sub-agents
- ✅ PRIORITY 1 mana conversion fix applied: removed double-counting of conversion drain in store.ts tick logic - ✅ PRIORITY 1 mana conversion fix applied
- Next steps: Dispatch parallel sub-agents for PRIORITY 2 Spire Mode fixes (2a-2g) - ✅ PRIORITY 2b, 2c verified completed
- Advisor tool will be used for ambiguous design decisions - ✅ Task 12 (CraftingTab) completed
- Sub-agent instructions passed via inline prompts with full context - ✅ Task 13 (LootTab) completed
- ⏳ Current: Task 14 (AchievementsTab) context gathering
+451
View File
@@ -0,0 +1,451 @@
# Task 12 Context: Restructure CraftingTab
## Task Description
Restructure CraftingTab (remove 1-4 progress bar, split Fabricate/Enchant, top sub-tabs) (PRIORITY 3a)
## Source Files
### 1. `/src/components/game/tabs/CraftingTab.tsx` (268 lines)
**Imports and Dependencies:**
```typescript
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { GameCard } from '@/components/ui/game-card';
import { SectionHeader } from '@/components/ui/section-header';
import { ActionButton } from '@/components/ui/action-button';
import { Stepper } from '@/components/ui/stepper';
import { Scroll, Hammer, Sparkles, Anvil } from 'lucide-react';
import type { EquipmentInstance, EnchantmentDesign, DesignEffect, AppliedEnchantment, LootInventory, EquipmentCraftingProgress } from '@/lib/game/types';
import { fmt, type GameStore } from '@/lib/game/store';
import {
EnchantmentDesigner,
EnchantmentPreparer,
EnchantmentApplier,
EquipmentCrafter,
} from '@/components/game/crafting';
import { useGameToast } from '@/components/game/GameToast';
```
**Crafting Phases Constant:**
```typescript
// Crafting phases for the stepper
const CRAFTING_PHASES = ['Design', 'Prepare', 'Apply', 'Craft'];
```
**Component Props:**
```typescript
export interface CraftingTabProps {
store: GameStore;
}
```
**State and Stepper Mapping:**
```typescript
export function CraftingTab({ store }: CraftingTabProps) {
const showToast = useGameToast();
const currentAction = store.currentAction;
const designProgress = store.designProgress;
const preparationProgress = store.preparationProgress;
const applicationProgress = store.applicationProgress;
const equipmentCraftingProgress = store.equipmentCraftingProgress;
const pauseApplication = store.pauseApplication;
const resumeApplication = store.resumeApplication;
const [craftingStage, setCraftingStage] = useState<'design' | 'prepare' | 'apply' | 'craft'>('craft');
// Map crafting stage to stepper index
const getStepperIndex = (stage: string): number => {
switch (stage) {
case 'design': return 0;
case 'prepare': return 1;
case 'apply': return 2;
case 'craft': return 3;
default: return 0;
}
};
```
**Stepper Component (lines 58-68):**
```tsx
{/* Visual Stepper - Requirement: show Design, Prepare, Apply phases as visual stepper */}
<GameCard variant="default" className="p-4">
<Stepper
steps={CRAFTING_PHASES}
currentStep={getStepperIndex(craftingStage)}
className="px-4"
/>
</GameCard>
```
**Stage Content Conditional Rendering (lines 71-97):**
```tsx
{/* Stage Content - Without unlabeled Tabs, using conditional rendering instead */}
<div className="mt-4">
{craftingStage === 'craft' && (
<EquipmentCrafter store={store} />
)}
{craftingStage === 'design' && (
<EnchantmentDesigner
store={store}
selectedEquipmentType={null}
setSelectedEquipmentType={() => {}}
selectedEffects={[]}
setSelectedEffects={() => {}}
designName={''}
setDesignName={() => {}}
selectedDesign={null}
setSelectedDesign={() => {}}
/>
)}
{craftingStage === 'prepare' && (
<EnchantmentPreparer
store={store}
selectedEquipmentInstance={null}
setSelectedEquipmentInstance={() => {}}
/>
)}
{craftingStage === 'apply' && (
<EnchantmentApplier
store={store}
selectedEquipmentInstance={null}
setSelectedEquipmentInstance={() => {}}
selectedDesign={null}
setSelectedDesign={() => {}}
onEnchantmentApplied={handleEnchantmentApplied}
onCapacityExceeded={handleCapacityExceeded}
/>
)}
</div>
```
**Stage Navigation Buttons (lines 99-131):**
```tsx
{/* Stage Navigation Buttons */}
<GameCard variant="default" className="p-4">
<div className="flex justify-center gap-2 flex-wrap">
<ActionButton
variant={craftingStage === 'craft' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('craft')}
className={craftingStage === 'craft' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Anvil size={14} className="mr-1" />
Craft
</ActionButton>
<ActionButton
variant={craftingStage === 'design' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('design')}
className={craftingStage === 'design' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Scroll size={14} className="mr-1" />
Design
</ActionButton>
<ActionButton
variant={craftingStage === 'prepare' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('prepare')}
className={craftingStage === 'prepare' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Hammer size={14} className="mr-1" />
Prepare
</ActionButton>
<ActionButton
variant={craftingStage === 'apply' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('apply')}
className={craftingStage === 'apply' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Sparkles size={14} className="mr-1" />
Apply
</ActionButton>
</div>
</GameCard>
```
**Current Activity Indicators (Progress Bars to be removed - lines 133-236):**
```tsx
{/* Current Activity Indicator */}
{currentAction === 'craft' && equipmentCraftingProgress && (
<GameCard variant="default" className="border-[var(--mana-water)]/60 bg-[var(--mana-water)]/10">
<SectionHeader
title="Crafting Equipment"
action={
<span className="text-sm text-[var(--text-muted)]">
{safeToFixed(calcPercent(equipmentCraftingProgress.progress, equipmentCraftingProgress.required), 0)}%
</span>
}
/>
<Progress
value={calcPercent(equipmentCraftingProgress.progress, equipmentCraftingProgress.required)}
className="h-3 bg-[var(--bg-sunken)]"
/>
{/* ... more content ... */}
</GameCard>
)}
{currentAction === 'design' && designProgress && (
<GameCard variant="default" className="border-[var(--mana-stellar)]/60 bg-[var(--mana-stellar)]/10">
{/* ... Progress bar and content ... */}
</GameCard>
)}
{currentAction === 'prepare' && preparationProgress && (
<GameCard variant="default" className="border-[var(--color-warning)]/60 bg-[var(--color-warning)]/10">
{/* ... Progress bar and content ... */}
</GameCard>
)}
{currentAction === 'enchant' && applicationProgress && (
<GameCard variant="default" className="border-[var(--mana-light)]/60 bg-[var(--mana-light)]/10">
{/* ... Progress bar and content ... */}
</GameCard>
)}
```
---
### 2. Files in `/src/components/game/crafting/` Directory
| File | Size | Last Modified |
|------|------|---------------|
| `EnchantmentApplier.tsx` | 12,206 bytes | 1777364523 |
| `EnchantmentDesigner.tsx` | 19,568 bytes | 1777361558 |
| `EnchantmentPreparer.tsx` | 14,816 bytes | 1777365343 |
| `EquipmentCrafter.tsx` | 9,121 bytes | 1777205526 |
| `index.tsx` | 396 bytes | 1777028644 |
**Barrel File (`index.tsx`):**
```typescript
// Barrel file for crafting components
export { EnchantmentDesigner, type EnchantmentDesignerProps } from './EnchantmentDesigner';
export { EnchantmentPreparer, type EnchantmentPreparerProps } from './EnchantmentPreparer';
export { EnchantmentApplier, type EnchantmentApplierProps } from './EnchantmentApplier';
export { EquipmentCrafter, type EquipmentCrafterProps } from './EquipmentCrafter';
```
---
### 3. Stepper Component (`/src/components/ui/stepper.tsx`)
**Interface:**
```typescript
interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
steps: string[];
currentStep: number; // 0-indexed
orientation?: "horizontal" | "vertical";
}
```
**Full Implementation (100 lines):**
```typescript
import * as React from "react";
import { cn } from "@/lib/utils";
import { Check, Circle, ArrowRight } from "lucide-react";
interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
steps: string[];
currentStep: number; // 0-indexed
orientation?: "horizontal" | "vertical";
}
interface StepProps {
label: string;
stepNumber: number;
isActive: boolean;
isCompleted: boolean;
isLast: boolean;
orientation?: "horizontal" | "vertical";
}
const Step = ({ label, stepNumber, isActive, isCompleted, isLast, orientation = "horizontal" }: StepProps) => {
return (
<div
className={cn(
"flex items-center",
orientation === "vertical" ? "flex-col" : "flex-row",
orientation === "vertical" && "w-full"
)}
>
<div className="flex flex-col items-center">
<div
className={cn(
"flex items-center justify-center w-8 h-8 rounded-full border-2 transition-all duration-200",
isActive && "border-[var(--interactive-primary)] bg-[var(--interactive-primary)]/20 text-[var(--interactive-primary)]",
isCompleted && "border-[var(--color-success)] bg-[var(--color-success)]/20 text-[var(--color-success)]",
!isActive && !isCompleted && "border-[var(--border-default)] bg-[var(--bg-sunken)] text-[var(--text-muted)]"
)}
aria-current={isActive ? "step" : undefined}
>
{isCompleted ? (
<Check size={16} />
) : (
<span className="text-xs font-semibold">{stepNumber}</span>
)}
</div>
<span
className={cn(
"text-xs mt-1 font-medium",
isActive && "text-[var(--interactive-primary)]",
isCompleted && "text-[var(--color-success)]",
!isActive && !isCompleted && "text-[var(--text-muted)]"
)}
>
{label}
</span>
</div>
{!isLast && (
<div
className={cn(
"flex-1 mx-2",
orientation === "vertical" ? "h-8 w-px my-1" : "h-px",
isCompleted ? "bg-[var(--color-success)]" : "bg-[var(--border-default)]"
)}
/>
)}
</div>
);
};
export function Stepper({ steps, currentStep, orientation = "horizontal", className, ...props }: StepperProps) {
return (
<div
data-slot="stepper"
className={cn(
"flex w-full",
orientation === "horizontal" ? "flex-row items-center" : "flex-col",
className
)}
role="list"
aria-label="Progress steps"
{...props}
>
{steps.map((step, index) => (
<div
key={step}
className={cn("flex items-center", orientation === "vertical" && "w-full")}
role="listitem"
>
<Step
label={step}
stepNumber={index + 1}
isActive={index === currentStep}
isCompleted={index < currentStep}
isLast={index === steps.length - 1}
orientation={orientation}
/>
</div>
))}
</div>
);
}
```
---
### 4. Current Sub-Tab/Navigation Implementation Details
**Current Structure:**
The CraftingTab currently uses a **4-stage linear workflow** with:
1. A visual Stepper component showing phases: Design → Prepare → Apply → Craft
2. Navigation buttons at the bottom to switch between stages
3. Conditional rendering of content based on `craftingStage` state
**Current Stages:**
- `design` - EnchantmentDesigner component (Design enchantments)
- `prepare` - EnchantmentPreparer component (Prepare equipment)
- `apply` - EnchantmentApplier component (Apply enchantments)
- `craft` - EquipmentCrafter component (Craft equipment)
**Issues to Address (Task Requirements):**
1. **Remove 1-4 progress bar** - The Stepper component (lines 58-68) needs to be removed
2. **Split Fabricate/Enchant** - Currently "Craft" (EquipmentCrafter) is mixed in with enchantment workflow. Need to split into:
- "Fabricate" tab - for EquipmentCrafter (crafting equipment)
- "Enchant" tab - for the Design → Prepare → Apply workflow
3. **Top sub-tabs** - Replace the bottom navigation buttons with proper top-level sub-tabs
**Current Navigation Pattern:**
- State: `craftingStage` (useState with 4 possible values)
- Navigation: 4 ActionButtons at the bottom of the tab
- Visual indicator: Stepper at the top showing progress through phases
**Suggested New Structure (for implementation):**
```
CraftingTab
├── Top Sub-Tabs: [Fabricate] [Enchant]
├── Fabricate Content: EquipmentCrafter
└── Enchant Content:
├── Sub-Navigation: [Design] [Prepare] [Apply]
├── Design: EnchantmentDesigner
├── Prepare: EnchantmentPreparer
└── Apply: EnchantmentApplier
```
---
### 5. Component Props Signatures
**EquipmentCrafterProps:**
```typescript
export interface EquipmentCrafterProps {
store: GameStore;
}
```
**EnchantmentDesignerProps:**
```typescript
export interface EnchantmentDesignerProps {
store: GameStore;
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;
}
```
**EnchantmentPreparerProps:**
```typescript
export interface EnchantmentPreparerProps {
store: GameStore;
selectedEquipmentInstance: string | null;
setSelectedEquipmentInstance: (id: string | null) => void;
}
```
**EnchantmentApplierProps:**
```typescript
export interface EnchantmentApplierProps {
store: GameStore;
selectedEquipmentInstance: string | null;
setSelectedEquipmentInstance: (id: string | null) => void;
selectedDesign: string | null;
setSelectedDesign: (id: string | null) => void;
onEnchantmentApplied?: () => void;
onCapacityExceeded?: (itemName: string, used: number, total: number) => void;
}
```
---
### 6. Key Observations for Restructuring
1. **Stepper Removal**: The `CRAFTING_PHASES` constant and `Stepper` component usage must be removed from CraftingTab
2. **State Management**: The `craftingStage` state will need to be replaced with:
- A top-level tab state (`fabricate` | `enchant`)
- An enchant sub-stage state (`design` | `prepare` | `apply`) when in enchant mode
3. **Progress Bars**: The activity indicators with Progress components (lines 133-236) should potentially be moved into their respective components (EquipmentCrafter, EnchantmentDesigner, etc.) rather than being in CraftingTab
4. **No Tab Component**: Currently, the app doesn't use a Tab component - it uses conditional rendering with ActionButtons. The restructured version should implement proper tabs at the top level
5. **Helper Functions**: The `safeToFixed` and `calcPercent` helpers are used for progress bars - these may no longer be needed in CraftingTab after restructuring
+282
View File
@@ -0,0 +1,282 @@
# Task 13 Context: Fix LootTab Nesting (Remove Redundant Layers)
## Task Description
Fix LootTab nesting (remove redundant layers) (PRIORITY 3b)
## Source Files
### 1. `/src/components/game/tabs/LootTab.tsx` (48 lines)
**Full Content:**
```typescript
'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import type { GameStore } from '@/lib/game/store';
import { LootInventoryDisplay } from '@/components/game/LootInventory';
export interface LootTabProps {
store: GameStore;
}
export function LootTab({ store }: LootTabProps) {
const inventory = store.lootInventory;
const elements = store.elements;
const equipmentInstances = store.equipmentInstances;
// Count items for badge
const materialCount = Object.values(inventory.materials).reduce((a, b) => a + b, 0);
const blueprintCount = inventory.blueprints.length;
const equipmentCount = Object.keys(equipmentInstances).length;
const totalItems = materialCount + blueprintCount + equipmentCount;
return (
<div className="space-y-4">
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 text-sm flex items-center gap-2">
💎 Loot Inventory
<Badge className="ml-auto bg-gray-800 text-gray-300">
{totalItems} items
</Badge>
</CardTitle>
</CardHeader>
<CardContent>
<LootInventoryDisplay
inventory={inventory}
elements={elements}
equipmentInstances={equipmentInstances}
onDeleteMaterial={store.deleteMaterial}
onDeleteEquipment={store.deleteEquipmentInstance}
/>
</CardContent>
</Card>
</div>
);
}
LootTab.displayName = "LootTab";
```
**Key Observations - LootTab Redundant Wrapper:**
- Uses `Card` component from `@/components/ui/card` with header "💎 Loot Inventory"
- Shows a badge with total items count
- Wraps `LootInventoryDisplay` inside `CardContent`
- **This creates the outer layer of nesting**
---
### 2. `/src/components/game/LootInventory.tsx` (499 lines)
**Component Interface:**
```typescript
interface LootInventoryProps {
inventory: LootInventoryType;
elements?: Record<string, ElementState>;
equipmentInstances?: Record<string, EquipmentInstance>;
onDeleteMaterial?: (materialId: string, amount: number) => void;
onDeleteEquipment?: (instanceId: string) => void;
}
```
**Main Component Export:**
```typescript
export function LootInventoryDisplay({
inventory,
elements,
equipmentInstances = {},
onDeleteMaterial,
onDeleteEquipment,
}: LootInventoryProps) {
// ... state and handlers ...
// Check if we have anything to show
const hasItems = totalItems > 0 || essenceCount > 0;
if (!hasItems) {
return (
<GameCard variant="default" className="w-full">
<div className="flex items-center gap-2 mb-2">
<Gem className="w-4 h-4 text-[var(--mana-light)]" />
<h3 className="text-[var(--mana-light)] game-panel-title text-xs uppercase tracking-wider">
Inventory
</h3>
</div>
<div className="text-[var(--text-muted)] text-sm text-center py-4">
No items collected yet. Defeat floors and guardians to find loot!
</div>
</GameCard>
);
}
// ... handlers ...
return (
<>
<GameCard variant="default" className="w-full">
<div className="flex items-center gap-2 mb-3">
<Gem className="w-4 h-4 text-[var(--mana-light)]" />
<h3 className="text-[var(--mana-light)] game-panel-title text-xs uppercase tracking-wider">
Inventory
</h3>
<Badge
className="ml-auto bg-[var(--bg-sunken)] text-[var(--text-secondary)] text-xs border-[var(--border-subtle)]"
aria-label={`${totalItems} items in inventory`}
>
{totalItems} items
</Badge>
</div>
{/* Search and Filter Controls */}
{/* ... */}
{/* Filter Tabs */}
{/* ... */}
<Separator className="bg-[var(--border-subtle)] mb-3" />
<ScrollArea className="h-64 w-full">
{/* Materials, Essence, Blueprints, Equipment sections */}
{/* ... */}
</ScrollArea>
</GameCard>
{/* Delete Confirmation Dialog */}
<AlertDialog open={!!deleteConfirm} onOpenChange={() => setDeleteConfirm(null)}>
{/* ... */}
</AlertDialog>
</>
);
}
```
**Key Observations - LootInventory Redundant Wrapper:**
- Uses `GameCard` component (from `@/components/ui/game-card`)
- Has its own header with "Inventory" title and `Gem` icon
- Shows a badge with total items count (duplicating LootTab's badge)
- Contains all the actual content: search, filters, items display
- **This creates the inner layer of nesting**
---
## 3. Duplicate Headings/Wrappers Issue
### Redundant Card Nesting:
```
LootTab (Outer Card)
├── CardHeader: "💎 Loot Inventory" + Badge: "{totalItems} items"
└── CardContent
└── LootInventoryDisplay (Inner GameCard)
├── Header: "Inventory" + Badge: "{totalItems} items" ← DUPLICATE
├── Search/Filter Controls
├── Items Display
└── Delete Dialog
```
### Specific Code Duplication:
**LootTab.tsx (lines 24-33) - Outer Header:**
```tsx
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 text-sm flex items-center gap-2">
💎 Loot Inventory
<Badge className="ml-auto bg-gray-800 text-gray-300">
{totalItems} items
</Badge>
</CardTitle>
</CardHeader>
```
**LootInventory.tsx (lines 191-202) - Inner Header (DUPLICATE):**
```tsx
<div className="flex items-center gap-2 mb-3">
<Gem className="w-4 h-4 text-[var(--mana-light)]" />
<h3 className="text-[var(--mana-light)] game-panel-title text-xs uppercase tracking-wider">
Inventory
</h3>
<Badge
className="ml-auto bg-[var(--bg-sunken)] text-[var(--text-secondary)] text-xs border-[var(--border-subtle)]"
aria-label={`${totalItems} items in inventory`}
>
{totalItems} items
</Badge>
</div>
```
### Redundant Badge Count:
- LootTab shows: `{totalItems} items`
- LootInventoryDisplay also shows: `{totalItems} items`
- Both calculate the same `totalItems` value
---
## 4. Current Component Hierarchy
```
Game.tsx / Main Game Layout
└── Tabs Component (renders active tab)
└── LootTab ({ store })
├── Card (bg-gray-900/80 border-gray-700)
│ ├── CardHeader
│ │ └── CardTitle: "💎 Loot Inventory" + Badge
│ └── CardContent
│ │
│ └── LootInventoryDisplay ({ inventory, elements, equipmentInstances, ... })
│ │
│ ├── GameCard (variant="default" className="w-full")
│ │ ├── Header: "Inventory" + Badge + Search/Filter
│ │ ├── Separator
│ │ ├── ScrollArea
│ │ │ ├── Materials Section
│ │ │ ├── Essence Section
│ │ │ ├── Blueprints Section
│ │ │ └── Equipment Section
│ │ └── (content)
│ │
│ └── AlertDialog (Delete Confirmation)
└── (end Card)
```
---
## 5. Comparison with Other Tabs
Looking at other tabs in `/src/components/game/tabs/`:
- **EquipmentTab.tsx**: Uses `GameCard` directly without an outer `Card` wrapper
- **CraftingTab.tsx**: Uses multiple `GameCard` components for different sections, no outer `Card`
- **GolemancyTab.tsx**: Uses `GameCard` directly
- **LabTab.tsx**: Uses `GameCard` directly
**Pattern**: Most tabs render their content directly using `GameCard` components without an additional `Card` wrapper from the tab itself.
---
## 6. Summary of Issues to Fix
1. **Redundant Card Wrapper in LootTab**: The `Card` + `CardHeader` + `CardContent` wrapper in LootTab.tsx is unnecessary since LootInventoryDisplay already provides its own `GameCard` wrapper.
2. **Duplicate Header**: Both LootTab and LootInventoryDisplay show similar headers with:
- Title text ("Loot Inventory" vs "Inventory")
- Item count badge
- Icon (emoji 💎 vs Gem icon)
3. **Double Wrapping**: Content is wrapped in two card-like components:
- Outer: `Card` from `@/components/ui/card`
- Inner: `GameCard` from `@/components/ui/game-card`
4. **Solution Direction**: Remove the outer `Card` wrapper from LootTab.tsx and let LootInventoryDisplay handle the card styling, OR remove the inner `GameCard` from LootInventoryDisplay and keep only the outer wrapper.
---
## 7. Files That Would Need Modification
1. **`/src/components/game/tabs/LootTab.tsx`** - Remove outer Card wrapper, pass props directly to LootInventoryDisplay
2. **`/src/components/game/LootInventory.tsx`** - Potentially remove GameCard wrapper if keeping LootTab's Card, or keep as-is if removing LootTab's Card
**Recommended Approach**: Remove the outer `Card` wrapper from `LootTab.tsx` and let `LootInventoryDisplay` handle the full display (since it already has a complete GameCard wrapper with header, controls, and content).
+17
View File
@@ -84,6 +84,7 @@
"bun-types": "^1.3.4", "bun-types": "^1.3.4",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "^16.1.1", "eslint-config-next": "^16.1.1",
"husky": "^9.1.7",
"jsdom": "^29.0.1", "jsdom": "^29.0.1",
"tailwindcss": "^4", "tailwindcss": "^4",
"tw-animate-css": "^1.3.5", "tw-animate-css": "^1.3.5",
@@ -9619,6 +9620,22 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/husky": {
"version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true,
"license": "MIT",
"bin": {
"husky": "bin.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/icu-minify": { "node_modules/icu-minify": {
"version": "4.9.1", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.9.1.tgz", "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.9.1.tgz",
+3 -1
View File
@@ -12,7 +12,8 @@
"db:push": "prisma db push", "db:push": "prisma db push",
"db:generate": "prisma generate", "db:generate": "prisma generate",
"db:migrate": "prisma migrate dev", "db:migrate": "prisma migrate dev",
"db:reset": "prisma migrate reset" "db:reset": "prisma migrate reset",
"prepare": "husky"
}, },
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.3.1", "@dnd-kit/core": "^6.3.1",
@@ -91,6 +92,7 @@
"bun-types": "^1.3.4", "bun-types": "^1.3.4",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "^16.1.1", "eslint-config-next": "^16.1.1",
"husky": "^9.1.7",
"jsdom": "^29.0.1", "jsdom": "^29.0.1",
"tailwindcss": "^4", "tailwindcss": "^4",
"tw-animate-css": "^1.3.5", "tw-animate-css": "^1.3.5",
+7 -29
View File
@@ -1,7 +1,5 @@
'use client'; 'use client';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import type { GameStore } from '@/lib/game/store'; import type { GameStore } from '@/lib/game/store';
import { LootInventoryDisplay } from '@/components/game/LootInventory'; import { LootInventoryDisplay } from '@/components/game/LootInventory';
@@ -14,34 +12,14 @@ export function LootTab({ store }: LootTabProps) {
const elements = store.elements; const elements = store.elements;
const equipmentInstances = store.equipmentInstances; const equipmentInstances = store.equipmentInstances;
// Count items for badge
const materialCount = Object.values(inventory.materials).reduce((a, b) => a + b, 0);
const blueprintCount = inventory.blueprints.length;
const equipmentCount = Object.keys(equipmentInstances).length;
const totalItems = materialCount + blueprintCount + equipmentCount;
return ( return (
<div className="space-y-4"> <LootInventoryDisplay
<Card className="bg-gray-900/80 border-gray-700"> inventory={inventory}
<CardHeader className="pb-2"> elements={elements}
<CardTitle className="text-amber-400 text-sm flex items-center gap-2"> equipmentInstances={equipmentInstances}
💎 Loot Inventory onDeleteMaterial={store.deleteMaterial}
<Badge className="ml-auto bg-gray-800 text-gray-300"> onDeleteEquipment={store.deleteEquipmentInstance}
{totalItems} items />
</Badge>
</CardTitle>
</CardHeader>
<CardContent>
<LootInventoryDisplay
inventory={inventory}
elements={elements}
equipmentInstances={equipmentInstances}
onDeleteMaterial={store.deleteMaterial}
onDeleteEquipment={store.deleteEquipmentInstance}
/>
</CardContent>
</Card>
</div>
); );
} }
-1
View File
@@ -1 +0,0 @@
console.log('test');