feat(core): initialize project skeleton with Electron + React + TypeScript

Set up the complete project foundation for ClawX, a graphical AI assistant:

- Electron main process with IPC handlers, menu, tray, and gateway management
- React renderer with routing, layout components, and page scaffolding
- Zustand state management for gateway, settings, channels, skills, chat, and cron
- shadcn/ui components with Tailwind CSS and CSS variable theming
- Build tooling with Vite, electron-builder, and TypeScript configuration
- Testing setup with Vitest and Playwright
- Development configurations (ESLint, Prettier, gitignore, env example)
This commit is contained in:
Haze
2026-02-05 23:09:17 +08:00
Unverified
parent 9442e5f77a
commit b8ab0208d0
71 changed files with 14086 additions and 3 deletions

102
src/stores/skills.ts Normal file
View File

@@ -0,0 +1,102 @@
/**
* Skills State Store
* Manages skill/plugin state
*/
import { create } from 'zustand';
import type { Skill } from '../types/skill';
interface SkillsState {
skills: Skill[];
loading: boolean;
error: string | null;
// Actions
fetchSkills: () => Promise<void>;
enableSkill: (skillId: string) => Promise<void>;
disableSkill: (skillId: string) => Promise<void>;
setSkills: (skills: Skill[]) => void;
updateSkill: (skillId: string, updates: Partial<Skill>) => void;
}
export const useSkillsStore = create<SkillsState>((set, get) => ({
skills: [],
loading: false,
error: null,
fetchSkills: async () => {
set({ loading: true, error: null });
try {
const result = await window.electron.ipcRenderer.invoke(
'gateway:rpc',
'skills.list'
) as { success: boolean; result?: Skill[]; error?: string };
if (result.success && result.result) {
set({ skills: result.result, loading: false });
} else {
set({ error: result.error || 'Failed to fetch skills', loading: false });
}
} catch (error) {
set({ error: String(error), loading: false });
}
},
enableSkill: async (skillId) => {
const { updateSkill } = get();
try {
const result = await window.electron.ipcRenderer.invoke(
'gateway:rpc',
'skills.enable',
{ skillId }
) as { success: boolean; error?: string };
if (result.success) {
updateSkill(skillId, { enabled: true });
} else {
throw new Error(result.error || 'Failed to enable skill');
}
} catch (error) {
console.error('Failed to enable skill:', error);
throw error;
}
},
disableSkill: async (skillId) => {
const { updateSkill, skills } = get();
// Check if skill is a core skill
const skill = skills.find((s) => s.id === skillId);
if (skill?.isCore) {
throw new Error('Cannot disable core skill');
}
try {
const result = await window.electron.ipcRenderer.invoke(
'gateway:rpc',
'skills.disable',
{ skillId }
) as { success: boolean; error?: string };
if (result.success) {
updateSkill(skillId, { enabled: false });
} else {
throw new Error(result.error || 'Failed to disable skill');
}
} catch (error) {
console.error('Failed to disable skill:', error);
throw error;
}
},
setSkills: (skills) => set({ skills }),
updateSkill: (skillId, updates) => {
set((state) => ({
skills: state.skills.map((skill) =>
skill.id === skillId ? { ...skill, ...updates } : skill
),
}));
},
}));