mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-09-19 17:24:57 +02:00
ui : extract settings localStorage persistence into SettingsService
Stateless load/save of the settings config and user-override keys, plus the legacy theme key migration. Business logic (default merging, mobile sendOnEnter default, applying the migrated theme) stays in the store.
This commit is contained in:
@@ -340,3 +340,13 @@ export { RouterService } from './router.service';
|
||||
* @see migration.service.ts — full implementation (non-destructive)
|
||||
*/
|
||||
export { MigrationService } from './migration.service';
|
||||
|
||||
/**
|
||||
* **SettingsService** - localStorage persistence layer for settings
|
||||
*
|
||||
* Stateless read/write of the settings config and user-override keys. Business
|
||||
* logic (default merging, mobile defaults, theme migration) stays in the store.
|
||||
*
|
||||
* @see settingsStore in stores/settings/index.svelte.ts - reactive state + business logic
|
||||
*/
|
||||
export { SettingsService } from './settings.service';
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { CONFIG_LOCALSTORAGE_KEY, USER_OVERRIDES_LOCALSTORAGE_KEY } from '$lib/constants';
|
||||
|
||||
/**
|
||||
* SettingsService - localStorage persistence layer for settings
|
||||
*
|
||||
* Stateless read/write of the settings config and user-override keys. Business
|
||||
* logic (default merging, mobile defaults, theme migration) stays in the store.
|
||||
*
|
||||
* **Architecture & Relationships:**
|
||||
* - **settingsStore**: Primary consumer - loads config on init and persists on change
|
||||
*
|
||||
* @see settingsStore in stores/settings/index.svelte.ts - reactive state + business logic
|
||||
*/
|
||||
export class SettingsService {
|
||||
/**
|
||||
* Read the raw config and user overrides from localStorage.
|
||||
* @returns Parsed values, or empty defaults when nothing is stored or parsing fails.
|
||||
*/
|
||||
static loadConfig(): {
|
||||
config: Record<string, unknown>;
|
||||
userOverrides: string[];
|
||||
isFirstVisit: boolean;
|
||||
} {
|
||||
if (!browser) {
|
||||
return { config: {}, isFirstVisit: false, userOverrides: [] };
|
||||
}
|
||||
|
||||
try {
|
||||
const storedConfigRaw = localStorage.getItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
const isFirstVisit = storedConfigRaw === null;
|
||||
const config = JSON.parse(storedConfigRaw || '{}') as Record<string, unknown>;
|
||||
const userOverrides = JSON.parse(
|
||||
localStorage.getItem(USER_OVERRIDES_LOCALSTORAGE_KEY) || '[]'
|
||||
) as string[];
|
||||
|
||||
return { config, isFirstVisit, userOverrides };
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse config from localStorage, using defaults:', error);
|
||||
|
||||
return { config: {}, isFirstVisit: false, userOverrides: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the legacy un-namespaced "theme" localStorage key.
|
||||
* Returns the legacy theme value (and removes the key) when present, else null.
|
||||
*/
|
||||
static migrateLegacyTheme(): string | null {
|
||||
if (!browser) return null;
|
||||
|
||||
const legacyTheme = localStorage.getItem('theme');
|
||||
|
||||
if (legacyTheme) {
|
||||
localStorage.removeItem('theme');
|
||||
|
||||
return legacyTheme;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the config and user overrides to localStorage.
|
||||
*/
|
||||
static saveConfig(config: Record<string, unknown>, userOverrides: string[]): void {
|
||||
if (!browser) return;
|
||||
|
||||
try {
|
||||
localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(config));
|
||||
localStorage.setItem(USER_OVERRIDES_LOCALSTORAGE_KEY, JSON.stringify(userOverrides));
|
||||
} catch (error) {
|
||||
console.error('Failed to save config to localStorage:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,10 @@
|
||||
*/
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import {
|
||||
CONFIG_LOCALSTORAGE_KEY,
|
||||
SETTING_CONFIG_DEFAULT,
|
||||
SETTINGS_KEYS,
|
||||
USER_OVERRIDES_LOCALSTORAGE_KEY
|
||||
} from '$lib/constants';
|
||||
import { SETTING_CONFIG_DEFAULT, SETTINGS_KEYS } from '$lib/constants';
|
||||
import { ColorMode } from '$lib/enums';
|
||||
import { ParameterSyncService } from '$lib/services/parameter-sync.service';
|
||||
import { SettingsService } from '$lib/services/settings.service';
|
||||
import { deviceStore } from '$lib/stores/device.svelte';
|
||||
// direct imports between stores, not via the barrel, to avoid circular deps
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
@@ -428,45 +424,37 @@ class SettingsStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from localStorage
|
||||
* Returns default values for missing keys to prevent breaking changes
|
||||
* Load configuration from localStorage via the persistence service.
|
||||
* Returns default values for missing keys to prevent breaking changes.
|
||||
*/
|
||||
private loadConfig() {
|
||||
if (!browser) return;
|
||||
|
||||
try {
|
||||
const storedConfigRaw = localStorage.getItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
const {
|
||||
config: savedVal,
|
||||
isFirstVisit,
|
||||
userOverrides: savedOverrides
|
||||
} = SettingsService.loadConfig();
|
||||
|
||||
// First visit: no stored config yet. Server ui_settings apply once in
|
||||
// this state, then the user's config diverges freely.
|
||||
this.isFirstVisit = storedConfigRaw === null;
|
||||
// First visit: no stored config yet. Server ui_settings apply once in
|
||||
// this state, then the user's config diverges freely.
|
||||
this.isFirstVisit = isFirstVisit;
|
||||
|
||||
const savedVal = JSON.parse(storedConfigRaw || '{}');
|
||||
// Merge with defaults to prevent breaking changes
|
||||
this.config = {
|
||||
...SETTING_CONFIG_DEFAULT,
|
||||
...savedVal
|
||||
};
|
||||
|
||||
// Merge with defaults to prevent breaking changes
|
||||
this.config = {
|
||||
...SETTING_CONFIG_DEFAULT,
|
||||
...savedVal
|
||||
};
|
||||
|
||||
// Default sendOnEnter to false on mobile when the user has no saved preference
|
||||
if (!(SETTINGS_KEYS.SEND_ON_ENTER in savedVal)) {
|
||||
if (deviceStore.isMobile) {
|
||||
this.config[SETTINGS_KEYS.SEND_ON_ENTER] = false;
|
||||
}
|
||||
// Default sendOnEnter to false on mobile when the user has no saved preference
|
||||
if (!(SETTINGS_KEYS.SEND_ON_ENTER in savedVal)) {
|
||||
if (deviceStore.isMobile) {
|
||||
this.config[SETTINGS_KEYS.SEND_ON_ENTER] = false;
|
||||
}
|
||||
|
||||
// Load user overrides
|
||||
const savedOverrides = JSON.parse(
|
||||
localStorage.getItem(USER_OVERRIDES_LOCALSTORAGE_KEY) || '[]'
|
||||
);
|
||||
|
||||
this.userOverrides = new Set(savedOverrides);
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse config from localStorage, using defaults:', error);
|
||||
this.config = { ...SETTING_CONFIG_DEFAULT };
|
||||
this.userOverrides = new Set();
|
||||
}
|
||||
|
||||
// Load user overrides
|
||||
this.userOverrides = new Set(savedOverrides);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -478,32 +466,22 @@ class SettingsStore {
|
||||
private migrateLegacyTheme() {
|
||||
if (!browser) return;
|
||||
|
||||
const legacyTheme = localStorage.getItem('theme');
|
||||
const legacyTheme = SettingsService.migrateLegacyTheme();
|
||||
|
||||
if (legacyTheme) {
|
||||
this.config[SETTINGS_KEYS.THEME] = legacyTheme;
|
||||
localStorage.removeItem('theme');
|
||||
this.saveConfig();
|
||||
setMode(legacyTheme as ColorMode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current configuration to localStorage
|
||||
* Save the current configuration to localStorage via the persistence service.
|
||||
*/
|
||||
private saveConfig() {
|
||||
if (!browser) return;
|
||||
|
||||
try {
|
||||
localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(this.config));
|
||||
|
||||
localStorage.setItem(
|
||||
USER_OVERRIDES_LOCALSTORAGE_KEY,
|
||||
JSON.stringify(Array.from(this.userOverrides))
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to save config to localStorage:', error);
|
||||
}
|
||||
SettingsService.saveConfig(this.config, Array.from(this.userOverrides));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user