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,307 @@
import { create } from 'zustand';
import { createJSONStorage, persist, type StateStorage } from 'zustand/middleware';
import type { BuiltInRoleSubagentId } from '@/lib/role-subagents';
import { toUserSyncPartner, type UserSyncPartner } from '../../shared/user-sync';
export const partnerAvatarToneOptions = [
'bg-[#b47745]',
'bg-[#FDE8E1]',
'bg-[#F26A3D]',
'bg-[#DCE6EF]',
'bg-[#EEF3F7]',
'bg-[#b8a7ff]',
] as const;
export type PartnerProfile = {
nickname?: string;
description?: string;
avatarTone?: string;
avatarDataUrl?: string;
updatedAt: string;
};
export type PartnerProfileInput = {
nickname?: string;
description?: string;
avatarTone?: string;
avatarDataUrl?: string;
};
export type PartnerTemplateRoleId = BuiltInRoleSubagentId | 'custom';
export type CreatedPartnerSubagent = {
id: string;
displayName: string;
templateRoleId: PartnerTemplateRoleId;
skillIds?: string[];
prompt?: string;
description?: string;
avatarTone?: string;
avatarDataUrl?: string;
filePath?: string;
createdAt: string;
updatedAt: string;
};
export type ProjectSubagentRuntimeConfig = {
filePath?: string;
skillIds?: string[];
updatedAt: string;
};
type PartnerProfilesState = {
subagents: CreatedPartnerSubagent[];
projectRuntimeConfigsByProjectId: Record<string, Record<string, ProjectSubagentRuntimeConfig>>;
profilesById: Partial<Record<BuiltInRoleSubagentId, PartnerProfile>>;
addSubagent: (subagent: CreatedPartnerSubagent) => void;
updatePartnerProfile: (agentId: string, profile: PartnerProfileInput) => void;
updateSubagentRuntimeConfig: (agentId: string, config: Pick<CreatedPartnerSubagent, 'skillIds' | 'filePath' | 'updatedAt'>) => void;
updateProjectSubagentRuntimeConfig: (projectId: string, agentId: string, config: ProjectSubagentRuntimeConfig) => void;
applySyncedPartners: (partners: UserSyncPartner[]) => void;
resetPartnerProfile: (agentId: string) => void;
removeSubagent: (agentId: string) => void;
};
const inMemoryPartnerProfileStorage = (() => {
const data = new Map<string, string>();
return {
getItem: (name: string) => data.get(name) ?? null,
setItem: (name: string, value: string) => {
data.set(name, value);
},
removeItem: (name: string) => {
data.delete(name);
},
} satisfies StateStorage;
})();
function getPartnerProfileStorage(): StateStorage {
if (typeof window === 'undefined') {
return inMemoryPartnerProfileStorage;
}
if (typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().includes('jsdom')) {
return inMemoryPartnerProfileStorage;
}
try {
const storage = window.localStorage;
const testKey = 'niancode-partner-profile-storage-test';
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return storage;
} catch {
return inMemoryPartnerProfileStorage;
}
}
function cleanOptionalText(value: string | undefined): string | undefined {
const trimmed = value?.trim();
return trimmed ? trimmed : undefined;
}
function cleanPartnerProfileInput(input: PartnerProfileInput): Omit<PartnerProfile, 'updatedAt'> {
return {
nickname: cleanOptionalText(input.nickname),
description: cleanOptionalText(input.description),
avatarTone: cleanOptionalText(input.avatarTone),
avatarDataUrl: cleanOptionalText(input.avatarDataUrl),
};
}
function hasProfileData(profile: Omit<PartnerProfile, 'updatedAt'>): boolean {
return Boolean(profile.nickname || profile.description || profile.avatarTone || profile.avatarDataUrl);
}
function normalizeSkillIds(value: string[] | undefined): string[] | undefined {
if (!Array.isArray(value)) return undefined;
const seen = new Set<string>();
const result: string[] = [];
for (const item of value) {
const skillId = item.trim();
if (skillId && !seen.has(skillId)) {
seen.add(skillId);
result.push(skillId);
}
}
return result;
}
function pushSyncedPartner(partner: CreatedPartnerSubagent): void {
void import('@/stores/user-sync')
.then(({ useUserSyncStore }) => useUserSyncStore.getState().pushPartner(toUserSyncPartner(partner)))
.catch(() => {});
}
export const usePartnerProfilesStore = create<PartnerProfilesState>()(
persist(
(set) => ({
subagents: [],
projectRuntimeConfigsByProjectId: {},
profilesById: {},
addSubagent: (subagent) => {
const now = new Date().toISOString();
const normalized: CreatedPartnerSubagent = {
...subagent,
displayName: cleanOptionalText(subagent.displayName) ?? subagent.displayName,
description: cleanOptionalText(subagent.description),
avatarTone: cleanOptionalText(subagent.avatarTone),
avatarDataUrl: cleanOptionalText(subagent.avatarDataUrl),
prompt: cleanOptionalText(subagent.prompt),
skillIds: normalizeSkillIds(subagent.skillIds),
filePath: undefined,
createdAt: subagent.createdAt || now,
updatedAt: subagent.updatedAt || now,
};
set((state) => {
return {
subagents: [
normalized,
...state.subagents.filter((current) => current.id !== normalized.id),
],
};
});
pushSyncedPartner(normalized);
},
updatePartnerProfile: (agentId, profile) => {
let updatedSubagent: CreatedPartnerSubagent | null = null;
set((state) => {
const cleaned = cleanPartnerProfileInput(profile);
const now = new Date().toISOString();
const nextSubagents = state.subagents.map((subagent) => {
if (subagent.id !== agentId) return subagent;
updatedSubagent = {
...subagent,
displayName: cleaned.nickname ?? subagent.displayName,
description: cleaned.description,
avatarTone: cleaned.avatarTone ?? subagent.avatarTone,
avatarDataUrl: cleaned.avatarDataUrl,
updatedAt: now,
};
return updatedSubagent;
});
if (nextSubagents.some((subagent) => subagent.id === agentId)) {
return { subagents: nextSubagents };
}
const nextProfiles = { ...state.profilesById };
const legacyAgentId = agentId as BuiltInRoleSubagentId;
if (!hasProfileData(cleaned)) {
delete nextProfiles[legacyAgentId];
return { profilesById: nextProfiles };
}
nextProfiles[legacyAgentId] = { ...cleaned, updatedAt: now };
return { profilesById: nextProfiles };
});
if (updatedSubagent) {
pushSyncedPartner(updatedSubagent);
}
},
updateSubagentRuntimeConfig: (agentId, config) => {
let updatedSubagent: CreatedPartnerSubagent | null = null;
set((state) => ({
subagents: state.subagents.map((subagent) => (
subagent.id === agentId
? (updatedSubagent = {
...subagent,
skillIds: normalizeSkillIds(config.skillIds) ?? [],
updatedAt: config.updatedAt || new Date().toISOString(),
})
: subagent
)),
}));
if (updatedSubagent) {
pushSyncedPartner(updatedSubagent);
}
},
updateProjectSubagentRuntimeConfig: (projectId, agentId, config) => {
const cleanProjectId = projectId.trim();
const cleanAgentId = agentId.trim();
if (!cleanProjectId || !cleanAgentId) return;
set((state) => ({
projectRuntimeConfigsByProjectId: {
...state.projectRuntimeConfigsByProjectId,
[cleanProjectId]: {
...(state.projectRuntimeConfigsByProjectId[cleanProjectId] ?? {}),
[cleanAgentId]: {
filePath: cleanOptionalText(config.filePath),
skillIds: normalizeSkillIds(config.skillIds) ?? [],
updatedAt: config.updatedAt || new Date().toISOString(),
},
},
},
}));
},
resetPartnerProfile: (agentId) => {
let updatedSubagent: CreatedPartnerSubagent | null = null;
set((state) => {
if (state.subagents.some((subagent) => subagent.id === agentId)) {
return {
subagents: state.subagents.map((subagent) => (
subagent.id === agentId
? (updatedSubagent = {
...subagent,
description: undefined,
avatarDataUrl: undefined,
updatedAt: new Date().toISOString(),
})
: subagent
)),
};
}
const nextProfiles = { ...state.profilesById };
delete nextProfiles[agentId as BuiltInRoleSubagentId];
return { profilesById: nextProfiles };
});
if (updatedSubagent) {
pushSyncedPartner(updatedSubagent);
}
},
applySyncedPartners: (partners) => {
set((state) => ({
subagents: partners.map((partner) => ({
id: partner.id,
displayName: partner.displayName,
templateRoleId: partner.templateRoleId as PartnerTemplateRoleId,
description: partner.description,
avatarTone: partner.avatarTone,
avatarDataUrl: partner.avatarDataUrl,
prompt: partner.prompt,
skillIds: normalizeSkillIds(partner.skillIds),
createdAt: partner.createdAt ?? new Date().toISOString(),
updatedAt: partner.updatedAt ?? new Date().toISOString(),
})),
projectRuntimeConfigsByProjectId: state.projectRuntimeConfigsByProjectId,
}));
},
removeSubagent: (agentId) => {
set((state) => ({
subagents: state.subagents.filter((subagent) => subagent.id !== agentId),
projectRuntimeConfigsByProjectId: Object.fromEntries(
Object.entries(state.projectRuntimeConfigsByProjectId).map(([projectId, configs]) => {
const nextConfigs = { ...configs };
delete nextConfigs[agentId];
return [projectId, nextConfigs];
}),
),
}));
},
}),
{
name: 'niancode-partner-profiles',
storage: createJSONStorage(getPartnerProfileStorage),
partialize: (state) => ({
subagents: state.subagents,
projectRuntimeConfigsByProjectId: state.projectRuntimeConfigsByProjectId,
}),
},
),
);