aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/packages/ui/src/stores/settings-store.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/ui/src/stores/settings-store.ts')
-rw-r--r--packages/ui/src/stores/settings-store.ts568
1 files changed, 0 insertions, 568 deletions
diff --git a/packages/ui/src/stores/settings-store.ts b/packages/ui/src/stores/settings-store.ts
deleted file mode 100644
index 0bfc1e1..0000000
--- a/packages/ui/src/stores/settings-store.ts
+++ /dev/null
@@ -1,568 +0,0 @@
-import { convertFileSrc, invoke } from "@tauri-apps/api/core";
-import { listen, type UnlistenFn } from "@tauri-apps/api/event";
-import { toast } from "sonner";
-import { create } from "zustand";
-import { downloadAdoptiumJava } from "@/client";
-import type { ModelInfo } from "../types/bindings/assistant";
-import type { LauncherConfig } from "../types/bindings/config";
-import type {
- JavaDownloadProgress,
- PendingJavaDownload,
-} from "../types/bindings/downloader";
-import type {
- JavaCatalog,
- JavaInstallation,
- JavaReleaseInfo,
-} from "../types/bindings/java";
-
-type JavaDownloadSource = "adoptium" | "mojang" | "azul";
-
-/**
- * State shape for settings store.
- *
- * Note: Uses camelCase naming to match ts-rs generated bindings (which now use
- * `serde(rename_all = "camelCase")`). When reading raw binding objects from
- * invoke, convert/mapping should be applied where necessary.
- */
-interface SettingsState {
- // State
- settings: LauncherConfig;
- javaInstallations: JavaInstallation[];
- isDetectingJava: boolean;
- showJavaDownloadModal: boolean;
- selectedDownloadSource: JavaDownloadSource;
- javaCatalog: JavaCatalog | null;
- isLoadingCatalog: boolean;
- catalogError: string;
- selectedMajorVersion: number | null;
- selectedImageType: "jre" | "jdk";
- showOnlyRecommended: boolean;
- searchQuery: string;
- isDownloadingJava: boolean;
- downloadProgress: JavaDownloadProgress | null;
- javaDownloadStatus: string;
- pendingDownloads: PendingJavaDownload[];
- ollamaModels: ModelInfo[];
- openaiModels: ModelInfo[];
- isLoadingOllamaModels: boolean;
- isLoadingOpenaiModels: boolean;
- ollamaModelsError: string;
- openaiModelsError: string;
- showConfigEditor: boolean;
- rawConfigContent: string;
- configFilePath: string;
- configEditorError: string;
-
- // Computed / derived
- backgroundUrl: string | undefined;
- filteredReleases: JavaReleaseInfo[];
- availableMajorVersions: number[];
- installStatus: (
- version: number,
- imageType: string,
- ) => "installed" | "downloading" | "available";
- selectedRelease: JavaReleaseInfo | null;
- currentModelOptions: Array<{
- value: string;
- label: string;
- details?: string;
- }>;
-
- // Actions
- loadSettings: () => Promise<void>;
- saveSettings: () => Promise<void>;
- // compatibility helper to mirror the older set({ key: value }) usage
- set: (patch: Partial<Record<string, unknown>>) => void;
-
- detectJava: () => Promise<void>;
- selectJava: (path: string) => void;
-
- openJavaDownloadModal: () => Promise<void>;
- closeJavaDownloadModal: () => void;
- loadJavaCatalog: (forceRefresh: boolean) => Promise<void>;
- refreshCatalog: () => Promise<void>;
- loadPendingDownloads: () => Promise<void>;
- selectMajorVersion: (version: number) => void;
- downloadJava: () => Promise<void>;
- cancelDownload: () => Promise<void>;
- resumeDownloads: () => Promise<void>;
-
- openConfigEditor: () => Promise<void>;
- closeConfigEditor: () => void;
- saveRawConfig: () => Promise<void>;
-
- loadOllamaModels: () => Promise<void>;
- loadOpenaiModels: () => Promise<void>;
-
- setSetting: <K extends keyof LauncherConfig>(
- key: K,
- value: LauncherConfig[K],
- ) => void;
- setAssistantSetting: <K extends keyof LauncherConfig["assistant"]>(
- key: K,
- value: LauncherConfig["assistant"][K],
- ) => void;
- setFeatureFlag: <K extends keyof LauncherConfig["featureFlags"]>(
- key: K,
- value: LauncherConfig["featureFlags"][K],
- ) => void;
-
- // Private
- progressUnlisten: UnlistenFn | null;
-}
-
-/**
- * Default settings (camelCase) — lightweight defaults used until `get_settings`
- * returns real values.
- */
-const defaultSettings: LauncherConfig = {
- minMemory: 1024,
- maxMemory: 2048,
- javaPath: "java",
- width: 854,
- height: 480,
- downloadThreads: 32,
- enableGpuAcceleration: false,
- enableVisualEffects: true,
- activeEffect: "constellation",
- theme: "dark",
- customBackgroundPath: null,
- logUploadService: "paste.rs",
- pastebinApiKey: null,
- assistant: {
- enabled: true,
- llmProvider: "ollama",
- ollamaEndpoint: "http://localhost:11434",
- ollamaModel: "llama3",
- openaiApiKey: null,
- openaiEndpoint: "https://api.openai.com/v1",
- openaiModel: "gpt-3.5-turbo",
- systemPrompt:
- "You are a helpful Minecraft expert assistant. You help players with game issues, mod installation, performance optimization, and gameplay tips. Analyze any game logs provided and give concise, actionable advice.",
- responseLanguage: "auto",
- ttsEnabled: false,
- ttsProvider: "disabled",
- },
- useSharedCaches: false,
- keepLegacyPerInstanceStorage: true,
- featureFlags: {
- demoUser: false,
- quickPlayEnabled: false,
- quickPlayPath: null,
- quickPlaySingleplayer: true,
- quickPlayMultiplayerServer: null,
- },
-};
-
-export const useSettingsStore = create<SettingsState>((set, get) => ({
- // initial state
- settings: defaultSettings,
- javaInstallations: [],
- isDetectingJava: false,
- showJavaDownloadModal: false,
- selectedDownloadSource: "adoptium",
- javaCatalog: null,
- isLoadingCatalog: false,
- catalogError: "",
- selectedMajorVersion: null,
- selectedImageType: "jre",
- showOnlyRecommended: true,
- searchQuery: "",
- isDownloadingJava: false,
- downloadProgress: null,
- javaDownloadStatus: "",
- pendingDownloads: [],
- ollamaModels: [],
- openaiModels: [],
- isLoadingOllamaModels: false,
- isLoadingOpenaiModels: false,
- ollamaModelsError: "",
- openaiModelsError: "",
- showConfigEditor: false,
- rawConfigContent: "",
- configFilePath: "",
- configEditorError: "",
- progressUnlisten: null,
-
- // derived getters
- get backgroundUrl() {
- const { settings } = get();
- if (settings.customBackgroundPath) {
- return convertFileSrc(settings.customBackgroundPath);
- }
- return undefined;
- },
-
- get filteredReleases() {
- const {
- javaCatalog,
- selectedMajorVersion,
- selectedImageType,
- showOnlyRecommended,
- searchQuery,
- } = get();
-
- if (!javaCatalog) return [];
-
- let releases = javaCatalog.releases;
-
- if (selectedMajorVersion !== null) {
- releases = releases.filter(
- (r) => r.majorVersion === selectedMajorVersion,
- );
- }
-
- releases = releases.filter((r) => r.imageType === selectedImageType);
-
- if (showOnlyRecommended) {
- releases = releases.filter((r) => r.isLts);
- }
-
- if (searchQuery.trim() !== "") {
- const q = searchQuery.toLowerCase();
- releases = releases.filter(
- (r) =>
- r.version.toLowerCase().includes(q) ||
- (r.releaseName ?? "").toLowerCase().includes(q),
- );
- }
-
- // sort newest-first by parsed version number
- return releases.sort((a, b) => {
- const aVer = parseFloat(a.version.split("-")[0]);
- const bVer = parseFloat(b.version.split("-")[0]);
- return bVer - aVer;
- });
- },
-
- get availableMajorVersions() {
- return get().javaCatalog?.availableMajorVersions || [];
- },
-
- installStatus: (version: number, imageType: string) => {
- const {
- javaInstallations,
- pendingDownloads,
- isDownloadingJava,
- downloadProgress,
- } = get();
-
- const installed = javaInstallations.some(
- (inst) => parseInt(inst.version.split(".")[0], 10) === version,
- );
- if (installed) return "installed";
-
- if (
- isDownloadingJava &&
- downloadProgress?.fileName?.includes(`${version}`)
- ) {
- return "downloading";
- }
-
- const pending = pendingDownloads.some(
- (d) => d.majorVersion === version && d.imageType === imageType,
- );
- if (pending) return "downloading";
-
- return "available";
- },
-
- get selectedRelease() {
- const { javaCatalog, selectedMajorVersion, selectedImageType } = get();
- if (!javaCatalog || selectedMajorVersion === null) return null;
- return (
- javaCatalog.releases.find(
- (r) =>
- r.majorVersion === selectedMajorVersion &&
- r.imageType === selectedImageType,
- ) || null
- );
- },
-
- get currentModelOptions() {
- const { settings, ollamaModels, openaiModels } = get();
- const provider = settings.assistant.llmProvider;
- if (provider === "ollama") {
- return ollamaModels.map((m) => ({
- value: m.id,
- label: m.name,
- details: m.details || m.size || "",
- }));
- } else {
- return openaiModels.map((m) => ({
- value: m.id,
- label: m.name,
- details: m.details || "",
- }));
- }
- },
-
- // actions
- loadSettings: async () => {
- try {
- const result = await invoke<LauncherConfig>("get_settings");
- // result already uses camelCase fields from bindings
- set({ settings: result });
-
- // enforce dark theme at app-level if necessary
- if (result.theme !== "dark") {
- const updated = { ...result, theme: "dark" } as LauncherConfig;
- set({ settings: updated });
- await invoke("save_settings", { config: updated });
- }
-
- // ensure customBackgroundPath is undefined rather than null for reactiveness
- if (!result.customBackgroundPath) {
- set((s) => ({
- settings: { ...s.settings, customBackgroundPath: null },
- }));
- }
- } catch (e) {
- console.error("Failed to load settings:", e);
- }
- },
-
- saveSettings: async () => {
- try {
- const { settings } = get();
-
- // Clean up empty strings to null where appropriate
- if ((settings.customBackgroundPath ?? "") === "") {
- set((state) => ({
- settings: { ...state.settings, customBackgroundPath: null },
- }));
- }
-
- await invoke("save_settings", { config: settings });
- toast.success("Settings saved!");
- } catch (e) {
- console.error("Failed to save settings:", e);
- toast.error(`Error saving settings: ${String(e)}`);
- }
- },
-
- set: (patch: Partial<Record<string, unknown>>) => {
- set(patch);
- },
-
- detectJava: async () => {
- set({ isDetectingJava: true });
- try {
- const installs = await invoke<JavaInstallation[]>("detect_java");
- set({ javaInstallations: installs });
- if (installs.length === 0) toast.info("No Java installations found");
- else toast.success(`Found ${installs.length} Java installation(s)`);
- } catch (e) {
- console.error("Failed to detect Java:", e);
- toast.error(`Error detecting Java: ${String(e)}`);
- } finally {
- set({ isDetectingJava: false });
- }
- },
-
- selectJava: (path: string) => {
- set((s) => ({ settings: { ...s.settings, javaPath: path } }));
- },
-
- openJavaDownloadModal: async () => {
- set({
- showJavaDownloadModal: true,
- javaDownloadStatus: "",
- catalogError: "",
- downloadProgress: null,
- });
-
- // attach event listener for download progress
- const state = get();
- if (state.progressUnlisten) {
- state.progressUnlisten();
- }
-
- const unlisten = await listen<JavaDownloadProgress>(
- "java-download-progress",
- (event) => {
- set({ downloadProgress: event.payload });
- },
- );
-
- set({ progressUnlisten: unlisten });
-
- // load catalog and pending downloads
- await get().loadJavaCatalog(false);
- await get().loadPendingDownloads();
- },
-
- closeJavaDownloadModal: () => {
- const { isDownloadingJava, progressUnlisten } = get();
-
- if (!isDownloadingJava) {
- set({ showJavaDownloadModal: false });
- if (progressUnlisten) {
- try {
- progressUnlisten();
- } catch {
- // ignore
- }
- set({ progressUnlisten: null });
- }
- }
- },
-
- loadJavaCatalog: async (forceRefresh: boolean) => {
- set({ isLoadingCatalog: true, catalogError: "" });
- try {
- const cmd = forceRefresh ? "refresh_java_catalog" : "get_java_catalog";
- const result = await invoke<JavaCatalog>(cmd);
- set({ javaCatalog: result, isLoadingCatalog: false });
- } catch (e) {
- console.error("Failed to load Java catalog:", e);
- set({ catalogError: String(e), isLoadingCatalog: false });
- }
- },
-
- refreshCatalog: async () => {
- await get().loadJavaCatalog(true);
- },
-
- loadPendingDownloads: async () => {
- try {
- const pending = await invoke<PendingJavaDownload[]>(
- "get_pending_java_downloads",
- );
- set({ pendingDownloads: pending });
- } catch (e) {
- console.error("Failed to load pending downloads:", e);
- }
- },
-
- selectMajorVersion: (version: number) => {
- set({ selectedMajorVersion: version });
- },
-
- downloadJava: async () => {
- const { selectedMajorVersion, selectedImageType, selectedDownloadSource } =
- get();
- if (!selectedMajorVersion) return;
- set({ isDownloadingJava: true, javaDownloadStatus: "Starting..." });
- try {
- const result = await downloadAdoptiumJava(
- selectedMajorVersion,
- selectedImageType,
- selectedDownloadSource,
- );
- set({
- javaDownloadStatus: `Java ${selectedMajorVersion} download started: ${result.path}`,
- });
- toast.success("Download started");
- } catch (e) {
- console.error("Failed to download Java:", e);
- toast.error(`Failed to start Java download: ${String(e)}`);
- } finally {
- set({ isDownloadingJava: false });
- }
- },
-
- cancelDownload: async () => {
- try {
- await invoke("cancel_java_download");
- toast.success("Cancelled Java download");
- set({ isDownloadingJava: false, javaDownloadStatus: "" });
- } catch (e) {
- console.error("Failed to cancel download:", e);
- toast.error(`Failed to cancel download: ${String(e)}`);
- }
- },
-
- resumeDownloads: async () => {
- try {
- const installed = await invoke<boolean>("resume_java_downloads");
- if (installed) toast.success("Resumed Java downloads");
- else toast.info("No downloads to resume");
- } catch (e) {
- console.error("Failed to resume downloads:", e);
- toast.error(`Failed to resume downloads: ${String(e)}`);
- }
- },
-
- openConfigEditor: async () => {
- try {
- const path = await invoke<string>("get_config_path");
- const content = await invoke<string>("read_config_raw");
- set({
- configFilePath: path,
- rawConfigContent: content,
- showConfigEditor: true,
- });
- } catch (e) {
- console.error("Failed to open config editor:", e);
- set({ configEditorError: String(e) });
- }
- },
-
- closeConfigEditor: () => {
- set({
- showConfigEditor: false,
- rawConfigContent: "",
- configFilePath: "",
- configEditorError: "",
- });
- },
-
- saveRawConfig: async () => {
- try {
- await invoke("write_config_raw", { content: get().rawConfigContent });
- toast.success("Config saved");
- set({ showConfigEditor: false });
- } catch (e) {
- console.error("Failed to save config:", e);
- set({ configEditorError: String(e) });
- toast.error(`Failed to save config: ${String(e)}`);
- }
- },
-
- loadOllamaModels: async () => {
- set({ isLoadingOllamaModels: true, ollamaModelsError: "" });
- try {
- const models = await invoke<ModelInfo[]>("get_ollama_models");
- set({ ollamaModels: models, isLoadingOllamaModels: false });
- } catch (e) {
- console.error("Failed to load Ollama models:", e);
- set({ isLoadingOllamaModels: false, ollamaModelsError: String(e) });
- }
- },
-
- loadOpenaiModels: async () => {
- set({ isLoadingOpenaiModels: true, openaiModelsError: "" });
- try {
- const models = await invoke<ModelInfo[]>("get_openai_models");
- set({ openaiModels: models, isLoadingOpenaiModels: false });
- } catch (e) {
- console.error("Failed to load OpenAI models:", e);
- set({ isLoadingOpenaiModels: false, openaiModelsError: String(e) });
- }
- },
-
- setSetting: (key, value) => {
- set((s) => ({
- settings: { ...s.settings, [key]: value } as unknown as LauncherConfig,
- }));
- },
-
- setAssistantSetting: (key, value) => {
- set((s) => ({
- settings: {
- ...s.settings,
- assistant: { ...s.settings.assistant, [key]: value },
- } as LauncherConfig,
- }));
- },
-
- setFeatureFlag: (key, value) => {
- set((s) => ({
- settings: {
- ...s.settings,
- featureFlags: { ...s.settings.featureFlags, [key]: value },
- } as LauncherConfig,
- }));
- },
-}));