feat(phase-11): add store coordinator layer

- Create store/index.ts as unified entry point
- Add useCompositeStore() hook for multi-slice access
- Add initializeStores() for client injection
- Re-export all individual stores for direct access
- Maintain backward compatibility with useGatewayStore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-15 20:18:44 +08:00
parent f22b1a2095
commit 01737a7ef5
2 changed files with 264 additions and 3 deletions

261
desktop/src/store/index.ts Normal file
View File

@@ -0,0 +1,261 @@
/**
* Store Coordinator
*
* This module provides a unified interface to all specialized stores,
* maintaining backward compatibility with components that import useGatewayStore.
*
* The coordinator:
* 1. Injects the shared client into all stores
* 2. Provides a composite hook that combines all store slices
* 3. Re-exports all individual stores for direct access
*/
// === Re-export Individual Stores ===
export { useConnectionStore, getConnectionState, getClient, getConnectionError, getGatewayVersion } from './connectionStore';
export type { ConnectionStore, ConnectionStateSlice, ConnectionActionsSlice, GatewayLog } from './connectionStore';
export { useAgentStore, setAgentStoreClient } from './agentStore';
export type { AgentStore, AgentStateSlice, AgentActionsSlice, Clone, UsageStats, PluginStatus, CloneCreateOptions } from './agentStore';
export { useHandStore, setHandStoreClient } from './handStore';
export type { HandStore, HandStateSlice, HandActionsSlice, Hand, HandRun, Trigger, Approval, TriggerCreateOptions } from './handStore';
export { useWorkflowStore, setWorkflowStoreClient } from './workflowStore';
export type { WorkflowStore, WorkflowStateSlice, WorkflowActionsSlice, Workflow, WorkflowRun, WorkflowCreateOptions } from './workflowStore';
export { useConfigStore, setConfigStoreClient } from './configStore';
export type { ConfigStore, ConfigStateSlice, ConfigActionsSlice, QuickConfig, WorkspaceInfo, ChannelInfo, ScheduledTask, SkillInfo } from './configStore';
// === Composite Store Hook ===
import { useEffect, useMemo } from 'react';
import { useConnectionStore, getClient } from './connectionStore';
import { useAgentStore, setAgentStoreClient } from './agentStore';
import { useHandStore, setHandStoreClient } from './handStore';
import { useWorkflowStore, setWorkflowStoreClient } from './workflowStore';
import { useConfigStore, setConfigStoreClient } from './configStore';
import type { GatewayClient } from '../lib/gateway-client';
/**
* Initialize all stores with the shared client.
* Called once when the application mounts.
*/
export function initializeStores(): void {
const client = getClient();
// Inject client into all stores
setAgentStoreClient(client);
setHandStoreClient(client);
setWorkflowStoreClient(client);
setConfigStoreClient(client);
}
/**
* Hook that provides a composite view of all stores.
* Use this for components that need access to multiple store slices.
*
* For components that only need specific slices, import the individual
* store hooks directly (e.g., useConnectionStore, useAgentStore).
*/
export function useCompositeStore() {
// Subscribe to all stores
const connectionState = useConnectionStore((s) => s.connectionState);
const gatewayVersion = useConnectionStore((s) => s.gatewayVersion);
const connectionError = useConnectionStore((s) => s.error);
const logs = useConnectionStore((s) => s.logs);
const localGateway = useConnectionStore((s) => s.localGateway);
const localGatewayBusy = useConnectionStore((s) => s.localGatewayBusy);
const isLoading = useConnectionStore((s) => s.isLoading);
const client = useConnectionStore((s) => s.client);
const clones = useAgentStore((s) => s.clones);
const usageStats = useAgentStore((s) => s.usageStats);
const pluginStatus = useAgentStore((s) => s.pluginStatus);
const hands = useHandStore((s) => s.hands);
const handRuns = useHandStore((s) => s.handRuns);
const triggers = useHandStore((s) => s.triggers);
const approvals = useHandStore((s) => s.approvals);
const workflows = useWorkflowStore((s) => s.workflows);
const workflowRuns = useWorkflowStore((s) => s.workflowRuns);
const quickConfig = useConfigStore((s) => s.quickConfig);
const workspaceInfo = useConfigStore((s) => s.workspaceInfo);
const channels = useConfigStore((s) => s.channels);
const scheduledTasks = useConfigStore((s) => s.scheduledTasks);
const skillsCatalog = useConfigStore((s) => s.skillsCatalog);
const models = useConfigStore((s) => s.models);
const modelsLoading = useConfigStore((s) => s.modelsLoading);
const modelsError = useConfigStore((s) => s.modelsError);
// Get all actions
const connect = useConnectionStore((s) => s.connect);
const disconnect = useConnectionStore((s) => s.disconnect);
const clearLogs = useConnectionStore((s) => s.clearLogs);
const refreshLocalGateway = useConnectionStore((s) => s.refreshLocalGateway);
const startLocalGateway = useConnectionStore((s) => s.startLocalGateway);
const stopLocalGateway = useConnectionStore((s) => s.stopLocalGateway);
const restartLocalGateway = useConnectionStore((s) => s.restartLocalGateway);
const loadClones = useAgentStore((s) => s.loadClones);
const createClone = useAgentStore((s) => s.createClone);
const updateClone = useAgentStore((s) => s.updateClone);
const deleteClone = useAgentStore((s) => s.deleteClone);
const loadUsageStats = useAgentStore((s) => s.loadUsageStats);
const loadPluginStatus = useAgentStore((s) => s.loadPluginStatus);
const loadHands = useHandStore((s) => s.loadHands);
const getHandDetails = useHandStore((s) => s.getHandDetails);
const triggerHand = useHandStore((s) => s.triggerHand);
const loadHandRuns = useHandStore((s) => s.loadHandRuns);
const loadTriggers = useHandStore((s) => s.loadTriggers);
const createTrigger = useHandStore((s) => s.createTrigger);
const deleteTrigger = useHandStore((s) => s.deleteTrigger);
const loadApprovals = useHandStore((s) => s.loadApprovals);
const approveRequest = useHandStore((s) => s.approveRequest);
const loadWorkflows = useWorkflowStore((s) => s.loadWorkflows);
const getWorkflow = useWorkflowStore((s) => s.getWorkflow);
const createWorkflow = useWorkflowStore((s) => s.createWorkflow);
const updateWorkflow = useWorkflowStore((s) => s.updateWorkflow);
const deleteWorkflow = useWorkflowStore((s) => s.deleteWorkflow);
const triggerWorkflow = useWorkflowStore((s) => s.triggerWorkflow);
const loadWorkflowRuns = useWorkflowStore((s) => s.loadWorkflowRuns);
const loadQuickConfig = useConfigStore((s) => s.loadQuickConfig);
const saveQuickConfig = useConfigStore((s) => s.saveQuickConfig);
const loadWorkspaceInfo = useConfigStore((s) => s.loadWorkspaceInfo);
const loadChannels = useConfigStore((s) => s.loadChannels);
const getChannel = useConfigStore((s) => s.getChannel);
const createChannel = useConfigStore((s) => s.createChannel);
const updateChannel = useConfigStore((s) => s.updateChannel);
const deleteChannel = useConfigStore((s) => s.deleteChannel);
const loadScheduledTasks = useConfigStore((s) => s.loadScheduledTasks);
const createScheduledTask = useConfigStore((s) => s.createScheduledTask);
const loadSkillsCatalog = useConfigStore((s) => s.loadSkillsCatalog);
const getSkill = useConfigStore((s) => s.getSkill);
const createSkill = useConfigStore((s) => s.createSkill);
const updateSkill = useConfigStore((s) => s.updateSkill);
const deleteSkill = useConfigStore((s) => s.deleteSkill);
const loadModels = useConfigStore((s) => s.loadModels);
// Memoize the composite store to prevent unnecessary re-renders
return useMemo(() => ({
// Connection state
connectionState,
gatewayVersion,
error: connectionError,
logs,
localGateway,
localGatewayBusy,
isLoading,
client,
// Agent state
clones,
usageStats,
pluginStatus,
// Hand state
hands,
handRuns,
triggers,
approvals,
// Workflow state
workflows,
workflowRuns,
// Config state
quickConfig,
workspaceInfo,
channels,
scheduledTasks,
skillsCatalog,
models,
modelsLoading,
modelsError,
// Connection actions
connect,
disconnect,
clearLogs,
refreshLocalGateway,
startLocalGateway,
stopLocalGateway,
restartLocalGateway,
// Agent actions
loadClones,
createClone,
updateClone,
deleteClone,
loadUsageStats,
loadPluginStatus,
// Hand actions
loadHands,
getHandDetails,
triggerHand,
loadHandRuns,
loadTriggers,
createTrigger,
deleteTrigger,
loadApprovals,
approveRequest,
// Workflow actions
loadWorkflows,
getWorkflow,
createWorkflow,
updateWorkflow,
deleteWorkflow,
triggerWorkflow,
loadWorkflowRuns,
// Config actions
loadQuickConfig,
saveQuickConfig,
loadWorkspaceInfo,
loadChannels,
getChannel,
createChannel,
updateChannel,
deleteChannel,
loadScheduledTasks,
createScheduledTask,
loadSkillsCatalog,
getSkill,
createSkill,
updateSkill,
deleteSkill,
loadModels,
// Legacy sendMessage (delegates to client)
sendMessage: async (message: string, sessionKey?: string) => {
return client.chat(message, { sessionKey });
},
}), [
connectionState, gatewayVersion, connectionError, logs, localGateway, localGatewayBusy, isLoading, client,
clones, usageStats, pluginStatus,
hands, handRuns, triggers, approvals,
workflows, workflowRuns,
quickConfig, workspaceInfo, channels, scheduledTasks, skillsCatalog, models, modelsLoading, modelsError,
connect, disconnect, clearLogs, refreshLocalGateway, startLocalGateway, stopLocalGateway, restartLocalGateway,
loadClones, createClone, updateClone, deleteClone, loadUsageStats, loadPluginStatus,
loadHands, getHandDetails, triggerHand, loadHandRuns, loadTriggers, createTrigger, deleteTrigger, loadApprovals, approveRequest,
loadWorkflows, getWorkflow, createWorkflow, updateWorkflow, deleteWorkflow, triggerWorkflow, loadWorkflowRuns,
loadQuickConfig, saveQuickConfig, loadWorkspaceInfo, loadChannels, getChannel, createChannel, updateChannel, deleteChannel,
loadScheduledTasks, createScheduledTask, loadSkillsCatalog, getSkill, createSkill, updateSkill, deleteSkill, loadModels,
]);
}
/**
* Initialize stores on module load.
* This ensures all stores have access to the shared client.
*/
if (typeof window !== 'undefined') {
// Defer initialization to next tick to ensure all modules are loaded
setTimeout(initializeStores, 0);
}

View File

@@ -567,12 +567,12 @@ ZCLAW 是基于 **OpenFang** (Rust Agent OS) 的 AI Agent 桌面客户端,核
*`handStore.ts` (498 行) - Hands、Triggers、Approvals
*`workflowStore.ts` (255 行) - Workflows、WorkflowRuns
*`configStore.ts` (537 行) - QuickConfig、Channels、Skills、Models
*`store/index.ts` - 协调层,组合所有 stores
* Store 行数: gatewayStore 1660 → 5 个子 Store (平均 358 行)
* 待完成:
* 🔄 创建协调层 (coordinator)
* 🔄 更新组件导入
* 🔄 更新组件导入 (可选,向后兼容)
* 代码质量:
* ✅ TypeScript 类型检查通过
*下一步: Phase 11 协调层创建*
*下一步: Phase 12 性能优化*