Makelore 2.0 initial clean snapshot

This commit is contained in:
inman
2026-07-29 17:22:35 +08:00
commit b8ca3f8eea
694 changed files with 139782 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
import { create } from 'zustand';
import { hostApiFetch } from '@/lib/host-api';
import type { ProjectConfig } from '../../shared/project-config';
type ProjectConfigResponse = {
status: 'valid' | 'missing' | 'invalid';
config?: ProjectConfig;
error?: string;
knowledgeFiles: string[];
};
type ProjectConfigState = {
configsByProjectId: Record<string, ProjectConfig>;
knowledgeByProjectId: Record<string, string[]>;
loadingProjectId: string | null;
errorsByProjectId: Record<string, string>;
load: (projectId: string) => Promise<ProjectConfigResponse>;
save: (projectId: string, config: ProjectConfig) => Promise<ProjectConfig>;
uploadKnowledge: (projectId: string, file: File) => Promise<void>;
remove: (projectId: string) => void;
};
export const useProjectConfigStore = create<ProjectConfigState>((set) => ({
configsByProjectId: {},
knowledgeByProjectId: {},
loadingProjectId: null,
errorsByProjectId: {},
async load(projectId) {
set({ loadingProjectId: projectId });
try {
const response = await hostApiFetch<ProjectConfigResponse>(`/api/opencode/projects/config?projectId=${encodeURIComponent(projectId)}`);
set((state) => ({
loadingProjectId: null,
configsByProjectId: response.status === 'valid' && response.config
? { ...state.configsByProjectId, [projectId]: response.config }
: state.configsByProjectId,
knowledgeByProjectId: { ...state.knowledgeByProjectId, [projectId]: response.knowledgeFiles ?? [] },
errorsByProjectId: response.status === 'invalid'
? { ...state.errorsByProjectId, [projectId]: response.error ?? '项目配置无效' }
: Object.fromEntries(Object.entries(state.errorsByProjectId).filter(([id]) => id !== projectId)),
}));
return response;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
set((state) => ({ loadingProjectId: null, errorsByProjectId: { ...state.errorsByProjectId, [projectId]: message } }));
throw error;
}
},
async save(projectId, config) {
const response = await hostApiFetch<{ success: boolean; config?: ProjectConfig; knowledgeFiles?: string[]; error?: string }>('/api/opencode/projects/config', {
method: 'PUT',
body: JSON.stringify({ projectId, config }),
});
if (!response.success || !response.config) throw new Error(response.error || '保存项目配置失败');
set((state) => ({
configsByProjectId: { ...state.configsByProjectId, [projectId]: response.config as ProjectConfig },
knowledgeByProjectId: { ...state.knowledgeByProjectId, [projectId]: response.knowledgeFiles ?? [] },
errorsByProjectId: Object.fromEntries(Object.entries(state.errorsByProjectId).filter(([id]) => id !== projectId)),
}));
return response.config;
},
async uploadKnowledge(projectId, file) {
const bytes = new Uint8Array(await file.arrayBuffer());
let binary = '';
for (let offset = 0; offset < bytes.length; offset += 0x8000) {
binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
}
const response = await hostApiFetch<{ success: boolean; knowledgeFiles?: string[]; error?: string }>('/api/opencode/projects/knowledge', {
method: 'POST',
body: JSON.stringify({ projectId, fileName: file.name, contentBase64: btoa(binary) }),
});
if (!response.success) throw new Error(response.error || '知识文件上传失败');
set((state) => ({ knowledgeByProjectId: { ...state.knowledgeByProjectId, [projectId]: response.knowledgeFiles ?? [] } }));
},
remove(projectId) {
set((state) => ({
configsByProjectId: Object.fromEntries(Object.entries(state.configsByProjectId).filter(([id]) => id !== projectId)),
knowledgeByProjectId: Object.fromEntries(Object.entries(state.knowledgeByProjectId).filter(([id]) => id !== projectId)),
errorsByProjectId: Object.fromEntries(Object.entries(state.errorsByProjectId).filter(([id]) => id !== projectId)),
}));
},
}));