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:
102
src/stores/skills.ts
Normal file
102
src/stores/skills.ts
Normal 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
|
||||
),
|
||||
}));
|
||||
},
|
||||
}));
|
||||
Reference in New Issue
Block a user