ui: Stores architecture improvements (#26910)

* refactor: Stores barrel imports + SSR gates

* refactor: Drop agenticStore wrapper exports

* refactor: Drop chatStore wrapper exports

* refactor: Drop modelsStore wrapper exports

* refactor: Drop serverStore wrapper exports

* refactor: Drop unused mcpStore wrapper exports

* refactor: Drop mcpResourceStore wrapper exports

* refactor: Drop conversationsStore wrapper exports + move buildConversationTree to utils

* refactor: Drop settingsStore wrapper exports

* refactor: Drop unused toolsStore wrapper exports

* refactor: Fix lint errors from store wrapper removal

* fix: Missing change

* refactor: Cleanup

* refactor: Context Stats store
This commit is contained in:
Aleksander Grygier
2026-08-13 08:11:30 +02:00
committed by GitHub
parent a6040c925c
commit 094e53db1c
116 changed files with 937 additions and 945 deletions
+8 -7
View File
@@ -3,10 +3,7 @@
import { page } from '$app/state';
import { DialogModelNotAvailable } from '$lib/components/app';
import { APP_NAME, URL_PARAMS } from '$lib/constants';
import { chatStore } from '$lib/stores/chat.svelte';
import { conversationsStore, isConversationsInitialized } from '$lib/stores/conversations.svelte';
import { modelOptions, modelsStore } from '$lib/stores/models.svelte';
import { isRouterMode } from '$lib/stores/server.svelte';
import { chatStore, conversationsStore, modelsStore, serverStore } from '$lib/stores';
import { onMount } from 'svelte';
let qParam = $derived(page.url.searchParams.get(URL_PARAMS.QUERY));
@@ -17,7 +14,7 @@
// Dialog state for model not available error
let showModelNotAvailable = $state(false);
let requestedModelName = $state('');
let availableModelNames = $derived(modelOptions().map((m) => m.model));
let availableModelNames = $derived(modelsStore.models.map((m) => m.model));
/**
* Clear URL params after message is sent to prevent re-sending on refresh
@@ -45,7 +42,11 @@
// with ?load=true, start loading right away so the model is ready sooner;
// not awaited, so the UI stays usable during the load
if (loadParam === 'true' && isRouterMode() && !modelsStore.isModelLoaded(model.id)) {
if (
loadParam === 'true' &&
serverStore.isRouterMode &&
!modelsStore.isModelLoaded(model.id)
) {
modelsStore
.loadModel(model.id)
.catch((error) => console.error('Failed to load model:', error));
@@ -75,7 +76,7 @@
}
onMount(async () => {
if (!isConversationsInitialized()) {
if (!conversationsStore.isInitialized) {
await conversationsStore.initialize();
}
@@ -4,9 +4,7 @@
import { page } from '$app/state';
import { DialogModelNotAvailable } from '$lib/components/app';
import { APP_NAME, ROUTES, URL_PARAMS } from '$lib/constants';
import { chatStore } from '$lib/stores/chat.svelte';
import { activeConversation, conversationsStore } from '$lib/stores/conversations.svelte';
import { modelOptions, modelsStore } from '$lib/stores/models.svelte';
import { chatStore, conversationsStore, modelsStore } from '$lib/stores';
let chatId = $derived(page.params.id);
let currentChatId: string | undefined = undefined;
@@ -18,7 +16,7 @@
// Dialog state for model not available error
let showModelNotAvailable = $state(false);
let requestedModelName = $state('');
let availableModelNames = $derived(modelOptions().map((m) => m.model));
let availableModelNames = $derived(modelsStore.models.map((m) => m.model));
// Track if URL params have been processed for this chat
let urlParamsProcessed = $state(false);
@@ -86,7 +84,7 @@
urlParamsProcessed = false; // Reset for new chat
// Skip loading if this conversation is already active (e.g., just created)
if (activeConversation()?.id === chatId) {
if (conversationsStore.activeConversation?.id === chatId) {
void chatStore.discoverActiveStream(chatId);
if ((qParam !== null || modelParam !== null) && !urlParamsProcessed) {
@@ -136,7 +134,7 @@
</script>
<svelte:head>
<title>{activeConversation()?.name || 'Chat'} - {APP_NAME}</title>
<title>{conversationsStore.activeConversation?.name || 'Chat'} - {APP_NAME}</title>
</svelte:head>
<DialogModelNotAvailable
+20 -16
View File
@@ -18,15 +18,17 @@
import { useKeyboardShortcuts } from '$lib/hooks/use-keyboard-shortcuts.svelte';
import { usePwa } from '$lib/hooks/use-pwa.svelte';
import { RouterService } from '$lib/services/router.service';
import { buildInfoStore } from '$lib/stores/build-info.svelte';
import { chatStore } from '$lib/stores/chat.svelte';
import { conversations } from '$lib/stores/conversations.svelte';
import { mcpStore } from '$lib/stores/mcp.svelte';
import { modelsStore } from '$lib/stores/models.svelte';
import { isRouterMode, serverStore } from '$lib/stores/server.svelte';
import { config, settingsStore } from '$lib/stores/settings.svelte';
import { theme } from '$lib/stores/theme.svelte';
import { isMobile } from '$lib/stores/viewport.svelte';
import {
buildInfoStore,
chatStore,
conversationsStore,
isMobile,
mcpStore,
modelsStore,
serverStore,
settingsStore,
theme
} from '$lib/stores';
import { ModeWatcher } from 'mode-watcher';
import { untrack } from 'svelte';
import { onMount } from 'svelte';
@@ -44,7 +46,9 @@
}
| undefined = $state();
let showBuildVersion = $derived(config()[SETTINGS_KEYS.SHOW_BUILD_VERSION] as boolean);
let showBuildVersion = $derived(
settingsStore.config[SETTINGS_KEYS.SHOW_BUILD_VERSION] as boolean
);
// Keep the hook object intact: destructuring needRefreshByStorage reads the getter once and freezes it
const pwa = usePwa();
@@ -67,7 +71,7 @@
}
function navigateToConversation(direction: -1 | 1) {
const allConvs = conversations();
const allConvs = conversationsStore.conversations;
if (allConvs.length === 0) return;
@@ -100,7 +104,7 @@
});
function checkApiKey() {
const apiKey = config().apiKey;
const apiKey = settingsStore.config.apiKey;
// Without a stored key there is nothing to re-validate here; the keyless
// 401 case is handled by validateApiKey() at navigation time, and the
@@ -179,7 +183,7 @@
// textContent keeps the value as text, never parsed as HTML
function customCss(node: HTMLStyleElement) {
$effect(() => {
node.textContent = (config().customCss as string | undefined) ?? '';
node.textContent = (settingsStore.config.customCss as string | undefined) ?? '';
});
}
@@ -188,7 +192,7 @@
let routerModelsFetched = false;
$effect(() => {
const isRouter = isRouterMode();
const isRouter = serverStore.isRouterMode;
const modelsCount = modelsStore.models.length;
// Only fetch router models once when we have models loaded and in router mode
@@ -205,7 +209,7 @@
$effect(() => {
if (!browser) return;
if (!isRouterMode()) return;
if (!serverStore.isRouterMode) return;
untrack(() => {
modelsStore.subscribeStatus();
@@ -252,7 +256,7 @@
<meta name="theme-color" content={pwaAssetsHead.themeColor.content} />
{/if}
{#if config().customCss}
{#if settingsStore.config.customCss}
<style use:customCss></style>
{/if}
+4 -6
View File
@@ -5,9 +5,7 @@
import { SearchInput, SidebarNavigationSearchResults } from '$lib/components/app';
import { ROUTES } from '$lib/constants';
import { RouterService } from '$lib/services/router.service';
import { chatStore } from '$lib/stores/chat.svelte';
import { conversations, conversationsStore } from '$lib/stores/conversations.svelte';
import { isMobile } from '$lib/stores/viewport.svelte';
import { chatStore, conversationsStore, isMobile } from '$lib/stores';
let searchQuery = $state('');
let searchInputRef = $state<HTMLInputElement | null>(null);
@@ -19,7 +17,7 @@
if (query.length === 0) return [];
return conversations().filter((c) => c.name.toLowerCase().includes(query));
return conversationsStore.conversations.filter((c) => c.name.toLowerCase().includes(query));
});
// Search page is intended for mobile; on desktop the sidebar already exposes
@@ -35,7 +33,7 @@
}
async function handleEditConversation(id: string) {
const conversation = conversations().find((c) => c.id === id);
const conversation = conversationsStore.conversations.find((c) => c.id === id);
if (!conversation) return;
@@ -47,7 +45,7 @@
}
async function handleDeleteConversation(id: string) {
const conversation = conversations().find((c) => c.id === id);
const conversation = conversationsStore.conversations.find((c) => c.id === id);
if (!conversation) return;