Files
Lumotia/src/lib/stores/profiles.svelte.ts

124 lines
3.5 KiB
TypeScript

import { invoke } from "@tauri-apps/api/core";
import { toasts } from "$lib/stores/toasts.svelte.ts";
import { errorMessage } from "$lib/utils/errors.js";
import { hasTauriRuntime } from "$lib/utils/runtime.js";
// Matches the SQLite-seeded Default profile UUID (see migration v6). Used as
// the in-memory default for `activeProfileId` on boot. Cross-session
// persistence of the selection is out of scope for Phase 2 — see Task 14
// plan notes.
export const DEFAULT_PROFILE_ID = "00000000-0000-0000-0000-000000000001";
// DTOs mirror the serde-camelCase shape emitted by the Tauri commands added
// in Task 12. Kept local to this store until a second consumer appears.
export interface ProfileDto {
id: string;
name: string;
initialPrompt: string;
createdAt: string;
}
export interface ProfileTermDto {
id: string;
profileId: string;
term: string;
note: string;
createdAt: string;
}
let profilesState = $state<ProfileDto[]>([]);
let activeProfileIdState = $state<string>(DEFAULT_PROFILE_ID);
export const profilesStore = {
get profiles(): ProfileDto[] {
return profilesState;
},
get activeProfileId(): string {
return activeProfileIdState;
},
get active(): ProfileDto | null {
return profilesState.find((p) => p.id === activeProfileIdState) ?? null;
},
async load(): Promise<void> {
if (!hasTauriRuntime()) return;
try {
profilesState = await invoke<ProfileDto[]>("list_profiles_cmd");
} catch (err) {
toasts.error("Failed to load profiles", errorMessage(err));
}
},
setActive(id: string): void {
if (profilesState.find((p) => p.id === id)) {
activeProfileIdState = id;
}
},
async create(name: string, initialPrompt: string): Promise<ProfileDto | null> {
if (!hasTauriRuntime()) return null;
try {
const row = await invoke<ProfileDto>("create_profile_cmd", {
name,
initialPrompt,
});
profilesState = [...profilesState, row];
return row;
} catch (err) {
toasts.error("Failed to create profile", errorMessage(err));
return null;
}
},
async update(id: string, name: string, initialPrompt: string): Promise<void> {
if (!hasTauriRuntime()) return;
try {
await invoke("update_profile_cmd", { id, name, initialPrompt });
profilesState = profilesState.map((p) =>
p.id === id ? { ...p, name, initialPrompt } : p,
);
} catch (err) {
toasts.error("Failed to update profile", errorMessage(err));
}
},
async delete(id: string): Promise<void> {
if (id === DEFAULT_PROFILE_ID) {
toasts.error("Default profile cannot be deleted");
return;
}
if (!hasTauriRuntime()) return;
try {
await invoke("delete_profile_cmd", { id });
profilesState = profilesState.filter((p) => p.id !== id);
if (activeProfileIdState === id) activeProfileIdState = DEFAULT_PROFILE_ID;
} catch (err) {
toasts.error("Failed to delete profile", errorMessage(err));
}
},
async listTerms(profileId: string): Promise<ProfileTermDto[]> {
if (!hasTauriRuntime()) return [];
return invoke<ProfileTermDto[]>("list_profile_terms_cmd", { profileId });
},
async addTerm(
profileId: string,
term: string,
note = "",
): Promise<ProfileTermDto | null> {
if (!hasTauriRuntime()) return null;
return invoke<ProfileTermDto>("add_profile_term_cmd", {
profileId,
term,
note,
});
},
async deleteTerm(termId: string): Promise<void> {
if (!hasTauriRuntime()) return;
await invoke("delete_profile_term_cmd", { id: termId });
},
};