feat(hands): restructure Hands UI with Chinese localization

Major changes:
- Add HandList.tsx component for left sidebar
- Add HandTaskPanel.tsx for middle content area
- Restructure Sidebar tabs: 分身/HANDS/Workflow
- Remove Hands tab from RightPanel
- Localize all UI text to Chinese
- Archive legacy OpenClaw documentation
- Add Hands integration lessons document
- Update feature checklist with new components

UI improvements:
- Left sidebar now shows Hands list with status icons
- Middle area shows selected Hand's tasks and results
- Consistent styling with Tailwind CSS
- Chinese status labels and buttons

Documentation:
- Create docs/archive/openclaw-legacy/ for old docs
- Add docs/knowledge-base/hands-integration-lessons.md
- Update docs/knowledge-base/feature-checklist.md
- Update docs/knowledge-base/README.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-14 23:16:32 +08:00
parent 67e1da635d
commit 07079293f4
126 changed files with 36229 additions and 1035 deletions

View File

@@ -19,6 +19,14 @@ const OPENCLAW_HOME = process.env.OPENCLAW_HOME || path.join(
const ZCLAW_ROOT = path.resolve(__dirname, '..');
function uniqueStrings(values: string[]): string[] {
return Array.from(new Set(values.filter(Boolean)));
}
function resolveRepoPaths(values: string[] | undefined): string[] {
return (values || []).map((p) => path.resolve(ZCLAW_ROOT, p));
}
function log(msg: string) {
console.log(`[ZCLAW Setup] ${msg}`);
}
@@ -50,35 +58,37 @@ function checkOpenClaw(): boolean {
function setupConfig() {
const configDir = OPENCLAW_HOME;
const configFile = path.join(configDir, 'openclaw.json');
const defaultConfigPath = path.join(ZCLAW_ROOT, 'config', 'openclaw.default.json');
// Create directory
fs.mkdirSync(configDir, { recursive: true });
// Copy default config if no config exists
if (!fs.existsSync(configFile)) {
const defaultConfig = fs.readFileSync(
path.join(ZCLAW_ROOT, 'config', 'openclaw.default.json'),
'utf-8'
);
// Update plugin paths to absolute paths
const config = JSON.parse(defaultConfig);
if (config.plugins?.load?.paths) {
config.plugins.load.paths = config.plugins.load.paths.map((p: string) =>
path.resolve(ZCLAW_ROOT, p)
);
}
if (config.skills?.load?.extraDirs) {
config.skills.load.extraDirs = config.skills.load.extraDirs.map((p: string) =>
path.resolve(ZCLAW_ROOT, p)
);
}
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf-8');
success(`Config written to ${configFile}`);
} else {
log(`Config already exists at ${configFile}, skipping`);
}
const defaultConfig = JSON.parse(fs.readFileSync(defaultConfigPath, 'utf-8'));
const config = fs.existsSync(configFile)
? JSON.parse(fs.readFileSync(configFile, 'utf-8'))
: defaultConfig;
const defaultPluginPaths = resolveRepoPaths(defaultConfig.plugins?.load?.paths);
const existingPluginPaths = Array.isArray(config.plugins?.load?.paths)
? config.plugins.load.paths
: [];
const mergedPluginPaths = uniqueStrings([...existingPluginPaths, ...defaultPluginPaths]);
const defaultSkillDirs = resolveRepoPaths(defaultConfig.skills?.load?.extraDirs);
const existingSkillDirs = Array.isArray(config.skills?.load?.extraDirs)
? config.skills.load.extraDirs
: [];
const mergedSkillDirs = uniqueStrings([...existingSkillDirs, ...defaultSkillDirs]);
config.plugins = config.plugins || {};
config.plugins.load = config.plugins.load || {};
config.plugins.load.paths = mergedPluginPaths;
config.skills = config.skills || {};
config.skills.load = config.skills.load || {};
config.skills.load.extraDirs = mergedSkillDirs;
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf-8');
success(fs.existsSync(configFile) ? `Config updated at ${configFile}` : `Config written to ${configFile}`);
// Copy bootstrap files
const bootstrapFiles = ['SOUL.md', 'AGENTS.md', 'IDENTITY.md', 'USER.md'];